Merge pull request #25808 from CastagnaIT/fix_url_parse
[xbmc.git] / xbmc / filesystem / FTPParse.cpp
blob563fb2a194c4e2792df30e93ca3900aa4f0083f1
1 /*
2 * Copyright (C) 2010-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "FTPParse.h"
11 #include <regex>
13 CFTPParse::CFTPParse()
15 m_flagtrycwd = 0;
16 m_flagtryretr = 0;
17 m_size = 0;
20 int CFTPParse::getFlagtrycwd()
22 return m_flagtrycwd;
25 int CFTPParse::getFlagtryretr()
27 return m_flagtryretr;
30 uint64_t CFTPParse::getSize()
32 return m_size;
35 time_t CFTPParse::getTime()
37 return m_time;
40 namespace
42 const std::string months = "janfebmaraprmayjunjulaugsepoctnovdec";
44 // set time_struct.tm_mon from the 3-letter "abbreviated month name"
45 void setMonFromName(struct tm& time_struct, const std::string& name)
47 std::smatch match;
48 if (std::regex_search(months, match, std::regex(name, std::regex_constants::icase)))
50 int pos = match.position();
51 if (name.size() == 3 && pos % 3 == 0)
52 time_struct.tm_mon = pos / 3;
55 } // namespace
57 void CFTPParse::setTime(const std::string& str)
59 /* time struct used to set the time_t variable */
60 struct tm time_struct = {};
62 // sub matches from regex_match() calls below
63 std::smatch match;
65 // Regex to read Unix, NetWare and NetPresenz time format
66 if (std::regex_match(str, match,
67 std::regex("^([A-Za-z]{3})" // month
68 "\\s+(\\d{1,2})" // day of month
69 "\\s+([:\\d]{4,5})$"))) // time of day or year
71 auto month = match[1].str();
72 auto day = match[2].str();
73 auto year = match[3].str();
75 /* set the month */
76 setMonFromName(time_struct, month);
78 /* set the day of the month */
79 time_struct.tm_mday = std::stol(day);
81 time_t t = time(NULL);
82 struct tm *current_time;
83 #ifdef LOCALTIME_R
84 struct tm result = {};
85 current_time = localtime_r(&t, &result);
86 #else
87 current_time = localtime(&t);
88 #endif
89 if (std::regex_match(year, match, std::regex("(\\d{2}):(\\d{2})")))
91 /* set the hour and minute */
92 time_struct.tm_hour = std::stol(match[1].str());
93 time_struct.tm_min = std::stol(match[2].str());
95 /* set the year */
96 if ((current_time->tm_mon - time_struct.tm_mon < 0) ||
97 ((current_time->tm_mon - time_struct.tm_mon == 0) &&
98 (current_time->tm_mday - time_struct.tm_mday < 0)))
99 time_struct.tm_year = current_time->tm_year - 1;
100 else
101 time_struct.tm_year = current_time->tm_year;
103 else
105 /* set the year */
106 time_struct.tm_year = std::stol(year) - 1900;
109 // Regex to read MultiNet time format
110 else if (std::regex_match(str, match,
111 std::regex("^(\\d{1,2})" // day of month
112 "-([A-Za-z]{3})" // month
113 "-(\\d{4})" // year
114 "\\s+(\\d{2})" // hour
115 ":(\\d{2})" // minute
116 "(:(\\d{2}))?$"))) // second
118 auto day = match[1].str();
119 auto month = match[2].str();
120 auto year = match[3].str();
121 auto hour = match[4].str();
122 auto minute = match[5].str();
123 // match[6] ignored
124 auto second = match[7].str();
126 /* set the month */
127 setMonFromName(time_struct, month);
129 /* set the day of the month and year */
130 time_struct.tm_mday = std::stol(day);
131 time_struct.tm_year = std::stol(year) - 1900;
133 /* set the hour and minute */
134 time_struct.tm_hour = std::stol(hour);
135 time_struct.tm_min = std::stol(minute);
137 /* set the second if given*/
138 if (second.length() > 0)
139 time_struct.tm_sec = std::stol(second);
141 // Regex to read MSDOS time format
142 else if (std::regex_match(str, match,
143 std::regex("^(\\d{2})" // month
144 "-(\\d{2})" // day of month
145 "-(\\d{2})" // year
146 "\\s+(\\d{2})" // hour
147 ":(\\d{2})" // minute
148 "([AP]M)$"))) // AM or PM
150 auto month = match[1].str();
151 auto day = match[2].str();
152 auto year = match[3].str();
153 auto hour = match[4].str();
154 auto minute = match[5].str();
155 auto am_or_pm = match[6].str();
157 /* set the month and the day of the month */
158 time_struct.tm_mon = std::stol(month) - 1;
159 time_struct.tm_mday = std::stol(day);
161 /* set the year */
162 time_struct.tm_year = std::stoi(year);
163 if (time_struct.tm_year < 70)
164 time_struct.tm_year += 100;
166 /* set the hour */
167 time_struct.tm_hour = std::stoi(hour);
168 if (time_struct.tm_hour == 12)
169 time_struct.tm_hour -= 12;
170 if (am_or_pm == "PM")
171 time_struct.tm_hour += 12;
173 /* set the minute */
174 time_struct.tm_min = std::stoi(minute);
177 /* now set m_time */
178 m_time = mktime(&time_struct);
181 int CFTPParse::FTPParse(const std::string& str)
183 // sub matches from regex_match() calls below
184 std::smatch match;
186 // Regex for standard Unix listing formats
187 if (std::regex_match(
188 str, match,
189 std::regex("^([-bcdlps])" // type
190 "([-rwxXsStT]{9})" // permissions
191 "\\s+(\\d+)" // hard link count
192 "\\s+(\\w+)" // owner
193 "\\s+(\\w+)" // group
194 "\\s+(\\d+)" // size
195 "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // modification date
196 "\\s+(.+)$"))) // name
198 auto type = match[1].str();
199 auto permissions = match[2].str();
200 auto link_count = match[3].str();
201 auto owner = match[4].str();
202 auto group = match[5].str();
203 auto size = match[6].str();
204 auto date = match[7].str();
205 m_name = match[8].str();
207 m_size = std::stoull(size);
208 if (type == "d")
209 m_flagtrycwd = 1;
210 if (type == "-")
211 m_flagtryretr = 1;
212 if (type == "l")
214 m_flagtrycwd = m_flagtryretr = 1;
215 // handle symlink
216 size_t found = m_name.find(" -> ");
217 if (found != std::string::npos)
218 m_name.resize(found);
220 setTime(date);
222 return 1;
224 // Regex for NetWare listing formats
225 // See http://www.novell.com/documentation/oes/ftp_enu/data/a3ep22p.html#fbhbaijf
226 if (std::regex_match(str, match,
227 std::regex("^([-d])" // type
228 "\\s+(\\[[-SRWCIEMFA]{8}\\])" // rights
229 "\\s+(\\w+)" // owner
230 "\\s+(\\d+)" // size
231 "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // time
232 "\\s+(.+)$"))) // name
234 auto type = match[1].str();
235 auto permissions = match[2].str();
236 auto owner = match[3].str();
237 auto size = match[4].str();
238 auto date = match[5].str();
239 m_name = match[6].str();
241 m_size = std::stoull(size);
242 if (type == "d")
243 m_flagtrycwd = 1;
244 if (type == "-")
245 m_flagtryretr = 1;
246 setTime(date);
248 return 1;
250 // Regex for NetPresenz
251 // See http://files.stairways.com/other/ftp-list-specs-info.txt
252 // Here we will capture permissions and size if given
253 if (std::regex_match(
254 str, match,
255 std::regex("^([-dl])" // type
256 "([-rwx]{9}|)" // permissions
257 "\\s+(.*)" // stuff
258 "\\s+(\\d+|)" // size
259 "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // modification date
260 "\\s+(.+)$"))) // name
262 auto type = match[1].str();
263 auto permissions = match[2].str();
264 auto stuff = match[3].str();
265 auto size = match[4].str();
266 auto date = match[5].str();
267 m_name = match[6].str();
269 m_size = std::stoull(size);
270 if (type == "d")
271 m_flagtrycwd = 1;
272 if (type == "-")
273 m_flagtryretr = 1;
274 if (type == "l")
276 m_flagtrycwd = m_flagtryretr = 1;
277 // handle symlink
278 size_t found = m_name.find(" -> ");
279 if (found != std::string::npos)
280 m_name.resize(found);
282 setTime(date);
284 return 1;
286 // Regex for EPLF
287 // See http://cr.yp.to/ftp/list/eplf.html
288 // SAVE: "(/,|r,|s\\d+,|m\\d+,|i[\\d!#@$%^&*()]+(\\.[\\d!#@$%^&*()]+|),)+"
289 if (std::regex_match(str, match,
290 std::regex("^\\+" // initial "plus" sign
291 "([^\\s]+)" // facts
292 "\\s(.+)$"))) // name
294 auto facts = match[1].str();
295 m_name = match[2].str();
297 /* Get the type, size, and date from the facts */
298 std::regex_search(facts, match, std::regex("(?:\\+|,)(r|/),"));
299 auto type = match[1].str();
300 std::regex_search(facts, match, std::regex("(?:\\+|,)s(\\d+),"));
301 auto size = match[1].str();
302 std::regex_search(facts, match, std::regex("(?:\\+|,)m(\\d+),"));
303 auto date = match[1].str();
305 m_size = std::stoull(size);
306 if (type == "/")
307 m_flagtrycwd = 1;
308 if (type == "r")
309 m_flagtryretr = 1;
310 /* eplf stores the date in time_t format already */
311 m_time = std::stoi(date);
313 return 1;
315 // Regex for MultiNet
316 // Best documentation found was
317 // http://www-sld.slac.stanford.edu/SLDWWW/workbook/vms_files.html
318 if (std::regex_match(
319 str, match,
320 std::regex("^([^;]+)" // name
321 ";(\\d+)" // version
322 "\\s+([\\d/]+)" // file id
323 "\\s+(\\d{1,2}-[A-Za-z]{3}-\\d{4}\\s+\\d{2}:\\d{2}(:\\d{2})?)" // date
324 "\\s+\\[([^\\]]+)\\]" // owner,group
325 "\\s+\\(([^\\)]+)\\)$"))) // permissions
327 auto name = match[1].str();
328 auto version = match[2].str();
329 auto file_id = match[3].str();
330 auto date = match[4].str();
331 // match[5] ignored
332 auto owner = match[6].str();
333 auto permissions = match[7].str();
335 if (std::regex_search(name, std::regex("\\.DIR$")))
337 name.resize(name.size() - 4);
338 m_flagtrycwd = 1;
340 else
341 m_flagtryretr = 1;
342 m_name = name;
343 setTime(date);
344 /* Multinet doesn't provide a size */
345 m_size = 0;
347 return 1;
349 // Regex for MSDOS
350 if (std::regex_match(str, match,
351 std::regex("^(\\d{2}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}[AP]M)" // date
352 "\\s+(<DIR>|[\\d]+)" // dir or size
353 "\\s+(.+)$"))) // name
355 auto date = match[1].str();
356 auto size = match[2].str();
357 m_name = match[3].str();
358 if (size == "<DIR>")
360 m_flagtrycwd = 1;
361 m_size = 0;
363 else
365 m_flagtryretr = 1;
366 m_size = std::stoull(size);
368 setTime(date);
370 return 1;
373 return 0;