eric6/Utilities/FtpUtilities.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 7229
53054eb5b15a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2012 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing some FTP related utilities.
8 """
9
10 from __future__ import unicode_literals
11
12 import os
13
14 from PyQt5.QtCore import QObject, QDate, QDateTime, QTime
15
16 from E5Network.E5UrlInfo import E5UrlInfo
17
18
19 class FtpDirLineParserError(Exception):
20 """
21 Exception class raised, if a parser issue was detected.
22 """
23 pass
24
25
26 class FtpDirLineParser(QObject):
27 """
28 Class to parse lines returned by a FTP LIST command.
29 """
30 MonthnamesNumbers = {
31 "jan": 1,
32 "feb": 2,
33 "mar": 3,
34 "apr": 4,
35 "may": 5,
36 "jun": 6,
37 "jul": 7,
38 "aug": 8,
39 "sep": 9,
40 "oct": 10,
41 "nov": 11,
42 "dec": 12,
43 }
44
45 def __init__(self, parent=None):
46 """
47 Constructor
48
49 @param parent reference to the parent object (QObject)
50 """
51 super(FtpDirLineParser, self).__init__(parent)
52
53 self.__parseLine = self.__parseUnixLine
54 self.__modeSwitchAllowed = True
55
56 def __ignoreLine(self, line):
57 """
58 Private method to check, if the line should be ignored.
59
60 @param line to check (string)
61 @return flag indicating to ignore the line (boolean)
62 """
63 return line.strip() == "" or \
64 line.strip().lower().startswith("total ")
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 (E5UrlInfo)
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 |= E5UrlInfo.ReadOwner
84 if modeString[2] != '-':
85 permission |= E5UrlInfo.WriteOwner
86 if modeString[3] != '-':
87 permission |= E5UrlInfo.ExeOwner
88 if modeString[4] != '-':
89 permission |= E5UrlInfo.ReadGroup
90 if modeString[5] != '-':
91 permission |= E5UrlInfo.WriteGroup
92 if modeString[6] != '-':
93 permission |= E5UrlInfo.ExeGroup
94 if modeString[7] != '-':
95 permission |= E5UrlInfo.ReadOther
96 if modeString[8] != '-':
97 permission |= E5UrlInfo.WriteOther
98 if modeString[9] != '-':
99 permission |= E5UrlInfo.ExeOther
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 (E5UrlInfo)
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 (E5UrlInfo)
200 """
201 modeString, nlink, user, group, size, month, day, \
202 yearOrTime, name = self.__splitUnixLine(line)
203
204 if name in [".", ".."]:
205 return None
206
207 urlInfo = E5UrlInfo()
208 self.__parseUnixMode(modeString, urlInfo)
209 self.__parseUnixTime(month, day, yearOrTime, urlInfo)
210 urlInfo.setOwner(user)
211 urlInfo.setGroup(group)
212 urlInfo.setSize(int(size))
213 name = name.strip()
214 i = name.find(" -> ")
215 if i >= 0:
216 name = name[:i]
217 urlInfo.setName(name)
218
219 return urlInfo
220
221 def __parseWindowsTime(self, date, time, urlInfo):
222 """
223 Private method to parse a Windows date and time indication modifying
224 the given URL info object.
225
226 Date time strings in Windows-style directory listings typically
227 have the format "10-23-12 03:25PM" (month-day_of_month-two_digit_year,
228 hour:minute, am/pm).
229
230 @param date date string (string)
231 @param time time string (string)
232 @param urlInfo reference to the URL info object (E5UrlInfo)
233 @exception FtpDirLineParserError Raised if either of the strings is not
234 recognized.
235 """
236 try:
237 month, day, year = [int(part) for part in date.split('-')]
238 if year >= 70:
239 year = 1900 + year
240 else:
241 year = 2000 + year
242 except (ValueError, IndexError):
243 raise FtpDirLineParserError(
244 "illegal date string '{0}'".format(month))
245 try:
246 hour, minute, am_pm = time[0:2], time[3:5], time[5]
247 hour = int(hour)
248 minute = int(minute)
249 except (ValueError, IndexError):
250 raise FtpDirLineParserError(
251 "illegal time string '{0}'".format(month))
252 if hour == 12 and am_pm == 'A':
253 hour = 0
254 if hour != 12 and am_pm == 'P':
255 hour = hour + 12
256
257 lastModified = QDateTime(QDate(year, month, day), QTime(hour, minute))
258 urlInfo.setLastModified(lastModified)
259
260 def __parseWindowsLine(self, line):
261 """
262 Private method to parse a Windows style directory listing line.
263
264 @param line directory line to be parsed (string)
265 @return URL info object containing the valid data (E5UrlInfo)
266 @exception FtpDirLineParserError Raised if the line is not of a
267 recognized Windows format.
268 """
269 try:
270 date, time, dirOrSize, name = line.split(None, 3)
271 except ValueError:
272 # "unpack list of wrong size"
273 raise FtpDirLineParserError(
274 "line '{0}' cannot be parsed".format(line))
275
276 if name in [".", ".."]:
277 return None
278
279 urlInfo = E5UrlInfo()
280 self.__parseWindowsTime(date, time, urlInfo)
281 if dirOrSize.lower() == "<dir>":
282 urlInfo.setDir(True)
283 urlInfo.setFile(False)
284 else:
285 urlInfo.setDir(False)
286 urlInfo.setFile(True)
287 try:
288 urlInfo.setSize(int(dirOrSize))
289 except ValueError:
290 raise FtpDirLineParserError(
291 "illegal size '{0}'".format(dirOrSize))
292 urlInfo.setName(name)
293
294 ext = os.path.splitext(name.lower())[1]
295 urlInfo.setSymLink(ext == ".lnk")
296
297 permissions = (E5UrlInfo.ReadOwner | E5UrlInfo.WriteOwner |
298 E5UrlInfo.ReadGroup | E5UrlInfo.WriteGroup |
299 E5UrlInfo.ReadOther | E5UrlInfo.WriteOther)
300 if ext in [".exe", ".com", ".bat", ".cmd"]:
301 permissions |= E5UrlInfo.ExeOwner | E5UrlInfo.ExeGroup | \
302 E5UrlInfo.ExeOther
303 urlInfo.setPermissions(permissions)
304
305 return urlInfo
306
307 def parseLine(self, line):
308 """
309 Public method to parse a directory listing line.
310
311 This implementation support Unix and Windows style directory
312 listings. It tries Unix style first and if that fails switches
313 to Windows style. If that fails as well, an exception is raised.
314
315 @param line directory line to be parsed (string)
316 @return URL info object containing the valid data (E5UrlInfo)
317 """
318 if self.__ignoreLine(line):
319 return None
320
321 try:
322 return self.__parseLine(line)
323 except FtpDirLineParserError:
324 if self.__modeSwitchAllowed:
325 self.__parseLine = self.__parseWindowsLine
326 self.__modeSwitchAllowed = False
327 return self.__parseLine(line)
328 else:
329 raise

eric ide

mercurial