Setup.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  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\" /unsafe \"%%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(CLEANTEMP_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. try
  542. {
  543. WinConfiguration->CleanupTemporaryFolders(Folders);
  544. }
  545. catch (Exception &E)
  546. {
  547. ShowExtendedException(&E);
  548. }
  549. }
  550. }
  551. __finally
  552. {
  553. delete Folders;
  554. }
  555. }
  556. //---------------------------------------------------------------------------
  557. int __fastcall CalculateCompoundVersion(int MajorVer,
  558. int MinorVer, int Release, int Build)
  559. {
  560. int CompoundVer = Build + 1000 * (Release + 100 * (MinorVer +
  561. 100 * MajorVer));
  562. return CompoundVer;
  563. }
  564. //---------------------------------------------------------------------------
  565. int __fastcall CurrentCompoundVersion()
  566. {
  567. TVSFixedFileInfo * FileInfo = Configuration->FixedApplicationInfo;
  568. return CalculateCompoundVersion(
  569. HIWORD(FileInfo->dwFileVersionMS), LOWORD(FileInfo->dwFileVersionMS),
  570. HIWORD(FileInfo->dwFileVersionLS), LOWORD(FileInfo->dwFileVersionLS));
  571. }
  572. //---------------------------------------------------------------------------
  573. AnsiString __fastcall VersionStrFromCompoundVersion(int Version)
  574. {
  575. int MajorVer = Version / (1000*100*100);
  576. int MinorVer = (Version % (1000*100*100)) / (1000*100);
  577. int Release = (Version % (1000*100)) / (1000);
  578. AnsiString Result;
  579. if (Release > 0)
  580. {
  581. Result = FORMAT("%d.%d.%d", (MajorVer, MinorVer, Release));
  582. }
  583. else
  584. {
  585. Result = FORMAT("%d.%d", (MajorVer, MinorVer));
  586. }
  587. return Result;
  588. }
  589. //---------------------------------------------------------------------------
  590. void __fastcall QueryUpdates()
  591. {
  592. bool Complete = false;
  593. try
  594. {
  595. AnsiString Response;
  596. TVSFixedFileInfo * FileInfo = Configuration->FixedApplicationInfo;
  597. int CurrentCompoundVer = CurrentCompoundVersion();
  598. AnsiString CurrentVersionStr =
  599. FORMAT("%d.%d.%d.%d",
  600. (HIWORD(FileInfo->dwFileVersionMS), LOWORD(FileInfo->dwFileVersionMS),
  601. HIWORD(FileInfo->dwFileVersionLS), LOWORD(FileInfo->dwFileVersionLS)));
  602. TUpdatesConfiguration Updates = WinConfiguration->Updates;
  603. THttp * CheckForUpdatesHTTP = new THttp(Application);
  604. try
  605. {
  606. AnsiString URL = LoadStr(UPDATES_URL) +
  607. FORMAT("?v=%s&lang=%s", (CurrentVersionStr,
  608. IntToHex(__int64(GUIConfiguration->Locale), 4)));
  609. AnsiString Proxy;
  610. switch (Updates.ConnectionType)
  611. {
  612. case ctAuto:
  613. AutodetectProxyUrl(Proxy);
  614. break;
  615. case ctProxy:
  616. Proxy = FORMAT("%s:%d", (Updates.ProxyHost, Updates.ProxyPort));
  617. break;
  618. }
  619. CheckForUpdatesHTTP->Proxy = Proxy;
  620. CheckForUpdatesHTTP->URL = URL;
  621. CheckForUpdatesHTTP->Action();
  622. // sanity check
  623. if (CheckForUpdatesHTTP->Stream->Size > 102400)
  624. {
  625. Abort();
  626. }
  627. Response.SetLength(static_cast<int>(CheckForUpdatesHTTP->Stream->Size));
  628. CheckForUpdatesHTTP->Stream->Read(Response.c_str(), Response.Length());
  629. }
  630. __finally
  631. {
  632. delete CheckForUpdatesHTTP;
  633. }
  634. bool Changed = !Updates.HaveResults;
  635. Updates.LastCheck = Now();
  636. Updates.HaveResults = true;
  637. TUpdatesData PrevResults = Updates.Results;
  638. Updates.Results.Reset();
  639. Updates.Results.ForVersion = CurrentCompoundVer;
  640. while (!Response.IsEmpty())
  641. {
  642. AnsiString Line = ::CutToChar(Response, '\n', false);
  643. AnsiString Name = ::CutToChar(Line, '=', false);
  644. if (AnsiSameText(Name, "Version"))
  645. {
  646. int MajorVer = StrToInt(::CutToChar(Line, '.', false));
  647. int MinorVer = StrToInt(::CutToChar(Line, '.', false));
  648. int Release = StrToInt(::CutToChar(Line, '.', false));
  649. int Build = StrToInt(::CutToChar(Line, '.', false));
  650. int NewVersion = CalculateCompoundVersion(MajorVer, MinorVer, Release, Build);
  651. Changed |= (NewVersion != PrevResults.Version);
  652. if (NewVersion <= CurrentCompoundVer)
  653. {
  654. NewVersion = 0;
  655. }
  656. Updates.Results.Version = NewVersion;
  657. Complete = true;
  658. }
  659. else if (AnsiSameText(Name, "Message"))
  660. {
  661. Changed |= (PrevResults.Message != Line);
  662. Updates.Results.Message = Line;
  663. }
  664. else if (AnsiSameText(Name, "Critical"))
  665. {
  666. bool NewCritical = (StrToIntDef(Line, 0) != 0);
  667. Changed |= (PrevResults.Critical != NewCritical);
  668. Updates.Results.Critical = NewCritical;
  669. }
  670. else if (AnsiSameText(Name, "Release"))
  671. {
  672. Changed |= (PrevResults.Release != Line);
  673. Updates.Results.Release = Line;
  674. }
  675. else if (AnsiSameText(Name, "Disabled"))
  676. {
  677. bool NewDisabled = (StrToIntDef(Line, 0) != 0);
  678. Changed |= (PrevResults.Disabled != NewDisabled);
  679. Updates.Results.Disabled = NewDisabled;
  680. Complete = true;
  681. }
  682. else if (AnsiSameText(Name, "Url"))
  683. {
  684. Changed |= (PrevResults.Url != Line);
  685. Updates.Results.Url = Line;
  686. }
  687. else if (AnsiSameText(Name, "UrlButton"))
  688. {
  689. Changed |= (PrevResults.UrlButton != Line);
  690. Updates.Results.UrlButton = Line;
  691. }
  692. }
  693. if (Changed)
  694. {
  695. Updates.ShownResults = false;
  696. }
  697. WinConfiguration->Updates = Updates;
  698. }
  699. catch(Exception & E)
  700. {
  701. throw ExtException(&E, LoadStr(CHECK_FOR_UPDATES_ERROR));
  702. }
  703. if (!Complete)
  704. {
  705. throw Exception(LoadStr(CHECK_FOR_UPDATES_ERROR));
  706. }
  707. }
  708. //---------------------------------------------------------------------------
  709. void __fastcall GetUpdatesMessage(AnsiString & Message, bool & New,
  710. TQueryType & Type, bool Force)
  711. {
  712. TUpdatesConfiguration Updates = WinConfiguration->Updates;
  713. assert(Updates.HaveResults);
  714. if (Updates.HaveResults)
  715. {
  716. if (Updates.Results.Disabled)
  717. {
  718. if (Force)
  719. {
  720. Message = LoadStr(UPDATE_DISABLED)+"%s";
  721. }
  722. }
  723. else
  724. {
  725. New = (Updates.Results.Version > 0);
  726. if (New)
  727. {
  728. AnsiString Version = VersionStrFromCompoundVersion(Updates.Results.Version);
  729. if (!Updates.Results.Release.IsEmpty())
  730. {
  731. Version = FORMAT("%s %s", (Version, Updates.Results.Release));
  732. }
  733. Message = FMTLOAD(NEW_VERSION3, (Version, "%s"));
  734. }
  735. else
  736. {
  737. Message = LoadStr(NO_NEW_VERSION) + "%s";
  738. }
  739. }
  740. if (!Updates.Results.Message.IsEmpty())
  741. {
  742. Message = FORMAT(Message,
  743. (FMTLOAD(UPDATE_MESSAGE,
  744. (StringReplace(Updates.Results.Message, "|", "\n", TReplaceFlags() << rfReplaceAll)))));
  745. }
  746. else
  747. {
  748. Message = FORMAT(Message, (""));
  749. }
  750. Type = (Updates.Results.Critical ? qtWarning : qtInformation);
  751. }
  752. else
  753. {
  754. New = false;
  755. }
  756. }
  757. //---------------------------------------------------------------------------
  758. void __fastcall CheckForUpdates(bool CachedResults)
  759. {
  760. TCustomForm * ActiveForm = Screen->ActiveCustomForm;
  761. Busy(true);
  762. try
  763. {
  764. if (ActiveForm)
  765. {
  766. assert(ActiveForm->Enabled);
  767. ActiveForm->Enabled = false;
  768. }
  769. bool Again = false;
  770. do
  771. {
  772. TUpdatesConfiguration Updates = WinConfiguration->Updates;
  773. bool Cached = !Again && Updates.HaveResults &&
  774. (double(Updates.Period) > 0) &&
  775. (Updates.Results.ForVersion == CurrentCompoundVersion()) &&
  776. CachedResults;
  777. if (!Cached)
  778. {
  779. QueryUpdates();
  780. // reread enw data
  781. Updates = WinConfiguration->Updates;
  782. }
  783. Again = false;
  784. if (!Updates.ShownResults)
  785. {
  786. Updates.ShownResults = true;
  787. WinConfiguration->Updates = Updates;
  788. }
  789. assert(Updates.HaveResults);
  790. AnsiString Message;
  791. bool New;
  792. TQueryType Type;
  793. GetUpdatesMessage(Message, New, Type, true);
  794. // add FLAGMASK(Cached, qaRetry) to enable "check again" button
  795. // for cached results
  796. int Answers = qaOK |
  797. FLAGMASK(New, qaCancel | qaAll) |
  798. FLAGMASK(!Updates.Results.Url.IsEmpty(), qaYes);
  799. TQueryButtonAlias Aliases[4];
  800. Aliases[0].Button = qaRetry;
  801. Aliases[0].Alias = LoadStr(CHECK_AGAIN_BUTTON);
  802. Aliases[1].Button = qaYes;
  803. if (Updates.Results.UrlButton.IsEmpty())
  804. {
  805. Aliases[1].Alias = LoadStr(UPDATE_URL_BUTTON);
  806. }
  807. else
  808. {
  809. Aliases[1].Alias = Updates.Results.UrlButton;
  810. }
  811. Aliases[2].Button = qaAll;
  812. Aliases[2].Alias = LoadStr(WHATS_NEW_BUTTON);
  813. Aliases[3].Button = qaOK;
  814. Aliases[3].Alias = LoadStr(DOWNLOAD_BUTTON);
  815. TMessageParams Params;
  816. Params.Aliases = Aliases;
  817. // alias "ok" button to "download" only if we have new version
  818. Params.AliasesCount = (New ? 4 : 3);
  819. int Answer =
  820. MessageDialog(Message, Type,
  821. Answers, HELP_UPDATES, &Params);
  822. switch (Answer)
  823. {
  824. case qaOK:
  825. if (New)
  826. {
  827. OpenBrowser(LoadStr(DOWNLOAD_URL));
  828. }
  829. break;
  830. case qaYes:
  831. OpenBrowser(Updates.Results.Url);
  832. break;
  833. case qaAll:
  834. OpenBrowser(LoadStr(HISTORY_URL));
  835. break;
  836. case qaRetry:
  837. Again = true;
  838. break;
  839. }
  840. }
  841. while (Again);
  842. }
  843. __finally
  844. {
  845. if (ActiveForm)
  846. {
  847. ActiveForm->Enabled = true;
  848. }
  849. Busy(false);
  850. }
  851. }
  852. //---------------------------------------------------------------------------
  853. class TUpdateThread : public TCompThread
  854. {
  855. public:
  856. __fastcall TUpdateThread(TThreadMethod OnUpdatesChecked);
  857. protected:
  858. virtual void __fastcall Execute();
  859. TThreadMethod FOnUpdatesChecked;
  860. };
  861. //---------------------------------------------------------------------------
  862. TUpdateThread * UpdateThread = NULL;
  863. //---------------------------------------------------------------------------
  864. __fastcall TUpdateThread::TUpdateThread(TThreadMethod OnUpdatesChecked) :
  865. TCompThread(false),
  866. FOnUpdatesChecked(OnUpdatesChecked)
  867. {
  868. }
  869. //---------------------------------------------------------------------------
  870. void __fastcall TUpdateThread::Execute()
  871. {
  872. try
  873. {
  874. QueryUpdates();
  875. if (FOnUpdatesChecked != NULL)
  876. {
  877. Synchronize(FOnUpdatesChecked);
  878. }
  879. }
  880. catch(...)
  881. {
  882. // ignore errors
  883. }
  884. }
  885. //---------------------------------------------------------------------------
  886. void __fastcall StartUpdateThread(TThreadMethod OnUpdatesChecked)
  887. {
  888. assert(UpdateThread == NULL);
  889. UpdateThread = new TUpdateThread(OnUpdatesChecked);
  890. }
  891. //---------------------------------------------------------------------------
  892. void __fastcall StopUpdateThread()
  893. {
  894. if (UpdateThread != NULL)
  895. {
  896. SAFE_DESTROY(UpdateThread);
  897. }
  898. }