src/eric7/Utilities/FtpUtilities.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2012 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing some FTP related utilities.
8 """
9
10 import os
11
12 from PyQt6.QtCore import QObject, QDate, QDateTime, QTime
13
14 from EricNetwork.EricUrlInfo import EricUrlInfo, EricUrlPermission
15
16
17 class FtpDirLineParserError(Exception):
18 """
19 Exception class raised, if a parser issue was detected.
20 """
21 pass
22
23
24 class FtpDirLineParser(QObject):
25 """
26 Class to parse lines returned by a FTP LIST command.
27 """
28 MonthnamesNumbers = {
29 "jan": 1,
30 "feb": 2,
31 "mar": 3,
32 "apr": 4,
33 "may": 5,
34 "jun": 6,
35 "jul": 7,
36 "aug": 8,
37 "sep": 9,
38 "oct": 10,
39 "nov": 11,
40 "dec": 12,
41 }
42
43 def __init__(self, parent=None):
44 """
45 Constructor
46
47 @param parent reference to the parent object (QObject)
48 """
49 super().__init__(parent)
50
51 self.__parseLine = self.__parseUnixLine
52 self.__modeSwitchAllowed = True
53
54 def __ignoreLine(self, line):
55 """
56 Private method to check, if the line should be ignored.
57
58 @param line to check (string)
59 @return flag indicating to ignore the line (boolean)
60 """
61 return (
62 line.strip() == "" or
63 line.strip().lower().startswith("total ")
64 )
65
66 def __parseUnixMode(self, modeString, urlInfo):
67 """
68 Private method to parse a Unix mode string modifying the
69 given URL info object.
70
71 @param modeString mode string to be parsed (string)
72 @param urlInfo reference to the URL info object (EricUrlInfo)
73 @exception FtpDirLineParserError Raised if the mode cannot be parsed.
74 """
75 if len(modeString) != 10:
76 raise FtpDirLineParserError(
77 "invalid mode string '{0}'".format(modeString))
78
79 modeString = modeString.lower()
80
81 permission = 0
82 if modeString[1] != '-':
83 permission |= EricUrlPermission.READ_OWNER
84 if modeString[2] != '-':
85 permission |= EricUrlPermission.WRITE_OWNER
86 if modeString[3] != '-':
87 permission |= EricUrlPermission.EXE_OWNER
88 if modeString[4] != '-':
89 permission |= EricUrlPermission.READ_GROUP
90 if modeString[5] != '-':
91 permission |= EricUrlPermission.WRITE_GROUP
92 if modeString[6] != '-':
93 permission |= EricUrlPermission.EXE_GROUP
94 if modeString[7] != '-':
95 permission |= EricUrlPermission.READ_OTHER
96 if modeString[8] != '-':
97 permission |= EricUrlPermission.WRITE_OTHER
98 if modeString[9] != '-':
99 permission |= EricUrlPermission.EXE_OTHER
100 urlInfo.setPermissions(permission)
101
102 if modeString[0] == "d":
103 urlInfo.setDir(True)
104 urlInfo.setFile(False)
105 urlInfo.setSymLink(False)
106 elif modeString[0] == "l":
107 urlInfo.setDir(True)
108 urlInfo.setFile(False)
109 urlInfo.setSymLink(True)
110 elif modeString[0] == "-":
111 urlInfo.setDir(False)
112 urlInfo.setFile(True)
113 urlInfo.setSymLink(False)
114
115 def __parseUnixTime(self, monthAbbreviation, day, yearOrTime, urlInfo):
116 """
117 Private method to parse a Unix date and time indication modifying
118 the given URL info object.
119
120
121 Date time strings in Unix-style directory listings typically
122 have one of these formats:
123 <ul>
124 <li>"Nov 23 02:33" (month name, day of month, time)</li>
125 <li>"May 26 2005" (month name, day of month, year)</li>
126 </ul>
127
128 @param monthAbbreviation abbreviation of the month name (string)
129 @param day day of the month (string)
130 @param yearOrTime string giving the year or a time (string)
131 @param urlInfo reference to the URL info object (EricUrlInfo)
132 @exception FtpDirLineParserError Raised if the month abbreviation is
133 not recognized.
134 """
135 try:
136 month = FtpDirLineParser.MonthnamesNumbers[
137 monthAbbreviation.lower()]
138 except KeyError:
139 raise FtpDirLineParserError(
140 "illegal month abbreviation '{0}'".format(
141 monthAbbreviation))
142 day = int(day)
143 if ':' in yearOrTime:
144 year = QDate.currentDate().year()
145 hour, minute = yearOrTime.split(':')
146 hour = int(hour)
147 minute = int(minute)
148 else:
149 year = int(yearOrTime)
150 hour = 0
151 minute = 0
152
153 lastModified = QDateTime(QDate(year, month, day), QTime(hour, minute))
154 urlInfo.setLastModified(lastModified)
155
156 def __splitUnixLine(self, line):
157 """
158 Private method to split a line of a Unix like directory listing.
159
160 It splits the line into meta data, number of links, user, group, size,
161 month, day, year or time and name.
162
163 @param line directory line to split (string)
164 @return tuple of nine strings giving the meta data,
165 number of links, user, group, size, month, day, year or time
166 and name
167 @exception FtpDirLineParserError Raised if the line is not of a
168 recognized Unix format.
169 """
170 # This method encapsulates the recognition of an unusual
171 # Unix format variant.
172 lineParts = line.split()
173 fieldCountWithoutUserID = 8
174 fieldCountWithUserID = fieldCountWithoutUserID + 1
175 if len(lineParts) < fieldCountWithoutUserID:
176 raise FtpDirLineParserError(
177 "line '{0}' cannot be parsed".format(line))
178
179 # If we have a valid format (either with or without user id field),
180 # the field with index 5 is either the month abbreviation or a day.
181 try:
182 int(lineParts[5])
183 except ValueError:
184 # Month abbreviation, "invalid literal for int"
185 lineParts = line.split(None, fieldCountWithUserID - 1)
186 else:
187 # Day
188 lineParts = line.split(None, fieldCountWithoutUserID - 1)
189 userFieldIndex = 2
190 lineParts.insert(userFieldIndex, "")
191
192 return lineParts
193
194 def __parseUnixLine(self, line):
195 """
196 Private method to parse a Unix style directory listing line.
197
198 @param line directory line to be parsed (string)
199 @return URL info object containing the valid data (EricUrlInfo)
200 """
201 modeString, nlink, user, group, size, month, day, yearOrTime, name = (
202 self.__splitUnixLine(line)
203 )
204
205 if name in [".", ".."]:
206 return None
207
208 urlInfo = EricUrlInfo()
209 self.__parseUnixMode(modeString, urlInfo)
210 self.__parseUnixTime(month, day, yearOrTime, urlInfo)
211 urlInfo.setOwner(user)
212 urlInfo.setGroup(group)
213 urlInfo.setSize(int(size))
214 name = name.strip()
215 i = name.find(" -> ")
216 if i >= 0:
217 name = name[:i]
218 urlInfo.setName(name)
219
220 return urlInfo
221
222 def __parseWindowsTime(self, date, time, urlInfo):
223 """
224 Private method to parse a Windows date and time indication modifying
225 the given URL info object.
226
227 Date time strings in Windows-style directory listings typically
228 have the format "10-23-12 03:25PM" (month-day_of_month-two_digit_year,
229 hour:minute, am/pm).
230
231 @param date date string (string)
232 @param time time string (string)
233 @param urlInfo reference to the URL info object (EricUrlInfo)
234 @exception FtpDirLineParserError Raised if either of the strings is not
235 recognized.
236 """
237 try:
238 month, day, year = [int(part) for part in date.split('-')]
239 year = 1900 + year if year >= 70 else 2000 + year
240 except (ValueError, IndexError):
241 raise FtpDirLineParserError(
242 "illegal date string '{0}'".format(month))
243 try:
244 hour, minute, am_pm = time[0:2], time[3:5], time[5]
245 hour = int(hour)
246 minute = int(minute)
247 except (ValueError, IndexError):
248 raise FtpDirLineParserError(
249 "illegal time string '{0}'".format(month))
250 if hour == 12 and am_pm == 'A':
251 hour = 0
252 if hour != 12 and am_pm == 'P':
253 hour += 12
254
255 lastModified = QDateTime(QDate(year, month, day), QTime(hour, minute))
256 urlInfo.setLastModified(lastModified)
257
258 def __parseWindowsLine(self, line):
259 """
260 Private method to parse a Windows style directory listing line.
261
262 @param line directory line to be parsed (string)
263 @return URL info object containing the valid data (EricUrlInfo)
264 @exception FtpDirLineParserError Raised if the line is not of a
265 recognized Windows format.
266 """
267 try:
268 date, time, dirOrSize, name = line.split(None, 3)
269 except ValueError:
270 # "unpack list of wrong size"
271 raise FtpDirLineParserError(
272 "line '{0}' cannot be parsed".format(line))
273
274 if name in [".", ".."]:
275 return None
276
277 urlInfo = EricUrlInfo()
278 self.__parseWindowsTime(date, time, urlInfo)
279 if dirOrSize.lower() == "<dir>":
280 urlInfo.setDir(True)
281 urlInfo.setFile(False)
282 else:
283 urlInfo.setDir(False)
284 urlInfo.setFile(True)
285 try:
286 urlInfo.setSize(int(dirOrSize))
287 except ValueError:
288 raise FtpDirLineParserError(
289 "illegal size '{0}'".format(dirOrSize))
290 urlInfo.setName(name)
291
292 ext = os.path.splitext(name.lower())[1]
293 urlInfo.setSymLink(ext == ".lnk")
294
295 permissions = (
296 EricUrlPermission.READ_OWNER | EricUrlPermission.WRITE_OWNER |
297 EricUrlPermission.READ_GROUP | EricUrlPermission.WRITE_GROUP |
298 EricUrlPermission.READ_OTHER | EricUrlPermission.WRITE_OTHER
299 )
300 if ext in [".exe", ".com", ".bat", ".cmd"]:
301 permissions |= (
302 EricUrlPermission.EXE_OWNER |
303 EricUrlPermission.EXE_GROUP |
304 EricUrlPermission.EXE_OTHER
305 )
306 urlInfo.setPermissions(permissions)
307
308 return urlInfo
309
310 def parseLine(self, line):
311 """
312 Public method to parse a directory listing line.
313
314 This implementation support Unix and Windows style directory
315 listings. It tries Unix style first and if that fails switches
316 to Windows style. If that fails as well, an exception is raised.
317
318 @param line directory line to be parsed (string)
319 @return URL info object containing the valid data (EricUrlInfo)
320 """
321 if self.__ignoreLine(line):
322 return None
323
324 try:
325 return self.__parseLine(line)
326 except FtpDirLineParserError:
327 if not self.__modeSwitchAllowed:
328 raise
329
330 self.__parseLine = self.__parseWindowsLine
331 self.__modeSwitchAllowed = False
332 return self.__parseLine(line)

eric ide

mercurial