summaryrefslogtreecommitdiffstats
path: root/xbmc/filesystem/FTPParse.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/filesystem/FTPParse.cpp
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/filesystem/FTPParse.cpp')
-rw-r--r--xbmc/filesystem/FTPParse.cpp504
1 files changed, 504 insertions, 0 deletions
diff --git a/xbmc/filesystem/FTPParse.cpp b/xbmc/filesystem/FTPParse.cpp
new file mode 100644
index 0000000..fb5e035
--- /dev/null
+++ b/xbmc/filesystem/FTPParse.cpp
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2010-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "FTPParse.h"
+
+#include <cmath>
+
+#include <pcrecpp.h>
+
+CFTPParse::CFTPParse()
+{
+ m_flagtrycwd = 0;
+ m_flagtryretr = 0;
+ m_size = 0;
+}
+
+std::string CFTPParse::getName()
+{
+ return m_name;
+}
+
+int CFTPParse::getFlagtrycwd()
+{
+ return m_flagtrycwd;
+}
+
+int CFTPParse::getFlagtryretr()
+{
+ return m_flagtryretr;
+}
+
+uint64_t CFTPParse::getSize()
+{
+ return m_size;
+}
+
+time_t CFTPParse::getTime()
+{
+ return m_time;
+}
+
+void CFTPParse::setTime(const std::string& str)
+{
+ /* Variables used to capture patterns via the regexes */
+ std::string month;
+ std::string day;
+ std::string year;
+ std::string hour;
+ std::string minute;
+ std::string second;
+ std::string am_or_pm;
+
+ /* time struct used to set the time_t variable */
+ struct tm time_struct = {};
+
+ /* Regex to read Unix, NetWare and NetPresenz time format */
+ pcrecpp::RE unix_re("^([A-Za-z]{3})" // month
+ "\\s+(\\d{1,2})" // day of month
+ "\\s+([:\\d]{4,5})$" // time of day or year
+ );
+
+ /* Regex to read MultiNet time format */
+ pcrecpp::RE multinet_re("^(\\d{1,2})" // day of month
+ "-([A-Za-z]{3})" // month
+ "-(\\d{4})" // year
+ "\\s+(\\d{2})" // hour
+ ":(\\d{2})" // minute
+ "(:(\\d{2}))?$" // second
+ );
+
+ /* Regex to read MSDOS time format */
+ pcrecpp::RE msdos_re("^(\\d{2})" // month
+ "-(\\d{2})" // day of month
+ "-(\\d{2})" // year
+ "\\s+(\\d{2})" // hour
+ ":(\\d{2})" // minute
+ "([AP]M)$" // AM or PM
+ );
+
+ if (unix_re.FullMatch(str, &month, &day, &year))
+ {
+ /* set the month */
+ if (pcrecpp::RE("jan",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 0;
+ else if (pcrecpp::RE("feb",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 1;
+ else if (pcrecpp::RE("mar",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 2;
+ else if (pcrecpp::RE("apr",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 3;
+ else if (pcrecpp::RE("may",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 4;
+ else if (pcrecpp::RE("jun",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 5;
+ else if (pcrecpp::RE("jul",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 6;
+ else if (pcrecpp::RE("aug",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 7;
+ else if (pcrecpp::RE("sep",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 8;
+ else if (pcrecpp::RE("oct",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 9;
+ else if (pcrecpp::RE("nov",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 10;
+ else if (pcrecpp::RE("dec",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 11;
+
+ /* set the day of the month */
+ time_struct.tm_mday = atoi(day.c_str());
+
+ time_t t = time(NULL);
+ struct tm *current_time;
+#ifdef LOCALTIME_R
+ struct tm result = {};
+ current_time = localtime_r(&t, &result);
+#else
+ current_time = localtime(&t);
+#endif
+ if (pcrecpp::RE("(\\d{2}):(\\d{2})").FullMatch(year, &hour, &minute))
+ {
+ /* set the hour and minute */
+ time_struct.tm_hour = atoi(hour.c_str());
+ time_struct.tm_min = atoi(minute.c_str());
+
+ /* set the year */
+ if ((current_time->tm_mon - time_struct.tm_mon < 0) ||
+ ((current_time->tm_mon - time_struct.tm_mon == 0) &&
+ (current_time->tm_mday - time_struct.tm_mday < 0)))
+ time_struct.tm_year = current_time->tm_year - 1;
+ else
+ time_struct.tm_year = current_time->tm_year;
+ }
+ else
+ {
+ /* set the year */
+ time_struct.tm_year = atoi(year.c_str()) - 1900;
+ }
+
+ /* set the day of the week */
+ time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
+ time_struct.tm_mday,
+ time_struct.tm_year + 1900);
+ }
+ else if (multinet_re.FullMatch(str, &day, &month, &year,
+ &hour, &minute, (void*)NULL, &second))
+ {
+ /* set the month */
+ if (pcrecpp::RE("jan",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 0;
+ else if (pcrecpp::RE("feb",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 1;
+ else if (pcrecpp::RE("mar",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 2;
+ else if (pcrecpp::RE("apr",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 3;
+ else if (pcrecpp::RE("may",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 4;
+ else if (pcrecpp::RE("jun",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 5;
+ else if (pcrecpp::RE("jul",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 6;
+ else if (pcrecpp::RE("aug",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 7;
+ else if (pcrecpp::RE("sep",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 8;
+ else if (pcrecpp::RE("oct",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 9;
+ else if (pcrecpp::RE("nov",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 10;
+ else if (pcrecpp::RE("dec",
+ pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+ time_struct.tm_mon = 11;
+
+ /* set the day of the month and year */
+ time_struct.tm_mday = atoi(day.c_str());
+ time_struct.tm_year = atoi(year.c_str()) - 1900;
+
+ /* set the hour and minute */
+ time_struct.tm_hour = atoi(hour.c_str());
+ time_struct.tm_min = atoi(minute.c_str());
+
+ /* set the second if given*/
+ if (second.length() > 0)
+ time_struct.tm_sec = atoi(second.c_str());
+
+ /* set the day of the week */
+ time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
+ time_struct.tm_mday,
+ time_struct.tm_year + 1900);
+ }
+ else if (msdos_re.FullMatch(str, &month, &day,
+ &year, &hour, &minute, &am_or_pm))
+ {
+ /* set the month and the day of the month */
+ time_struct.tm_mon = atoi(month.c_str()) - 1;
+ time_struct.tm_mday = atoi(day.c_str());
+
+ /* set the year */
+ time_struct.tm_year = atoi(year.c_str());
+ if (time_struct.tm_year < 70)
+ time_struct.tm_year += 100;
+
+ /* set the hour */
+ time_struct.tm_hour = atoi(hour.c_str());
+ if (time_struct.tm_hour == 12)
+ time_struct.tm_hour -= 12;
+ if (pcrecpp::RE("PM").FullMatch(am_or_pm))
+ time_struct.tm_hour += 12;
+
+ /* set the minute */
+ time_struct.tm_min = atoi(minute.c_str());
+
+ /* set the day of the week */
+ time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
+ time_struct.tm_mday,
+ time_struct.tm_year + 1900);
+ }
+
+ /* now set m_time */
+ m_time = mktime(&time_struct);
+}
+
+int CFTPParse::getDayOfWeek(int month, int date, int year)
+{
+ /* Here we use the Doomsday rule to calculate the day of the week */
+
+ /* First determine the anchor day */
+ int anchor;
+ if (year >= 1900 && year < 2000)
+ anchor = 3;
+ else if (year >= 2000 && year < 2100)
+ anchor = 2;
+ else if (year >= 2100 && year < 2200)
+ anchor = 0;
+ else if (year >= 2200 && year < 2300)
+ anchor = 5;
+ else // must have been given an invalid year :-/
+ return -1;
+
+ /* Now determine the doomsday */
+ int y = year % 100;
+ int dday =
+ ((y/12 + (y % 12) + ((y % 12)/4)) % 7) + anchor;
+
+ /* Determine if the given year is a leap year */
+ int leap_year = 0;
+ if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
+ leap_year = 1;
+
+ /* Now select a doomsday for the given month */
+ int mdday = 1;
+ if (month == 1)
+ {
+ if (leap_year)
+ mdday = 4;
+ else
+ mdday = 3;
+ }
+ if (month == 2)
+ {
+ if (leap_year)
+ mdday = 1;
+ else
+ mdday = 7;
+ }
+ if (month == 3)
+ mdday = 7;
+ if (month == 4)
+ mdday = 4;
+ if (month == 5)
+ mdday = 9;
+ if (month == 6)
+ mdday = 6;
+ if (month == 7)
+ mdday = 11;
+ if (month == 8)
+ mdday = 8;
+ if (month == 9)
+ mdday = 5;
+ if (month == 10)
+ mdday = 10;
+ if (month == 11)
+ mdday = 9;
+ if (month == 12)
+ mdday = 12;
+
+ /* Now calculate the day of the week
+ * Sunday = 0, Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4,
+ * Friday = 5, Saturday = 6.
+ */
+ int day_of_week = ((date - 1) % 7) - ((mdday - 1) % 7) + dday;
+ if (day_of_week >= 7)
+ day_of_week -= 7;
+
+ return day_of_week;
+}
+
+int CFTPParse::FTPParse(const std::string& str)
+{
+ /* Various variable to capture patterns via the regexes */
+ std::string permissions;
+ std::string link_count;
+ std::string owner;
+ std::string group;
+ std::string size;
+ std::string date;
+ std::string name;
+ std::string type;
+ std::string stuff;
+ std::string facts;
+ std::string version;
+ std::string file_id;
+
+ /* Regex for standard Unix listing formats */
+ pcrecpp::RE unix_re("^([-bcdlps])" // type
+ "([-rwxXsStT]{9})" // permissions
+ "\\s+(\\d+)" // hard link count
+ "\\s+(\\w+)" // owner
+ "\\s+(\\w+)" // group
+ "\\s+(\\d+)" // size
+ "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // modification date
+ "\\s+(.+)$" // name
+ );
+
+ /* Regex for NetWare listing formats */
+ /* See http://www.novell.com/documentation/oes/ftp_enu/data/a3ep22p.html#fbhbaijf */
+ pcrecpp::RE netware_re("^([-d])" // type
+ "\\s+(\\[[-SRWCIEMFA]{8}\\])" // rights
+ "\\s+(\\w+)" // owner
+ "\\s+(\\d+)" // size
+ "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // time
+ "\\s+(.+)$" // name
+ );
+
+ /* Regex for NetPresenz */
+ /* See http://files.stairways.com/other/ftp-list-specs-info.txt */
+ /* Here we will capture permissions and size if given */
+ pcrecpp::RE netpresenz_re("^([-dl])" // type
+ "([-rwx]{9}|)" // permissions
+ "\\s+(.*)" // stuff
+ "\\s+(\\d+|)" // size
+ "\\s+([A-Za-z]{3}\\s+\\d{1,2}\\s+[:\\d]{4,5})" // modification date
+ "\\s+(.+)$" // name
+ );
+
+ /* Regex for EPLF */
+ /* See http://cr.yp.to/ftp/list/eplf.html */
+ /* SAVE: "(/,|r,|s\\d+,|m\\d+,|i[\\d!#@$%^&*()]+(\\.[\\d!#@$%^&*()]+|),)+" */
+ pcrecpp::RE eplf_re("^\\+" // initial "plus" sign
+ "([^\\s]+)" // facts
+ "\\s(.+)$" // name
+ );
+
+ /* Regex for MultiNet */
+ /* Best documentation found was
+ * http://www-sld.slac.stanford.edu/SLDWWW/workbook/vms_files.html */
+ pcrecpp::RE multinet_re("^([^;]+)" // name
+ ";(\\d+)" // version
+ "\\s+([\\d/]+)" // file id
+ "\\s+(\\d{1,2}-[A-Za-z]{3}-\\d{4}\\s+\\d{2}:\\d{2}(:\\d{2})?)" // date
+ "\\s+\\[([^\\]]+)\\]" // owner,group
+ "\\s+\\(([^\\)]+)\\)$" // permissions
+ );
+
+ /* Regex for MSDOS */
+ pcrecpp::RE msdos_re("^(\\d{2}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}[AP]M)" // date
+ "\\s+(<DIR>|[\\d]+)" // dir or size
+ "\\s+(.+)$" // name
+ );
+
+ if (unix_re.FullMatch(str, &type, &permissions, &link_count, &owner, &group, &size, &date, &name))
+ {
+ m_name = name;
+ m_size = (uint64_t)strtod(size.c_str(), NULL);
+ if (pcrecpp::RE("d").FullMatch(type))
+ m_flagtrycwd = 1;
+ if (pcrecpp::RE("-").FullMatch(type))
+ m_flagtryretr = 1;
+ if (pcrecpp::RE("l").FullMatch(type))
+ {
+ m_flagtrycwd = m_flagtryretr = 1;
+ // handle symlink
+ size_t found = m_name.find(" -> ");
+ if (found != std::string::npos)
+ m_name = m_name.substr(0, found);
+ }
+ setTime(date);
+
+ return 1;
+ }
+ if (netware_re.FullMatch(str, &type, &permissions, &owner, &size, &date, &name))
+ {
+ m_name = name;
+ m_size = (uint64_t)strtod(size.c_str(), NULL);
+ if (pcrecpp::RE("d").FullMatch(type))
+ m_flagtrycwd = 1;
+ if (pcrecpp::RE("-").FullMatch(type))
+ m_flagtryretr = 1;
+ setTime(date);
+
+ return 1;
+ }
+ if (netpresenz_re.FullMatch(str, &type, &permissions, &stuff, &size, &date, &name))
+ {
+ m_name = name;
+ m_size = (uint64_t)strtod(size.c_str(), NULL);
+ if (pcrecpp::RE("d").FullMatch(type))
+ m_flagtrycwd = 1;
+ if (pcrecpp::RE("-").FullMatch(type))
+ m_flagtryretr = 1;
+ if (pcrecpp::RE("l").FullMatch(type))
+ {
+ m_flagtrycwd = m_flagtryretr = 1;
+ // handle symlink
+ size_t found = m_name.find(" -> ");
+ if (found != std::string::npos)
+ m_name = m_name.substr(0, found);
+ }
+ setTime(date);
+
+ return 1;
+ }
+ if (eplf_re.FullMatch(str, &facts, &name))
+ {
+ /* Get the type, size, and date from the facts */
+ pcrecpp::RE("(\\+|,)(r|/),").PartialMatch(facts, (void*)NULL, &type);
+ pcrecpp::RE("(\\+|,)s(\\d+),").PartialMatch(facts, (void*)NULL, &size);
+ pcrecpp::RE("(\\+|,)m(\\d+),").PartialMatch(facts, (void*)NULL, &date);
+
+ m_name = name;
+ m_size = (uint64_t)strtod(size.c_str(), NULL);
+ if (pcrecpp::RE("/").FullMatch(type))
+ m_flagtrycwd = 1;
+ if (pcrecpp::RE("r").FullMatch(type))
+ m_flagtryretr = 1;
+ /* eplf stores the date in time_t format already */
+ m_time = atoi(date.c_str());
+
+ return 1;
+ }
+ if (multinet_re.FullMatch(str, &name, &version, &file_id, &date, (void*)NULL, &owner, &permissions))
+ {
+ if (pcrecpp::RE("\\.DIR$").PartialMatch(name))
+ {
+ name.resize(name.size() - 4);
+ m_flagtrycwd = 1;
+ }
+ else
+ m_flagtryretr = 1;
+ m_name = name;
+ setTime(date);
+ /* Multinet doesn't provide a size */
+ m_size = 0;
+
+ return 1;
+ }
+ if (msdos_re.FullMatch(str, &date, &size, &name))
+ {
+ m_name = name;
+ if (pcrecpp::RE("<DIR>").FullMatch(size))
+ {
+ m_flagtrycwd = 1;
+ m_size = 0;
+ }
+ else
+ {
+ m_flagtryretr = 1;
+ m_size = (uint64_t)strtod(size.c_str(), NULL);
+ }
+ setTime(date);
+
+ return 1;
+ }
+
+ return 0;
+}