Setup.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  1. //---------------------------------------------------------------------------
  2. // Part of this code is
  3. // Copyright (C) 2002-2004, Marco Barisione <[email protected]>
  4. //---------------------------------------------------------------------------
  5. #include <vcl.h>
  6. #pragma hdrstop
  7. #include <stdio.h>
  8. #include <tchar.h>
  9. #include <Common.h>
  10. #include <CoreMain.h>
  11. #include <Exceptions.h>
  12. #include <TextsWin.h>
  13. #include <HelpWin.h>
  14. #include <TcpIp.hpp>
  15. #include <CompThread.hpp>
  16. #include "WinConfiguration.h"
  17. #include "WinInterface.h"
  18. #include "Tools.h"
  19. #include "Setup.h"
  20. //---------------------------------------------------------------------------
  21. /* Using quotes or not should make no difference but some programs (for
  22. instance cygwin and msys) don't like them. */
  23. /* #define USE_QUOTES */
  24. //---------------------------------------------------------------------------
  25. #define APP_NAME "WinSCP"
  26. #define KEY _T("SYSTEM\\CurrentControlSet\\Control\\") \
  27. _T("Session Manager\\Environment")
  28. #define AUTOEXEC_PATH _T("c:\\autoexec.bat")
  29. #define AUTOEXEC_INTRO "rem ***** The following line was added by " \
  30. APP_NAME " *****"
  31. #ifdef USE_QUOTES
  32. # define AUTOEXEC_CMD "set PATH=%%PATH%%;\"%s\""
  33. #else
  34. # define AUTOEXEC_CMD "set PATH=%%PATH%%;%s"
  35. #endif
  36. /* Command line options. */
  37. AnsiString LastPathError;
  38. //---------------------------------------------------------------------------
  39. #define verb_out(msg) ((void)0)
  40. #define verb_out_param(msg, param) ((void)0)
  41. //---------------------------------------------------------------------------
  42. // Display the error "err_msg".
  43. void err_out(LPCTSTR err_msg)
  44. {
  45. LastPathError = err_msg;
  46. }
  47. //---------------------------------------------------------------------------
  48. // Display "base_err_msg" followed by the description of the system error
  49. // identified by "sys_err".
  50. void err_out_sys(LPCTSTR base_err_msg, LONG sys_err)
  51. {
  52. LastPathError = FORMAT("%s%s", (base_err_msg, SysErrorMessage(sys_err)));
  53. }
  54. //---------------------------------------------------------------------------
  55. // Works as "strcmp" but the comparison is not case sensitive.
  56. int tcharicmp(LPCTSTR str1, LPCTSTR str2){
  57. for (; tolower(*str1) == tolower(*str2); ++str1, ++str2)
  58. if (*str1 == '\0')
  59. return 0;
  60. return tolower(*str1) - tolower(*str2);
  61. }
  62. //---------------------------------------------------------------------------
  63. // Returns un unquoted copy of "str" (or a copy of "str" if the quotes are
  64. // not present). The returned value must be freed with "free".
  65. LPTSTR unquote(LPCTSTR str){
  66. int last_pos;
  67. LPTSTR ret;
  68. size_t new_len;
  69. last_pos = _tcslen(str) - 1;
  70. if (last_pos != -1 && str[0] == '"' && str[last_pos] == '"'){
  71. new_len= (_tcslen(str) - 1);
  72. ret = (LPTSTR)malloc(new_len * sizeof(TCHAR));
  73. lstrcpyn(ret, &str[1], new_len);
  74. }
  75. else
  76. ret = _tcsdup(str);
  77. return ret;
  78. }
  79. //---------------------------------------------------------------------------
  80. // Find "what" in the ";" separated string "str" and returns a pointer to
  81. // the first letter of "what" in the string. If "next" is not "NULL" it
  82. // points to the first letter after "what" (excluding the trailing ";").
  83. // If "what" isn't find the functions returns "NULL".
  84. LPTSTR find_reg_str(LPTSTR str, LPCTSTR what, LPTSTR * next){
  85. LPTSTR tok_buff;
  86. LPTSTR curr_tok;
  87. LPTSTR curr_tok_dup;
  88. BOOL path_eq;
  89. TCHAR sh_path1[MAX_PATH], sh_path2[MAX_PATH];
  90. int pos = -1;
  91. LPTSTR ret;
  92. tok_buff = _tcsdup(str);
  93. curr_tok = _tcstok(tok_buff, _T(";"));
  94. while (pos == -1 && curr_tok){
  95. curr_tok_dup = unquote(curr_tok);
  96. path_eq = GetShortPathName(what, sh_path1, LENOF(sh_path1)) &&
  97. GetShortPathName(curr_tok_dup, sh_path2,
  98. LENOF(sh_path2)) &&
  99. (tcharicmp(sh_path1, sh_path2) == 0);
  100. if (path_eq || tcharicmp(what, curr_tok_dup) == 0){
  101. pos = curr_tok - tok_buff;
  102. }
  103. free(curr_tok_dup);
  104. curr_tok = _tcstok(NULL, _T(";"));
  105. if (pos != -1 && next){
  106. if (curr_tok)
  107. *next = str + (curr_tok - tok_buff);
  108. else
  109. *next = str + _tcslen(str);
  110. }
  111. }
  112. free(tok_buff);
  113. if (pos != -1)
  114. ret = str + pos;
  115. else
  116. ret = NULL;
  117. return ret;
  118. }
  119. //---------------------------------------------------------------------------
  120. void path_reg_propagate()
  121. {
  122. DWORD send_message_result;
  123. LONG ret = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
  124. (LPARAM)"Environment", SMTO_ABORTIFHUNG,
  125. 5000, &send_message_result);
  126. if (ret != ERROR_SUCCESS && GetLastError() != 0)
  127. {
  128. err_out_sys(_T("Cannot propagate the new enviroment to ")
  129. _T("other processes. The new value will be ")
  130. _T("avaible after a reboot."), GetLastError());
  131. SimpleErrorDialog(LastPathError);
  132. LastPathError = "";
  133. }
  134. }
  135. //---------------------------------------------------------------------------
  136. // Add "path" to the registry. Return "TRUE" if the path has been added or
  137. // was already in the registry, "FALSE" otherwise.
  138. BOOL add_path_reg(LPCTSTR path){
  139. HKEY key;
  140. LONG ret;
  141. DWORD data_size;
  142. LPTSTR reg_str;
  143. BOOL func_ret = TRUE;
  144. ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KEY, 0,
  145. KEY_WRITE | KEY_READ, &key);
  146. if (ret != ERROR_SUCCESS){
  147. err_out_sys(_T("Cannot open registry."), ret);
  148. return FALSE;
  149. }
  150. RegQueryValueEx(key, _T("PATH"), NULL, NULL, NULL, &data_size);
  151. data_size += _tcslen(path) + 3 ; /* ";" and quotes, "data_size" already
  152. includes '\0'. */
  153. reg_str = (LPTSTR)malloc(data_size * sizeof(TCHAR));
  154. ret = RegQueryValueEx(key, _T("PATH"), NULL, NULL, (LPBYTE)reg_str,
  155. &data_size);
  156. if (ret != ERROR_SUCCESS){
  157. err_out_sys(_T("Cannot read \"PATH\" key."), ret);
  158. func_ret = FALSE;
  159. }
  160. else{
  161. if (!find_reg_str(reg_str, path, NULL)){
  162. _tcscat(reg_str, _T(";"));
  163. #ifdef USE_QUOTES
  164. _tcscat(reg_str, _T(";\""));
  165. #endif
  166. _tcscat(reg_str, path);
  167. #ifdef USE_QUOTES
  168. _tcscat(reg_str, _T("\""));
  169. #endif
  170. ret = RegSetValueEx(key, _T("PATH"), 0, REG_EXPAND_SZ,
  171. (LPBYTE)reg_str,
  172. (_tcslen(reg_str) + 1) * sizeof(TCHAR));
  173. if (ret != ERROR_SUCCESS){
  174. err_out_sys(_T("Cannot write \"PATH\" key."), ret);
  175. func_ret = FALSE;
  176. }
  177. /* Is this needed to make the new key avaible? */
  178. RegFlushKey(key);
  179. SetLastError(0);
  180. path_reg_propagate();
  181. }
  182. else
  183. verb_out(_T("Value already exists in the registry."));
  184. }
  185. RegCloseKey(key);
  186. free(reg_str);
  187. return func_ret;
  188. }
  189. //---------------------------------------------------------------------------
  190. // Removes "path" from the registry. Return "TRUE" if the path has been
  191. // removed or it wasn't in the registry, "FALSE" otherwise.
  192. BOOL remove_path_reg(LPCTSTR path){
  193. HKEY key;
  194. LONG ret;
  195. DWORD data_size;
  196. LPTSTR reg_str;
  197. LPTSTR reg_str2;
  198. BOOL func_ret = TRUE;
  199. LPTSTR next;
  200. LPTSTR del_part;
  201. int last_pos;
  202. ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KEY, 0,
  203. KEY_WRITE | KEY_READ, &key);
  204. if (ret != ERROR_SUCCESS){
  205. err_out_sys(_T("Cannot open registry."), ret);
  206. return FALSE;
  207. }
  208. RegQueryValueEx(key, _T("PATH"), NULL, NULL, NULL, &data_size);
  209. data_size += _tcslen(path) + 3; /* ";" and quotes,"data_size" already
  210. includes '\0'. */
  211. reg_str = (LPTSTR)malloc(data_size * sizeof(TCHAR));
  212. ret = RegQueryValueEx(key, _T("PATH"), NULL, NULL,
  213. (LPBYTE)reg_str, &data_size);
  214. if (ret != ERROR_SUCCESS){
  215. err_out_sys(_T("Cannot read \"PATH\" key."), ret);
  216. func_ret = FALSE;
  217. }
  218. else{
  219. if ((del_part = find_reg_str(reg_str, path, &next)) != NULL){
  220. reg_str2 = (LPTSTR)malloc((_tcslen(reg_str) + 1) * sizeof(TCHAR));
  221. *del_part = '\0';
  222. _stprintf(reg_str2, _T("%s%s"), reg_str, next);
  223. last_pos = _tcslen(reg_str2) - 1;
  224. if (last_pos != -1 && reg_str2[last_pos] == ';')
  225. reg_str2[last_pos] = '\0';
  226. ret = RegSetValueEx(key, _T("PATH"), 0, REG_EXPAND_SZ,
  227. (LPBYTE)reg_str2,
  228. (_tcslen(reg_str2) + 1) * sizeof(TCHAR));
  229. if (ret != ERROR_SUCCESS){
  230. err_out_sys(_T("Cannot write \"PATH\" key."), ret);
  231. func_ret = FALSE;
  232. }
  233. free(reg_str2);
  234. /* Is this needed to make the new key avaible? */
  235. RegFlushKey(key);
  236. SetLastError(0);
  237. path_reg_propagate();
  238. }
  239. else
  240. verb_out(_T("Value does not exist in the registry."));
  241. }
  242. RegCloseKey(key);
  243. free(reg_str);
  244. return func_ret;
  245. }
  246. //---------------------------------------------------------------------------
  247. /* Can this program run under Win9x if compiled with unicode support? */
  248. #if !defined _UNICODE
  249. //---------------------------------------------------------------------------
  250. // Add "path" to "autoexec.bat". Return "TRUE" if the path has been added or
  251. // was already in the file, "FALSE" otherwise.
  252. BOOL add_path_autoexec(LPCTSTR long_path){
  253. FILE * file;
  254. LPTSTR path;
  255. size_t path_size;
  256. LPTSTR line;
  257. LPTSTR out_line;
  258. size_t line_size;
  259. size_t sz1, sz2;
  260. LPTSTR autoexec_intro;
  261. BOOL found;
  262. BOOL func_ret = TRUE;
  263. file = _tfopen(AUTOEXEC_PATH, _T("r+"));
  264. if (!file){
  265. err_out(_T("Cannot open \"autoexec.bat\"."));
  266. return FALSE;
  267. }
  268. path_size = _tcslen(long_path) + 1;
  269. path = (LPTSTR)malloc(path_size * sizeof(TCHAR));
  270. if (!GetShortPathName(long_path, path, path_size))
  271. _tcsncpy(path, long_path, path_size);
  272. sz1 = _tcslen(path) + _tcslen(AUTOEXEC_CMD);
  273. sz2 = _tcslen(AUTOEXEC_INTRO) + 2 /* '\n' and '\0'. */;
  274. line_size = sz1 > sz2 ? sz1 : sz2;
  275. line = (LPTSTR)malloc(line_size * sizeof(TCHAR));
  276. out_line = (LPTSTR)malloc(line_size * sizeof(TCHAR));
  277. _stprintf(out_line, AUTOEXEC_CMD, path);
  278. _tcscat(out_line, _T("\n"));
  279. autoexec_intro = (LPTSTR)malloc((_tcslen(AUTOEXEC_INTRO) + 2 /* '\0', '\n' */)
  280. * sizeof(TCHAR));
  281. _tcscpy(autoexec_intro, AUTOEXEC_INTRO);
  282. _tcscat(autoexec_intro, _T("\n"));
  283. found = FALSE;
  284. while (!found && _fgetts(line, line_size, file)){
  285. if (_tcscmp(autoexec_intro, line) == 0){
  286. _fgetts(line, line_size, file);
  287. if (_tcscmp(out_line, line) == 0)
  288. found = TRUE;
  289. }
  290. }
  291. if (!found){
  292. if (fseek(file, 0, SEEK_END) != 0 ||
  293. _fputts(_T("\n"), file) == _TEOF ||
  294. _fputts(autoexec_intro, file) == _TEOF ||
  295. _fputts(out_line, file) == _TEOF)
  296. func_ret = FALSE;
  297. }
  298. else
  299. verb_out(_T("Value already exists in \"autoexec.bat\"."));
  300. fclose(file);
  301. free(path);
  302. free(line);
  303. free(out_line);
  304. free(autoexec_intro);
  305. return func_ret;
  306. }
  307. //---------------------------------------------------------------------------
  308. // Removes "path" from "autoexec.bat". Return "TRUE" if the path has been
  309. // removed or it wasn't in the file, "FALSE" otherwise.
  310. BOOL remove_path_autoexec(LPTSTR long_path){
  311. FILE * file;
  312. LPTSTR path;
  313. size_t path_size;
  314. LPTSTR data;
  315. long file_size;
  316. LPTSTR expected_text;
  317. size_t expected_text_size;
  318. LPTSTR buff;
  319. size_t buff_size;
  320. LPTSTR begin_pos;
  321. LPTSTR final_part;
  322. size_t fread_ret;
  323. BOOL func_ret = TRUE;
  324. file = _tfopen(AUTOEXEC_PATH, _T("rb"));
  325. if (!file){
  326. err_out(_T("Cannot open \"autoexec.bat\" for reading."));
  327. return FALSE;
  328. }
  329. fseek(file, 0, SEEK_END);
  330. file_size = ftell(file);
  331. data = (LPTSTR)malloc(file_size + sizeof(TCHAR) /* '\0'. */);
  332. data[file_size / sizeof(TCHAR)] = '\0';
  333. fseek(file, 0, SEEK_SET);
  334. fread_ret = fread(data, file_size, 1, file);
  335. fclose(file);
  336. if (fread_ret != 1){
  337. err_out(_T("Cannot read \"autoexec.bat\"."));
  338. return FALSE;
  339. }
  340. path_size = _tcslen(long_path) + 1;
  341. path = (LPTSTR)malloc(path_size * sizeof(TCHAR));
  342. if (!GetShortPathName(long_path, path, path_size))
  343. _tcsncpy(path, long_path, path_size);
  344. buff_size = _tcslen(AUTOEXEC_CMD) + _tcslen(path);
  345. buff = (LPTSTR)malloc(buff_size * sizeof(TCHAR));
  346. expected_text_size = buff_size + _tcslen(AUTOEXEC_INTRO)
  347. + 4 /* 2 * '\r\n' */;
  348. expected_text = (LPTSTR)malloc(expected_text_size * sizeof(TCHAR));
  349. _tcscpy(expected_text, AUTOEXEC_INTRO);
  350. _tcscat(expected_text, _T("\r\n"));
  351. _stprintf(buff, AUTOEXEC_CMD, path);
  352. _tcscat(expected_text, buff);
  353. _tcscat(expected_text, _T("\r\n"));
  354. begin_pos = _tcsstr(data, expected_text);
  355. if (begin_pos){
  356. file = _tfopen(AUTOEXEC_PATH, _T("wb"));
  357. if (!file){
  358. err_out(_T("Cannot open \"autoexec.bat\" for writing."));
  359. func_ret = FALSE;
  360. }
  361. else{
  362. final_part = begin_pos + _tcslen(expected_text);
  363. if ((fwrite(data, begin_pos - data, 1, file) != 1 &&
  364. (begin_pos - data)) || /* "fwrite"fails if the
  365. second argument is 0 */
  366. (fwrite(final_part, _tcslen(final_part), 1, file) != 1 &&
  367. _tcslen(final_part)))
  368. func_ret = FALSE;
  369. fclose(file);
  370. }
  371. }
  372. else
  373. verb_out(_T("Value does not exist in \"autoexec.bat\"."));
  374. free(data);
  375. free(path);
  376. free(buff);
  377. free(expected_text);
  378. return func_ret;
  379. }
  380. //---------------------------------------------------------------------------
  381. #endif /* #if !defined _UNICODE */
  382. //---------------------------------------------------------------------------
  383. void __fastcall AddSearchPath(const AnsiString Path)
  384. {
  385. bool Result;
  386. if (Win32Platform == VER_PLATFORM_WIN32_NT)
  387. {
  388. Result = add_path_reg(Path.c_str());
  389. }
  390. else
  391. {
  392. Result = add_path_autoexec(Path.c_str());
  393. }
  394. if (!Result)
  395. {
  396. throw ExtException(FMTLOAD(ADD_PATH_ERROR, (Path)), LastPathError);
  397. }
  398. }
  399. //---------------------------------------------------------------------------
  400. void __fastcall RemoveSearchPath(const AnsiString Path)
  401. {
  402. bool Result;
  403. if (Win32Platform == VER_PLATFORM_WIN32_NT)
  404. {
  405. Result = remove_path_reg(Path.c_str());
  406. }
  407. else
  408. {
  409. Result = remove_path_autoexec(Path.c_str());
  410. }
  411. if (!Result)
  412. {
  413. throw ExtException(FMTLOAD(REMOVE_PATH_ERROR, (Path)), LastPathError);
  414. }
  415. }
  416. //---------------------------------------------------------------------------
  417. void __fastcall RegisterAsUrlHandler()
  418. {
  419. try
  420. {
  421. bool Success;
  422. bool User = true;
  423. TRegistry * Registry = new TRegistry();
  424. try
  425. {
  426. do
  427. {
  428. Success = true;
  429. User = !User;
  430. try
  431. {
  432. assert(Configuration != NULL);
  433. AnsiString FileName = Application->ExeName;
  434. AnsiString BaseKey;
  435. Registry->Access = KEY_WRITE;
  436. if (User)
  437. {
  438. Registry->RootKey = HKEY_CURRENT_USER;
  439. BaseKey = "Software\\Classes\\";
  440. }
  441. else
  442. {
  443. Registry->RootKey = HKEY_CLASSES_ROOT;
  444. BaseKey = "";
  445. }
  446. AnsiString Protocol;
  447. for (int Index = 0; Index <= 1; Index++)
  448. {
  449. Protocol = (Index == 0) ? "SCP" : "SFTP";
  450. if (Registry->OpenKey(BaseKey + Protocol, true))
  451. {
  452. Registry->WriteString("", FMTLOAD(PROTOCOL_URL_DESC, (Protocol)));
  453. Registry->WriteString("URL Protocol", "");
  454. Registry->WriteInteger("EditFlags", 0x02);
  455. Registry->WriteInteger("BrowserFlags", 0x08);
  456. if (Registry->OpenKey("DefaultIcon", true))
  457. {
  458. Registry->WriteString("", FORMAT("\"%s\",0", (FileName)));
  459. Registry->CloseKey();
  460. }
  461. else
  462. {
  463. Abort();
  464. }
  465. }
  466. else
  467. {
  468. Abort();
  469. }
  470. if (Registry->OpenKey(BaseKey + Protocol, false) &&
  471. Registry->OpenKey("shell", true) &&
  472. Registry->OpenKey("open", true) &&
  473. Registry->OpenKey("command", true))
  474. {
  475. Registry->WriteString("", FORMAT("\"%s\" \"%%1\"", (FileName)));
  476. Registry->CloseKey();
  477. }
  478. else
  479. {
  480. Abort();
  481. }
  482. }
  483. }
  484. catch(...)
  485. {
  486. Success = false;
  487. }
  488. }
  489. while (!Success && !User);
  490. }
  491. __finally
  492. {
  493. delete Registry;
  494. }
  495. }
  496. catch(Exception & E)
  497. {
  498. throw ExtException(&E, LoadStr(REGISTER_URL_ERROR));
  499. }
  500. }
  501. //---------------------------------------------------------------------------
  502. void __fastcall TemporaryDirectoryCleanup()
  503. {
  504. bool Continue = true;
  505. TStrings * Folders = NULL;
  506. try
  507. {
  508. if (WinConfiguration->ConfirmTemporaryDirectoryCleanup)
  509. {
  510. Folders = WinConfiguration->FindTemporaryFolders();
  511. Continue = (Folders != NULL);
  512. if (Continue)
  513. {
  514. TQueryButtonAlias Aliases[1];
  515. Aliases[0].Button = qaRetry;
  516. Aliases[0].Alias = LoadStr(OPEN_BUTTON);
  517. TMessageParams Params(mpNeverAskAgainCheck);
  518. Params.Aliases = Aliases;
  519. Params.AliasesCount = LENOF(Aliases);
  520. int Answer = MoreMessageDialog(
  521. FMTLOAD(CLEAN_TEMP_CONFIRM, (Folders->Count)), Folders,
  522. qtWarning, qaYes | qaNo | qaRetry, HELP_CLEAN_TEMP_CONFIRM, &Params);
  523. if (Answer == qaNeverAskAgain)
  524. {
  525. WinConfiguration->ConfirmTemporaryDirectoryCleanup = false;
  526. Answer = qaYes;
  527. }
  528. else if (Answer == qaRetry)
  529. {
  530. for (int Index = 0; Index < Folders->Count; Index++)
  531. {
  532. ShellExecute(Application->Handle, NULL,
  533. Folders->Strings[Index].c_str(), NULL, NULL, SW_SHOWNORMAL);
  534. }
  535. }
  536. Continue = (Answer == qaYes);
  537. }
  538. }
  539. if (Continue)
  540. {
  541. TStrings * F = Folders;
  542. Folders = NULL;
  543. try
  544. {
  545. WinConfiguration->CleanupTemporaryFolders(F);
  546. }
  547. catch (Exception &E)
  548. {
  549. ShowExtendedException(&E);
  550. }
  551. }
  552. }
  553. __finally
  554. {
  555. delete Folders;
  556. }
  557. }
  558. //---------------------------------------------------------------------------
  559. int __fastcall CalculateCompoundVersion(int MajorVer,
  560. int MinorVer, int Release, int Build)
  561. {
  562. int CompoundVer = Build + 1000 * (Release + 100 * (MinorVer +
  563. 100 * MajorVer));
  564. return CompoundVer;
  565. }
  566. //---------------------------------------------------------------------------
  567. int __fastcall CurrentCompoundVersion()
  568. {
  569. TVSFixedFileInfo * FileInfo = Configuration->FixedApplicationInfo;
  570. return CalculateCompoundVersion(
  571. HIWORD(FileInfo->dwFileVersionMS), LOWORD(FileInfo->dwFileVersionMS),
  572. HIWORD(FileInfo->dwFileVersionLS), LOWORD(FileInfo->dwFileVersionLS));
  573. }
  574. //---------------------------------------------------------------------------
  575. AnsiString __fastcall VersionStrFromCompoundVersion(int Version)
  576. {
  577. int MajorVer = Version / (1000*100*100);
  578. int MinorVer = (Version % (1000*100*100)) / (1000*100);
  579. int Release = (Version % (1000*100)) / (1000);
  580. AnsiString Result = FORMAT("%d.%d", (MajorVer, MinorVer));
  581. if (Release > 0)
  582. {
  583. Result += IntToStr(Release);
  584. }
  585. return Result;
  586. }
  587. //---------------------------------------------------------------------------
  588. void __fastcall QueryUpdates()
  589. {
  590. bool Complete = false;
  591. try
  592. {
  593. AnsiString Response;
  594. TVSFixedFileInfo * FileInfo = Configuration->FixedApplicationInfo;
  595. int CurrentCompoundVer = CurrentCompoundVersion();
  596. AnsiString CurrentVersionStr =
  597. FORMAT("%d.%d.%d.%d",
  598. (HIWORD(FileInfo->dwFileVersionMS), LOWORD(FileInfo->dwFileVersionMS),
  599. HIWORD(FileInfo->dwFileVersionLS), LOWORD(FileInfo->dwFileVersionLS)));
  600. TUpdatesConfiguration Updates = WinConfiguration->Updates;
  601. THttp * CheckForUpdatesHTTP = new THttp(Application);
  602. try
  603. {
  604. AnsiString URL = LoadStr(UPDATES_URL) +
  605. FORMAT("?v=%s&lang=%s", (CurrentVersionStr,
  606. IntToHex(__int64(GUIConfiguration->Locale), 4)));
  607. if (!Updates.ProxyHost.IsEmpty())
  608. {
  609. CheckForUpdatesHTTP->Proxy = FORMAT("%s:%d", (Updates.ProxyHost, Updates.ProxyPort));
  610. }
  611. CheckForUpdatesHTTP->URL = URL;
  612. CheckForUpdatesHTTP->Action();
  613. // sanity check
  614. if (CheckForUpdatesHTTP->Stream->Size > 102400)
  615. {
  616. Abort();
  617. }
  618. Response.SetLength(static_cast<int>(CheckForUpdatesHTTP->Stream->Size));
  619. CheckForUpdatesHTTP->Stream->Read(Response.c_str(), Response.Length());
  620. }
  621. __finally
  622. {
  623. delete CheckForUpdatesHTTP;
  624. }
  625. bool Changed = !Updates.HaveResults;
  626. Updates.LastCheck = Now();
  627. Updates.HaveResults = true;
  628. TUpdatesData PrevResults = Updates.Results;
  629. Updates.Results.Reset();
  630. Updates.Results.ForVersion = CurrentCompoundVer;
  631. while (!Response.IsEmpty())
  632. {
  633. AnsiString Line = ::CutToChar(Response, '\n', false);
  634. AnsiString Name = ::CutToChar(Line, '=', false);
  635. if (AnsiSameText(Name, "Version"))
  636. {
  637. int MajorVer = StrToInt(::CutToChar(Line, '.', false));
  638. int MinorVer = StrToInt(::CutToChar(Line, '.', false));
  639. int Release = StrToInt(::CutToChar(Line, '.', false));
  640. int Build = StrToInt(::CutToChar(Line, '.', false));
  641. int NewVersion = CalculateCompoundVersion(MajorVer, MinorVer, Release, Build);
  642. Changed |= (NewVersion != PrevResults.Version);
  643. if (NewVersion <= CurrentCompoundVer)
  644. {
  645. NewVersion = 0;
  646. }
  647. Updates.Results.Version = NewVersion;
  648. Complete = true;
  649. }
  650. else if (AnsiSameText(Name, "Message"))
  651. {
  652. Changed |= (PrevResults.Message != Line);
  653. Updates.Results.Message = Line;
  654. }
  655. else if (AnsiSameText(Name, "Critical"))
  656. {
  657. bool NewCritical = (StrToIntDef(Line, 0) != 0);
  658. Changed |= (PrevResults.Critical != NewCritical);
  659. Updates.Results.Critical = NewCritical;
  660. }
  661. else if (AnsiSameText(Name, "Release"))
  662. {
  663. Changed |= (PrevResults.Release != Line);
  664. Updates.Results.Release = Line;
  665. }
  666. else if (AnsiSameText(Name, "Disabled"))
  667. {
  668. bool NewDisabled = (StrToIntDef(Line, 0) != 0);
  669. Changed |= (PrevResults.Disabled != NewDisabled);
  670. Updates.Results.Disabled = NewDisabled;
  671. Complete = true;
  672. }
  673. else if (AnsiSameText(Name, "Url"))
  674. {
  675. Changed |= (PrevResults.Url != Line);
  676. Updates.Results.Url = Line;
  677. }
  678. else if (AnsiSameText(Name, "UrlButton"))
  679. {
  680. Changed |= (PrevResults.UrlButton != Line);
  681. Updates.Results.UrlButton = Line;
  682. }
  683. }
  684. if (Changed)
  685. {
  686. Updates.ShownResults = false;
  687. }
  688. WinConfiguration->Updates = Updates;
  689. }
  690. catch(Exception & E)
  691. {
  692. throw ExtException(&E, LoadStr(CHECK_FOR_UPDATES_ERROR));
  693. }
  694. if (!Complete)
  695. {
  696. throw Exception(LoadStr(CHECK_FOR_UPDATES_ERROR));
  697. }
  698. }
  699. //---------------------------------------------------------------------------
  700. void __fastcall GetUpdatesMessage(AnsiString & Message, bool & New,
  701. TQueryType & Type, bool Force)
  702. {
  703. TUpdatesConfiguration Updates = WinConfiguration->Updates;
  704. assert(Updates.HaveResults);
  705. if (Updates.HaveResults)
  706. {
  707. if (Updates.Results.Disabled)
  708. {
  709. if (Force)
  710. {
  711. Message = LoadStr(UPDATE_DISABLED)+"%s";
  712. }
  713. }
  714. else
  715. {
  716. New = (Updates.Results.Version > 0);
  717. if (New)
  718. {
  719. AnsiString Version = VersionStrFromCompoundVersion(Updates.Results.Version);
  720. if (!Updates.Results.Release.IsEmpty())
  721. {
  722. Version = FORMAT("%s %s", (Version, Updates.Results.Release));
  723. }
  724. Message = FMTLOAD(NEW_VERSION3, (Version, "%s"));
  725. }
  726. else
  727. {
  728. Message = LoadStr(NO_NEW_VERSION) + "%s";
  729. }
  730. }
  731. if (!Updates.Results.Message.IsEmpty())
  732. {
  733. Message = FORMAT(Message,
  734. (FMTLOAD(UPDATE_MESSAGE,
  735. (StringReplace(Updates.Results.Message, "|", "\n", TReplaceFlags() << rfReplaceAll)))));
  736. }
  737. else
  738. {
  739. Message = FORMAT(Message, (""));
  740. }
  741. Type = (Updates.Results.Critical ? qtWarning : qtInformation);
  742. }
  743. else
  744. {
  745. New = false;
  746. }
  747. }
  748. //---------------------------------------------------------------------------
  749. void __fastcall CheckForUpdates(bool CachedResults)
  750. {
  751. TCustomForm * ActiveForm = Screen->ActiveCustomForm;
  752. Busy(true);
  753. try
  754. {
  755. if (ActiveForm)
  756. {
  757. assert(ActiveForm->Enabled);
  758. ActiveForm->Enabled = false;
  759. }
  760. bool Again = false;
  761. do
  762. {
  763. TUpdatesConfiguration Updates = WinConfiguration->Updates;
  764. bool Cached = !Again && Updates.HaveResults &&
  765. (double(Updates.Period) > 0) &&
  766. (Updates.Results.ForVersion == CurrentCompoundVersion()) &&
  767. CachedResults;
  768. if (!Cached)
  769. {
  770. QueryUpdates();
  771. // reread enw data
  772. Updates = WinConfiguration->Updates;
  773. }
  774. Again = false;
  775. if (!Updates.ShownResults)
  776. {
  777. Updates.ShownResults = true;
  778. WinConfiguration->Updates = Updates;
  779. }
  780. assert(Updates.HaveResults);
  781. AnsiString Message;
  782. bool New;
  783. TQueryType Type;
  784. GetUpdatesMessage(Message, New, Type, true);
  785. // add FLAGMASK(Cached, qaRetry) to enable "check again" button
  786. // for cached results
  787. int Answers = qaOK |
  788. FLAGMASK(New, qaCancel | qaAll) |
  789. FLAGMASK(!Updates.Results.Url.IsEmpty(), qaYes);
  790. TQueryButtonAlias Aliases[4];
  791. Aliases[0].Button = qaRetry;
  792. Aliases[0].Alias = LoadStr(CHECK_AGAIN_BUTTON);
  793. Aliases[1].Button = qaYes;
  794. if (Updates.Results.UrlButton.IsEmpty())
  795. {
  796. Aliases[1].Alias = LoadStr(UPDATE_URL_BUTTON);
  797. }
  798. else
  799. {
  800. Aliases[1].Alias = Updates.Results.UrlButton;
  801. }
  802. Aliases[2].Button = qaAll;
  803. Aliases[2].Alias = LoadStr(WHATS_NEW_BUTTON);
  804. Aliases[3].Button = qaOK;
  805. Aliases[3].Alias = LoadStr(DOWNLOAD_BUTTON);
  806. TMessageParams Params;
  807. Params.Aliases = Aliases;
  808. // alias "ok" button to "download" only if we have new version
  809. Params.AliasesCount = (New ? 4 : 3);
  810. int Answer =
  811. MessageDialog(Message, Type,
  812. Answers, HELP_UPDATES, &Params);
  813. switch (Answer)
  814. {
  815. case qaOK:
  816. if (New)
  817. {
  818. OpenBrowser(LoadStr(DOWNLOAD_URL));
  819. }
  820. break;
  821. case qaYes:
  822. OpenBrowser(Updates.Results.Url);
  823. break;
  824. case qaAll:
  825. OpenBrowser(LoadStr(HISTORY_URL));
  826. break;
  827. case qaRetry:
  828. Again = true;
  829. break;
  830. }
  831. }
  832. while (Again);
  833. }
  834. __finally
  835. {
  836. if (ActiveForm)
  837. {
  838. ActiveForm->Enabled = true;
  839. }
  840. Busy(false);
  841. }
  842. }
  843. //---------------------------------------------------------------------------
  844. class TUpdateThread : public TCompThread
  845. {
  846. public:
  847. __fastcall TUpdateThread(TThreadMethod OnUpdatesChecked);
  848. protected:
  849. virtual void __fastcall Execute();
  850. TThreadMethod FOnUpdatesChecked;
  851. };
  852. //---------------------------------------------------------------------------
  853. TUpdateThread * UpdateThread = NULL;
  854. //---------------------------------------------------------------------------
  855. __fastcall TUpdateThread::TUpdateThread(TThreadMethod OnUpdatesChecked) :
  856. TCompThread(false),
  857. FOnUpdatesChecked(OnUpdatesChecked)
  858. {
  859. }
  860. //---------------------------------------------------------------------------
  861. void __fastcall TUpdateThread::Execute()
  862. {
  863. try
  864. {
  865. QueryUpdates();
  866. if (FOnUpdatesChecked != NULL)
  867. {
  868. Synchronize(FOnUpdatesChecked);
  869. }
  870. }
  871. catch(...)
  872. {
  873. // ignore errors
  874. }
  875. }
  876. //---------------------------------------------------------------------------
  877. void __fastcall StartUpdateThread(TThreadMethod OnUpdatesChecked)
  878. {
  879. assert(UpdateThread == NULL);
  880. UpdateThread = new TUpdateThread(OnUpdatesChecked);
  881. }
  882. //---------------------------------------------------------------------------
  883. void __fastcall StopUpdateThread()
  884. {
  885. if (UpdateThread != NULL)
  886. {
  887. SAFE_DESTROY(UpdateThread);
  888. }
  889. }