| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169 | //---------------------------------------------------------------------------#include "stdafx.h"#include "FtpListResult.h"#include "FileZillaApi.h"#include <WideStrUtils.hpp>//////////////////////////////////////////////////////////////////////// Konstruktion/Destruktion////////////////////////////////////////////////////////////////////////#define LISTDEBUG#ifdef LISTDEBUG  //It's the normal UNIX format (or even another nonstandard format)  //Some samples are from https://cr.yp.to/ftpparse/ftpparse.c  /* UNIX-style listing, without inum and without blocks */  static char data[][110]={    "-rw-r--r--   1 root     other        531 Jan 29 03:26 01-unix-std file",    "dr-xr-xr-x   2 root     other        512 Apr  8  1994 02-unix-std dir",    "dr-xr-xr-x   2 root                  512 Apr  8  1994 03-unix-nogroup dir",    "lrwxrwxrwx   1 root     other          7 Jan 25 00:17 04-unix-std link -> usr/bin",    /* Some listings with uncommon date/time format: */    "-rw-r--r--   1 root     other        531 09-26 2000 05-unix-date file",    "-rw-r--r--   1 root     other        531 09-26 13:45 06-unix-date file",    "-rw-r--r--   1 root     other        531 2005-06-07 21:22 07-unix-date file",    /* Unix style with size information in kilobytes */    "-rw-r--r--   1 root     other  34.5k Oct 5 21:22 08-unix-namedsize file",    /* Also NetWare: */    "d [R----F--] supervisor            512       Jan 16 18:53    09-netware dir",    "- [R----F--] rhesus             214059       Oct 20 15:27    10-netware file",    /* Also NetPresenz for the Mac: */    "-------r--         326  1391972  1392298 Nov 22  1995 11-netpresenz file",    "drwxrwxr-x               folder        2 May 10  1996 12-netpresenz dir",    /* A format with domain field some windows servers send */    "-rw-r--r--   1 group domain user 531 Jan 29 03:26 13-unix-domain file",    /* EPLF directory listings */    "+i8388621.48594,m825718503,r,s280,\t14-eplf file",    "+i8388621.50690,m824255907,/,\t15-eplf dir",    /* MSDOS type listing used by IIS */    "04-27-00  12:09PM       <DIR>          16-dos-dateambigious dir",    "04-14-00  03:47PM                  589 17-dos-dateambigious file",    /* Another type of MSDOS style listings */    "2002-09-02  18:48       <DIR>          18-dos-longyear dir",    "2002-09-02  19:06                9,730 19-dos-longyear file",    /* Numerical Unix style format */    "0100644   500  101   12345    123456789       20-unix-numerical file",    /* This one is used by SSH-2.0-VShell_2_1_2_143, this is the old VShell format */    "206876  Apr 04, 2000 21:06 21-vshell-old file",    "0  Dec 12, 2002 02:13 22-vshell-old dir/",    /* This type of directory listings is sent by some newer versions of VShell     * both year and time in one line is uncommon.     */    "-rwxr-xr-x    1 user group        9 Oct 08, 2002 09:47 23-vshell-new file",    /* Next ones come from an OS/2 server. The server obviously isn't Y2K aware */    "36611      A    04-23-103  10:57  24-os2 file",    " 1123      A    07-14-99   12:37  25-os2 file",    "    0 DIR       02-11-103  16:15  26-os2 dir",    " 1123 DIR  A    10-05-100  23:38  27-os2 dir",    /* Some servers send localized date formats, here the German one: */    "dr-xr-xr-x   2 root     other      2235 26. Juli, 20:10 28-datetest-ger dir",    "-r-xr-xr-x   2 root     other      2235 2.   Okt.  2003 29-datetest-ger file",    "-r-xr-xr-x   2 root     other      2235 1999/10/12 17:12 30-datetest file",    "-r-xr-xr-x   2 root     other      2235 24-04-2003 17:12 31-datetest file",    /* Here a Japanese one: */    "-rw-r--r--   1 root       sys           8473  4\x8c\x8e 18\x93\xfa 2003\x94\x4e 32-datatest-japanese file",    /* VMS style listings */    "33-vms-dir.DIR;1  1 19-NOV-2001 21:41 [root,root] (RWE,RWE,RE,RE)",    "34-vms-file;1       155   2-JUL-2003 10:30:13.64",    /* VMS style listings without time */    "35-vms-notime-file;1    2/8    15-JAN-2000    [IV2_XXX]   (RWED,RWED,RE,)",    "36-vms-notime-file;1    6/8    15-JUI-2002    PRONAS   (RWED,RWED,RE,)",    /* VMS multiline */    "37-vms-multiline-file;1\r\n170774/170775     24-APR-2003 08:16:15  [FTP_CLIENT,SCOT]      (RWED,RWED,RE,)",    "38-vms-multiline-file;1\r\n10           2-JUL-2003 10:30:08.59  [FTP_CLIENT,SCOT]      (RWED,RWED,RE,)",    /* IBM AS/400 style listing */    "QSYS            77824 02/23/00 15:09:55 *DIR 39-ibm-as400 dir/",    "QSYS            77824 23/02/00 15:09:55 *FILE 40-ibm-as400-date file",    /* aligned directory listing with too long size */    "-r-xr-xr-x longowner longgroup123456 Feb 12 17:20 41-unix-concatsize file",    /* short directory listing with month name */    "-r-xr-xr-x 2 owner group 4512 01-jun-99 42_unix_shortdatemonth file",    /* the following format is sent by the Connect:Enterprise server by Sterling Commerce */    "-C--E-----FTP B BCC3I1       7670  1294495 Jan 13 07:42 43-conent file",    "-C--E-----FTS B BCC3I1       7670  1294495 Jan 13 07:42 44-conent-file",    "-AR--M----TCP B ceunix      17570  2313708 Mar 29 08:56 45-conent-file",    /* Nortel wfFtp router */    "46-nortel-wfftp-file       1014196  06/03/04  Thur.   10:20:03",    /* VxWorks based server used in Nortel routers */    "2048    Feb-28-1998  05:23:30   47-nortel-vxworks dir <DIR>",    /* IBM MVS listings */    // Volume Unit    Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname    "  WYOSPT 3420   2003/05/21  1  200  FB      80  8053  PS  48-MVS.FILE",    "  WPTA01 3290   2004/03/04  1    3  FB      80  3125  PO  49-MVS.DATASET",    "  TSO004 3390   VSAM 50-mvs-file",    "  TSO005 3390   2005/06/06 213000 U 0 27998 PO 51-mvs-dir",    /* Dataset members */    // Name         VV.MM   Created      Changed       Size  Init  Mod Id    // ADATAB /* filenames without data, only check for those on MVS servers */    "  52-MVSPDSMEMBER 01.01 2004/06/22 2004/06/22 16:32   128   128    0 BOBY12",    "53-MVSPDSMEMBER2 00B308 000411  00 FO        RU      31    ANY",    "54-MVSPDSMEMBER3 00B308 000411  00 FO        RU      ANY    24",    // Some asian listing format. Those >127 chars are just examples    "-rwxrwxrwx   1 root     staff          0 2003   3\xed\xef 20 55-asian date file",        "-r--r--r-- 1 root root 2096 8\xed 17 08:52 56-asian date file",    // VMS style listing with a different field order    "57-vms-alternate-field-order-file;1   [SUMMARY]    1/3     2-AUG-2006 13:05  (RWE,RWE,RE,)",    ""};#endifCFtpListResult::CFtpListResult(t_server server, bool *bUTF8){  listhead=curpos=0;  m_server = server;  m_bUTF8 = bUTF8;  pos=0;  m_prevline=0;  m_curline=0;  m_curlistaddpos=0;  //Fill the month names map  //English month names  m_MonthNamesMap[L"jan"] = 1;  m_MonthNamesMap[L"feb"] = 2;  m_MonthNamesMap[L"mar"] = 3;  m_MonthNamesMap[L"apr"] = 4;  m_MonthNamesMap[L"may"] = 5;  m_MonthNamesMap[L"jun"] = 6;  m_MonthNamesMap[L"june"] = 6;  m_MonthNamesMap[L"jul"] = 7;  m_MonthNamesMap[L"july"] = 7;  m_MonthNamesMap[L"aug"] = 8;  m_MonthNamesMap[L"sep"] = 9;  m_MonthNamesMap[L"sept"] = 9;  m_MonthNamesMap[L"oct"] = 10;  m_MonthNamesMap[L"nov"] = 11;  m_MonthNamesMap[L"dec"] = 12;  //Numerical values for the month  m_MonthNamesMap[L"1"] = 1;  m_MonthNamesMap[L"01"] = 1;  m_MonthNamesMap[L"2"] = 2;  m_MonthNamesMap[L"02"] = 2;  m_MonthNamesMap[L"3"] = 3;  m_MonthNamesMap[L"03"] = 3;  m_MonthNamesMap[L"4"] = 4;  m_MonthNamesMap[L"04"] = 4;  m_MonthNamesMap[L"5"] = 5;  m_MonthNamesMap[L"05"] = 5;  m_MonthNamesMap[L"6"] = 6;  m_MonthNamesMap[L"06"] = 6;  m_MonthNamesMap[L"7"] = 7;  m_MonthNamesMap[L"07"] = 7;  m_MonthNamesMap[L"8"] = 8;  m_MonthNamesMap[L"08"] = 8;  m_MonthNamesMap[L"9"] = 9;  m_MonthNamesMap[L"09"] = 9;  m_MonthNamesMap[L"10"] = 10;  m_MonthNamesMap[L"11"] = 11;  m_MonthNamesMap[L"12"] = 12;  //German month names  m_MonthNamesMap[L"mrz"] = 3;  m_MonthNamesMap[L"m\xE4r"] = 3;  m_MonthNamesMap[L"m\xE4rz"] = 3;  m_MonthNamesMap[L"mai"] = 5;  m_MonthNamesMap[L"juni"] = 6;  m_MonthNamesMap[L"m\xC3\xA4r"] = 3; // UTF-8  m_MonthNamesMap[L"m\xC3\xA4rz"] = 3; // UTF-8  m_MonthNamesMap[L"juli"] = 7;  m_MonthNamesMap[L"okt"] = 10;  m_MonthNamesMap[L"dez"] = 12;  //Austrian month names  m_MonthNamesMap[L"j\xE4n"] = 1;  m_MonthNamesMap[L"j\xC3\xA4n"] = 1; // UTF-8  //French month names  m_MonthNamesMap[L"janv"] = 1;  m_MonthNamesMap[L"f\xE9"L"b"] = 1;  m_MonthNamesMap[L"f\xE9v"] = 2;  m_MonthNamesMap[L"fev"] = 2;  m_MonthNamesMap[L"f\xE9vr"] = 2;  m_MonthNamesMap[L"fevr"] = 2;  m_MonthNamesMap[L"mars"] = 3;  m_MonthNamesMap[L"mrs"] = 3;  m_MonthNamesMap[L"avr"] = 4;  m_MonthNamesMap[L"juin"] = 6;  m_MonthNamesMap[L"juil"] = 7;  m_MonthNamesMap[L"jui"] = 7;  m_MonthNamesMap[L"ao\xFB"] = 8;  m_MonthNamesMap[L"ao\xFBt"] = 8;  m_MonthNamesMap[L"aout"] = 8;  m_MonthNamesMap[L"d\xE9"L"c"] = 12;  m_MonthNamesMap[L"dec"] = 12;  //Italian month names  m_MonthNamesMap[L"gen"] = 1;  m_MonthNamesMap[L"mag"] = 5;  m_MonthNamesMap[L"giu"] = 6;  m_MonthNamesMap[L"lug"] = 7;  m_MonthNamesMap[L"ago"] = 8;  m_MonthNamesMap[L"set"] = 9;  m_MonthNamesMap[L"ott"] = 10;  m_MonthNamesMap[L"dic"] = 12;  //Spanish month names  m_MonthNamesMap[L"ene"] = 1;  m_MonthNamesMap[L"fbro"] = 2;  m_MonthNamesMap[L"mzo"] = 3;  m_MonthNamesMap[L"ab"] = 4;  m_MonthNamesMap[L"abr"] = 4;  m_MonthNamesMap[L"agto"] = 8;  m_MonthNamesMap[L"sbre"] = 9;  m_MonthNamesMap[L"obre"] = 9;  m_MonthNamesMap[L"nbre"] = 9;  m_MonthNamesMap[L"dbre"] = 9;  //Polish month names  m_MonthNamesMap[L"sty"] = 1;  m_MonthNamesMap[L"lut"] = 2;  m_MonthNamesMap[L"kwi"] = 4;  m_MonthNamesMap[L"maj"] = 5;  m_MonthNamesMap[L"cze"] = 6;  m_MonthNamesMap[L"lip"] = 7;  m_MonthNamesMap[L"sie"] = 8;  m_MonthNamesMap[L"wrz"] = 9;  m_MonthNamesMap[L"pa"] = 10;  m_MonthNamesMap[L"lis"] = 11;  m_MonthNamesMap[L"gru"] = 12;  //Russian month names  m_MonthNamesMap[L"\xFF\xED\xE2"] = 1;  m_MonthNamesMap[L"\xF4\xE5\xE2"] = 2;  m_MonthNamesMap[L"\xEC\xE0\xF0"] = 3;  m_MonthNamesMap[L"\xE0\xEF\xF0"] = 4;  m_MonthNamesMap[L"\xEC\xE0\xE9"] = 5;  m_MonthNamesMap[L"\xE8\xFE\xED"] = 6;  m_MonthNamesMap[L"\xE8\xFE\xEB"] = 7;  m_MonthNamesMap[L"\xE0\xE2\xE3"] = 8;  m_MonthNamesMap[L"\xF1\xE5\xED"] = 9;  m_MonthNamesMap[L"\xEE\xEA\xF2"] = 10;  m_MonthNamesMap[L"\xED\xEE\xFF"] = 11;  m_MonthNamesMap[L"\xE4\xE5\xEA"] = 12;  //Dutch month names  m_MonthNamesMap[L"mrt"] = 3;  m_MonthNamesMap[L"mei"] = 5;  //Portuguese month names  m_MonthNamesMap[L"out"] = 10;  //Japanese month names  m_MonthNamesMap[L"1\x8c\x8e"] = 1;  m_MonthNamesMap[L"2\x8c\x8e"] = 2;  m_MonthNamesMap[L"3\x8c\x8e"] = 3;  m_MonthNamesMap[L"4\x8c\x8e"] = 4;  m_MonthNamesMap[L"5\x8c\x8e"] = 5;  m_MonthNamesMap[L"6\x8c\x8e"] = 6;  m_MonthNamesMap[L"7\x8c\x8e"] = 7;  m_MonthNamesMap[L"8\x8c\x8e"] = 8;  m_MonthNamesMap[L"9\x8c\x8e"] = 9;  m_MonthNamesMap[L"10\x8c\x8e"] = 10;  m_MonthNamesMap[L"11\x8c\x8e"] = 11;  m_MonthNamesMap[L"12\x8c\x8e"] = 12;  //Korean (Unicode) month names  m_MonthNamesMap[L"1\xC6\xD4"] = 1;  m_MonthNamesMap[L"2\xC6\xD4"] = 2;  m_MonthNamesMap[L"3\xC6\xD4"] = 3;  m_MonthNamesMap[L"4\xC6\xD4"] = 4;  m_MonthNamesMap[L"5\xC6\xD4"] = 5;  m_MonthNamesMap[L"6\xC6\xD4"] = 6;  m_MonthNamesMap[L"7\xC6\xD4"] = 7;  m_MonthNamesMap[L"8\xC6\xD4"] = 8;  m_MonthNamesMap[L"9\xC6\xD4"] = 9;  m_MonthNamesMap[L"10\xC6\xD4"] = 10;  m_MonthNamesMap[L"11\xC6\xD4"] = 11;  m_MonthNamesMap[L"12\xC6\xD4"] = 12;  //Korean (EUC-KR) month names  m_MonthNamesMap[L"1\xBF\xF9"] = 1;  m_MonthNamesMap[L"2\xBF\xF9"] = 2;  m_MonthNamesMap[L"3\xBF\xF9"] = 3;  m_MonthNamesMap[L"4\xBF\xF9"] = 4;  m_MonthNamesMap[L"5\xBF\xF9"] = 5;  m_MonthNamesMap[L"6\xBF\xF9"] = 6;  m_MonthNamesMap[L"7\xBF\xF9"] = 7;  m_MonthNamesMap[L"8\xBF\xF9"] = 8;  m_MonthNamesMap[L"9\xBF\xF9"] = 9;  m_MonthNamesMap[L"10\xBF\xF9"] = 10;  m_MonthNamesMap[L"11\xBF\xF9"] = 11;  m_MonthNamesMap[L"12\xBF\xF9"] = 12;  //Finnish month names  m_MonthNamesMap[L"tammi"] = 1;  m_MonthNamesMap[L"helmi"] = 2;  m_MonthNamesMap[L"maalis"] = 3;  m_MonthNamesMap[L"huhti"] = 4;  m_MonthNamesMap[L"touko"] = 5;  m_MonthNamesMap[L"kes\xE4"] = 6;  m_MonthNamesMap[L"hein\xE4"] = 7;  m_MonthNamesMap[L"elo"] = 8;  m_MonthNamesMap[L"syys"] = 9;  m_MonthNamesMap[L"loka"] = 10;  m_MonthNamesMap[L"marras"] = 11;  m_MonthNamesMap[L"joulu"] = 12;  //There are more languages and thus month  //names, but as long nobody reports a  //problem, I won't add them, there are way  //too much languages  //Some very strange combinations of names and numbers I've seen.  //The developers of those ftp servers must have been dumb.  m_MonthNamesMap[L"jan1"] = 1;  m_MonthNamesMap[L"feb2"] = 2;  m_MonthNamesMap[L"mar3"] = 3;  m_MonthNamesMap[L"apr4"] = 4;  m_MonthNamesMap[L"may5"] = 5;  m_MonthNamesMap[L"jun6"] = 6;  m_MonthNamesMap[L"jul7"] = 7;  m_MonthNamesMap[L"aug8"] = 8;  m_MonthNamesMap[L"sep9"] = 9;  m_MonthNamesMap[L"sept9"] = 9;  m_MonthNamesMap[L"oct0"] = 10;  m_MonthNamesMap[L"nov1"] = 11;  m_MonthNamesMap[L"dec2"] = 12;  // Slovenian month names  m_MonthNamesMap[L"avg"] = 8;#ifdef LISTDEBUG  int i=-1;  while (*data[++i])  {    char *pData=new char[strlen(data[i])+3];    sprintf(pData, "%s\r\n", data[i]);    AddData(pData, strlen(pData));  }  TRACE1("%d lines added\n", i);#endif}CFtpListResult::~CFtpListResult(){  t_list *ptr=listhead;  t_list *ptr2;  while (ptr)  {    delete [] ptr->buffer;    ptr2=ptr;    ptr=ptr->next;    delete ptr2;  }  if (m_prevline)    delete [] m_prevline;  if (m_curline)    delete [] m_curline;}t_directory::t_direntry *CFtpListResult::getList(int &num, bool mlst){  #ifdef _DEBUG  USES_CONVERSION;  #endif  char *line=GetLine();  m_curline=line;  while (line)  {    int tmp;    char *tmpline = new char[strlen(line) + 1];    strcpy(tmpline, line);    t_directory::t_direntry direntry;    if (parseLine(tmpline, strlen(tmpline), direntry, tmp, mlst))    {      delete [] tmpline;      if (tmp)        m_server.nServerType |= tmp;      if (direntry.name!=L"." && direntry.name!=L"..")      {        AddLine(direntry);      }      if (m_prevline)      {        delete [] m_prevline;        m_prevline=0;      }      if (m_curline!=line)        delete [] m_curline;      delete [] line;      line=GetLine();      m_curline=line;    }    else    {      delete [] tmpline;      if (m_prevline)      {        if (m_curline!=line)        {          delete [] m_prevline;          m_prevline=m_curline;          delete [] line;          line=GetLine();          m_curline=line;        }        else        {          line=new char[strlen(m_prevline)+strlen(m_curline)+2];          sprintf(line, "%s %s", m_prevline, m_curline);        }      }      else      {        m_prevline=line;        line=GetLine();        m_curline=line;      }    }  }  if (m_prevline)  {    delete [] m_prevline;    m_prevline=0;  }  if (m_curline!=line)    delete [] m_curline;  delete [] line;  m_curline=0;  num=m_EntryList.size();  if (!num)    return 0;  t_directory::t_direntry *res=new t_directory::t_direntry[num];  int i=0;  for (tEntryList::iterator iter=m_EntryList.begin();iter!=m_EntryList.end();iter++, i++)  {    res[i]=*iter;  }  m_EntryList.clear();  return res;}BOOL CFtpListResult::parseLine(const char *lineToParse, const int linelen, t_directory::t_direntry &direntry, int &nFTPServerType, bool mlst){  USES_CONVERSION;  nFTPServerType = 0;  direntry.ownergroup = L"";  direntry.owner = L"";  direntry.group = L"";  if (parseAsMlsd(lineToParse, linelen, direntry, mlst))    return TRUE;  if (parseAsUnix(lineToParse, linelen, direntry))    return TRUE;  if (parseAsDos(lineToParse, linelen, direntry))    return TRUE;  if (parseAsEPLF(lineToParse, linelen, direntry))    return TRUE;  if (parseAsVMS(lineToParse, linelen, direntry))  {#ifndef LISTDEBUG    m_server.nServerType |= FZ_SERVERTYPE_SUB_FTP_VMS;#endif // LISTDEBUG    return TRUE;  }  if (parseAsOther(lineToParse, linelen, direntry))    return TRUE;  if (parseAsIBMMVS(lineToParse, linelen, direntry))    return TRUE;  if (parseAsIBMMVSPDS(lineToParse, linelen, direntry))    return TRUE;  if (parseAsIBM(lineToParse, linelen, direntry))    return TRUE;  if (parseAsWfFtp(lineToParse, linelen, direntry))    return TRUE;  // Should be last  if (parseAsIBMMVSPDS2(lineToParse, linelen, direntry))    return TRUE;  // name-only entries  if (strchr(lineToParse, ' ') == NULL)  {    copyStr(direntry.name, 0, lineToParse, strlen(lineToParse));    return TRUE;  }  return FALSE;}// Used only with LISTDEBUGvoid CFtpListResult::AddData(char *data, int size){  #ifdef _DEBUG  USES_CONVERSION;  #endif  if (!size)    return;  if (!m_curlistaddpos)    m_curlistaddpos = new t_list;  else  {    m_curlistaddpos->next = new t_list;    m_curlistaddpos = m_curlistaddpos->next;  }  if (!listhead)  {    curpos = m_curlistaddpos;    listhead = m_curlistaddpos;  }  m_curlistaddpos->buffer = data;  m_curlistaddpos->len = size;  m_curlistaddpos->next = 0;  t_list *pOldListPos = curpos;  int nOldListBufferPos = pos;  //Try if there are already some complete lines  t_directory::t_direntry direntry;  char *line = GetLine();  m_curline = line;  while (line)  {    if (curpos)    {      pOldListPos = curpos;      nOldListBufferPos = pos;    }    else    {      delete [] line;      if (m_curline != line)        delete [] m_curline;      m_curline = 0;      break;    }    int tmp;    char *tmpline = new char[strlen(line) + 1];    strcpy(tmpline, line);    if (parseLine(tmpline, strlen(tmpline), direntry, tmp, false))    {      delete [] tmpline;      if (tmp)        m_server.nServerType |= tmp;      if (direntry.name!=L"." && direntry.name!=L"..")      {        AddLine(direntry);      }      if (m_prevline)      {        delete [] m_prevline;        m_prevline=0;      }      if (m_curline!=line)        delete [] m_curline;      delete [] line;      line = GetLine();      m_curline = line;    }    else    {      delete [] tmpline;      if (m_prevline)      {        if (m_curline != line)        {          delete [] m_prevline;          m_prevline = m_curline;          delete [] line;          line = GetLine();          m_curline = line;        }        else        {          line=new char[strlen(m_prevline)+strlen(m_curline)+2];          sprintf(line, "%s %s", m_prevline, m_curline);        }      }      else      {        m_prevline=line;        line=GetLine();        m_curline=line;      }    }  }  curpos=pOldListPos;  pos=nOldListBufferPos;}void CFtpListResult::SendToMessageLog(){  t_list *oldlistpos = curpos;  int oldbufferpos = pos;  curpos = listhead;  pos=0;  char *line = GetLine();  // Note that FZ_LOG_INFO here is not checked against debug level, as the direct  // call to PostMessage bypasses check in LogMessage.  // So we get the listing on any logging level, what is actually what we want  if (!line)  {    //Displays a message in the message log    t_ffam_statusmessage *pStatus = new t_ffam_statusmessage;    pStatus->post = TRUE;    pStatus->status = L"<Empty directory listing>";    pStatus->type = FZ_LOG_INFO;    GetIntern()->PostMessage(FZ_MSG_MAKEMSG(FZ_MSG_STATUS, 0), (LPARAM)pStatus);  }  while (line)  {    CString status = line;    delete [] line;    //Displays a message in the message log    t_ffam_statusmessage *pStatus = new t_ffam_statusmessage;    pStatus->post = TRUE;    pStatus->status = status;    pStatus->type = FZ_LOG_INFO;    if (!GetIntern()->PostMessage(FZ_MSG_MAKEMSG(FZ_MSG_STATUS, 0), (LPARAM)pStatus))      delete pStatus;    line = GetLine();  }  curpos = oldlistpos;  pos = oldbufferpos;}char * CFtpListResult::GetLine(){  if (!curpos)    return 0;  int len=curpos->len;  while (curpos->buffer[pos]=='\r' || curpos->buffer[pos]=='\n' || curpos->buffer[pos]==' ' || curpos->buffer[pos]=='\t')  {    pos++;    if (pos>=len)    {      curpos=curpos->next;      if (!curpos)        return 0;      len=curpos->len;      pos=0;    }  }  t_list *startptr=curpos;  int startpos=pos;  int reslen=0;  int emptylen=0;  while ((curpos->buffer[pos]!='\n')&&(curpos->buffer[pos]!='\r'))  {    if (curpos->buffer[pos]!=' ' && curpos->buffer[pos]!='\t')    {      reslen+=emptylen+1;      emptylen=0;    }    else      emptylen++;    pos++;    if (pos>=len)    {      curpos=curpos->next;      if (!curpos)        break;      len=curpos->len;      pos=0;    }  }  char *res = new char[reslen+1];  res[reslen]=0;  int respos=0;  while (startptr!=curpos && reslen)  {    int copylen=startptr->len-startpos;    if (copylen>reslen)      copylen=reslen;    memcpy(&res[respos],&startptr->buffer[startpos], copylen);    reslen-=copylen;    respos+=startptr->len-startpos;    startpos=0;    startptr=startptr->next;  }  if (curpos && reslen)  {    int copylen=pos-startpos;    if (copylen>reslen)      copylen=reslen;    memcpy(&res[respos], &curpos->buffer[startpos], copylen);  }  return res;}void CFtpListResult::AddLine(t_directory::t_direntry &direntry){  if (m_server.nTimeZoneOffset &&    direntry.date.hasdate && direntry.date.hastime && !direntry.date.utc)  {    SYSTEMTIME st = {0};    st.wYear = direntry.date.year;    st.wMonth = direntry.date.month;    st.wDay = direntry.date.day;    st.wHour = direntry.date.hour;    st.wMinute = direntry.date.minute;    st.wSecond = direntry.date.second;    FILETIME ft;    SystemTimeToFileTime(&st, &ft);    _int64 nFt = ((_int64)ft.dwHighDateTime << 32) + ft.dwLowDateTime;    _int64 nFt2 = nFt;    nFt += ((_int64)m_server.nTimeZoneOffset) * 10000000 * 60;    ft.dwHighDateTime = static_cast<unsigned long>(nFt >> 32);    ft.dwLowDateTime = static_cast<unsigned long>(nFt & 0xFFFFFFFF);    FileTimeToSystemTime(&ft, &st);    direntry.date.year = st.wYear;    direntry.date.month = st.wMonth;    direntry.date.day = st.wDay;    direntry.date.hour = st.wHour;    direntry.date.minute = st.wMinute;    direntry.date.second = st.wSecond;  }  if (m_server.nServerType&FZ_SERVERTYPE_SUB_FTP_VMS &&    (!GetOptionVal(OPTION_VMSALLREVISIONS) || direntry.dir))  { //Remove version information, only keep the latest file    int pos=direntry.name.ReverseFind(L';');    if (pos<=0 || pos>=(direntry.name.GetLength()-1))      return;;    int version=_ttoi(direntry.name.Mid(pos+1));    direntry.name=direntry.name.Left(pos);    tEntryList::iterator entryiter=m_EntryList.begin();    tTempData::iterator dataiter=m_TempData.begin();    BOOL bContinue=FALSE;    while (entryiter!=m_EntryList.end())    {      DebugAssert(dataiter!=m_TempData.end());      t_directory::t_direntry dir=*entryiter;      int oldversion=*dataiter;      if (direntry.name==dir.name)      {        bContinue=TRUE;        if (version>oldversion)        {          *entryiter=direntry;          *dataiter=version;        }        break;      }      entryiter++;      dataiter++;    }    if (bContinue)      return;    m_EntryList.push_back(direntry);    m_TempData.push_back(version);  }  else  {    m_EntryList.push_back(direntry);    m_TempData.push_back(0);  }}bool CFtpListResult::IsNumeric(const char *str, int len) const{  if (!str)    return false;  if (!*str)    return false;  const char *p=str;  while(*p)  {    if (len != -1)      if ((p - str) >= len)        return true;    if (*p<'0' || *p>'9')    {      return false;    }    p++;  }  return true;}bool CFtpListResult::ParseShortDate(const char *str, int len, t_directory::t_direntry::t_date &date) const{  if (!str)    return false;  if (len <= 0)    return false;  int i=0;  //Extract the date  BOOL bGotYear = FALSE;  BOOL bGotMonth = FALSE;  BOOL bGotDay = FALSE;  int value = 0;  bool numeric = true;  while (str[i] != '-' && str[i] != '.' && str[i] != '/')  {    if (!str[i])      return false;    // Left half of token not numeric, check if it's a month name    if (str[i] < '0' || str[i] > '9')      numeric = false;    else    {      value *= 10;      value += str[i] - '0';    }    i++;    if (i == len)      return false;  }  if (!i)    return false;  if (!numeric)  {    std::map<CString, int>::const_iterator iter;    char *tmpstr = new char[i + 1];    strncpy(tmpstr, str, i);    tmpstr[i] = 0;    strlwr(tmpstr);    USES_CONVERSION;    iter = m_MonthNamesMap.find(A2T(tmpstr));    delete [] tmpstr;    if (iter == m_MonthNamesMap.end())      return false;    date.month = iter->second;    bGotMonth = true;  }  else if (i == 4)  { //Seems to be yyyy-mm-dd    if (value < 1900)      return false;    date.year = value;    bGotYear = TRUE;  }  else if (i <= 2)  {    if (str[i] == '.')    {      // Maybe dd.mm.yyyy      if (!value || value > 31)        return false;      date.day = value;      bGotDay = TRUE;    }    else    {      // Seems to be mm-dd-yyyy or mm/dd/yyyy (stupid format, though)      if (!value)        return false;      else if (value > 12) // sigh, guess dd/mm/yyyy instead      {        date.day = value;        bGotDay = true;      }      else      {        date.month = value;        bGotMonth = TRUE;      }    }  }  else    return false;  //Extract the second date field  const char *p = str + i + 1;  len -= i + 1;  i=0;  value=0;  if (i >= len)    return false;  while (p[i]!='-' && p[i]!='.' && p[i]!='/')  {    value *= 10;    value += p[i]-'0';    i++;    if (i >= len)      return false;  }  if (bGotYear || bGotDay)  {    // Month field in yyyy-mm-dd or dd-mm-yyyy    if (!value || value > 12)      return false;    date.month = value;    bGotMonth = TRUE;  }  else  {    // Day field in mm-dd-yyyy    if (!value || value > 31)      return false;    date.day = value;    bGotDay = TRUE;  }  //Extract the last date field  p += i+1;  len -= i + 1;  i=0;  value=0;  if (i >= len)    return false;  while (p[i]!='-' && p[i]!='.' && p[i]!='/')  {    value *= 10;    value += p[i]-'0';    i++;    if (i >= len)      break;  }  if (bGotYear)  {    // Day field in yyyy-mm-dd    if (!value || value > 31)      return false;    date.day = value;  }  else  {    //Year in dd.mm.yyyy or mm-dd-yyyy    date.year = value;    if (date.year<50)      date.year+=2000;    else if (date.year<1000)      date.year += 1900;  }  date.hasdate = TRUE;  return true;}BOOL CFtpListResult::parseAsVMS(const char *line, const int linelen, t_directory::t_direntry &direntry){  int tokenlen = 0;  int pos = 0;  USES_CONVERSION;  std::map<CString, int>::const_iterator iter;  t_directory::t_direntry dir;  dir.bUnsure = FALSE;  const char *str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!strnchr(str, tokenlen, ';'))    return FALSE;  dir.size = -2;  const char *separator = strnchr(str, tokenlen, ';');  if (!separator)    return FALSE;  dir.dir = FALSE;  if ((separator - str) > 4)    if (*(separator - 4) == '.')      if (*(separator - 3) == 'D')        if (*(separator - 2) == 'I')          if (*(separator - 1) == 'R')          {            dir.dir = TRUE;          }  if (dir.dir)  {    int i;    LPTSTR pBuffer = dir.name.GetBuffer(tokenlen - 4);    for (i = 0; i < (separator - str - 4); i++)      pBuffer[i] = str[i];    for (i = 0; i < (tokenlen - (separator - str)); i++)      pBuffer[i + (separator - str) - 4] = separator[i];    dir.name.ReleaseBuffer(tokenlen - 4);  }  else    copyStr(dir.name, 0, str, tokenlen);  // This field is either the size or a username (???) enclosed in [].  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  bool gotSize = false;  const char *p = strnchr(str, tokenlen, '/');  if (!p && IsNumeric(str, tokenlen))  {    gotSize = true;    dir.size = strntoi64(str, tokenlen) * 512;  }  else if (p && p > str && IsNumeric(str, p - str))  {    gotSize = true;    dir.size = strntoi64(str, p - str) * 512;  }  else  {    if (tokenlen < 3 || str[0] != '[' || str[tokenlen - 1] != ']')      return false;    copyStr(dir.ownergroup, 0, str + 1, tokenlen - 2);  }  if (!gotSize)  {    //Size    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    const char *p = strnchr(str, tokenlen, '/');    int len;    if (p)      len = p - str;    else      len = tokenlen;    if (!IsNumeric(str, len))      return FALSE;    dir.size = strntoi64(str, len) * 512;  }  //Get date  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  dir.date.hasdate = TRUE;  //Day  p = str;  while (*p != '-')    if ((++p - str) == tokenlen)      return 0;  dir.date.day = static_cast<int>(strntoi64(str, p-str));  if (!dir.date.day || dir.date.day > 31)    return FALSE;  p++;  const char *pMonth = p;  //Month  while (*p != '-')    if ((++p - str) == tokenlen)      return FALSE;  if ((p - pMonth) >= 15)    return FALSE;  char buffer[15] = {0};  memcpy(buffer, pMonth, p-pMonth);  strlwr(buffer);  iter = m_MonthNamesMap.find(A2T(buffer));  if (iter == m_MonthNamesMap.end())    return FALSE;  dir.date.month = iter->second;  p++;  dir.date.year = static_cast<int>(strntoi64(p, tokenlen - (p - str)));  //Get time  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (str && strnchr(str + 1, tokenlen - 2, ':'))  {    dir.date.hastime = TRUE;    p = str;    //Hours    while (*p != ':')      if ((++p - str) == tokenlen)        return 0;    dir.date.hour = static_cast<int>(strntoi64(str, p - str));    if (dir.date.hour < 0 || dir.date.hour > 23)      return FALSE;    p++;    const char *pMinute = p;    //Minutes    while (*p && *p != ':' )      p++;    dir.date.minute = static_cast<int>(strntoi64(pMinute, p - pMinute));    if (dir.date.minute < 0 || dir.date.minute > 59)      return FALSE;    str = GetNextToken(line, linelen, tokenlen, pos, 0);  }  else  {    dir.date.hastime = FALSE;  }  while (str)  {    if (tokenlen > 2 && str[0] == '(' && str[tokenlen - 1] == ')')    {      if (dir.permissionstr != L"")        dir.permissionstr += L" ";      CString tmp;      copyStr(tmp, 0, str + 1, tokenlen - 2);      dir.permissionstr += tmp;    }    else if (tokenlen > 2 && str[0] == '[' && str[tokenlen - 1] == ']')    {      if (dir.ownergroup != L"")        dir.ownergroup += L" ";      CString tmp;      copyStr(tmp, 0, str + 1, tokenlen - 2);      dir.ownergroup += tmp;    }    else    {      if (dir.permissionstr != L"")        dir.permissionstr += L" ";      CString tmp;      copyStr(tmp, 0, str, tokenlen);      dir.permissionstr += tmp;    }    str = GetNextToken(line, linelen, tokenlen, pos, 0);  }  direntry = dir;  return TRUE;}BOOL CFtpListResult::parseAsEPLF(const char *line, const int linelen, t_directory::t_direntry &direntry){  t_directory::t_direntry dir;  const char *str = strstr(line, "\t");  //Check if directory listing is an EPLF one  if (*line=='+' && str)  {    str++;    if (!*str)      return FALSE;    dir.bLink = FALSE;    dir.bUnsure = FALSE;    dir.date.hasdate = dir.date.hastime = FALSE;    dir.dir = FALSE;    dir.size = -2;    dir.name = str;    const char *fact = line + 1;    const char *nextfact = fact;    nextfact = strstr(nextfact, ",");    //if (nextfact && nextfact < str)    //  *nextfact=0;    while (fact<=(str-2))    {      int len;      if (!nextfact)        len = str - fact - 1;      else        len = nextfact - fact;      if (len == 1 && fact[0] == '/')        dir.dir = TRUE;      else if (*fact=='s')        dir.size = strntoi64(fact+1, len-1);      else if (*fact=='m')      {        __int64 rawtime = strntoi64(fact+1, len-1);        COleDateTime time((time_t)rawtime);        dir.date.hasdate = TRUE;        dir.date.hastime = TRUE;        dir.date.year = time.GetYear();        dir.date.month = time.GetMonth();        dir.date.day = time.GetDay();        dir.date.hour = time.GetHour();        dir.date.minute = time.GetMinute();      }      else if (len == 5 && *fact=='u' && *(fact+1)=='p')      {        char buffer[4] = {0};        memcpy(buffer, fact+2, len-2);        direntry.permissionstr = buffer;      }      if (!nextfact || nextfact>=(str-2))        break;      fact = nextfact+1;      nextfact = strstr(nextfact+1, ",");//      if (nextfact && nextfact<str)//        *nextfact=0;    }    direntry = dir;    return TRUE;  }  return FALSE;}BOOL CFtpListResult::parseAsMlsd(const char *line, const int linelen, t_directory::t_direntry &direntry, bool mlst){  #ifdef _DEBUG  USES_CONVERSION;  #endif  // MLSD format as described here: https://tools.ietf.org/html/rfc3659  // Parsing is done strict, abort on slightest error.  // If we ever add some detection that entry is symlink,  // make sure to add support for resolving the symlink  // using MLST to TFTPFileSystem::ReadSymlink  int pos = 0;  int tokenlen = 0;  const char *str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)  {    return FALSE;  }  CString facts(str, tokenlen);  if (facts.IsEmpty())  {    return FALSE;  }  direntry.bUnsure = FALSE;  direntry.dir = FALSE;  direntry.bLink = FALSE;  direntry.ownergroup = L"";  direntry.owner = L"";  direntry.group = L"";  direntry.permissionstr = L"";  CString owner, group, uid, gid, ownername, groupname;  while (!facts.IsEmpty())  {    int delim = facts.Find(';');    if (delim < 3)    {      if (delim != -1)      {        return 0;      }      else        delim = facts.GetLength();    }    int pos = facts.Find('=');    if (pos < 1 || pos > delim)    {      return FALSE;    }    CString factname = facts.Left(pos);    factname.MakeLower();    CString value = facts.Mid(pos + 1, delim - pos - 1);    // When adding new facts, update filter in CFtpControlSocket::LogOnToServer    // (CONNECT_FEAT state)    if (factname == L"type")    {      if (!value.CompareNoCase(L"dir"))      {        direntry.dir = TRUE;      }      // This is syntax used by proftpd by default      // http://www.proftpd.org/docs/modules/mod_facts.html      // They claim it's the correct one.      // See also      // https://www.rfc-editor.org/errata_search.php?rfc=3659&eid=1500      else if (!value.Left(15).CompareNoCase(L"OS.unix=symlink"))      {        direntry.dir = TRUE;        direntry.bLink = TRUE;        // actually symlink target should not be included in this syntax,        // but just in case some servers do.        if ((value.GetLength() > 16) && (value[15] == ':'))          direntry.linkTarget = value.Mid(16);      }      // This is syntax shown in RFC 3659 section 7.7.4 "A More Complex Example"      // Type=OS.unix=slink:/foobar;Perm=;Unique=keVO1+4G4; foobar      // https://tools.ietf.org/html/rfc3659      else if (!value.Left(13).CompareNoCase(L"OS.unix=slink"))      {        direntry.dir = TRUE;        direntry.bLink = TRUE;        if ((value.GetLength() > 14) && (value[13] == ':'))          direntry.linkTarget = value.Mid(14);      }      else if (!value.CompareNoCase(L"cdir"))      {        // ProFTPD up to 1.3.6rc1 and 1.3.5a incorrectly uses "cdir" for the current working directory.        // So at least in MLST, where this would be the only entry, we treat it like "dir".        if (mlst)        {          direntry.dir = TRUE;        }        else        {          return FALSE;        }      }      else if (!value.CompareNoCase(L"pdir"))      {        return FALSE;      }    }    else if (factname == L"size")    {      direntry.size = 0;      for (unsigned int i = 0; i < value.GetLength(); ++i)      {        if (value[i] < '0' || value[i] > '9')        {          return FALSE;        }        direntry.size *= 10;        direntry.size += value[i] - '0';      }    }    else if (factname == L"modify" ||      (!direntry.date.hasdate && factname == L"create"))    {      if (!parseMlsdDateTime(value, direntry.date))      {        return FALSE;      }    }    else if (factname == L"perm")    {      // there's no way we can convert Perm fact to unix-style permissions,      // so we at least present Perm as-is to a user      direntry.humanpermstr = value;    }    else if (factname == L"unix.mode")    {      direntry.permissionstr = value;    }    else if (factname == L"unix.owner" || factname == L"unix.user")    {      owner = value;    }    else if (factname == L"unix.group")    {      group = value;    }    else if (factname == L"unix.uid")    {      uid = value;    }    else if (factname == L"unix.gid")    {      gid = value;    }    else if (factname == L"unix.ownername")    {      ownername = value;    }    else if (factname == L"unix.groupname")    {      groupname = value;    }    facts = facts.Mid(delim + 1);  }  // The order of the facts is undefined  if (!ownername.IsEmpty())    direntry.owner = ownername;  else if (!owner.IsEmpty())    direntry.owner = owner;  else if (!uid.IsEmpty())    direntry.owner = uid;  if (!groupname.IsEmpty())    direntry.group = groupname;  else if (!group.IsEmpty())    direntry.group = group;  else if (!gid.IsEmpty())    direntry.group = gid;  if (line[pos] != L' ')  {    return FALSE;  }  pos++;  CString fileName;  copyStr(fileName, 0, line + pos, linelen - pos, true);  if (mlst)  {    // do not try to detect path type, assume a standard *nix syntax + do not trim    CServerPath path(fileName, FZ_SERVERTYPE_FTP, false);    direntry.name = path.GetLastSegment();    if (direntry.name.IsEmpty())    {      direntry.name = fileName;    }  }  else  {    direntry.name = fileName;  }  return TRUE;}bool CFtpListResult::parseMlsdDateTime(const CString value, t_directory::t_direntry::t_date &date) const{  if (value.IsEmpty())  {    return FALSE;  }  bool result = FALSE;  int Year, Month, Day, Hours, Minutes, Seconds;  Year=Month=Day=Hours=Minutes=Seconds=0;  // Time can include a fraction after a dot, this will ignore the fraction part.  if (swscanf((LPCWSTR)value, L"%4d%2d%2d%2d%2d%2d", &Year, &Month, &Day, &Hours, &Minutes, &Seconds) == 6)  {    date.hasdate = TRUE;    date.hastime = TRUE;    date.hasseconds = TRUE;    result = TRUE;  }  else if (swscanf((LPCWSTR)value, L"%4d%2d%2d", &Year, &Month, &Day) == 3)  {    date.hasdate = TRUE;    date.hastime = FALSE;    result = TRUE;  }  if (result)  {    date.year = Year;    date.month = Month;    date.day = Day;    date.hour = Hours;    date.minute = Minutes;    date.second = Seconds;    date.utc = TRUE;  }  return result;}BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_directory::t_direntry &direntry){  int pos = 0;  int tokenlen = 0;  const char *str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)  {    return FALSE;  }  //Check the first token  if (str[0] != 'b' &&    str[0] != 'c' &&    str[0] != 'd' &&    str[0] != 'l' &&    str[0] != 'p' &&    str[0] != 's' &&    str[0] != '-')  {    return FALSE;  }  //First check if it is a netware server  bool bNetWare = false;  copyStr(direntry.permissionstr, 0, str, tokenlen);  if (tokenlen == 1)  {    //Yes, it's most likely a netware server    //Now get the full permission string    bNetWare = true;    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)    {      return FALSE;    }    direntry.permissionstr += L" ";    copyStr(direntry.permissionstr, direntry.permissionstr.GetLength(), str, tokenlen);  }  //Set directory and link flags  //Always assume links point directories  //GUI frontend should try to figure out  //to where the link really points  if (direntry.permissionstr[0]==L'd' || direntry.permissionstr[0]==L'l')    direntry.dir = true;  else    direntry.dir = false;  if (direntry.permissionstr[0]==L'l')    direntry.bLink = true;  else    direntry.bLink = false;  bool bNetPresenz = false;  if (!bNetWare) //On non-netware servers, expect at least two unused tokens  {    bool groupid = false;    // Unused param    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)    {      return FALSE;    }    if (tokenlen == 6 && !strncmp(str, "folder", tokenlen) && direntry.dir)      bNetPresenz = true;  //Assume NetPresenz server                //However, it's possible that we mark a non-NetPresenz                //server if the fileowner is "folder"    else if (!IsNumeric(str, tokenlen))    {      // Check for Connect:Enterprise server      if (direntry.permissionstr.GetLength() > 3 &&        (direntry.permissionstr.Right(3) == L"FTP" ||         direntry.permissionstr.Right(3) == L"FTS" ||         direntry.permissionstr.Right(3) == L"TCP" ||         direntry.permissionstr.Right(3) == L"SSH"))        groupid = TRUE;      copyStr(direntry.ownergroup, direntry.ownergroup.GetLength(), str, tokenlen);    }    else      groupid = TRUE;    if (!bNetPresenz && groupid)    {      //Unused param      str = GetNextToken(line, linelen, tokenlen, pos, 0);      if (!str)      {        return FALSE;      }      if (direntry.ownergroup != L"")        direntry.ownergroup += L" ";      copyStr(direntry.ownergroup, direntry.ownergroup.GetLength(), str, tokenlen);    }  }  //Skip param, may be used for size  int skippedlen = 0;  const char *skipped = GetNextToken(line, linelen,skippedlen, pos, 0);  if (!skipped)  {    return FALSE;  }  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)  {    return FALSE;  }  // Keep parsing the information until we get a numerical string,  // because some broken servers may send 3 tags: domain/network, group and user  const char *prevstr = 0;  int prevstrlen = 0;  __int64 tmp = 0;  std::map<CString, int>::const_iterator iter;  while (str && !ParseSize(str, tokenlen, tmp) && !IsNumeric(skipped, skippedlen))  {    //Maybe the server has left no space between the group and the size    //because of stupid alignment    char *tmpstr = new char[tokenlen + 1];    strncpy(tmpstr, str, tokenlen);    tmpstr[tokenlen] = 0;    strlwr(tmpstr);    USES_CONVERSION;    iter = m_MonthNamesMap.find(A2T(tmpstr));    delete [] tmpstr;    if (iter != m_MonthNamesMap.end())    {      BOOL bRightNumeric = true;      if (skipped[skippedlen-1]<'0' || skipped[skippedlen-1]>'9')        bRightNumeric = false;      if (bRightNumeric)        break;      bRightNumeric = true;      if (prevstr && prevstrlen)      {        if (prevstr[prevstrlen-1]<'0' || prevstr[prevstrlen-1]>'9')          bRightNumeric = false;        if (bRightNumeric)        {          if (direntry.ownergroup != L"")            direntry.ownergroup += L" ";          copyStr(direntry.ownergroup, direntry.ownergroup.GetLength(), str, tokenlen);          skipped = prevstr;          skippedlen = prevstrlen;          break;        }      }    }    if (prevstr)    {      if (direntry.ownergroup != L"")        direntry.ownergroup += L" ";      copyStr(direntry.ownergroup, direntry.ownergroup.GetLength(), prevstr, prevstrlen);    }    prevstr = str;    prevstrlen = tokenlen;    str = GetNextToken(line, linelen, tokenlen, pos, 0);  }  if (!str)  {    return FALSE;  }  const char *size = str;  int sizelen = tokenlen;  if (!ParseSize(str, tokenlen, direntry.size))  {    //Maybe we've skipped too much tokens    if (!ParseSize(skipped, skippedlen, direntry.size))    {      //Maybe the server has left no space between the group and the size      //because of stupid alignment      bool bRightNumeric = true;      const char *pos;      for (pos=(str+tokenlen-1); pos > str; pos--)      {        if (*pos<'0' || *pos>'9')        {          if (pos==(str+tokenlen-1))            bRightNumeric=false;          break;        }      }      if (bRightNumeric && pos>str)      {        size = pos + 1;        sizelen = pos - str;        direntry.ownergroup += L" ";        if (direntry.ownergroup != L"")          direntry.ownergroup += L" ";        copyStr(direntry.ownergroup, direntry.ownergroup.GetLength(), str, pos-str);      }      else      {        for (pos=(skipped+skippedlen-1); pos > skipped; pos--)        {          if (*pos<'0' || *pos>'9')          {            if (pos==(skipped+skippedlen-1))            {              return false;            }            break;          }        }        size = pos + 1;        sizelen = skippedlen + skipped - size;        if (direntry.ownergroup != L"")          direntry.ownergroup += L" ";        copyStr(direntry.ownergroup, direntry.ownergroup.GetLength(), skipped, skippedlen - sizelen);      }      direntry.size = strntoi64(size, sizelen);    }    else    {      //We should've not skipped the last token      //This also fixes the problem with the NetPresenz detection      if (bNetPresenz && direntry.dir && direntry.ownergroup != L"")      {        direntry.ownergroup = L"folder " + direntry.ownergroup;      }    }  }  else  {    if (direntry.ownergroup != L"")      direntry.ownergroup += L" ";    copyStr(direntry.ownergroup, direntry.ownergroup.GetLength(), skipped, skippedlen);    if (prevstr)    {      direntry.ownergroup += L" ";      copyStr(direntry.ownergroup, direntry.ownergroup.GetLength(), prevstr, prevstrlen);    }    str = 0;  }  //Month  if (!str)    str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)  {    return FALSE;  }  const char *smonth = str;  int smonthlen = tokenlen;  direntry.date.year = 0;  //Day  const char *sday = 0;  int sdaylen = 0;  // Some VShell server send both the year and the time, try to detect them  BOOL bCouldBeVShell = FALSE;  //Some servers use the following date formats:  // 26-09 2002, 2002-10-14, 01-jun-99  const char *p = strnchr(smonth, smonthlen, '-');  if (p)  {    int plen = smonthlen - (p - smonth);    const char *pos2 = strnchr(p+1, plen - 1, '-');    if (!pos2) //26-09 2002    {      sday = p + 1;      sdaylen = plen - 1;      smonthlen = p-smonth;    }    else if (p-smonth == 4) //2002-10-14    {      direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));      sday = pos2 + 1;      sdaylen = smonthlen - (pos2 - smonth) - 1;      smonthlen = pos2-smonth - (p-smonth) - 1;      smonth = p + 1;      /* Try to detect difference between yyyy/dd/mm and yyyy/mm/dd       * Unfortunately we have to guess which one is the right if       * the month is < 12       */      if (strntoi64(smonth, smonthlen) > 12)      {        const char *tmp = smonth;        smonth = sday;        sday = tmp;        int tmplen = smonthlen;        smonthlen = sdaylen;        sdaylen = tmplen;      }    }    else if (p-smonth) //14-10-2002 or 01-jun-99    {      direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));      sday = smonth;      sdaylen = p - smonth;      smonthlen = pos2-smonth - (p-smonth) - 1;      smonth = p + 1;      /* Try to detect difference between yyyy/dd/mm and yyyy/mm/dd       * Unfortunately we have to guess which one is the right if       * the month is < 12       */      if (strntoi64(smonth, smonthlen) > 12)      {        const char *tmp = smonth;        smonth = sday;        sday = tmp;        int tmplen = smonthlen;        smonthlen = sdaylen;        sdaylen = tmplen;      }    }    else    {      return FALSE;    }  }  /* Some servers use the following date formats:   * yyyy/dd/mm, yyyy/mm/dd, dd/mm/yyyy, mm/dd/yyyy   * try to detect them.   */  else if (strnchr(smonth, smonthlen, '/'))  {    const char *p = strnchr(smonth, smonthlen, '/');    int plen = smonthlen - (p - smonth);    const char *pos2 = strnchr(p+1, plen - 1, '/');    if (!pos2) //Assume 26/09 2002    {      sday = p + 1;      sdaylen = plen - 1;      smonthlen = p-smonth;    }    else if (p-smonth==4)    {      direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));      sday = pos2 + 1;      sdaylen = smonthlen - (pos2 - smonth) - 1;      smonthlen = pos2-smonth - (p-smonth) - 1;      smonth = p + 1;      /* Try to detect difference between yyyy/dd/mm and yyyy/mm/dd       * Unfortunately we have to guess which one is the right if       * the month is < 12       */      if (strntoi64(smonth, smonthlen) > 12)      {        const char *tmp = smonth;        smonth = sday;        sday = tmp;        int tmplen = smonthlen;        smonthlen = sdaylen;        sdaylen = tmplen;      }    }    else if (p-smonth==2)    {      direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));      sday = smonth;      sdaylen = p - smonth;      smonthlen = pos2-smonth - (p-smonth) - 1;      smonth = p + 1;      /* Try to detect difference between yyyy/dd/mm and yyyy/mm/dd       * Unfortunately we have to guess which one is the right if       * the month is < 12       */      if (strntoi64(smonth, smonthlen) > 12)      {        const char *tmp = smonth;        smonth = sday;        sday = tmp;        int tmplen = smonthlen;        smonthlen = sdaylen;        sdaylen = tmplen;      }    }    else    {      return FALSE;    }  }  else  {    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)    {      return FALSE;    }    sday = str;    sdaylen = tokenlen;    if (sdaylen && sday[sdaylen-1] == ',')    {      sdaylen--;      bCouldBeVShell = TRUE;    }    //Trim trailing characters    while (sdaylen && (sday[sdaylen-1]=='.' || sday[sdaylen-1]==','))    {      bCouldBeVShell = FALSE;      sdaylen--;    }    int i;    for (i = 0; i < sdaylen; i++)      if (sday[i] < '0' || sday[i] > '9')        break;    if (i && i < sdaylen)    {      if ((unsigned char)sday[i] > 127)        sdaylen = i;    }    if (!sdaylen)    {      return FALSE;    }  }  if (!strntoi64(sday, sdaylen)) //Day field invalid  { //Maybe the server is sending a directory listing with localized date format.    //Try to fix this really bad behavior    bCouldBeVShell = FALSE;    const char *tmp = smonth;    smonth = sday;    sday = tmp;    int tmplen = smonthlen;    smonthlen = sdaylen;    sdaylen = tmplen;  }  //Time/Year  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)  {    return FALSE;  }  //Trim trailing characters  while (smonthlen && (smonth[smonthlen-1]=='.' || smonth[smonthlen-1]==','))    smonthlen--;  if (!smonthlen)  {    return FALSE;  }  char *lwr = new char[smonthlen + 1];  memcpy(lwr, smonth, smonthlen);  lwr[smonthlen] = 0;  _strlwr(lwr);  bool gotYear = false;  int year = (int)strntoi64(smonth, smonthlen);  if (year > 1000)  {    gotYear = true;    direntry.date.year = year;  }  else  {    //Try if we can recognize the month name    USES_CONVERSION;    iter = m_MonthNamesMap.find(A2T(lwr));    delete [] lwr;    if (iter == m_MonthNamesMap.end())    {      int i;      for (i = 0; i < smonthlen; i++)        if (smonth[i] < '0' || smonth[i] > '9')          break;      if (!i || i == smonthlen)      {        return false;      }      if ((unsigned char)smonth[i] < 128)      {        return false;      }      smonthlen = i;      direntry.date.month = (int)strntoi64(smonth, smonthlen);      if (!direntry.date.month || direntry.date.month > 12)      {        return false;      }    }    else      direntry.date.month = iter->second;  }  if (!gotYear)  {    direntry.date.day = static_cast<int>(strntoi64(sday, sdaylen));    if (direntry.date.day < 1 || direntry.date.day > 31)    {      return false;    }  }  else  {    direntry.date.month = static_cast<int>(strntoi64(sday, sdaylen));    if (direntry.date.month < 1 || direntry.date.month > 12)    {      return false;    }  }  const char *stimeyear = str;  int stimeyearlen = tokenlen;  //Parse the time/year token  const char *strpos = strnchr(stimeyear, stimeyearlen, ':');  if (!strpos)    strpos = strnchr(stimeyear, stimeyearlen, '.');  if (!strpos)    strpos = strnchr(stimeyear, stimeyearlen, '-');  if (strpos)  {    //stimeyear has delimiter, so it's a time    direntry.date.hour = static_cast<int>(strntoi64(stimeyear, strpos - stimeyear));    int stimeyearrem = stimeyearlen - (strpos - stimeyear) - 1;    const char *strpos2 = strnchr(strpos + 1, stimeyearrem, ':');    if (strpos2 == NULL)    {      direntry.date.minute = static_cast<int>(strntoi64(strpos + 1, stimeyearrem));    }    else    {      direntry.date.minute = static_cast<int>(strntoi64(strpos + 1, strpos2 - strpos - 1));      direntry.date.second = static_cast<int>(strntoi64(strpos2 + 1, stimeyearlen - (strpos2 - stimeyear) - 1));      direntry.date.hasseconds = TRUE;    }    direntry.date.hastime = TRUE;    //Problem: Some servers use times only for files newer than 6 months,    //others use one year as limit. IIS shows time for files from the current year (jan-dec).    //So there is no support for files with time    //dated in the near future. Under normal conditions there should not be such files.    if (!direntry.date.year)    {      CTime curtime = CTime::GetCurrentTime();      int curday = curtime.GetDay();      int curmonth = curtime.GetMonth();      int curyear = curtime.GetYear();      int now = curmonth*31+curday;      int file = direntry.date.month*31+direntry.date.day;      if ((now+1)>=file)        direntry.date.year = curyear;      else        direntry.date.year = curyear-1;    }    bCouldBeVShell = FALSE;  }  else  {    if (gotYear)    {      direntry.date.day = static_cast<int>(strntoi64(stimeyear, stimeyearlen));      if (direntry.date.day < 1 || direntry.date.day > 31)      {        return false;      }    }    else if (!direntry.date.year)    {      //No delimiters -> year      direntry.date.hastime = FALSE;      direntry.date.year = static_cast<int>(strntoi64(stimeyear, stimeyearlen));    }    else    {      // File has no time token and short date format      pos -= stimeyearlen;    }  }  if (!direntry.date.year) //Year 0? Really ancient file, this is invalid!  {    return FALSE;  }  // Check if server could still be one of the newer VShell servers  if (bCouldBeVShell)  {    int oldpos = pos;    //Get time    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)    {      return FALSE;    }    const char *p = strnchr(str, tokenlen, ':');    if (pos && (p - str) == 2 && IsNumeric(str, 2) && IsNumeric(str + 3, 2))    {      //stimeyear has delimiter, so it's a time      int hour = static_cast<int>(strntoi64(str, 2));      int minute = static_cast<int>(strntoi64(str + 3, 2));      if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60)      {        direntry.date.hour = hour;        direntry.date.minute = minute;        direntry.date.hastime = TRUE;      }      else        pos = oldpos;    }    else      pos = oldpos;  }  //Get filename  str = GetNextToken(line, linelen, tokenlen, pos, 1);  if (!str)  {    return FALSE;  }  //Trim link data from filename  if (direntry.bLink)  {    const char *pos = strnstr(str, tokenlen, " -> ");    if (pos)    {      copyStr(direntry.linkTarget, 0, pos + 4, tokenlen - (pos - str) - 4);      tokenlen = pos - str;    }    if (!tokenlen)    {      return FALSE;    }  }  //Trim indicators, some server add those to mark special files  if (str[tokenlen - 1] == '*' ||    str[tokenlen - 1] == '/' ||//    str[tokenlen - 1] == '=' || //Don't trim this char, it would cause problems on certain servers                  //This char just marks sockets, so it will never appear as indicator                  //However it is valid as character for filenames on some systems    str[tokenlen - 1] == '|')    tokenlen--;  copyStr(direntry.name, 0, str, tokenlen, true);  direntry.bUnsure = FALSE;  direntry.date.hasdate = TRUE;  return TRUE;}BOOL CFtpListResult::parseAsDos(const char *line, const int linelen, t_directory::t_direntry &direntry){  int pos = 0;  int tokenlen = 0;  const char *str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  //Check the first token  if (str[0]=='b' ||    str[0]=='c' ||    str[0]=='d' ||    str[0]=='l' ||    str[0]=='p' ||    str[0]=='s' ||    str[0]=='-')    return FALSE;  if (IsNumeric(str, tokenlen))    return FALSE;  //It's a NT server with MSDOS directory format  if (!ParseShortDate(str, tokenlen, direntry.date))    return FALSE;  //Extract time  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!parseTime(str, tokenlen, direntry.date))    return FALSE;  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (tokenlen == 5 && !memcmp(str, "<DIR>", 5))  {    direntry.dir = TRUE;  }  else  {    char * buffer = new char[tokenlen];    int i, j;    for (i = 0, j = 0; i < tokenlen; i++)    {      if (str[i] != ',')      buffer[j++] = str[i];    }    direntry.dir = FALSE;    direntry.size = strntoi64(buffer, j);    delete [] buffer;  }  str = GetNextToken(line, linelen, tokenlen, pos, 1);  if (!str)    return FALSE;  copyStr(direntry.name, 0, str, tokenlen, true);  direntry.bUnsure = FALSE;  return TRUE;}BOOL CFtpListResult::parseAsOther(const char *line, const int linelen, t_directory::t_direntry &direntry){  int pos = 0;  int tokenlen = 0;  const char *str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  //Check the first token  if (str[0]=='b' ||    str[0]=='c' ||    str[0]=='d' ||    str[0]=='l' ||    str[0]=='p' ||    str[0]=='s' ||    str[0]=='-')    return FALSE;  if (!IsNumeric(str, tokenlen))    return FALSE;  //Could be numerical Unix style format or VShell format  //or even an OS2 server  const char *skipped = str;  int skippedtokenlen = tokenlen;  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  //If next token is numerical, than it's the numerical Unix style format,  //else it's the VShell or OS/2 format  if (IsNumeric(str, tokenlen))  {    copyStr(direntry.permissionstr, 0, skipped, skippedtokenlen);    if (skippedtokenlen >= 2 && skipped[1] == '4')      direntry.dir = TRUE;    else      direntry.dir = FALSE;    //Unused token    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    //Size    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    direntry.size = strntoi64(str, tokenlen);    //Date/Time    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    time_t secsSince1970 = static_cast<long>(strntoi64(str, tokenlen));    tm *sTime = gmtime(&secsSince1970);    direntry.date.year = sTime->tm_year + 1900;    direntry.date.month = sTime->tm_mon+1;    direntry.date.day = sTime->tm_mday;    direntry.date.hour = sTime->tm_hour;    direntry.date.minute = sTime->tm_min;    direntry.date.hasdate = direntry.date.hastime = TRUE;    str = GetNextToken(line, linelen, tokenlen, pos, 1);    if (!str)      return FALSE;    copyStr(direntry.name, 0, str, tokenlen, true);  }  else  {    std::map<CString, int>::const_iterator iter;    //Get size    direntry.size = strntoi64(skipped, skippedtokenlen);    //Get date, month first    if (tokenlen >= 15)      return FALSE;    char buffer[15] = {0};    memcpy(buffer, str, tokenlen);    strlwr(buffer);    USES_CONVERSION;    iter = m_MonthNamesMap.find(A2T(buffer));    if (iter == m_MonthNamesMap.end())    {      direntry.dir = FALSE;      while (str)      {        //Could be an OS/2 server        if (tokenlen == 3 && !memcmp(str, "dir", 3))          direntry.dir = TRUE;        else if (tokenlen == 3 && !memcmp(str, "DIR", 3))          direntry.dir = TRUE;        else if (strnchr(str, tokenlen, '-'))          break;        str = GetNextToken(line, linelen, tokenlen, pos, 0);      }      if (!str)        return FALSE;      if (!ParseShortDate(str, tokenlen, direntry.date))        return FALSE;      str = GetNextToken(line, linelen, tokenlen, pos, 0);      if (!str)        return FALSE;      //Parse the time token      const char *strpos = strnchr(str, tokenlen, ':');      if (!strpos)        return FALSE;      if (strpos)      {        //stimeyear has delimiter, so it's a time        direntry.date.hour = static_cast<int>(strntoi64(str, strpos - str));        direntry.date.minute = static_cast<int>(strntoi64(strpos+1, tokenlen - (strpos - str) - 1));        direntry.date.hastime = TRUE;        //Problem: Some servers use times only for files newer than 6 months,        //others use one year as limit. So there is no support for files with time        //dated in the near future. Under normal conditions there should not be such files        if (!direntry.date.year)        {          CTime curtime = CTime::GetCurrentTime();          int curday = curtime.GetDay();          int curmonth = curtime.GetMonth();          int curyear = curtime.GetYear();          int now = curmonth*31+curday;          int file = direntry.date.month*31+direntry.date.day;          if ((now+1)>=file)            direntry.date.year = curyear;          else            direntry.date.year = curyear-1;        }      }      str = GetNextToken(line, linelen, tokenlen, pos, 1);      if (!str)        return FALSE;      if (tokenlen > 6)      {        if (!strnicmp(str + tokenlen - 5, "<DIR>", 5))        {          direntry.dir = TRUE;          tokenlen -= 5;          while (tokenlen && str[tokenlen - 1] == ' ' || str[tokenlen - 1] == '\t')            tokenlen--;        }        if (!tokenlen)          return FALSE;      }      copyStr(direntry.name, 0, str, tokenlen, true);    }    else    {      direntry.date.month = iter->second;      //Day      str = GetNextToken(line, linelen, tokenlen, pos, 0);      if (!str)        return FALSE;      if (str[tokenlen-1]==',')        tokenlen--;      if (!tokenlen)        return FALSE;      if (!IsNumeric(str, tokenlen))        return FALSE;      direntry.date.day = static_cast<int>(strntoi64(str, tokenlen));      if (direntry.date.day < 1 || direntry.date.day > 31)        return FALSE;      //Year      str = GetNextToken(line, linelen, tokenlen, pos, 0);      if (!str)        return FALSE;      if (!IsNumeric(str, tokenlen))        return FALSE;      direntry.date.year = static_cast<int>(strntoi64(str, tokenlen));      if (direntry.date.year < 50)        direntry.date.year += 2000;      else if (direntry.date.year < 1000)        direntry.date.year += 1900;      direntry.date.hasdate = TRUE;      //Now get the time      str  = GetNextToken(line, linelen, tokenlen, pos, 0);      const char *p = strnchr(str, tokenlen, ':');      if (!p)        return FALSE;      if (p==str || !IsNumeric(str, p-str) || (p-str + 1) >= tokenlen || !IsNumeric(p+1, tokenlen - (p-str) - 1))        return FALSE;      direntry.date.hour = static_cast<int>(strntoi64(str, p-str));      direntry.date.minute = static_cast<int>(strntoi64(p+1, tokenlen - (p-str) - 1));      if (direntry.date.hour < 0 || direntry.date.hour > 24)        return FALSE;      if (direntry.date.minute < 0 || direntry.date.minute > 59)        return FALSE;      direntry.date.hastime = TRUE;      str = GetNextToken(line, linelen, tokenlen, pos, 1);      if (!str)        return FALSE;      if (tokenlen > 1 && (str[tokenlen-1] == '\\' || str[tokenlen-1] == '/'))      {        direntry.dir = TRUE;        tokenlen--;      }      else        direntry.dir = FALSE;      if (!tokenlen)        return FALSE;      copyStr(direntry.name, 0, str, tokenlen, true);    }  }  direntry.bUnsure = FALSE;  direntry.date.hasdate = TRUE;  return TRUE;}_int64 CFtpListResult::strntoi64(const char *str, int len) const{  _int64 res = 0;  const char *p = str;  while ((p-str) < len)  {    if (*p < '0' || *p > '9')      break;    res *= 10;    res += *p++ - '0';  }  return res;}const char *CFtpListResult::GetNextToken(const char *line, const int linelen, int &len, int &pos, int type) const{  const char *p = line + pos;  if ((p - line) >= linelen)    return NULL;  while ((p - line) < linelen && (!p || *p==' ' || *p=='\t'))    p++;  if ((p - line) >= linelen)    return NULL;  const char *res = p;  if (type)  {    pos = linelen;    len = linelen - (p - line);  }  else  {    while ((p - line) < linelen && *p && *p != ' ' && *p!='\t')      p++;    len = p - res;    pos = p - line;  }  return res;}const char * CFtpListResult::strnchr(const char *str, int len, char c) const{  if (!str)    return NULL;  const char *p = str;  while (len > 0)  {    if (!*p)      return NULL;    if (*p == c)      return p;    p++;    len--;  }  return NULL;}const char * CFtpListResult::strnstr(const char *str, int len, const char *c) const{  if (!str)    return NULL;  if (!c)    return NULL;  int clen = strlen(c);  const char *p = str;  while (len > 0)  {    if (!*p)      return NULL;    if (*p == *c)    {      if (clen == 1)        return p;      else if (len >= clen)      {        if (!memcmp(p + 1, c+1, clen-1))          return p;      }      else        return NULL;    }    p++;    len--;  }  return NULL;}void CFtpListResult::copyStr(CString &target, int pos, const char *source, int len, bool mayInvalidateUTF8 /*=false*/){  USES_CONVERSION;  char *p = new char[len + 1];  memcpy(p, source, len);  p[len] = '\0';  if (m_bUTF8 && *m_bUTF8)  {    // convert from UTF-8 to ANSI    if (DetectUTF8Encoding(RawByteString(p, len)) == etANSI)    {      if (mayInvalidateUTF8 && m_server.nUTF8 != 1)      {        LogMessage(FZ_LOG_WARNING, L"Server does not send proper UTF-8, falling back to local charset");        *m_bUTF8 = false;      }      target = target.Left(pos) + A2CT(p);    }    else    {      // convert from UTF-8 to ANSI      int len = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)p, -1, NULL, 0);      if (len != 0)      {        LPWSTR p1 = new WCHAR[len + 1];        MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)p, -1 , (LPWSTR)p1, len + 1);        target = target.Left(pos) + W2CT(p1);        delete [] p1;      }      else        target = target.Left(pos) + A2CT(p);    }  }  else    target = target.Left(pos) + A2CT(p);  delete [] p;}BOOL CFtpListResult::parseAsIBM(const char *line, const int linelen, t_directory::t_direntry &direntry){  int pos = 0;  int tokenlen = 0;  const char *str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  copyStr(direntry.ownergroup , 0, str, tokenlen);  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!IsNumeric(str, tokenlen))    return FALSE;  direntry.size = strntoi64(str, tokenlen);  //Date  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!ParseShortDate(str, tokenlen, direntry.date))    return FALSE;  //Time  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!parseTime(str, tokenlen, direntry.date))    return FALSE;  //Unused Token  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  //Name  str = GetNextToken(line, linelen, tokenlen, pos, 1);  if (!str)    return FALSE;  if (str[tokenlen-1] == '/')  {    direntry.dir = TRUE;    if (!--tokenlen)      return FALSE;  }  else    direntry.dir = FALSE;  copyStr(direntry.name, 0, str, tokenlen, true);  direntry.bUnsure = FALSE;  return true;}BOOL CFtpListResult::parseAsIBMMVS(const char *line, const int linelen, t_directory::t_direntry &direntry){  int pos = 0;  int tokenlen = 0;  // volume  const char *str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (strncmp(str, "Migrated", tokenlen) != 0)  {    // unit    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    //Referred Date    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    if (!ParseShortDate(str, tokenlen, direntry.date))    {      // Perhaps of the following type:      // TSO004 3390 VSAM FOO.BAR      if (tokenlen != 4 || strncmp(str, "VSAM", tokenlen))        return FALSE;      str = GetNextToken(line, linelen, tokenlen, pos, 1);      if (!str)        return FALSE;      if (strnchr(str, tokenlen, ' '))        return FALSE;      copyStr(direntry.name, 0, str, tokenlen, true);      direntry.dir = false;      return true;    }    // ext    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    if (!IsNumeric(str, tokenlen))      return FALSE;    int prevLen = tokenlen;    // used    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    if (IsNumeric(str, tokenlen))    {      // recfm      str = GetNextToken(line, linelen, tokenlen, pos, 0);      if (!str)        return FALSE;      if (IsNumeric(str, tokenlen))        return false;    }    else    {      if (prevLen < 6)        return false;    }    // lrecl    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    if (!IsNumeric(str, tokenlen))      return FALSE;    // blksize    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    if (!IsNumeric(str, tokenlen))      return FALSE;    // dsorg    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    if (tokenlen == 2 && !memcmp(str, "PO", 2))    {      direntry.dir = TRUE;    }    else    {      direntry.dir = FALSE;      direntry.size = 100;    }  }  // name of dataset or sequential name  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  copyStr(direntry.name, 0, str, tokenlen, true);  direntry.bUnsure = FALSE;  return true;}BOOL CFtpListResult::parseAsIBMMVSPDS(const char *line, const int linelen, t_directory::t_direntry &direntry){  int pos = 0;  int tokenlen = 0;  // pds member name  const char *str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  copyStr(direntry.name, 0, str, tokenlen);  // vv.mm  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  // creation date  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  // change date  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!ParseShortDate(str, tokenlen, direntry.date))    return FALSE;  // change time  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!parseTime(str, tokenlen, direntry.date))    return FALSE;  // size  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!IsNumeric(str, tokenlen))    return FALSE;  direntry.size = strntoi64(str, tokenlen);  // init  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!IsNumeric(str, tokenlen))    return FALSE;  // mod  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!IsNumeric(str, tokenlen))    return FALSE;  // id  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  direntry.dir = FALSE;  direntry.bUnsure = FALSE;  return true;}BOOL CFtpListResult::parseAsIBMMVSPDS2(const char *line, const int linelen, t_directory::t_direntry &direntry){  // Use this one only on MVS servers, as it will cause problems on other servers#ifndef LISTDEBUG  if (!(m_server.nServerType & (FZ_SERVERTYPE_SUB_FTP_MVS | FZ_SERVERTYPE_SUB_FTP_BS2000)))    return false;#endif  int pos = 0;  int tokenlen = 0;  direntry.bUnsure = FALSE;  direntry.dir = FALSE;  direntry.bLink = FALSE;  direntry.ownergroup = L"";  direntry.permissionstr = L"";  direntry.date.hasdate = direntry.date.hastime = FALSE;  // pds member name  const char *str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (m_server.nServerType & FZ_SERVERTYPE_SUB_FTP_BS2000 &&    IsNumeric(str, tokenlen))  {    int prevlen = tokenlen;    const char* prev = str;    int oldpos = pos;    str = GetNextToken(line, linelen, tokenlen, pos, 0);    if (!str)      return FALSE;    if (str[0] != ':')    {      str = prev;      tokenlen = prevlen;      pos = oldpos;    }  }  copyStr(direntry.name, 0, str, tokenlen);  if (m_server.nServerType & FZ_SERVERTYPE_SUB_FTP_BS2000)  {    int pos = direntry.name.ReverseFind(L':');    if (pos != -1)      direntry.name = direntry.name.Mid(pos + 1);    if (direntry.name[0] == L'$')    {      int pos = direntry.name.Find(L'.');      if (pos != -1)        direntry.name = direntry.name.Mid(pos + 1);    }    if (direntry.name == L"")      return FALSE;  }  // Hexadecimal filesize  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    // We can return true since sometimes MVS servers send pure filenames    return true;  direntry.size = 0;  const char *end = str + tokenlen;  const char *p = str;  while (p < end)  {    direntry.size *= 16;    if (*p >= '0' && *p <= '9')      direntry.size += *p - '0';    else if (*p >= 'A' && *p <= 'F')      direntry.size += *p - 'A' + 10;    else      return false;    p++;  }  // Unused hexadecimal token  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return false;  end = str + tokenlen;  p = str;  while (p < end)  {    if ((*p < '0' || *p > '9') && (*p < 'A' || *p > 'F'))      return false;    p++;  }  // Unused token  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return false;  if (!IsNumeric(str, tokenlen))    return false;  const char* prevprev = 0;  const char* prev = 0;  int prevprevlen = 0;  int prevlen = 0;  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return false;  while (str)  {    if (prevprev)    {      for (int i = 0; i < prevprevlen; i++)        if (prevprev[i] < 'A' || prevprev[i] > 'Z')          return false;    }    prevprev = prev;    prevprevlen = prevlen;    prev = str;    prevlen = tokenlen;    str = GetNextToken(line, linelen, tokenlen, pos, 0);  }  if (!prev || !prevprev)    return false;  if (!IsNumeric(prev, prevlen) && (prevlen != 3 || memcmp(prev, "ANY", 3)))    return false;  if (!IsNumeric(prevprev, prevprevlen) && (prevprevlen != 3 || memcmp(prevprev, "ANY", 3)))    return false;  return true;}bool CFtpListResult::parseTime(const char *str, int len, t_directory::t_direntry::t_date &date) const{  int i = 0;  //Extract the hour  date.hastime = TRUE;  date.hour = 0;  while (str[i] != ':')  {    if (str[i] < '0' || str[i] > '9')      return false;    date.hour *= 10;    date.hour += str[i] - '0';    if (date.hour > 24)      return false;    i++;    if (i == len)      return false;  }  // Make sure we did read at least one digit  if (!i)    return false;  i++;  if (i == len)    return false;  //Extract the minute  date.minute = 0;  while (str[i] >= '0' && str[i] <= '9')  {    date.minute *= 10;    date.minute += str[i] - '0';    if (date.minute > 59)      return false;    i++;    if (i == len)      break;  }  //Convert to 24h format  //I really wish we would have the following system:  //one year->ten months->ten days->ten hours->ten minutes->ten seconds and so on...  //I should modifiy the earth rotation to force everyone to use this system *g*  if (i != len)  {    if (str[i]=='P')    {      if (date.hour < 12)        date.hour += 12;    }    else      if (date.hour == 12)        date.hour = 0;  }  date.hastime = TRUE;  return true;}BOOL CFtpListResult::parseAsWfFtp(const char *line, const int linelen, t_directory::t_direntry &direntry){  int pos = 0;  int tokenlen = 0;  const char *str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  copyStr(direntry.name, 0, str, tokenlen);  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!IsNumeric(str, tokenlen))    return FALSE;  direntry.size = strntoi64(str, tokenlen);  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!ParseShortDate(str, tokenlen, direntry.date))    return FALSE;  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (str[tokenlen - 1] != '.')    return FALSE;  str = GetNextToken(line, linelen, tokenlen, pos, 0);  if (!str)    return FALSE;  if (!parseTime(str, tokenlen, direntry.date))    return FALSE;  direntry.bUnsure = FALSE;  direntry.dir = false;  direntry.bLink = false;  direntry.permissionstr = L"";  direntry.ownergroup = L"";  return TRUE;}bool CFtpListResult::ParseSize(const char* str, int len, __int64 &size) const{  if (len < 1)    return false;  if (IsNumeric(str, len))  {    size = strntoi64(str, len);    return true;  }  size = 0;  char last = str[--len];  int delimiter = -1;  for (int i = 0; i < len; i++)  {    char c = str[i];    if (c >= '0' && c <= '9')    {      size *= 10;      size += c - '0';    }    else if (c == '.')      delimiter = len - i;    else      return false;  }  // Check for digit before or after delimiterr  if (!delimiter || delimiter == len)    return false;  switch (last)  {  case 'k':  case 'K':    size *= 1 << 10;    break;  case 'm':  case 'M':    size *= 1 << 20;    break;  case 'g':  case 'G':    size *= 1 << 30;    break;  case 't':  case 'T':    size *= (__int64)1 << 40;    break;  default:    return false;  }  if (delimiter == -1)    return true;  while (delimiter--)    size /= 10;  return true;}
 |