| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866 | //---------------------------------------------------------------------------#define NO_WIN32_LEAN_AND_MEAN#include <vcl.h>#pragma hdrstop#include "Common.h"#include "Exceptions.h"#include "TextsCore.h"#include "Interface.h"#include <StrUtils.hpp>#include <DateUtils.hpp>#include <math.h>#include <shlobj.h>#include <limits>#include <shlwapi.h>#include <CoreMain.h>#include <openssl/pkcs12.h>#include <openssl/pem.h>#include <openssl/err.h>//---------------------------------------------------------------------------#pragma package(smart_init)//---------------------------------------------------------------------------const wchar_t * DSTModeNames = L"Win;Unix;Keep";//---------------------------------------------------------------------------//---------------------------------------------------------------------------const wchar_t EngShortMonthNames[12][4] =  {L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun",   L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec"};const char Bom[3] = "\xEF\xBB\xBF";const wchar_t TokenPrefix = L'%';const wchar_t NoReplacement = wchar_t(false);const wchar_t TokenReplacement = wchar_t(true);const UnicodeString LocalInvalidChars = L"/\\:*?\"<>|";const UnicodeString PasswordMask = L"***";//---------------------------------------------------------------------------UnicodeString ReplaceChar(UnicodeString Str, wchar_t A, wchar_t B){  for (Integer Index = 0; Index < Str.Length(); Index++)    if (Str[Index+1] == A) Str[Index+1] = B;  return Str;}//---------------------------------------------------------------------------UnicodeString DeleteChar(UnicodeString Str, wchar_t C){  int P;  while ((P = Str.Pos(C)) > 0)  {    Str.Delete(P, 1);  }  return Str;}//---------------------------------------------------------------------------template<typename T>void DoPackStr(T & Str){  // Following will free unnecessary bytes  Str = Str.c_str();}//---------------------------------------------------------------------------void PackStr(UnicodeString & Str){  DoPackStr(Str);}//---------------------------------------------------------------------------void PackStr(RawByteString & Str){  DoPackStr(Str);}//---------------------------------------------------------------------------void PackStr(AnsiString & Str){  DoPackStr(Str);}//---------------------------------------------------------------------------template<typename T>void __fastcall DoShred(T & Str){  if (!Str.IsEmpty())  {    Str.Unique();    memset(Str.c_str(), 0, Str.Length() * sizeof(*Str.c_str()));    Str = L"";  }}//---------------------------------------------------------------------------void __fastcall Shred(UnicodeString & Str){  DoShred(Str);}//---------------------------------------------------------------------------void __fastcall Shred(UTF8String & Str){  DoShred(Str);}//---------------------------------------------------------------------------void __fastcall Shred(AnsiString & Str){  DoShred(Str);}//---------------------------------------------------------------------------UnicodeString AnsiToString(const RawByteString & S){  return UnicodeString(AnsiString(S));}//---------------------------------------------------------------------------UnicodeString AnsiToString(const char * S, size_t Len){  return UnicodeString(AnsiString(S, Len));}//---------------------------------------------------------------------------UnicodeString MakeValidFileName(UnicodeString FileName){  UnicodeString IllegalChars = L":;,=+<>|\"[] \\/?*";  for (int Index = 0; Index < IllegalChars.Length(); Index++)  {    FileName = ReplaceChar(FileName, IllegalChars[Index+1], L'-');  }  return FileName;}//---------------------------------------------------------------------------UnicodeString RootKeyToStr(HKEY RootKey){  if (RootKey == HKEY_USERS) return L"HKU";    else  if (RootKey == HKEY_LOCAL_MACHINE) return L"HKLM";    else  if (RootKey == HKEY_CURRENT_USER) return L"HKCU";    else  if (RootKey == HKEY_CLASSES_ROOT) return L"HKCR";    else  if (RootKey == HKEY_CURRENT_CONFIG) return L"HKCC";    else  if (RootKey == HKEY_DYN_DATA) return L"HKDD";    else  {  Abort(); return L""; };}//---------------------------------------------------------------------------UnicodeString BooleanToEngStr(bool B){  if (B)  {    return L"Yes";  }  else  {    return L"No";  }}//---------------------------------------------------------------------------UnicodeString BooleanToStr(bool B){  if (B)  {    return LoadStr(YES_STR);  }  else  {    return LoadStr(NO_STR);  }}//---------------------------------------------------------------------------UnicodeString DefaultStr(const UnicodeString & Str, const UnicodeString & Default){  if (!Str.IsEmpty())  {    return Str;  }  else  {    return Default;  }}//---------------------------------------------------------------------------UnicodeString CutToChar(UnicodeString &Str, wchar_t Ch, bool Trim){  Integer P = Str.Pos(Ch);  UnicodeString Result;  if (P)  {    Result = Str.SubString(1, P-1);    Str.Delete(1, P);  }  else  {    Result = Str;    Str = L"";  }  if (Trim)  {    Result = Result.TrimRight();    Str = Str.TrimLeft();  }  return Result;}//---------------------------------------------------------------------------UnicodeString CopyToChars(const UnicodeString & Str, int & From, UnicodeString Chs, bool Trim,  wchar_t * Delimiter, bool DoubleDelimiterEscapes){  UnicodeString Result;  int P;  for (P = From; P <= Str.Length(); P++)  {    if (IsDelimiter(Chs, Str, P))    {      if (DoubleDelimiterEscapes &&          (P < Str.Length()) &&          IsDelimiter(Chs, Str, P + 1))      {        Result += Str[P];        P++;      }      else      {        break;      }    }    else    {      Result += Str[P];    }  }  if (P <= Str.Length())  {    if (Delimiter != NULL)    {      *Delimiter = Str[P];    }  }  else  {    if (Delimiter != NULL)    {      *Delimiter = L'\0';    }  }  // even if we reached the end, return index, as if there were the delimiter,  // so caller can easily find index of the end of the piece by subtracting  // 2 from From (as long as he did not asked for trimming)  From = P+1;  if (Trim)  {    Result = Result.TrimRight();    while ((From <= Str.Length()) && (Str[From] == L' '))    {      From++;    }  }  return Result;}//---------------------------------------------------------------------------UnicodeString CopyToChar(const UnicodeString & Str, wchar_t Ch, bool Trim){  int From = 1;  return CopyToChars(Str, From, UnicodeString(Ch), Trim);}//---------------------------------------------------------------------------UnicodeString DelimitStr(UnicodeString Str, UnicodeString Chars){  for (int i = 1; i <= Str.Length(); i++)  {    if (Str.IsDelimiter(Chars, i))    {      Str.Insert(L"\\", i);      i++;    }  }  return Str;}//---------------------------------------------------------------------------UnicodeString ShellDelimitStr(UnicodeString Str, wchar_t Quote){  UnicodeString Chars = L"$\\";  if (Quote == L'"')  {    Chars += L"`\"";  }  return DelimitStr(Str, Chars);}//---------------------------------------------------------------------------UnicodeString ExceptionLogString(Exception *E){  DebugAssert(E);  if (E->InheritsFrom(__classid(Exception)))  {    UnicodeString Msg;    Msg = FORMAT(L"(%s) %s", (E->ClassName(), E->Message));    if (E->InheritsFrom(__classid(ExtException)))    {      TStrings * MoreMessages = ((ExtException*)E)->MoreMessages;      if (MoreMessages)      {        Msg += L"\n" +          ReplaceStr(MoreMessages->Text, L"\r", L"");      }    }    return Msg;  }  else  {    wchar_t Buffer[1024];    ExceptionErrorMessage(ExceptObject(), ExceptAddr(), Buffer, LENOF(Buffer));    return UnicodeString(Buffer);  }}//---------------------------------------------------------------------------UnicodeString __fastcall MainInstructions(const UnicodeString & S){  UnicodeString MainMsgTag = LoadStr(MAIN_MSG_TAG);  return MainMsgTag + S + MainMsgTag;}//---------------------------------------------------------------------------bool __fastcall HasParagraphs(const UnicodeString & S){  return (S.Pos(L"\n\n") > 0);}//---------------------------------------------------------------------------UnicodeString __fastcall MainInstructionsFirstParagraph(const UnicodeString & S){  // WORKAROUND, we consider it bad practice, the highlighting should better  // be localized (but maybe we change our mind later)  UnicodeString Result;  int Pos = S.Pos(L"\n\n");  // we would not be calling this on single paragraph message  if (DebugAlwaysTrue(Pos > 0))  {    Result =      MainInstructions(S.SubString(1, Pos - 1)) +      S.SubString(Pos, S.Length() - Pos + 1);  }  else  {    Result = MainInstructions(S);  }  return Result;}//---------------------------------------------------------------------------bool ExtractMainInstructions(UnicodeString & S, UnicodeString & MainInstructions){  bool Result = false;  UnicodeString MainMsgTag = LoadStr(MAIN_MSG_TAG);  if (StartsStr(MainMsgTag, S))  {    int EndTagPos =      S.SubString(MainMsgTag.Length() + 1, S.Length() - MainMsgTag.Length()).Pos(MainMsgTag);    if (EndTagPos > 0)    {      MainInstructions = S.SubString(MainMsgTag.Length() + 1, EndTagPos - 1);      S.Delete(1, EndTagPos + (2 * MainMsgTag.Length()) - 1);      Result = true;    }  }  DebugAssert(MainInstructions.Pos(MainMsgTag) == 0);  DebugAssert(S.Pos(MainMsgTag) == 0);  return Result;}//---------------------------------------------------------------------------static int FindInteractiveMsgStart(const UnicodeString & S){  int Result = 0;  UnicodeString InteractiveMsgTag = LoadStr(INTERACTIVE_MSG_TAG);  if (EndsStr(InteractiveMsgTag, S) &&      (S.Length() >= 2 * InteractiveMsgTag.Length()))  {    Result = S.Length() - 2 * InteractiveMsgTag.Length() + 1;    while ((Result > 0) && (S.SubString(Result, InteractiveMsgTag.Length()) != InteractiveMsgTag))    {      Result--;    }  }  return Result;}//---------------------------------------------------------------------------UnicodeString RemoveMainInstructionsTag(UnicodeString S){  UnicodeString MainInstruction;  if (ExtractMainInstructions(S, MainInstruction))  {    S = MainInstruction + S;  }  return S;}//---------------------------------------------------------------------------UnicodeString UnformatMessage(UnicodeString S){  S = RemoveMainInstructionsTag(S);  int InteractiveMsgStart = FindInteractiveMsgStart(S);  if (InteractiveMsgStart > 0)  {    S = S.SubString(1, InteractiveMsgStart - 1);  }  return S;}//---------------------------------------------------------------------------UnicodeString RemoveInteractiveMsgTag(UnicodeString S){  int InteractiveMsgStart = FindInteractiveMsgStart(S);  if (InteractiveMsgStart > 0)  {    UnicodeString InteractiveMsgTag = LoadStr(INTERACTIVE_MSG_TAG);    S.Delete(InteractiveMsgStart, InteractiveMsgTag.Length());    S.Delete(S.Length() - InteractiveMsgTag.Length() + 1, InteractiveMsgTag.Length());  }  return S;}//---------------------------------------------------------------------------UnicodeString RemoveEmptyLines(const UnicodeString & S){  return    ReplaceStr(      ReplaceStr(S.TrimRight(), L"\n\n", L"\n"),      L"\n \n", L"\n");}//---------------------------------------------------------------------------bool IsNumber(const UnicodeString Str){  int Value;  return TryStrToInt(Str, Value);}//---------------------------------------------------------------------------UnicodeString __fastcall SystemTemporaryDirectory(){  UnicodeString TempDir;  TempDir.SetLength(MAX_PATH);  TempDir.SetLength(GetTempPath(MAX_PATH, TempDir.c_str()));  return TempDir;}//---------------------------------------------------------------------------UnicodeString __fastcall GetShellFolderPath(int CSIdl){  UnicodeString Result;  wchar_t Path[2 * MAX_PATH + 10] = L"\0";  if (SUCCEEDED(SHGetFolderPath(NULL, CSIdl, NULL, SHGFP_TYPE_CURRENT, Path)))  {    Result = Path;  }  return Result;}//---------------------------------------------------------------------------// Particularly needed when using file name selected by TFilenameEdit,// as it wraps a path to double-quotes, when there is a space in the path.UnicodeString __fastcall StripPathQuotes(const UnicodeString Path){  if ((Path.Length() >= 2) &&      (Path[1] == L'\"') && (Path[Path.Length()] == L'\"'))  {    return Path.SubString(2, Path.Length() - 2);  }  else  {    return Path;  }}//---------------------------------------------------------------------------UnicodeString __fastcall AddQuotes(UnicodeString Str){  if (Str.Pos(L" ") > 0)  {    Str = L"\"" + Str + L"\"";  }  return Str;}//---------------------------------------------------------------------------UnicodeString __fastcall AddPathQuotes(UnicodeString Path){  Path = StripPathQuotes(Path);  return AddQuotes(Path);}//---------------------------------------------------------------------------static wchar_t * __fastcall ReplaceChar(  UnicodeString & FileName, wchar_t * InvalidChar, wchar_t InvalidCharsReplacement){  int Index = InvalidChar - FileName.c_str() + 1;  if (InvalidCharsReplacement == TokenReplacement)  {    // currently we do not support unicode chars replacement    if (FileName[Index] > 0xFF)    {      EXCEPTION;    }    FileName.Insert(ByteToHex(static_cast<unsigned char>(FileName[Index])), Index + 1);    FileName[Index] = TokenPrefix;    InvalidChar = FileName.c_str() + Index + 2;  }  else  {    FileName[Index] = InvalidCharsReplacement;    InvalidChar = FileName.c_str() + Index;  }  return InvalidChar;}//---------------------------------------------------------------------------UnicodeString __fastcall ValidLocalFileName(UnicodeString FileName){  return ValidLocalFileName(FileName, L'_', L"", LocalInvalidChars);}//---------------------------------------------------------------------------UnicodeString __fastcall ValidLocalFileName(  UnicodeString FileName, wchar_t InvalidCharsReplacement,  const UnicodeString & TokenizibleChars, const UnicodeString & LocalInvalidChars){  if (InvalidCharsReplacement != NoReplacement)  {    bool ATokenReplacement = (InvalidCharsReplacement == TokenReplacement);    const wchar_t * Chars =      (ATokenReplacement ? TokenizibleChars : LocalInvalidChars).c_str();    wchar_t * InvalidChar = FileName.c_str();    while ((InvalidChar = wcspbrk(InvalidChar, Chars)) != NULL)    {      int Pos = (InvalidChar - FileName.c_str() + 1);      wchar_t Char;      if (ATokenReplacement &&          (*InvalidChar == TokenPrefix) &&          (((FileName.Length() - Pos) <= 1) ||           (((Char = static_cast<wchar_t>(HexToByte(FileName.SubString(Pos + 1, 2)))) == L'\0') ||            (TokenizibleChars.Pos(Char) == 0))))      {        InvalidChar++;      }      else      {        InvalidChar = ReplaceChar(FileName, InvalidChar, InvalidCharsReplacement);      }    }    // Windows trim trailing space or dot, hence we must encode it to preserve it    if (!FileName.IsEmpty() &&        ((FileName[FileName.Length()] == L' ') ||         (FileName[FileName.Length()] == L'.')))    {      ReplaceChar(FileName, FileName.c_str() + FileName.Length() - 1, InvalidCharsReplacement);    }    if (IsReservedName(FileName))    {      int P = FileName.Pos(".");      if (P == 0)      {        P = FileName.Length() + 1;      }      FileName.Insert(L"%00", P);    }  }  return FileName;}//---------------------------------------------------------------------------void __fastcall SplitCommand(UnicodeString Command, UnicodeString &Program,  UnicodeString & Params, UnicodeString & Dir){  Command = Command.Trim();  Params = L"";  Dir = L"";  if (!Command.IsEmpty() && (Command[1] == L'\"'))  {    Command.Delete(1, 1);    int P = Command.Pos(L'"');    if (P)    {      Program = Command.SubString(1, P-1).Trim();      Params = Command.SubString(P + 1, Command.Length() - P).Trim();    }    else    {      throw Exception(FMTLOAD(INVALID_SHELL_COMMAND, (L"\"" + Command)));    }  }  else  {    int P = Command.Pos(L" ");    if (P)    {      Program = Command.SubString(1, P).Trim();      Params = Command.SubString(P + 1, Command.Length() - P).Trim();    }    else    {      Program = Command;    }  }  int B = Program.LastDelimiter(L"\\/");  if (B)  {    Dir = Program.SubString(1, B).Trim();  }}//---------------------------------------------------------------------------UnicodeString __fastcall ExtractProgram(UnicodeString Command){  UnicodeString Program;  UnicodeString Params;  UnicodeString Dir;  SplitCommand(Command, Program, Params, Dir);  return Program;}//---------------------------------------------------------------------------UnicodeString __fastcall ExtractProgramName(UnicodeString Command){  UnicodeString Name = ExtractFileName(ExtractProgram(Command));  int Dot = Name.LastDelimiter(L".");  if (Dot > 0)  {    Name = Name.SubString(1, Dot - 1);  }  return Name;}//---------------------------------------------------------------------------UnicodeString __fastcall FormatCommand(UnicodeString Program, UnicodeString Params){  Program = Program.Trim();  Params = Params.Trim();  if (!Params.IsEmpty()) Params = L" " + Params;  Program = AddQuotes(Program);  return Program + Params;}//---------------------------------------------------------------------------const wchar_t ShellCommandFileNamePattern[] = L"!.!";//---------------------------------------------------------------------------void __fastcall ReformatFileNameCommand(UnicodeString & Command){  if (!Command.IsEmpty())  {    UnicodeString Program, Params, Dir;    SplitCommand(Command, Program, Params, Dir);    if (Params.Pos(ShellCommandFileNamePattern) == 0)    {      Params = Params + (Params.IsEmpty() ? L"" : L" ") + ShellCommandFileNamePattern;    }    Command = FormatCommand(Program, Params);  }}//---------------------------------------------------------------------------UnicodeString __fastcall ExpandFileNameCommand(const UnicodeString Command,  const UnicodeString FileName){  return AnsiReplaceStr(Command, ShellCommandFileNamePattern,    AddPathQuotes(FileName));}//---------------------------------------------------------------------------UnicodeString __fastcall EscapeParam(const UnicodeString & Param){  return ReplaceStr(Param, L"\"", L"\"\"");}//---------------------------------------------------------------------------UnicodeString __fastcall EscapePuttyCommandParam(UnicodeString Param){  bool Space = false;  for (int i = 1; i <= Param.Length(); i++)  {    switch (Param[i])    {      case L'"':        Param.Insert(L"\\", i);        i++;        break;      case L' ':        Space = true;        break;      case L'\\':        int i2 = i;        while ((i2 <= Param.Length()) && (Param[i2] == L'\\'))        {          i2++;        }        if ((i2 <= Param.Length()) && (Param[i2] == L'"'))        {          while (Param[i] == L'\\')          {            Param.Insert(L"\\", i);            i += 2;          }          i--;        }        break;    }  }  if (Space)  {    Param = L"\"" + Param + L'"';  }  return Param;}//---------------------------------------------------------------------------UnicodeString __fastcall ExpandEnvironmentVariables(const UnicodeString & Str){  UnicodeString Buf;  unsigned int Size = 1024;  Buf.SetLength(Size);  Buf.Unique();  unsigned int Len = ExpandEnvironmentStrings(Str.c_str(), Buf.c_str(), Size);  if (Len > Size)  {    Buf.SetLength(Len);    Buf.Unique();    ExpandEnvironmentStrings(Str.c_str(), Buf.c_str(), Len);  }  PackStr(Buf);  return Buf;}//---------------------------------------------------------------------------bool __fastcall CompareFileName(const UnicodeString & Path1, const UnicodeString & Path2){  UnicodeString ShortPath1 = ExtractShortPathName(Path1);  UnicodeString ShortPath2 = ExtractShortPathName(Path2);  bool Result;  // ExtractShortPathName returns empty string if file does not exist  if (ShortPath1.IsEmpty() || ShortPath2.IsEmpty())  {    Result = AnsiSameText(Path1, Path2);  }  else  {    Result = AnsiSameText(ShortPath1, ShortPath2);  }  return Result;}//---------------------------------------------------------------------------bool __fastcall ComparePaths(const UnicodeString & Path1, const UnicodeString & Path2){  // TODO: ExpandUNCFileName  return AnsiSameText(IncludeTrailingBackslash(Path1), IncludeTrailingBackslash(Path2));}//---------------------------------------------------------------------------int __fastcall CompareLogicalText(const UnicodeString & S1, const UnicodeString & S2){  return StrCmpLogicalW(S1.c_str(), S2.c_str());}//---------------------------------------------------------------------------bool __fastcall IsReservedName(UnicodeString FileName){  int P = FileName.Pos(L".");  int Len = (P > 0) ? P - 1 : FileName.Length();  if ((Len == 3) || (Len == 4))  {    if (P > 0)    {      FileName.SetLength(P - 1);    }    static UnicodeString Reserved[] = {      L"CON", L"PRN", L"AUX", L"NUL",      L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9",      L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9" };    for (unsigned int Index = 0; Index < LENOF(Reserved); Index++)    {      if (SameText(FileName, Reserved[Index]))      {        return true;      }    }  }  return false;}//---------------------------------------------------------------------------// ApiPath support functions// Inspired by// http://stackoverflow.com/questions/18580945/need-clarification-for-converting-paths-into-long-unicode-paths-or-the-ones-star// This can be reimplemented using PathCchCanonicalizeEx on Windows 8 and laterenum PATH_PREFIX_TYPE{  PPT_UNKNOWN,  PPT_ABSOLUTE,           //Found absolute path that is none of the other types  PPT_UNC,                //Found \\server\share\ prefix  PPT_LONG_UNICODE,       //Found \\?\ prefix  PPT_LONG_UNICODE_UNC,   //Found \\?\UNC\ prefix};//---------------------------------------------------------------------------static int __fastcall PathRootLength(UnicodeString Path){  // Correction for PathSkipRoot API  // Replace all /'s with \'s because PathSkipRoot can't handle /'s  UnicodeString Result = ReplaceChar(Path, L'/', L'\\');  // Now call the API  LPCTSTR Buffer = PathSkipRoot(Result.c_str());  return (Buffer != NULL) ? (Buffer - Result.c_str()) : -1;}//---------------------------------------------------------------------------static bool __fastcall PathIsRelative_CorrectedForMicrosoftStupidity(UnicodeString Path){  // Correction for PathIsRelative API  // Replace all /'s with \'s because PathIsRelative can't handle /'s  UnicodeString Result = ReplaceChar(Path, L'/', L'\\');  //Now call the API  return PathIsRelative(Result.c_str());}//---------------------------------------------------------------------------static int __fastcall GetOffsetAfterPathRoot(UnicodeString Path, PATH_PREFIX_TYPE & PrefixType){  // Checks if 'pPath' begins with the drive, share, prefix, etc  // EXAMPLES:  //    Path                          Return:   Points at:                 PrefixType:  //   Relative\Folder\File.txt        0         Relative\Folder\File.txt   PPT_UNKNOWN  //   \RelativeToRoot\Folder          1         RelativeToRoot\Folder      PPT_ABSOLUTE  //   C:\Windows\Folder               3         Windows\Folder             PPT_ABSOLUTE  //   \\server\share\Desktop          15        Desktop                    PPT_UNC  //   \\?\C:\Windows\Folder           7         Windows\Folder             PPT_LONG_UNICODE  //   \\?\UNC\server\share\Desktop    21        Desktop                    PPT_LONG_UNICODE_UNC  // RETURN:  //      = Index in 'pPath' after the root, or  //      = 0 if no root was found  int Result = 0;  PrefixType = PPT_UNKNOWN;  if (!Path.IsEmpty())  {    int Len = Path.Length();    bool WinXPOnly = !IsWinVista();    // The PathSkipRoot() API doesn't work correctly on Windows XP    if (!WinXPOnly)    {      // Works since Vista and up, but still needs correction :)      int RootLength = PathRootLength(Path);      if (RootLength >= 0)      {        Result = RootLength + 1;      }    }    // Now determine the type of prefix    int IndCheckUNC = -1;    if ((Len >= 8) &&        (Path[1] == L'\\' || Path[1] == L'/') &&        (Path[2] == L'\\' || Path[2] == L'/') &&        (Path[3] == L'?') &&        (Path[4] == L'\\' || Path[4] == L'/') &&        (Path[5] == L'U' || Path[5] == L'u') &&        (Path[6] == L'N' || Path[6] == L'n') &&        (Path[7] == L'C' || Path[7] == L'c') &&        (Path[8] == L'\\' || Path[8] == L'/'))    {      // Found \\?\UNC\ prefix      PrefixType = PPT_LONG_UNICODE_UNC;      if (WinXPOnly)      {          //For older OS          Result += 8;      }      //Check for UNC share later      IndCheckUNC = 8;    }    else if ((Len >= 4) &&        (Path[1] == L'\\' || Path[1] == L'/') &&        (Path[2] == L'\\' || Path[2] == L'/') &&        (Path[3] == L'?') &&        (Path[4] == L'\\' || Path[4] == L'/'))    {      // Found \\?\ prefix      PrefixType = PPT_LONG_UNICODE;      if (WinXPOnly)      {          //For older OS          Result += 4;      }    }    else if ((Len >= 2) &&        (Path[1] == L'\\' || Path[1] == L'/') &&        (Path[2] == L'\\' || Path[2] == L'/'))    {      // Check for UNC share later      IndCheckUNC = 2;    }    if (IndCheckUNC >= 0)    {      // Check for UNC, i.e. \\server\share\ part      int Index = IndCheckUNC;      for (int SkipSlashes = 2; SkipSlashes > 0; SkipSlashes--)      {        for(; Index <= Len; Index++)        {          TCHAR z = Path[Index];          if ((z == L'\\') || (z == L'/') || (Index >= Len))          {            Index++;            if (SkipSlashes == 1)            {              if (PrefixType == PPT_UNKNOWN)              {                PrefixType = PPT_UNC;              }              if (WinXPOnly)              {                  //For older OS                  Result = Index;              }            }            break;          }        }      }    }    if (WinXPOnly)    {      // Only if we didn't determine any other type      if (PrefixType == PPT_UNKNOWN)      {        if (!PathIsRelative_CorrectedForMicrosoftStupidity(Path.SubString(Result, Path.Length() - Result + 1)))        {          PrefixType = PPT_ABSOLUTE;        }      }      // For older OS only      int RootLength = PathRootLength(Path.SubString(Result, Path.Length() - Result + 1));      if (RootLength >= 0)      {        Result = RootLength + 1;      }    }    else    {      // Only if we didn't determine any other type      if (PrefixType == PPT_UNKNOWN)      {        if (!PathIsRelative_CorrectedForMicrosoftStupidity(Path))        {          PrefixType = PPT_ABSOLUTE;        }      }    }  }  return Result;}//---------------------------------------------------------------------------UnicodeString __fastcall MakeUnicodeLargePath(UnicodeString Path){  // Convert path from 'into a larger Unicode path, that allows up to 32,767 character length  UnicodeString Result;  if (!Path.IsEmpty())  {      // Determine the type of the existing prefix      PATH_PREFIX_TYPE PrefixType;      GetOffsetAfterPathRoot(Path, PrefixType);      // Assume path to be without change      Result = Path;      switch (PrefixType)      {        case PPT_ABSOLUTE:          {            // First we need to check if its an absolute path relative to the root            bool AddPrefix = true;            if ((Path.Length() >= 1) &&                ((Path[1] == L'\\') || (Path[1] == L'/')))            {              AddPrefix = FALSE;              // Get current root path              UnicodeString CurrentDir = GetCurrentDir();              PATH_PREFIX_TYPE PrefixType2; // unused              int Following = GetOffsetAfterPathRoot(CurrentDir, PrefixType2);              if (Following > 0)              {                AddPrefix = true;                Result = CurrentDir.SubString(1, Following - 1) + Result.SubString(2, Result.Length() - 1);              }            }            if (AddPrefix)            {              // Add \\?\ prefix              Result = L"\\\\?\\" + Result;            }          }          break;      case PPT_UNC:        // First we need to remove the opening slashes for UNC share        if ((Result.Length() >= 2) &&            ((Result[1] == L'\\') || (Result[1] == L'/')) &&            ((Result[2] == L'\\') || (Result[2] == L'/')))        {          Result = Result.SubString(3, Result.Length() - 2);        }        // Add \\?\UNC\ prefix        Result = L"\\\\?\\UNC\\" + Result;        break;      case PPT_LONG_UNICODE:      case PPT_LONG_UNICODE_UNC:        // nothing to do        break;    }  }  return Result;}//---------------------------------------------------------------------------UnicodeString __fastcall ApiPath(UnicodeString Path){  if (Path.Length() >= MAX_PATH)  {    if (Configuration != NULL)    {      Configuration->Usage->Inc(L"LongPath");    }    Path = MakeUnicodeLargePath(Path);  }  return Path;}//---------------------------------------------------------------------------UnicodeString __fastcall DisplayableStr(const RawByteString & Str){  bool Displayable = true;  int Index = 1;  while ((Index <= Str.Length()) && Displayable)  {    if (((Str[Index] < '\x20') || (static_cast<unsigned char>(Str[Index]) >= static_cast<unsigned char>('\x80'))) &&        (Str[Index] != '\n') && (Str[Index] != '\r') && (Str[Index] != '\t') && (Str[Index] != '\b'))    {      Displayable = false;    }    Index++;  }  UnicodeString Result;  if (Displayable)  {    Result = L"\"";    for (int Index = 1; Index <= Str.Length(); Index++)    {      switch (Str[Index])      {        case '\n':          Result += L"\\n";          break;        case '\r':          Result += L"\\r";          break;        case '\t':          Result += L"\\t";          break;        case '\b':          Result += L"\\b";          break;        case '\\':          Result += L"\\\\";          break;        case '"':          Result += L"\\\"";          break;        default:          Result += wchar_t(Str[Index]);          break;      }    }    Result += L"\"";  }  else  {    Result = L"0x" + BytesToHex(Str);  }  return Result;}//---------------------------------------------------------------------------UnicodeString __fastcall ByteToHex(unsigned char B, bool UpperCase){  static wchar_t UpperDigits[] = L"0123456789ABCDEF";  static wchar_t LowerDigits[] = L"0123456789abcdef";  const wchar_t * Digits = (UpperCase ? UpperDigits : LowerDigits);  UnicodeString Result;  Result.SetLength(2);  Result[1] = Digits[(B & 0xF0) >> 4];  Result[2] = Digits[(B & 0x0F) >> 0];  return Result;}//---------------------------------------------------------------------------UnicodeString __fastcall BytesToHex(const unsigned char * B, size_t Length, bool UpperCase, wchar_t Separator){  UnicodeString Result;  for (size_t i = 0; i < Length; i++)  {    Result += ByteToHex(B[i], UpperCase);    if ((Separator != L'\0') && (i < Length - 1))    {      Result += Separator;    }  }  return Result;}//---------------------------------------------------------------------------UnicodeString __fastcall BytesToHex(RawByteString Str, bool UpperCase, wchar_t Separator){  return BytesToHex(reinterpret_cast<const unsigned char *>(Str.c_str()), Str.Length(), UpperCase, Separator);}//---------------------------------------------------------------------------UnicodeString __fastcall CharToHex(wchar_t Ch, bool UpperCase){  return BytesToHex(reinterpret_cast<const unsigned char *>(&Ch), sizeof(Ch), UpperCase);}//---------------------------------------------------------------------------RawByteString __fastcall HexToBytes(const UnicodeString Hex){  static UnicodeString Digits = L"0123456789ABCDEF";  RawByteString Result;  int L, P1, P2;  L = Hex.Length();  if (L % 2 == 0)  {    for (int i = 1; i <= Hex.Length(); i += 2)    {      P1 = Digits.Pos((wchar_t)toupper(Hex[i]));      P2 = Digits.Pos((wchar_t)toupper(Hex[i + 1]));      if (P1 <= 0 || P2 <= 0)      {        Result = L"";        break;      }      else      {        Result += static_cast<char>((P1 - 1) * 16 + P2 - 1);      }    }  }  return Result;}//---------------------------------------------------------------------------unsigned char __fastcall HexToByte(const UnicodeString Hex){  static UnicodeString Digits = L"0123456789ABCDEF";  DebugAssert(Hex.Length() == 2);  int P1 = Digits.Pos((wchar_t)toupper(Hex[1]));  int P2 = Digits.Pos((wchar_t)toupper(Hex[2]));  return    static_cast<unsigned char>(((P1 <= 0) || (P2 <= 0)) ? 0 : (((P1 - 1) << 4) + (P2 - 1)));}//---------------------------------------------------------------------------bool __fastcall IsLowerCaseLetter(wchar_t Ch){  return (Ch >= 'a') && (Ch <= 'z');}//---------------------------------------------------------------------------bool __fastcall IsUpperCaseLetter(wchar_t Ch){  return (Ch >= 'A') && (Ch <= 'Z');}//---------------------------------------------------------------------------bool __fastcall IsLetter(wchar_t Ch){  return IsLowerCaseLetter(Ch) || IsUpperCaseLetter(Ch);}//---------------------------------------------------------------------------bool __fastcall IsDigit(wchar_t Ch){  return (Ch >= '0') && (Ch <= '9');}//---------------------------------------------------------------------------bool __fastcall IsHex(wchar_t Ch){  return    IsDigit(Ch) ||    ((Ch >= 'A') && (Ch <= 'F')) ||    ((Ch >= 'a') && (Ch <= 'f'));}//---------------------------------------------------------------------------int __fastcall FindCheck(int Result, const UnicodeString & Path){  if ((Result != ERROR_SUCCESS) &&      (Result != ERROR_FILE_NOT_FOUND) &&      (Result != ERROR_NO_MORE_FILES))  {    throw EOSExtException(FMTLOAD(FIND_FILE_ERROR, (Path)), Result);  }  return Result;}//---------------------------------------------------------------------------int __fastcall FindFirstUnchecked(const UnicodeString & Path, int Attr, TSearchRecChecked & F){  F.Path = Path;  return FindFirst(ApiPath(Path), Attr, F);}//---------------------------------------------------------------------------int __fastcall FindFirstChecked(const UnicodeString & Path, int Attr, TSearchRecChecked & F){  int Result = FindFirstUnchecked(Path, Attr, F);  return FindCheck(Result, F.Path);}//---------------------------------------------------------------------------// Equivalent to FindNext, just to complement to FindFirstUncheckedint __fastcall FindNextUnchecked(TSearchRecChecked & F){  return FindNext(F);}//---------------------------------------------------------------------------// It can make sense to use FindNextChecked, even if unchecked FindFirst is used.// I.e. even if we do not care that FindFirst failed, if FindNext// fails after successful FindFirst, it means some terrible problemint __fastcall FindNextChecked(TSearchRecChecked & F){  return FindCheck(FindNextUnchecked(F), F.Path);}//---------------------------------------------------------------------------bool __fastcall FileSearchRec(const UnicodeString FileName, TSearchRec & Rec){  int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;  bool Result = (FindFirst(ApiPath(FileName), FindAttrs, Rec) == 0);  if (Result)  {    FindClose(Rec);  }  return Result;}//---------------------------------------------------------------------------void __fastcall ProcessLocalDirectory(UnicodeString DirName,  TProcessLocalFileEvent CallBackFunc, void * Param,  int FindAttrs){  DebugAssert(CallBackFunc);  if (FindAttrs < 0)  {    FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;  }  TSearchRecChecked SearchRec;  DirName = IncludeTrailingBackslash(DirName);  if (FindFirstChecked(DirName + L"*.*", FindAttrs, SearchRec) == 0)  {    try    {      do      {        if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))        {          CallBackFunc(DirName + SearchRec.Name, SearchRec, Param);        }      } while (FindNextChecked(SearchRec) == 0);    }    __finally    {      FindClose(SearchRec);    }  }}//---------------------------------------------------------------------------TDateTime __fastcall EncodeDateVerbose(Word Year, Word Month, Word Day){  try  {    TDateTime DateTime = EncodeDate(Year, Month, Day);    return DateTime;  }  catch (EConvertError & E)  {    throw EConvertError(FORMAT(L"%s [%d-%d-%d]", (E.Message, int(Year), int(Month), int(Day))));  }}//---------------------------------------------------------------------------TDateTime __fastcall EncodeTimeVerbose(Word Hour, Word Min, Word Sec, Word MSec){  try  {    TDateTime DateTime = EncodeTime(Hour, Min, Sec, MSec);    return DateTime;  }  catch (EConvertError & E)  {    throw EConvertError(FORMAT(L"%s [%d:%d:%d.%d]", (E.Message, int(Hour), int(Min), int(Sec), int(MSec))));  }}//---------------------------------------------------------------------------TDateTime __fastcall SystemTimeToDateTimeVerbose(const SYSTEMTIME & SystemTime){  try  {    TDateTime DateTime = SystemTimeToDateTime(SystemTime);    return DateTime;  }  catch (EConvertError & E)  {    throw EConvertError(FORMAT(L"%s [%d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%3.3d]", (E.Message, int(SystemTime.wYear), int(SystemTime.wMonth), int(SystemTime.wDay), int(SystemTime.wHour), int(SystemTime.wMinute), int(SystemTime.wSecond), int(SystemTime.wMilliseconds))));  }}//---------------------------------------------------------------------------struct TDateTimeParams{  TDateTime UnixEpoch;  double BaseDifference;  long BaseDifferenceSec;  // All Current* are actually global, not per-year and  // are valid for Year 0 (current) only  double CurrentDaylightDifference;  long CurrentDaylightDifferenceSec;  double CurrentDifference;  long CurrentDifferenceSec;  double StandardDifference;  long StandardDifferenceSec;  double DaylightDifference;  long DaylightDifferenceSec;  SYSTEMTIME SystemStandardDate;  SYSTEMTIME SystemDaylightDate;  TDateTime StandardDate;  TDateTime DaylightDate;  UnicodeString StandardName;  UnicodeString DaylightName;  // This is actually global, not per-year  bool DaylightHack;  bool HasDST() const  {    // On some systems it occurs that StandardDate is unset, while    // DaylightDate is set. MSDN states that this is invalid and    // should be treated as if there is no daylight saving.    // So check both.    return      (SystemStandardDate.wMonth != 0) &&      (SystemDaylightDate.wMonth != 0);  }  bool SummerDST() const  {    return HasDST() && (DaylightDate < StandardDate);  }};typedef std::map<int, TDateTimeParams> TYearlyDateTimeParams;static TYearlyDateTimeParams YearlyDateTimeParams;static std::unique_ptr<TCriticalSection> DateTimeParamsSection(new TCriticalSection());static void __fastcall EncodeDSTMargin(const SYSTEMTIME & Date, unsigned short Year,  TDateTime & Result);//---------------------------------------------------------------------------static unsigned short __fastcall DecodeYear(const TDateTime & DateTime){  unsigned short Year, Month, Day;  DecodeDate(DateTime, Year, Month, Day);  return Year;}//---------------------------------------------------------------------------static const TDateTimeParams * __fastcall GetDateTimeParams(unsigned short Year){  TGuard Guard(DateTimeParamsSection.get());  TDateTimeParams * Result;  TYearlyDateTimeParams::iterator i = YearlyDateTimeParams.find(Year);  if (i != YearlyDateTimeParams.end())  {    Result = &(*i).second;  }  else  {    // creates new entry as a side effect    Result = &YearlyDateTimeParams[Year];    TIME_ZONE_INFORMATION TZI;    unsigned long GTZI;    HINSTANCE Kernel32 = GetModuleHandle(kernel32);    typedef BOOL WINAPI (* TGetTimeZoneInformationForYear)(USHORT wYear, PDYNAMIC_TIME_ZONE_INFORMATION pdtzi, LPTIME_ZONE_INFORMATION ptzi);    TGetTimeZoneInformationForYear GetTimeZoneInformationForYear =      (TGetTimeZoneInformationForYear)GetProcAddress(Kernel32, "GetTimeZoneInformationForYear");    if ((Year == 0) || (GetTimeZoneInformationForYear == NULL))    {      GTZI = GetTimeZoneInformation(&TZI);    }    else    {      GetTimeZoneInformationForYear(Year, NULL, &TZI);      GTZI = TIME_ZONE_ID_UNKNOWN;    }    switch (GTZI)    {      case TIME_ZONE_ID_UNKNOWN:        Result->CurrentDaylightDifferenceSec = 0;        break;      case TIME_ZONE_ID_STANDARD:        Result->CurrentDaylightDifferenceSec = TZI.StandardBias;        break;      case TIME_ZONE_ID_DAYLIGHT:        Result->CurrentDaylightDifferenceSec = TZI.DaylightBias;        break;      case TIME_ZONE_ID_INVALID:      default:        throw Exception(TIMEZONE_ERROR);    }    Result->BaseDifferenceSec = TZI.Bias;    Result->BaseDifference = double(TZI.Bias) / MinsPerDay;    Result->BaseDifferenceSec *= SecsPerMin;    Result->CurrentDifferenceSec = TZI.Bias +      Result->CurrentDaylightDifferenceSec;    Result->CurrentDifference =      double(Result->CurrentDifferenceSec) / MinsPerDay;    Result->CurrentDifferenceSec *= SecsPerMin;    Result->CurrentDaylightDifference =      double(Result->CurrentDaylightDifferenceSec) / MinsPerDay;    Result->CurrentDaylightDifferenceSec *= SecsPerMin;    Result->DaylightDifferenceSec = TZI.DaylightBias * SecsPerMin;    Result->DaylightDifference = double(TZI.DaylightBias) / MinsPerDay;    Result->StandardDifferenceSec = TZI.StandardBias * SecsPerMin;    Result->StandardDifference = double(TZI.StandardBias) / MinsPerDay;    Result->SystemStandardDate = TZI.StandardDate;    Result->SystemDaylightDate = TZI.DaylightDate;    unsigned short AYear = (Year != 0) ? Year : DecodeYear(Now());    if (Result->HasDST())    {      EncodeDSTMargin(Result->SystemStandardDate, AYear, Result->StandardDate);      EncodeDSTMargin(Result->SystemDaylightDate, AYear, Result->DaylightDate);    }    Result->StandardName = TZI.StandardName;    Result->DaylightName = TZI.DaylightName;    Result->DaylightHack = !IsWin7();  }  return Result;}//---------------------------------------------------------------------------static void __fastcall EncodeDSTMargin(const SYSTEMTIME & Date, unsigned short Year,  TDateTime & Result){  if (Date.wYear == 0)  {    TDateTime Temp = EncodeDateVerbose(Year, Date.wMonth, 1);    Result = Temp + ((Date.wDayOfWeek - DayOfWeek(Temp) + 8) % 7) +      (7 * (Date.wDay - 1));    // Day 5 means, the last occurence of day-of-week in month    if (Date.wDay == 5)    {      unsigned short Month = static_cast<unsigned short>(Date.wMonth + 1);      if (Month > 12)      {        Month = static_cast<unsigned short>(Month - 12);        Year++;      }      if (Result >= EncodeDateVerbose(Year, Month, 1))      {        Result -= 7;      }    }    Result += EncodeTimeVerbose(Date.wHour, Date.wMinute, Date.wSecond,      Date.wMilliseconds);  }  else  {    Result = EncodeDateVerbose(Year, Date.wMonth, Date.wDay) +      EncodeTimeVerbose(Date.wHour, Date.wMinute, Date.wSecond, Date.wMilliseconds);  }}//---------------------------------------------------------------------------static bool __fastcall IsDateInDST(const TDateTime & DateTime){  const TDateTimeParams * Params = GetDateTimeParams(DecodeYear(DateTime));  bool Result;  // On some systems it occurs that StandardDate is unset, while  // DaylightDate is set. MSDN states that this is invalid and  // should be treated as if there is no daylight saving.  // So check both.  if (!Params->HasDST())  {    Result = false;  }  else  {    if (Params->SummerDST())    {      Result =        (DateTime >= Params->DaylightDate) &&        (DateTime < Params->StandardDate);    }    else    {      Result =        (DateTime < Params->StandardDate) ||        (DateTime >= Params->DaylightDate);    }  }  return Result;}//---------------------------------------------------------------------------bool __fastcall UsesDaylightHack(){  return GetDateTimeParams(0)->DaylightHack;}//---------------------------------------------------------------------------TDateTime __fastcall UnixToDateTime(__int64 TimeStamp, TDSTMode DSTMode){  DebugAssert(int(EncodeDateVerbose(1970, 1, 1)) == UnixDateDelta);  TDateTime Result = UnixDateDelta + (double(TimeStamp) / SecsPerDay);  const TDateTimeParams * Params = GetDateTimeParams(DecodeYear(Result));  if (Params->DaylightHack)  {    if ((DSTMode == dstmWin) || (DSTMode == dstmUnix))    {      const TDateTimeParams * CurrentParams = GetDateTimeParams(0);      Result -= CurrentParams->CurrentDifference;    }    else if (DSTMode == dstmKeep)    {      Result -= Params->BaseDifference;    }  }  else  {    Result -= Params->BaseDifference;  }  if ((DSTMode == dstmUnix) || (DSTMode == dstmKeep))  {    Result -= DSTDifferenceForTime(Result);  }  return Result;}//---------------------------------------------------------------------------__int64 __fastcall Round(double Number){  double Floor = floor(Number);  double Ceil = ceil(Number);  return static_cast<__int64>(((Number - Floor) > (Ceil - Number)) ? Ceil : Floor);}//---------------------------------------------------------------------------bool __fastcall TryRelativeStrToDateTime(UnicodeString S, TDateTime & DateTime){  S = S.Trim();  int Index = 1;  while ((Index <= S.Length()) && IsDigit(S[Index]))  {    Index++;  }  UnicodeString NumberStr = S.SubString(1, Index - 1);  int Number;  bool Result = TryStrToInt(NumberStr, Number);  if (Result)  {    S.Delete(1, Index - 1);    S = S.Trim().UpperCase();    DateTime = Now();    // These may not overlap with ParseSize (K, M and G)    if (S == "S")    {      DateTime = IncSecond(DateTime, -Number);    }    else if (S == "N")    {      DateTime = IncMinute(DateTime, -Number);    }    else if (S == "H")    {      DateTime = IncHour(DateTime, -Number);    }    else if (S == "D")    {      DateTime = IncDay(DateTime, -Number);    }    else if (S == "Y")    {      DateTime = IncYear(DateTime, -Number);    }    else    {      Result = false;    }  }  return Result;}//---------------------------------------------------------------------------static __int64 __fastcall DateTimeToUnix(const TDateTime DateTime){  const TDateTimeParams * CurrentParams = GetDateTimeParams(0);  DebugAssert(int(EncodeDateVerbose(1970, 1, 1)) == UnixDateDelta);  return Round(double(DateTime - UnixDateDelta) * SecsPerDay) +    CurrentParams->CurrentDifferenceSec;}//---------------------------------------------------------------------------FILETIME __fastcall DateTimeToFileTime(const TDateTime DateTime,  TDSTMode /*DSTMode*/){  __int64 UnixTimeStamp = ::DateTimeToUnix(DateTime);  const TDateTimeParams * Params = GetDateTimeParams(DecodeYear(DateTime));  if (!Params->DaylightHack)  {    // We should probably use reversed code of FileTimeToDateTime here instead of custom implementation    // We are incrementing and decrementing BaseDifferenceSec because it    // can actually change between years    // (as it did in Belarus from GMT+2 to GMT+3 between 2011 and 2012)    UnixTimeStamp += (IsDateInDST(DateTime) ?      Params->DaylightDifferenceSec : Params->StandardDifferenceSec) +      Params->BaseDifferenceSec;    const TDateTimeParams * CurrentParams = GetDateTimeParams(0);    UnixTimeStamp -=      CurrentParams->CurrentDaylightDifferenceSec +      CurrentParams->BaseDifferenceSec;  }  FILETIME Result;  (*(__int64*)&(Result) = (__int64(UnixTimeStamp) + 11644473600LL) * 10000000LL);  return Result;}//---------------------------------------------------------------------------TDateTime __fastcall FileTimeToDateTime(const FILETIME & FileTime){  // duplicated in DirView.pas  TDateTime Result;  // The 0xFFF... is sometime seen for invalid timestamps,  // it would cause failure in SystemTimeToDateTime below  if (FileTime.dwLowDateTime == std::numeric_limits<DWORD>::max())  {    Result = MinDateTime;  }  else  {    SYSTEMTIME SysTime;    if (!UsesDaylightHack())    {      SYSTEMTIME UniverzalSysTime;      FileTimeToSystemTime(&FileTime, &UniverzalSysTime);      SystemTimeToTzSpecificLocalTime(NULL, &UniverzalSysTime, &SysTime);    }    else    {      FILETIME LocalFileTime;      FileTimeToLocalFileTime(&FileTime, &LocalFileTime);      FileTimeToSystemTime(&LocalFileTime, &SysTime);    }    Result = SystemTimeToDateTimeVerbose(SysTime);  }  return Result;}//---------------------------------------------------------------------------__int64 __fastcall ConvertTimestampToUnix(const FILETIME & FileTime,  TDSTMode DSTMode){  __int64 Result = ((*(__int64*)&(FileTime)) / 10000000LL - 11644473600LL);  if (UsesDaylightHack())  {    if ((DSTMode == dstmUnix) || (DSTMode == dstmKeep))    {      FILETIME LocalFileTime;      SYSTEMTIME SystemTime;      FileTimeToLocalFileTime(&FileTime, &LocalFileTime);      FileTimeToSystemTime(&LocalFileTime, &SystemTime);      TDateTime DateTime = SystemTimeToDateTimeVerbose(SystemTime);      const TDateTimeParams * Params = GetDateTimeParams(DecodeYear(DateTime));      Result += (IsDateInDST(DateTime) ?        Params->DaylightDifferenceSec : Params->StandardDifferenceSec);      if (DSTMode == dstmKeep)      {        const TDateTimeParams * CurrentParams = GetDateTimeParams(0);        Result -= CurrentParams->CurrentDaylightDifferenceSec;      }    }  }  else  {    if (DSTMode == dstmWin)    {      FILETIME LocalFileTime;      SYSTEMTIME SystemTime;      FileTimeToLocalFileTime(&FileTime, &LocalFileTime);      FileTimeToSystemTime(&LocalFileTime, &SystemTime);      TDateTime DateTime = SystemTimeToDateTimeVerbose(SystemTime);      const TDateTimeParams * Params = GetDateTimeParams(DecodeYear(DateTime));      Result -= (IsDateInDST(DateTime) ?        Params->DaylightDifferenceSec : Params->StandardDifferenceSec);    }  }  return Result;}//---------------------------------------------------------------------------TDateTime __fastcall ConvertTimestampToUTC(TDateTime DateTime){  const TDateTimeParams * Params = GetDateTimeParams(DecodeYear(DateTime));  DateTime += DSTDifferenceForTime(DateTime);  DateTime += Params->BaseDifference;  if (Params->DaylightHack)  {    const TDateTimeParams * CurrentParams = GetDateTimeParams(0);    DateTime += CurrentParams->CurrentDaylightDifference;  }  return DateTime;}//---------------------------------------------------------------------------TDateTime __fastcall ConvertTimestampFromUTC(TDateTime DateTime){  const TDateTimeParams * Params = GetDateTimeParams(DecodeYear(DateTime));  DateTime -= DSTDifferenceForTime(DateTime);  DateTime -= Params->BaseDifference;  if (Params->DaylightHack)  {    const TDateTimeParams * CurrentParams = GetDateTimeParams(0);    DateTime -= CurrentParams->CurrentDaylightDifference;  }  return DateTime;}//---------------------------------------------------------------------------__int64 __fastcall ConvertTimestampToUnixSafe(const FILETIME & FileTime,  TDSTMode DSTMode){  __int64 Result;  if ((FileTime.dwLowDateTime == 0) &&      (FileTime.dwHighDateTime == 0))  {    Result = ::DateTimeToUnix(Now());  }  else  {    Result = ConvertTimestampToUnix(FileTime, DSTMode);  }  return Result;}//---------------------------------------------------------------------------double __fastcall DSTDifferenceForTime(TDateTime DateTime){  double Result;  const TDateTimeParams * Params = GetDateTimeParams(DecodeYear(DateTime));  if (IsDateInDST(DateTime))  {    Result = Params->DaylightDifference;  }  else  {    Result = Params->StandardDifference;  }  return Result;}//---------------------------------------------------------------------------TDateTime __fastcall AdjustDateTimeFromUnix(TDateTime DateTime, TDSTMode DSTMode){  const TDateTimeParams * Params = GetDateTimeParams(DecodeYear(DateTime));  if (Params->DaylightHack)  {    if ((DSTMode == dstmWin) || (DSTMode == dstmUnix))    {      const TDateTimeParams * CurrentParams = GetDateTimeParams(0);      DateTime = DateTime - CurrentParams->CurrentDaylightDifference;    }    if (!IsDateInDST(DateTime))    {      if (DSTMode == dstmWin)      {        DateTime = DateTime - Params->DaylightDifference;      }    }    else    {      DateTime = DateTime - Params->StandardDifference;    }  }  else  {    if (DSTMode == dstmWin)    {      DateTime = DateTime + DSTDifferenceForTime(DateTime);    }  }  return DateTime;}//---------------------------------------------------------------------------UnicodeString __fastcall FixedLenDateTimeFormat(const UnicodeString & Format){  UnicodeString Result = Format;  bool AsIs = false;  int Index = 1;  while (Index <= Result.Length())  {    wchar_t F = Result[Index];    if ((F == L'\'') || (F == L'\"'))    {      AsIs = !AsIs;      Index++;    }    else if (!AsIs && ((F == L'a') || (F == L'A')))    {      if (Result.SubString(Index, 5).LowerCase() == L"am/pm")      {        Index += 5;      }      else if (Result.SubString(Index, 3).LowerCase() == L"a/p")      {        Index += 3;      }      else if (Result.SubString(Index, 4).LowerCase() == L"ampm")      {        Index += 4;      }      else      {        Index++;      }    }    else    {      if (!AsIs && (wcschr(L"dDeEmMhHnNsS", F) != NULL) &&          ((Index == Result.Length()) || (Result[Index + 1] != F)))      {        Result.Insert(F, Index);      }      while ((Index <= Result.Length()) && (F == Result[Index]))      {        Index++;      }    }  }  return Result;}//---------------------------------------------------------------------------UnicodeString __fastcall FormatTimeZone(long Sec){  TTimeSpan Span = TTimeSpan::FromSeconds(Sec);  UnicodeString Str;  if ((Span.Seconds == 0) && (Span.Minutes == 0))  {    Str = FORMAT(L"%d", (-Span.Hours));  }  else if (Span.Seconds == 0)  {    Str = FORMAT(L"%d:%2.2d", (-Span.Hours, abs(Span.Minutes)));  }  else  {    Str = FORMAT(L"%d:%2.2d:%2.2d", (-Span.Hours, abs(Span.Minutes), abs(Span.Seconds)));  }  Str = ((Span <= TTimeSpan::Zero) ? L"+" : L"") + Str;  return Str;}//---------------------------------------------------------------------------UnicodeString __fastcall GetTimeZoneLogString(){  const TDateTimeParams * CurrentParams = GetDateTimeParams(0);  UnicodeString Result =    FORMAT(L"Current: GMT%s", (FormatTimeZone(CurrentParams->CurrentDifferenceSec)));  if (!CurrentParams->HasDST())  {    Result += FORMAT(L" (%s), No DST", (CurrentParams->StandardName));  }  else  {    Result +=      FORMAT(L", Standard: GMT%s (%s), DST: GMT%s (%s), DST Start: %s, DST End: %s",        (FormatTimeZone(CurrentParams->BaseDifferenceSec + CurrentParams->StandardDifferenceSec),         CurrentParams->StandardName,         FormatTimeZone(CurrentParams->BaseDifferenceSec + CurrentParams->DaylightDifferenceSec),         CurrentParams->DaylightName,         CurrentParams->DaylightDate.DateString(),         CurrentParams->StandardDate.DateString()));  }  return Result;}//---------------------------------------------------------------------------bool __fastcall AdjustClockForDSTEnabled(){  // Windows XP deletes the DisableAutoDaylightTimeSet value when it is off  // (the later versions set it to DynamicDaylightTimeDisabled to 0)  bool DynamicDaylightTimeDisabled = false;  TRegistry * Registry = new TRegistry(KEY_READ);  try  {    Registry->RootKey = HKEY_LOCAL_MACHINE;    if (Registry->OpenKey(L"SYSTEM", false) &&        Registry->OpenKey(L"CurrentControlSet", false) &&        Registry->OpenKey(L"Control", false) &&        Registry->OpenKey(L"TimeZoneInformation", false))    {      if (Registry->ValueExists(L"DynamicDaylightTimeDisabled"))      {        DynamicDaylightTimeDisabled = Registry->ReadBool(L"DynamicDaylightTimeDisabled");      }      // WORKAROUND      // Windows XP equivalent      else if (Registry->ValueExists(L"DisableAutoDaylightTimeSet"))      {        DynamicDaylightTimeDisabled = Registry->ReadBool(L"DisableAutoDaylightTimeSet");      }    }    delete Registry;  }  catch(...)  {  }  return !DynamicDaylightTimeDisabled;}//---------------------------------------------------------------------------UnicodeString __fastcall StandardDatestamp(){  return FormatDateTime(L"yyyy'-'mm'-'dd", ConvertTimestampToUTC(Now()));}//---------------------------------------------------------------------------UnicodeString __fastcall StandardTimestamp(const TDateTime & DateTime){  return FormatDateTime(L"yyyy'-'mm'-'dd'T'hh':'nn':'ss'.'zzz'Z'", ConvertTimestampToUTC(DateTime));}//---------------------------------------------------------------------------UnicodeString __fastcall StandardTimestamp(){  return StandardTimestamp(Now());}//---------------------------------------------------------------------------static TDateTime TwoSeconds(0, 0, 2, 0);int __fastcall CompareFileTime(TDateTime T1, TDateTime T2){  // "FAT" time precision  // (when one time is seconds-precision and other is millisecond-precision,  // we may have times like 12:00:00.000 and 12:00:01.999, which should  // be treated the same)  int Result;  if (T1 == T2)  {    // just optimization    Result = 0;  }  else if ((T1 < T2) && (T2 - T1 >= TwoSeconds))  {    Result = -1;  }  else if ((T1 > T2) && (T1 - T2 >= TwoSeconds))  {    Result = 1;  }  else  {    Result = 0;  }  return Result;}//---------------------------------------------------------------------------int __fastcall TimeToMSec(TDateTime T){  return int(Round(double(T) * double(MSecsPerDay)));}//---------------------------------------------------------------------------int __fastcall TimeToSeconds(TDateTime T){  return TimeToMSec(T) / MSecsPerSec;}//---------------------------------------------------------------------------int __fastcall TimeToMinutes(TDateTime T){  return TimeToSeconds(T) / SecsPerMin;}//---------------------------------------------------------------------------static bool __fastcall DoRecursiveDeleteFile(const UnicodeString FileName, bool ToRecycleBin, UnicodeString & ErrorPath){  bool Result;  UnicodeString AErrorPath = FileName;  if (!ToRecycleBin)  {    TSearchRecChecked SearchRec;    Result = FileSearchRec(FileName, SearchRec);    if (Result)    {      if (FLAGCLEAR(SearchRec.Attr, faDirectory))      {        Result = DeleteFile(ApiPath(FileName));      }      else      {        Result = (FindFirstUnchecked(FileName + L"\\*", faAnyFile, SearchRec) == 0);        if (Result)        {          try          {            do            {              UnicodeString FileName2 = FileName + L"\\" + SearchRec.Name;              if (FLAGSET(SearchRec.Attr, faDirectory))              {                if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))                {                  Result = DoRecursiveDeleteFile(FileName2, DebugAlwaysFalse(ToRecycleBin), AErrorPath);                }              }              else              {                Result = DeleteFile(ApiPath(FileName2));                if (!Result)                {                  AErrorPath = FileName2;                }              }            }            while (Result && (FindNextUnchecked(SearchRec) == 0));          }          __finally          {            FindClose(SearchRec);          }          if (Result)          {            Result = RemoveDir(ApiPath(FileName));          }        }      }    }  }  else  {    SHFILEOPSTRUCT Data;    memset(&Data, 0, sizeof(Data));    Data.hwnd = NULL;    Data.wFunc = FO_DELETE;    // SHFileOperation does not support long paths anyway    UnicodeString FileList(ApiPath(FileName));    FileList.SetLength(FileList.Length() + 2);    FileList[FileList.Length() - 1] = L'\0';    FileList[FileList.Length()] = L'\0';    Data.pFrom = FileList.c_str();    Data.pTo = L"\0\0"; // this will actually give one null more than needed    Data.fFlags = FOF_NOCONFIRMATION | FOF_RENAMEONCOLLISION | FOF_NOCONFIRMMKDIR |      FOF_NOERRORUI | FOF_SILENT;    if (DebugAlwaysTrue(ToRecycleBin))    {      Data.fFlags |= FOF_ALLOWUNDO;    }    int ErrorCode = SHFileOperation(&Data);    Result = (ErrorCode == 0);    if (!Result)    {      // according to MSDN, SHFileOperation may return following non-Win32      // error codes      if (((ErrorCode >= 0x71) && (ErrorCode <= 0x88)) ||          (ErrorCode == 0xB7) || (ErrorCode == 0x402) || (ErrorCode == 0x10000) ||          (ErrorCode == 0x10074))      {        ErrorCode = 0;      }      SetLastError(ErrorCode);    }  }  if (!Result)  {    ErrorPath = AErrorPath;  }  return Result;}//---------------------------------------------------------------------------bool __fastcall RecursiveDeleteFile(const UnicodeString & FileName, bool ToRecycleBin){  UnicodeString ErrorPath; // unused  return DoRecursiveDeleteFile(FileName, ToRecycleBin, ErrorPath);}//---------------------------------------------------------------------------void __fastcall RecursiveDeleteFileChecked(const UnicodeString & FileName, bool ToRecycleBin){  UnicodeString ErrorPath;  if (!DoRecursiveDeleteFile(FileName, ToRecycleBin, ErrorPath))  {    throw EOSExtException(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (ErrorPath)));  }}//---------------------------------------------------------------------------void __fastcall DeleteFileChecked(const UnicodeString & FileName){  if (!DeleteFile(ApiPath(FileName)))  {    throw EOSExtException(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (FileName)));  }}//---------------------------------------------------------------------------unsigned int __fastcall CancelAnswer(unsigned int Answers){  unsigned int Result;  if ((Answers & qaCancel) != 0)  {    Result = qaCancel;  }  else if ((Answers & qaNo) != 0)  {    Result = qaNo;  }  else if ((Answers & qaAbort) != 0)  {    Result = qaAbort;  }  else if ((Answers & qaOK) != 0)  {    Result = qaOK;  }  else  {    DebugFail();    Result = qaCancel;  }  return Result;}//---------------------------------------------------------------------------unsigned int __fastcall AbortAnswer(unsigned int Answers){  unsigned int Result;  if (FLAGSET(Answers, qaAbort))  {    Result = qaAbort;  }  else  {    Result = CancelAnswer(Answers);  }  return Result;}//---------------------------------------------------------------------------unsigned int __fastcall ContinueAnswer(unsigned int Answers){  unsigned int Result;  if (FLAGSET(Answers, qaSkip))  {    Result = qaSkip;  }  else if (FLAGSET(Answers, qaIgnore))  {    Result = qaIgnore;  }  else if (FLAGSET(Answers, qaYes))  {    Result = qaYes;  }  else if (FLAGSET(Answers, qaOK))  {    Result = qaOK;  }  else if (FLAGSET(Answers, qaRetry))  {    Result = qaRetry;  }  else  {    Result = CancelAnswer(Answers);  }  return Result;}//---------------------------------------------------------------------------TLibModule * __fastcall FindModule(void * Instance){  TLibModule * CurModule;  CurModule = reinterpret_cast<TLibModule*>(LibModuleList);  while (CurModule)  {    if (CurModule->Instance == (unsigned)Instance)    {      break;    }    else    {      CurModule = CurModule->Next;    }  }  return CurModule;}//---------------------------------------------------------------------------UnicodeString __fastcall LoadStr(int Ident, unsigned int MaxLength){  TLibModule * MainModule = FindModule(HInstance);  DebugAssert(MainModule != NULL);  UnicodeString Result;  Result.SetLength(MaxLength);  int Length = LoadString((HINSTANCE)MainModule->ResInstance, Ident, Result.c_str(), MaxLength);  Result.SetLength(Length);  return Result;}//---------------------------------------------------------------------------UnicodeString __fastcall LoadStrPart(int Ident, int Part){  UnicodeString Result;  UnicodeString Str = LoadStr(Ident);  while (Part > 0)  {    Result = CutToChar(Str, L'|', false);    Part--;  }  return Result;}//---------------------------------------------------------------------------UnicodeString __fastcall DecodeUrlChars(UnicodeString S){  int i = 1;  while (i <= S.Length())  {    switch (S[i])    {      case L'+':        S[i] = ' ';        break;      case L'%':        {          UnicodeString Hex;          while ((i + 2 <= S.Length()) && (S[i] == L'%') &&                 IsHex(S[i + 1]) && IsHex(S[i + 2]))          {            Hex += S.SubString(i + 1, 2);            S.Delete(i, 3);          }          if (!Hex.IsEmpty())          {            RawByteString Bytes = HexToBytes(Hex);            UnicodeString Chars(UTF8ToString(Bytes));            S.Insert(Chars, i);            i += Chars.Length() - 1;          }        }        break;    }    i++;  }  return S;}//---------------------------------------------------------------------------UnicodeString __fastcall DoEncodeUrl(UnicodeString S, bool EncodeSlash){  int Index = 1;  while (Index <= S.Length())  {    wchar_t C = S[Index];    if (IsLetter(C) ||        IsDigit(C) ||        (C == L'_') || (C == L'-') || (C == L'.') ||        ((C == L'/') && !EncodeSlash))    {      Index++;    }    else    {      UTF8String UtfS(S.SubString(Index, 1));      UnicodeString H;      for (int Index2 = 1; Index2 <= UtfS.Length(); Index2++)      {        H += L"%" + ByteToHex(static_cast<unsigned char>(UtfS[Index2]));      }      S.Delete(Index, 1);      S.Insert(H, Index);      Index += H.Length();    }  }  return S;}//---------------------------------------------------------------------------UnicodeString __fastcall EncodeUrlString(UnicodeString S){  return DoEncodeUrl(S, true);}//---------------------------------------------------------------------------UnicodeString __fastcall EncodeUrlPath(UnicodeString S){  return DoEncodeUrl(S, false);}//---------------------------------------------------------------------------UnicodeString __fastcall AppendUrlParams(UnicodeString AURL, UnicodeString Params){  // see also TWebHelpSystem::ShowHelp  const wchar_t FragmentSeparator = L'#';  UnicodeString URL = CutToChar(AURL, FragmentSeparator, false);  if (URL.Pos(L"?") == 0)  {    URL += L"?";  }  else  {    URL += L"&";  }  URL += Params;  AddToList(URL, AURL, FragmentSeparator);  return URL;}//---------------------------------------------------------------------------UnicodeString __fastcall EscapeHotkey(const UnicodeString & Caption){  return ReplaceStr(Caption, L"&", L"&&");}//---------------------------------------------------------------------------// duplicated in console's Main.cppbool __fastcall CutToken(UnicodeString & Str, UnicodeString & Token,  UnicodeString * RawToken, UnicodeString * Separator){  bool Result;  Token = L"";  // inspired by Putty's sftp_getcmd() from PSFTP.C  int Index = 1;  while ((Index <= Str.Length()) &&    ((Str[Index] == L' ') || (Str[Index] == L'\t')))  {    Index++;  }  if (Index <= Str.Length())  {    bool Quoting = false;    while (Index <= Str.Length())    {      if (!Quoting && ((Str[Index] == L' ') || (Str[Index] == L'\t')))      {        break;      }      // We should escape quotes only within quotes      // otherwise the "" means " (quote), but it should mean empty string.      // Or have a special case for bare "".      else if ((Str[Index] == L'"') && (Index + 1 <= Str.Length()) &&        (Str[Index + 1] == L'"'))      {        Index += 2;        Token += L'"';      }      else if (Str[Index] == L'"')      {        Index++;        Quoting = !Quoting;      }      else      {        Token += Str[Index];        Index++;      }    }    if (RawToken != NULL)    {      (*RawToken) = Str.SubString(1, Index - 1);    }    if (Index <= Str.Length())    {      if (Separator != NULL)      {        *Separator = Str.SubString(Index, 1);      }      Index++;    }    else    {      if (Separator != NULL)      {        *Separator = UnicodeString();      }    }    Str = Str.SubString(Index, Str.Length());    Result = true;  }  else  {    Result = false;    Str = L"";  }  return Result;}//---------------------------------------------------------------------------void __fastcall AddToList(UnicodeString & List, const UnicodeString & Value, const UnicodeString & Delimiter){  if (!Value.IsEmpty())  {    if (!List.IsEmpty() &&        ((List.Length() < Delimiter.Length()) ||         (List.SubString(List.Length() - Delimiter.Length() + 1, Delimiter.Length()) != Delimiter)))    {      List += Delimiter;    }    List += Value;  }}//---------------------------------------------------------------------------bool __fastcall IsWinVista(){  // Vista is 6.0  // Win XP is 5.1  // There also 5.2, what is Windows 2003 or Windows XP 64bit  // (we consider it WinXP for now)  return CheckWin32Version(6, 0);}//---------------------------------------------------------------------------bool __fastcall IsWin7(){  return CheckWin32Version(6, 1);}//---------------------------------------------------------------------------bool __fastcall IsWin8(){  return CheckWin32Version(6, 2);}//---------------------------------------------------------------------------bool __fastcall IsWin10(){  return CheckWin32Version(10, 0);}//---------------------------------------------------------------------------bool __fastcall IsWine(){  HMODULE NtDll = GetModuleHandle(L"ntdll.dll");  return    DebugAlwaysTrue(NtDll != NULL) &&    (GetProcAddress(NtDll, "wine_get_version") != NULL);}//---------------------------------------------------------------------------LCID __fastcall GetDefaultLCID(){  return GetUserDefaultLCID();}//---------------------------------------------------------------------------static UnicodeString ADefaultEncodingName;UnicodeString __fastcall DefaultEncodingName(){  if (ADefaultEncodingName.IsEmpty())  {    CPINFOEX Info;    GetCPInfoEx(CP_ACP, 0, &Info);    ADefaultEncodingName = Info.CodePageName;  }  return ADefaultEncodingName;}//---------------------------------------------------------------------------bool _fastcall GetWindowsProductType(DWORD & Type){  bool Result;  HINSTANCE Kernel32 = GetModuleHandle(kernel32);  typedef BOOL WINAPI (* TGetProductInfo)(DWORD, DWORD, DWORD, DWORD, PDWORD);  TGetProductInfo GetProductInfo =      (TGetProductInfo)GetProcAddress(Kernel32, "GetProductInfo");  if (GetProductInfo == NULL)  {    Result = false;  }  else  {    GetProductInfo(Win32MajorVersion, Win32MinorVersion, 0, 0, &Type);    Result = true;  }  return Result;}//---------------------------------------------------------------------------UnicodeString __fastcall WindowsProductName(){  UnicodeString Result;  TRegistry * Registry = new TRegistry(KEY_READ);  try  {    Registry->RootKey = HKEY_LOCAL_MACHINE;    if (Registry->OpenKey(L"SOFTWARE", false) &&        Registry->OpenKey(L"Microsoft", false) &&        Registry->OpenKey(L"Windows NT", false) &&        Registry->OpenKey(L"CurrentVersion", false))    {      Result = Registry->ReadString(L"ProductName");    }    delete Registry;  }  catch(...)  {  }  return Result;}//---------------------------------------------------------------------------bool __fastcall IsDirectoryWriteable(const UnicodeString & Path){  UnicodeString FileName =    IncludeTrailingPathDelimiter(Path) +    FORMAT(L"wscp_%s_%d.tmp", (FormatDateTime(L"nnzzz", Now()), int(GetCurrentProcessId())));  HANDLE Handle = CreateFile(ApiPath(FileName).c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,    CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, 0);  bool Result = (Handle != INVALID_HANDLE_VALUE);  if (Result)  {    CloseHandle(Handle);  }  return Result;}//---------------------------------------------------------------------------UnicodeString __fastcall FormatNumber(__int64 Number){  return FormatFloat(L"#,##0", Number);}//---------------------------------------------------------------------------// simple alternative to FormatBytesUnicodeString __fastcall FormatSize(__int64 Size){  return FormatNumber(Size);}//---------------------------------------------------------------------------UnicodeString __fastcall ExtractFileBaseName(const UnicodeString & Path){  return ChangeFileExt(ExtractFileName(Path), L"");}//---------------------------------------------------------------------------TStringList * __fastcall TextToStringList(const UnicodeString & Text){  std::unique_ptr<TStringList> List(new TStringList());  List->Text = Text;  return List.release();}//---------------------------------------------------------------------------TStrings * __fastcall CloneStrings(TStrings * Strings){  std::unique_ptr<TStringList> List(new TStringList());  List->AddStrings(Strings);  return List.release();}//---------------------------------------------------------------------------UnicodeString __fastcall TrimVersion(UnicodeString Version){  while ((Version.Pos(L".") != Version.LastDelimiter(L".")) &&    (Version.SubString(Version.Length() - 1, 2) == L".0"))  {    Version.SetLength(Version.Length() - 2);  }  return Version;}//---------------------------------------------------------------------------UnicodeString __fastcall FormatVersion(int MajovVersion, int MinorVersion, int Release){  return    TrimVersion(FORMAT(L"%d.%d.%d",      (MajovVersion, MinorVersion, Release)));}//---------------------------------------------------------------------------TFormatSettings __fastcall GetEngFormatSettings(){  return TFormatSettings::Create((TLocaleID)1033);}//---------------------------------------------------------------------------int __fastcall ParseShortEngMonthName(const UnicodeString & MonthStr){  TFormatSettings FormatSettings = GetEngFormatSettings();  return IndexStr(MonthStr, FormatSettings.ShortMonthNames, FormatSettings.ShortMonthNames.Size()) + 1;}//---------------------------------------------------------------------------TStringList * __fastcall CreateSortedStringList(bool CaseSensitive, System::Types::TDuplicates Duplicates){  TStringList * Result = new TStringList();  Result->CaseSensitive = CaseSensitive;  Result->Sorted = true;  Result->Duplicates = Duplicates;  return Result;}//---------------------------------------------------------------------------static UnicodeString __fastcall NormalizeIdent(UnicodeString Ident){  int Index = 1;  while (Index <= Ident.Length())  {    if (Ident[Index] == L'-')    {      Ident.Delete(Index, 1);    }    else    {      Index++;    }  }  return Ident;}//---------------------------------------------------------------------------UnicodeString __fastcall FindIdent(const UnicodeString & Ident, TStrings * Idents){  UnicodeString NormalizedIdent(NormalizeIdent(Ident));  for (int Index = 0; Index < Idents->Count; Index++)  {    if (SameText(NormalizedIdent, NormalizeIdent(Idents->Strings[Index])))    {      return Idents->Strings[Index];    }  }  return Ident;}//---------------------------------------------------------------------------static UnicodeString __fastcall GetTlsErrorStr(int Err){  char * Buffer = new char[512];  ERR_error_string(Err, Buffer);  // not sure about the UTF8  return UnicodeString(UTF8String(Buffer));}//---------------------------------------------------------------------------static FILE * __fastcall OpenCertificate(const UnicodeString & Path){  FILE * Result = _wfopen(ApiPath(Path).c_str(), L"rb");  if (Result == NULL)  {    int Error = errno;    throw EOSExtException(MainInstructions(FMTLOAD(CERTIFICATE_OPEN_ERROR, (Path))), Error);  }  return Result;}//---------------------------------------------------------------------------struct TPemPasswordCallbackData{  UnicodeString * Passphrase;};//---------------------------------------------------------------------------static int PemPasswordCallback(char * Buf, int Size, int /*RWFlag*/, void * UserData){  TPemPasswordCallbackData & Data = *reinterpret_cast<TPemPasswordCallbackData *>(UserData);  UTF8String UtfPassphrase = UTF8String(*Data.Passphrase);  strncpy(Buf, UtfPassphrase.c_str(), Size);  Shred(UtfPassphrase);  Buf[Size - 1] = '\0';  return strlen(Buf);}//---------------------------------------------------------------------------static bool __fastcall IsTlsPassphraseError(int Error){  bool Result =    ((ERR_GET_LIB(Error) == ERR_LIB_PKCS12) &&     (ERR_GET_REASON(Error) == PKCS12_R_MAC_VERIFY_FAILURE)) ||    ((ERR_GET_LIB(Error) == ERR_LIB_PEM) &&     (ERR_GET_REASON(Error) == PEM_R_BAD_PASSWORD_READ));  return Result;}//---------------------------------------------------------------------------static void __fastcall ThrowTlsCertificateErrorIgnorePassphraseErrors(const UnicodeString & Path){  int Error = ERR_get_error();  if (!IsTlsPassphraseError(Error))  {    throw ExtException(MainInstructions(FMTLOAD(CERTIFICATE_READ_ERROR, (Path))), GetTlsErrorStr(Error));  }}//---------------------------------------------------------------------------void __fastcall ParseCertificate(const UnicodeString & Path,  const UnicodeString & Passphrase, X509 *& Certificate, EVP_PKEY *& PrivateKey,  bool & WrongPassphrase){  Certificate = NULL;  PrivateKey = NULL;  FILE * File;  // Inspired by neon's ne_ssl_clicert_read  File = OpenCertificate(Path);  // openssl pkcs12 -inkey cert.pem -in cert.crt -export -out cert.pfx  // Binary file  PKCS12 * Pkcs12 = d2i_PKCS12_fp(File, NULL);  fclose(File);  if (Pkcs12 != NULL)  {    // Not sure about the UTF-8 encoding, but there's no wchar_t API    bool Result =      (PKCS12_parse(Pkcs12, UTF8String(Passphrase).c_str(), &PrivateKey, &Certificate, NULL) == 1);    PKCS12_free(Pkcs12);    if (!Result)    {      ThrowTlsCertificateErrorIgnorePassphraseErrors(Path);      WrongPassphrase = true;    }  }  else  {    ERR_clear_error();    TPemPasswordCallbackData CallbackUserData;    // PemPasswordCallback never writes to the .Passphrase    CallbackUserData.Passphrase = const_cast<UnicodeString *>(&Passphrase);    File = OpenCertificate(Path);    // Encrypted:    // openssl req -x509 -newkey rsa:2048 -keyout cert.pem -out cert.crt    // -----BEGIN ENCRYPTED PRIVATE KEY-----    // ...    // -----END ENCRYPTED PRIVATE KEY-----    // Not encrypted (add -nodes):    // -----BEGIN PRIVATE KEY-----    // ...    // -----END PRIVATE KEY-----    // Or (openssl genrsa -out client.key 1024   # used for certificate signing request)    // -----BEGIN RSA PRIVATE KEY-----    // ...    // -----END RSA PRIVATE KEY-----    PrivateKey = PEM_read_PrivateKey(File, NULL, PemPasswordCallback, &CallbackUserData);    fclose(File);    try    {      if (PrivateKey == NULL)      {        ThrowTlsCertificateErrorIgnorePassphraseErrors(Path);        WrongPassphrase = true;      }      File = OpenCertificate(Path);      // The file can contain both private and public key      // (basically cert.pem and cert.crt appended one to each other)      // -----BEGIN ENCRYPTED PRIVATE KEY-----      // ...      // -----END ENCRYPTED PRIVATE KEY-----      // -----BEGIN CERTIFICATE-----      // ...      // -----END CERTIFICATE-----      Certificate = PEM_read_X509(File, NULL, PemPasswordCallback, &CallbackUserData);      fclose(File);      if (Certificate == NULL)      {        int Error = ERR_get_error();        // unlikely        if (IsTlsPassphraseError(Error))        {          WrongPassphrase = true;        }        else        {          UnicodeString CertificatePath = ChangeFileExt(Path, L".cer");          if (!FileExists(CertificatePath))          {            CertificatePath = ChangeFileExt(Path, L".crt");          }          if (!FileExists(CertificatePath))          {            throw Exception(MainInstructions(FMTLOAD(CERTIFICATE_PUBLIC_KEY_NOT_FOUND, (Path))));          }          else          {            File = OpenCertificate(CertificatePath);            // -----BEGIN CERTIFICATE-----            // ...            // -----END CERTIFICATE-----            Certificate = PEM_read_X509(File, NULL, PemPasswordCallback, &CallbackUserData);            fclose(File);            if (Certificate == NULL)            {              ThrowTlsCertificateErrorIgnorePassphraseErrors(CertificatePath);              WrongPassphrase = true;            }          }        }      }    }    __finally    {      // We loaded private key, but failed to load certificate, discard the certificate      // (either exception was thrown or WrongPassphrase)      if ((PrivateKey != NULL) && (Certificate == NULL))      {        EVP_PKEY_free(PrivateKey);        PrivateKey = NULL;      }      // Certificate was verified, but passphrase was wrong when loading private key,      // so discard the certificate      else if ((Certificate != NULL) && (PrivateKey == NULL))      {        X509_free(Certificate);        Certificate = NULL;      }    }  }}//---------------------------------------------------------------------------void __fastcall CheckCertificate(const UnicodeString & Path){  X509 * Certificate;  EVP_PKEY * PrivateKey;  bool WrongPassphrase;  ParseCertificate(Path, L"", Certificate, PrivateKey, WrongPassphrase);  if (PrivateKey != NULL)  {    EVP_PKEY_free(PrivateKey);  }  if (Certificate != NULL)  {    X509_free(Certificate);  }}//---------------------------------------------------------------------------bool __fastcall IsHttpUrl(const UnicodeString & S){  return SameText(S.SubString(1, 4), L"http");}
 |