Setup.cpp 29 KB

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