Utilities/FtpUtilities.py

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

eric ide

mercurial