PageRenderTime 147ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/peek-build/src/netsurf/riscos/download.c

https://bitbucket.org/C0deMaver1ck/peeklinux
C | 1604 lines | 1495 code | 44 blank | 65 comment | 30 complexity | bc9e49153b2322e8dab93236e5034d80 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-2.0
  1. /*
  2. * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
  3. * Copyright 2003 Rob Jackson <jacko@xms.ms>
  4. * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
  5. *
  6. * This file is part of NetSurf, http://www.netsurf-browser.org/
  7. *
  8. * NetSurf is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; version 2 of the License.
  11. *
  12. * NetSurf is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. /** \file
  21. * Download windows (RISC OS implementation).
  22. *
  23. * This file implements the interface given by desktop/gui.h for download
  24. * windows. Each download window has an associated fetch. Downloads start by
  25. * writing received data to a temporary file. At some point the user chooses
  26. * a destination (by drag & drop), and the temporary file is then moved to the
  27. * destination and the download continues until complete.
  28. */
  29. #include <assert.h>
  30. #include <string.h>
  31. #include <sys/time.h>
  32. #include <time.h>
  33. #include <curl/curl.h>
  34. #include "oslib/mimemap.h"
  35. #include "oslib/osargs.h"
  36. #include "oslib/osfile.h"
  37. #include "oslib/osfind.h"
  38. #include "oslib/osfscontrol.h"
  39. #include "oslib/osgbpb.h"
  40. #include "oslib/wimp.h"
  41. #include "oslib/wimpspriteop.h"
  42. #include "desktop/gui.h"
  43. #include "desktop/netsurf.h"
  44. #include "riscos/dialog.h"
  45. #include "riscos/options.h"
  46. #include "riscos/save.h"
  47. #include "riscos/query.h"
  48. #include "riscos/wimp.h"
  49. #include "riscos/wimp_event.h"
  50. #include "utils/log.h"
  51. #include "utils/messages.h"
  52. #include "utils/url.h"
  53. #include "utils/utf8.h"
  54. #include "utils/utils.h"
  55. #define ICON_DOWNLOAD_ICON 0
  56. #define ICON_DOWNLOAD_URL 1
  57. #define ICON_DOWNLOAD_PATH 2
  58. #define ICON_DOWNLOAD_DESTINATION 3
  59. #define ICON_DOWNLOAD_PROGRESS 5
  60. #define ICON_DOWNLOAD_STATUS 6
  61. typedef enum
  62. {
  63. QueryRsn_Quit,
  64. QueryRsn_Abort,
  65. QueryRsn_Overwrite
  66. } query_reason;
  67. /** Data for a download window. */
  68. struct gui_download_window {
  69. /** Associated context, or 0 if the fetch has completed or aborted. */
  70. download_context *ctx;
  71. unsigned int received; /**< Amount of data received so far. */
  72. unsigned int total_size; /**< Size of resource, or 0 if unknown. */
  73. wimp_w window; /**< RISC OS window handle. */
  74. bits file_type; /**< RISC OS file type. */
  75. char url[256]; /**< Buffer for URL icon. */
  76. char sprite_name[20]; /**< Buffer for sprite icon. */
  77. char path[256]; /**< Buffer for pathname icon. */
  78. char status[256]; /**< Buffer for status icon. */
  79. /** User has chosen the destination, and it is being written. */
  80. bool saved;
  81. bool close_confirmed;
  82. bool error; /**< Error occurred, aborted. */
  83. /** RISC OS file handle, of temporary file when !saved, and of
  84. * destination when saved. */
  85. os_fw file;
  86. query_id query;
  87. query_reason query_rsn;
  88. struct timeval start_time; /**< Time download started. */
  89. struct timeval last_time; /**< Time status was last updated. */
  90. unsigned int last_received; /**< Value of received at last_time. */
  91. bool send_dataload; /**< Should send DataLoad message when finished */
  92. wimp_message save_message; /**< Copy of wimp DataSaveAck message */
  93. struct gui_download_window *prev; /**< Previous in linked list. */
  94. struct gui_download_window *next; /**< Next in linked list. */
  95. };
  96. /** List of all download windows. */
  97. static struct gui_download_window *download_window_list = 0;
  98. /** Download window with current save operation. */
  99. static struct gui_download_window *download_window_current = 0;
  100. /** Template for a download window. */
  101. static wimp_window *download_template;
  102. /** Width of progress bar at 100%. */
  103. static int download_progress_width;
  104. /** Coordinates of progress bar. */
  105. static int download_progress_x0;
  106. static int download_progress_y0;
  107. static int download_progress_y1;
  108. /** Current download directory. */
  109. static char *download_dir = NULL;
  110. static size_t download_dir_len;
  111. static const char *ro_gui_download_temp_name(struct gui_download_window *dw);
  112. static void ro_gui_download_update_status(struct gui_download_window *dw);
  113. static void ro_gui_download_update_status_wrapper(void *p);
  114. static void ro_gui_download_window_hide_caret(struct gui_download_window *dw);
  115. static char *ro_gui_download_canonicalise(const char *path);
  116. static bool ro_gui_download_check_space(struct gui_download_window *dw,
  117. const char *dest_file, const char *orig_file);
  118. static os_error *ro_gui_download_move(struct gui_download_window *dw,
  119. const char *dest_file, const char *src_file);
  120. static void ro_gui_download_remember_dir(const char *path);
  121. static bool ro_gui_download_save(struct gui_download_window *dw,
  122. const char *file_name, bool force_overwrite);
  123. static void ro_gui_download_send_dataload(struct gui_download_window *dw);
  124. static void ro_gui_download_window_destroy_wrapper(void *p);
  125. static bool ro_gui_download_window_destroy(struct gui_download_window *dw, bool quit);
  126. static void ro_gui_download_close_confirmed(query_id, enum query_response res, void *p);
  127. static void ro_gui_download_close_cancelled(query_id, enum query_response res, void *p);
  128. static void ro_gui_download_overwrite_confirmed(query_id, enum query_response res, void *p);
  129. static void ro_gui_download_overwrite_cancelled(query_id, enum query_response res, void *p);
  130. static bool ro_gui_download_click(wimp_pointer *pointer);
  131. static bool ro_gui_download_keypress(wimp_key *key);
  132. static void ro_gui_download_close(wimp_w w);
  133. static const query_callback close_funcs =
  134. {
  135. ro_gui_download_close_confirmed,
  136. ro_gui_download_close_cancelled
  137. };
  138. static const query_callback overwrite_funcs =
  139. {
  140. ro_gui_download_overwrite_confirmed,
  141. ro_gui_download_overwrite_cancelled
  142. };
  143. /**
  144. * Load the download window template.
  145. */
  146. void ro_gui_download_init(void)
  147. {
  148. download_template = ro_gui_dialog_load_template("download");
  149. download_progress_width =
  150. download_template->icons[ICON_DOWNLOAD_STATUS].extent.x1 -
  151. download_template->icons[ICON_DOWNLOAD_STATUS].extent.x0;
  152. download_progress_x0 =
  153. download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.x0;
  154. download_progress_y0 =
  155. download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.y0;
  156. download_progress_y1 =
  157. download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.y1;
  158. }
  159. /**
  160. * Returns the pathname of a temporary file for this download.
  161. *
  162. * \param dw download window
  163. * \return ptr to pathname
  164. */
  165. const char *ro_gui_download_temp_name(struct gui_download_window *dw)
  166. {
  167. static char temp_name[40];
  168. snprintf(temp_name, sizeof temp_name, "<Wimp$ScrapDir>.ns%x",
  169. (unsigned int) dw);
  170. return temp_name;
  171. }
  172. /**
  173. * Create and open a download progress window.
  174. *
  175. * \param ctx Download context
  176. * \return a new gui_download_window structure, or 0 on error and error
  177. * reported
  178. */
  179. struct gui_download_window *gui_download_window_create(download_context *ctx,
  180. struct gui_window *gui)
  181. {
  182. const char *url = download_context_get_url(ctx);
  183. const char *mime_type = download_context_get_mime_type(ctx);
  184. const char *temp_name;
  185. char *nice, *scheme = NULL;
  186. struct gui_download_window *dw;
  187. bool space_warning = false;
  188. os_error *error;
  189. url_func_result res;
  190. char *local_path;
  191. utf8_convert_ret err;
  192. size_t leaf_ofst;
  193. size_t i;
  194. dw = malloc(sizeof *dw);
  195. if (!dw) {
  196. warn_user("NoMemory", 0);
  197. return 0;
  198. }
  199. dw->ctx = ctx;
  200. dw->saved = false;
  201. dw->close_confirmed = false;
  202. dw->error = false;
  203. dw->query = QUERY_INVALID;
  204. dw->received = 0;
  205. dw->total_size = download_context_get_total_length(ctx);
  206. strncpy(dw->url, url, sizeof dw->url);
  207. dw->url[sizeof dw->url - 1] = 0;
  208. dw->status[0] = 0;
  209. gettimeofday(&dw->start_time, 0);
  210. dw->last_time = dw->start_time;
  211. dw->last_received = 0;
  212. dw->file_type = 0;
  213. /* Get scheme from URL */
  214. res = url_scheme(url, &scheme);
  215. if (res == URL_FUNC_NOMEM) {
  216. warn_user("NoMemory", 0);
  217. free(dw);
  218. return 0;
  219. } else if (res == URL_FUNC_OK) {
  220. /* If we have a scheme and it's "file", then
  221. * attempt to use the local filetype directly */
  222. if (strcasecmp(scheme, "file") == 0) {
  223. char *path = NULL;
  224. res = url_path(url, &path);
  225. if (res == URL_FUNC_NOMEM) {
  226. warn_user("NoMemory", 0);
  227. free(scheme);
  228. free(dw);
  229. return 0;
  230. } else if (res == URL_FUNC_OK) {
  231. char *raw_path = curl_unescape(path,
  232. strlen(path));
  233. if (raw_path == NULL) {
  234. warn_user("NoMemory", 0);
  235. free(path);
  236. free(scheme);
  237. free(dw);
  238. return 0;
  239. }
  240. dw->file_type =
  241. ro_filetype_from_unix_path(raw_path);
  242. curl_free(raw_path);
  243. free(path);
  244. }
  245. }
  246. free(scheme);
  247. }
  248. /* If we still don't have a filetype (i.e. failed reading local
  249. * one or fetching a remote object), then use the MIME type */
  250. if (dw->file_type == 0) {
  251. /* convert MIME type to RISC OS file type */
  252. error = xmimemaptranslate_mime_type_to_filetype(mime_type,
  253. &(dw->file_type));
  254. if (error) {
  255. LOG(("xmimemaptranslate_mime_type_to_filetype: 0x%x: %s",
  256. error->errnum, error->errmess));
  257. warn_user("MiscError", error->errmess);
  258. dw->file_type = 0xffd;
  259. }
  260. }
  261. /* open temporary output file */
  262. temp_name = ro_gui_download_temp_name(dw);
  263. if (!ro_gui_download_check_space(dw, temp_name, NULL)) {
  264. /* issue a warning but continue with the download because the
  265. user can save it to another medium whilst it's downloading */
  266. space_warning = true;
  267. }
  268. error = xosfind_openoutw(osfind_NO_PATH | osfind_ERROR_IF_DIR,
  269. temp_name, 0, &dw->file);
  270. if (error) {
  271. LOG(("xosfind_openoutw: 0x%x: %s",
  272. error->errnum, error->errmess));
  273. warn_user("SaveError", error->errmess);
  274. free(dw);
  275. return 0;
  276. }
  277. /* fill in download window icons */
  278. download_template->icons[ICON_DOWNLOAD_URL].data.indirected_text.text =
  279. dw->url;
  280. download_template->icons[ICON_DOWNLOAD_URL].data.indirected_text.size =
  281. sizeof dw->url;
  282. download_template->icons[ICON_DOWNLOAD_STATUS].data.indirected_text.
  283. text = dw->status;
  284. download_template->icons[ICON_DOWNLOAD_STATUS].data.indirected_text.
  285. size = sizeof dw->status;
  286. sprintf(dw->sprite_name, "file_%.3x", dw->file_type);
  287. if (!ro_gui_wimp_sprite_exists(dw->sprite_name))
  288. strcpy(dw->sprite_name, "file_xxx");
  289. download_template->icons[ICON_DOWNLOAD_ICON].data.indirected_sprite.id =
  290. (osspriteop_id) dw->sprite_name;
  291. if (download_dir) {
  292. memcpy(dw->path, download_dir, download_dir_len);
  293. dw->path[download_dir_len] = '.';
  294. leaf_ofst = download_dir_len + 1;
  295. } else
  296. leaf_ofst = 0;
  297. if (url_nice(url, &nice, option_strip_extensions) == URL_FUNC_OK) {
  298. size_t imax = sizeof dw->path - (leaf_ofst + 1);
  299. for (i = 0; i < imax && nice[i]; i++) {
  300. if (nice[i] == '.')
  301. nice[i] = '/';
  302. else if (nice[i] <= ' ' ||
  303. strchr(":*#$&@^%\\", nice[i]))
  304. nice[i] = '_';
  305. }
  306. memcpy(dw->path + leaf_ofst, nice, i);
  307. dw->path[leaf_ofst + i] = '\0';
  308. free(nice);
  309. } else {
  310. const char *leaf = messages_get("SaveObject");
  311. size_t len = strlen(leaf);
  312. if (len >= sizeof dw->path - leaf_ofst)
  313. len = sizeof dw->path - leaf_ofst - 1;
  314. memcpy(dw->path + leaf_ofst, leaf, len);
  315. dw->path[leaf_ofst + len] = '\0';
  316. }
  317. err = utf8_to_local_encoding(dw->path, 0, &local_path);
  318. if (err != UTF8_CONVERT_OK) {
  319. /* badenc should never happen */
  320. assert(err != UTF8_CONVERT_BADENC);
  321. LOG(("utf8_to_local_encoding failed"));
  322. warn_user("NoMemory", 0);
  323. free(dw);
  324. return 0;
  325. }
  326. else {
  327. strncpy(dw->path, local_path, sizeof dw->path);
  328. free(local_path);
  329. }
  330. download_template->icons[ICON_DOWNLOAD_PATH].data.indirected_text.text =
  331. dw->path;
  332. download_template->icons[ICON_DOWNLOAD_PATH].data.indirected_text.size =
  333. sizeof dw->path;
  334. download_template->icons[ICON_DOWNLOAD_DESTINATION].data.
  335. indirected_text.text = dw->path;
  336. download_template->icons[ICON_DOWNLOAD_DESTINATION].data.
  337. indirected_text.size = sizeof dw->path;
  338. download_template->icons[ICON_DOWNLOAD_DESTINATION].flags |=
  339. wimp_ICON_DELETED;
  340. /* create and open the download window */
  341. error = xwimp_create_window(download_template, &dw->window);
  342. if (error) {
  343. LOG(("xwimp_create_window: 0x%x: %s",
  344. error->errnum, error->errmess));
  345. warn_user("WimpError", error->errmess);
  346. free(dw);
  347. return 0;
  348. }
  349. dw->prev = 0;
  350. dw->next = download_window_list;
  351. if (download_window_list)
  352. download_window_list->prev = dw;
  353. download_window_list = dw;
  354. ro_gui_download_update_status(dw);
  355. ro_gui_dialog_open(dw->window);
  356. ro_gui_wimp_event_set_user_data(dw->window, dw);
  357. ro_gui_wimp_event_register_mouse_click(dw->window, ro_gui_download_click);
  358. ro_gui_wimp_event_register_keypress(dw->window, ro_gui_download_keypress);
  359. ro_gui_wimp_event_register_close_window(dw->window, ro_gui_download_close);
  360. /* issue the warning now, so that it appears in front of the download
  361. * window! */
  362. if (space_warning)
  363. warn_user("DownloadWarn", messages_get("NoDiscSpace"));
  364. return dw;
  365. }
  366. /**
  367. * Handle received download data.
  368. *
  369. * \param dw download window
  370. * \param data pointer to block of data received
  371. * \param size size of data
  372. * \return NSERROR_OK on success, appropriate error otherwise
  373. */
  374. nserror gui_download_window_data(struct gui_download_window *dw,
  375. const char *data, unsigned int size)
  376. {
  377. while (true) {
  378. const char *msg;
  379. int unwritten;
  380. os_error *error;
  381. error = xosgbpb_writew(dw->file, (const byte *) data, size,
  382. &unwritten);
  383. if (error) {
  384. LOG(("xosgbpb_writew: 0x%x: %s",
  385. error->errnum, error->errmess));
  386. msg = error->errmess;
  387. } else if (unwritten) {
  388. LOG(("xosgbpb_writew: unwritten %i", unwritten));
  389. msg = messages_get("Unwritten");
  390. }
  391. else {
  392. dw->received += size;
  393. return NSERROR_OK;
  394. }
  395. warn_user("SaveError", msg);
  396. if (dw->saved) {
  397. /* try to continue with the temporary file */
  398. const char *temp_name = ro_gui_download_temp_name(dw);
  399. error = ro_gui_download_move(dw, temp_name, dw->path);
  400. if (!error) {
  401. /* re-allow saving */
  402. dw->saved = false;
  403. error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON,
  404. wimp_ICON_SHADED, 0);
  405. if (error) {
  406. LOG(("xwimp_set_icon_state: 0x%x: %s",
  407. error->errnum, error->errmess));
  408. warn_user("WimpError", error->errmess);
  409. }
  410. error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_DESTINATION,
  411. wimp_ICON_DELETED, wimp_ICON_DELETED);
  412. if (error) {
  413. LOG(("xwimp_set_icon_state: 0x%x: %s",
  414. error->errnum, error->errmess));
  415. warn_user("WimpError", error->errmess);
  416. }
  417. error = xwimp_set_icon_state(dw->window,
  418. ICON_DOWNLOAD_PATH, wimp_ICON_DELETED, 0);
  419. if (error) {
  420. LOG(("xwimp_set_icon_state: 0x%x: %s",
  421. error->errnum, error->errmess));
  422. warn_user("WimpError", error->errmess);
  423. }
  424. continue;
  425. }
  426. }
  427. /* give up then */
  428. assert(dw->ctx);
  429. download_context_abort(dw->ctx);
  430. gui_download_window_error(dw, msg);
  431. return NSERROR_SAVE_FAILED;
  432. }
  433. }
  434. /**
  435. * Update the status text and progress bar.
  436. *
  437. * \param dw download window
  438. */
  439. void ro_gui_download_update_status(struct gui_download_window *dw)
  440. {
  441. char *received;
  442. char *total_size;
  443. char *speed;
  444. char time[20] = "?";
  445. struct timeval t;
  446. float dt;
  447. unsigned int left;
  448. float rate;
  449. os_error *error;
  450. int width;
  451. char *local_status;
  452. utf8_convert_ret err;
  453. gettimeofday(&t, 0);
  454. dt = (t.tv_sec + 0.000001 * t.tv_usec) - (dw->last_time.tv_sec +
  455. 0.000001 * dw->last_time.tv_usec);
  456. if (dt == 0)
  457. dt = 0.001;
  458. total_size = human_friendly_bytesize(max(dw->received, dw->total_size));
  459. if (dw->ctx) {
  460. rate = (dw->received - dw->last_received) / dt;
  461. received = human_friendly_bytesize(dw->received);
  462. speed = human_friendly_bytesize(rate);
  463. if (dw->total_size) {
  464. float f;
  465. if (rate) {
  466. left = (dw->total_size - dw->received) / rate;
  467. sprintf(time, "%u:%.2u", left / 60, left % 60);
  468. }
  469. /* convert to local encoding */
  470. err = utf8_to_local_encoding(
  471. messages_get("Download"), 0, &local_status);
  472. if (err != UTF8_CONVERT_OK) {
  473. /* badenc should never happen */
  474. assert(err != UTF8_CONVERT_BADENC);
  475. /* hide nomem error */
  476. snprintf(dw->status, sizeof dw->status,
  477. messages_get("Download"),
  478. received, total_size, speed, time);
  479. }
  480. else {
  481. snprintf(dw->status, sizeof dw->status,
  482. local_status,
  483. received, total_size, speed, time);
  484. free(local_status);
  485. }
  486. f = (float) dw->received / (float) dw->total_size;
  487. width = download_progress_width * f;
  488. } else {
  489. left = t.tv_sec - dw->start_time.tv_sec;
  490. sprintf(time, "%u:%.2u", left / 60, left % 60);
  491. err = utf8_to_local_encoding(
  492. messages_get("DownloadU"), 0, &local_status);
  493. if (err != UTF8_CONVERT_OK) {
  494. /* badenc should never happen */
  495. assert(err != UTF8_CONVERT_BADENC);
  496. /* hide nomem error */
  497. snprintf(dw->status, sizeof dw->status,
  498. messages_get("DownloadU"),
  499. received, speed, time);
  500. }
  501. else {
  502. snprintf(dw->status, sizeof dw->status,
  503. local_status,
  504. received, speed, time);
  505. free(local_status);
  506. }
  507. /* length unknown, stay at 0 til finished */
  508. width = 0;
  509. }
  510. } else {
  511. left = dw->last_time.tv_sec - dw->start_time.tv_sec;
  512. if (left == 0)
  513. left = 1;
  514. rate = (float) dw->received / (float) left;
  515. sprintf(time, "%u:%.2u", left / 60, left % 60);
  516. speed = human_friendly_bytesize(rate);
  517. err = utf8_to_local_encoding(messages_get("Downloaded"), 0,
  518. &local_status);
  519. if (err != UTF8_CONVERT_OK) {
  520. /* badenc should never happen */
  521. assert(err != UTF8_CONVERT_BADENC);
  522. /* hide nomem error */
  523. snprintf(dw->status, sizeof dw->status,
  524. messages_get("Downloaded"),
  525. total_size, speed, time);
  526. }
  527. else {
  528. snprintf(dw->status, sizeof dw->status, local_status,
  529. total_size, speed, time);
  530. free(local_status);
  531. }
  532. /* all done */
  533. width = download_progress_width;
  534. }
  535. dw->last_time = t;
  536. dw->last_received = dw->received;
  537. error = xwimp_resize_icon(dw->window, ICON_DOWNLOAD_PROGRESS,
  538. download_progress_x0,
  539. download_progress_y0,
  540. download_progress_x0 + width,
  541. download_progress_y1);
  542. if (error) {
  543. LOG(("xwimp_resize_icon: 0x%x: %s",
  544. error->errnum, error->errmess));
  545. warn_user("WimpError", error->errmess);
  546. }
  547. error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_STATUS, 0, 0);
  548. if (error) {
  549. LOG(("xwimp_set_icon_state: 0x%x: %s",
  550. error->errnum, error->errmess));
  551. warn_user("WimpError", error->errmess);
  552. }
  553. if (dw->ctx)
  554. schedule(100, ro_gui_download_update_status_wrapper, dw);
  555. else
  556. schedule_remove(ro_gui_download_update_status_wrapper, dw);
  557. }
  558. /**
  559. * Wrapper for ro_gui_download_update_status(), suitable for schedule().
  560. */
  561. void ro_gui_download_update_status_wrapper(void *p)
  562. {
  563. ro_gui_download_update_status((struct gui_download_window *) p);
  564. }
  565. /**
  566. * Hide the caret but preserve input focus.
  567. *
  568. * \param dw download window
  569. */
  570. void ro_gui_download_window_hide_caret(struct gui_download_window *dw)
  571. {
  572. wimp_caret caret;
  573. os_error *error;
  574. error = xwimp_get_caret_position(&caret);
  575. if (error) {
  576. LOG(("xwimp_get_caret_position: 0x%x : %s",
  577. error->errnum, error->errmess));
  578. warn_user("WimpError", error->errmess);
  579. }
  580. else if (caret.w == dw->window) {
  581. error = xwimp_set_caret_position(dw->window, (wimp_i)-1, 0, 0, 1 << 25, -1);
  582. if (error) {
  583. LOG(("xwimp_get_caret_position: 0x%x : %s",
  584. error->errnum, error->errmess));
  585. warn_user("WimpError", error->errmess);
  586. }
  587. }
  588. }
  589. /**
  590. * Handle failed downloads.
  591. *
  592. * \param dw download window
  593. * \param error_msg error message
  594. */
  595. void gui_download_window_error(struct gui_download_window *dw,
  596. const char *error_msg)
  597. {
  598. os_error *error;
  599. if (dw->ctx != NULL)
  600. download_context_destroy(dw->ctx);
  601. dw->ctx = NULL;
  602. dw->error = true;
  603. schedule_remove(ro_gui_download_update_status_wrapper, dw);
  604. /* place error message in status icon in red */
  605. strncpy(dw->status, error_msg, sizeof dw->status);
  606. error = xwimp_set_icon_state(dw->window,
  607. ICON_DOWNLOAD_STATUS,
  608. wimp_COLOUR_RED << wimp_ICON_FG_COLOUR_SHIFT,
  609. wimp_ICON_FG_COLOUR);
  610. if (error) {
  611. LOG(("xwimp_set_icon_state: 0x%x: %s",
  612. error->errnum, error->errmess));
  613. warn_user("WimpError", error->errmess);
  614. }
  615. /* grey out pathname icon */
  616. error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_PATH,
  617. wimp_ICON_SHADED, 0);
  618. if (error) {
  619. LOG(("xwimp_set_icon_state: 0x%x: %s",
  620. error->errnum, error->errmess));
  621. warn_user("WimpError", error->errmess);
  622. }
  623. /* grey out file icon */
  624. error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON,
  625. wimp_ICON_SHADED, wimp_ICON_SHADED);
  626. if (error) {
  627. LOG(("xwimp_set_icon_state: 0x%x: %s",
  628. error->errnum, error->errmess));
  629. warn_user("WimpError", error->errmess);
  630. }
  631. ro_gui_download_window_hide_caret(dw);
  632. }
  633. /**
  634. * Handle completed downloads.
  635. *
  636. * \param dw download window
  637. */
  638. void gui_download_window_done(struct gui_download_window *dw)
  639. {
  640. os_error *error;
  641. if (dw->ctx != NULL)
  642. download_context_destroy(dw->ctx);
  643. dw->ctx = NULL;
  644. ro_gui_download_update_status(dw);
  645. error = xosfind_closew(dw->file);
  646. if (error) {
  647. LOG(("xosfind_closew: 0x%x: %s",
  648. error->errnum, error->errmess));
  649. warn_user("SaveError", error->errmess);
  650. }
  651. dw->file = 0;
  652. if (dw->saved) {
  653. error = xosfile_set_type(dw->path,
  654. dw->file_type);
  655. if (error) {
  656. LOG(("xosfile_set_type: 0x%x: %s",
  657. error->errnum, error->errmess));
  658. warn_user("SaveError", error->errmess);
  659. }
  660. if (dw->send_dataload)
  661. ro_gui_download_send_dataload(dw);
  662. schedule(200, ro_gui_download_window_destroy_wrapper, dw);
  663. }
  664. }
  665. /**
  666. * Handle Mouse_Click events in a download window.
  667. *
  668. * \param dw download window
  669. * \param pointer block returned by Wimp_Poll
  670. */
  671. bool ro_gui_download_click(wimp_pointer *pointer)
  672. {
  673. struct gui_download_window *dw;
  674. char command[256] = "Filer_OpenDir ";
  675. char *dot;
  676. os_error *error;
  677. dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(pointer->w);
  678. if (pointer->i == ICON_DOWNLOAD_ICON && !dw->error &&
  679. !dw->saved) {
  680. const char *sprite = ro_gui_get_icon_string(pointer->w, pointer->i);
  681. int x = pointer->pos.x, y = pointer->pos.y;
  682. wimp_window_state wstate;
  683. wimp_icon_state istate;
  684. /* start the drag from the icon's exact location, rather than the pointer */
  685. istate.w = wstate.w = pointer->w;
  686. istate.i = pointer->i;
  687. if (!xwimp_get_window_state(&wstate) && !xwimp_get_icon_state(&istate)) {
  688. x = (istate.icon.extent.x1 + istate.icon.extent.x0)/2 +
  689. wstate.visible.x0 - wstate.xscroll;
  690. y = (istate.icon.extent.y1 + istate.icon.extent.y0)/2 +
  691. wstate.visible.y1 - wstate.yscroll;
  692. }
  693. gui_current_drag_type = GUI_DRAG_DOWNLOAD_SAVE;
  694. download_window_current = dw;
  695. ro_gui_drag_icon(x, y, sprite);
  696. } else if (pointer->i == ICON_DOWNLOAD_DESTINATION) {
  697. strncpy(command + 14, dw->path, 242);
  698. command[255] = 0;
  699. dot = strrchr(command, '.');
  700. if (dot) {
  701. *dot = 0;
  702. error = xos_cli(command);
  703. if (error) {
  704. LOG(("xos_cli: 0x%x: %s",
  705. error->errnum, error->errmess));
  706. warn_user("MiscError", error->errmess);
  707. }
  708. }
  709. }
  710. return true;
  711. }
  712. /**
  713. * Handler Key_Press events in a download window.
  714. *
  715. * \param dw download window
  716. * \param key key press returned by Wimp_Poll
  717. * \return true iff key press handled
  718. */
  719. bool ro_gui_download_keypress(wimp_key *key)
  720. {
  721. struct gui_download_window *dw;
  722. dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(key->w);
  723. switch (key->c)
  724. {
  725. case wimp_KEY_ESCAPE:
  726. ro_gui_download_window_destroy(dw, false);
  727. return true;
  728. case wimp_KEY_RETURN: {
  729. const char *name = ro_gui_get_icon_string(dw->window,
  730. ICON_DOWNLOAD_PATH);
  731. if (!strrchr(name, '.')) {
  732. warn_user("NoPathError", NULL);
  733. return true;
  734. }
  735. ro_gui_convert_save_path(dw->path, sizeof dw->path, name);
  736. dw->send_dataload = false;
  737. if (ro_gui_download_save(dw, dw->path,
  738. !option_confirm_overwrite) && !dw->ctx)
  739. {
  740. /* finished already */
  741. schedule(200, ro_gui_download_window_destroy_wrapper, dw);
  742. }
  743. return true;
  744. }
  745. break;
  746. }
  747. /* ignore all other keypresses (F12 etc) */
  748. return false;
  749. }
  750. /**
  751. * Handle User_Drag_Box event for a drag from a download window.
  752. *
  753. * \param drag block returned by Wimp_Poll
  754. */
  755. void ro_gui_download_drag_end(wimp_dragged *drag)
  756. {
  757. wimp_pointer pointer;
  758. wimp_message message;
  759. struct gui_download_window *dw = download_window_current;
  760. const char *leaf;
  761. os_error *error;
  762. if (dw->saved || dw->error)
  763. return;
  764. error = xwimp_get_pointer_info(&pointer);
  765. if (error) {
  766. LOG(("xwimp_get_pointer_info: 0x%x: %s",
  767. error->errnum, error->errmess));
  768. warn_user("WimpError", error->errmess);
  769. return;
  770. }
  771. /* ignore drags to the download window itself */
  772. if (pointer.w == dw->window) return;
  773. leaf = strrchr(dw->path, '.');
  774. if (leaf)
  775. leaf++;
  776. else
  777. leaf = dw->path;
  778. ro_gui_convert_save_path(message.data.data_xfer.file_name, 212, leaf);
  779. message.your_ref = 0;
  780. message.action = message_DATA_SAVE;
  781. message.data.data_xfer.w = pointer.w;
  782. message.data.data_xfer.i = pointer.i;
  783. message.data.data_xfer.pos.x = pointer.pos.x;
  784. message.data.data_xfer.pos.y = pointer.pos.y;
  785. message.data.data_xfer.est_size = dw->total_size ? dw->total_size :
  786. dw->received;
  787. message.data.data_xfer.file_type = dw->file_type;
  788. message.size = 44 + ((strlen(message.data.data_xfer.file_name) + 4) &
  789. (~3u));
  790. error = xwimp_send_message_to_window(wimp_USER_MESSAGE, &message,
  791. pointer.w, pointer.i, 0);
  792. if (error) {
  793. LOG(("xwimp_send_message_to_window: 0x%x: %s",
  794. error->errnum, error->errmess));
  795. warn_user("WimpError", error->errmess);
  796. }
  797. }
  798. /**
  799. * Handle Message_DataSaveAck for a drag from a download window.
  800. *
  801. * \param message block returned by Wimp_Poll
  802. */
  803. void ro_gui_download_datasave_ack(wimp_message *message)
  804. {
  805. struct gui_download_window *dw = download_window_current;
  806. dw->send_dataload = true;
  807. memcpy(&dw->save_message, message, sizeof(wimp_message));
  808. if (!ro_gui_download_save(dw, message->data.data_xfer.file_name,
  809. !option_confirm_overwrite))
  810. return;
  811. if (!dw->ctx) {
  812. /* Ack successful completed save with message_DATA_LOAD immediately
  813. to reduce the chance of the target app getting confused by it
  814. being delayed */
  815. ro_gui_download_send_dataload(dw);
  816. schedule(200, ro_gui_download_window_destroy_wrapper, dw);
  817. }
  818. }
  819. /**
  820. * Return a pathname in canonical form
  821. *
  822. * \param path pathnamee to be canonicalised
  823. * \return ptr to pathname in malloc block, or NULL
  824. */
  825. char *ro_gui_download_canonicalise(const char *path)
  826. {
  827. os_error *error;
  828. int spare = 0;
  829. char *buf;
  830. error = xosfscontrol_canonicalise_path(path, NULL, NULL, NULL, 0, &spare);
  831. if (error) {
  832. LOG(("xosfscontrol_canonicalise_path: 0x%x: %s",
  833. error->errnum, error->errmess));
  834. return NULL;
  835. }
  836. buf = malloc(1 - spare);
  837. if (buf) {
  838. error = xosfscontrol_canonicalise_path(path, buf, NULL, NULL,
  839. 1 - spare, NULL);
  840. if (error) {
  841. LOG(("xosfscontrol_canonicalise_path: 0x%x: %s",
  842. error->errnum, error->errmess));
  843. free(buf);
  844. return NULL;
  845. }
  846. }
  847. return buf;
  848. }
  849. /**
  850. * Check the available space on the medium containing the destination file,
  851. * taking into account any space currently occupied by the file at its
  852. * original location.
  853. *
  854. * \param dw download window
  855. * \param dest_file destination pathname
  856. * \param orig_file current pathname, NULL if no existing file
  857. * \return true iff there's enough space
  858. */
  859. bool ro_gui_download_check_space(struct gui_download_window *dw,
  860. const char *dest_file, const char *orig_file)
  861. {
  862. /* is there enough free space for this file? */
  863. int dest_len = strlen(dest_file);
  864. os_error *error;
  865. int max_file;
  866. bits free_lo;
  867. int free_hi;
  868. char *dir;
  869. dir = malloc(dest_len + 1);
  870. if (!dir) return true;
  871. while (dest_len > 0 && dest_file[--dest_len] != '.');
  872. memcpy(dir, dest_file, dest_len);
  873. dir[dest_len] = '\0';
  874. /* try the 64-bit variant first (RO 3.6+) */
  875. error = xosfscontrol_free_space64(dir, &free_lo, &free_hi,
  876. &max_file, NULL, NULL);
  877. if (error) {
  878. LOG(("xosfscontrol_free_space64: 0x%x: %s",
  879. error->errnum, error->errmess));
  880. free_hi = 0;
  881. error = xosfscontrol_free_space(dir, (int*)&free_lo,
  882. &max_file, NULL);
  883. if (error) {
  884. LOG(("xosfscontrol_free_space: 0x%x: %s",
  885. error->errnum, error->errmess));
  886. /* close our eyes and hope */
  887. free(dir);
  888. return true;
  889. }
  890. }
  891. free(dir);
  892. if ((bits)max_file < dw->total_size || (!free_hi && free_lo < dw->total_size)) {
  893. char *dest_canon, *orig_canon;
  894. bits space;
  895. if (!orig_file || !dw->file) {
  896. /* no original file to take into account */
  897. return false;
  898. }
  899. space = min((bits)max_file, free_lo);
  900. dest_canon = ro_gui_download_canonicalise(dest_file);
  901. if (!dest_canon) dest_canon = (char*)dest_file;
  902. orig_canon = ro_gui_download_canonicalise(orig_file);
  903. if (!orig_canon) orig_canon = (char*)orig_file;
  904. /* not enough space; allow for the file's original location
  905. when space is tight by comparing the first part of the two
  906. pathnames (and assuming the FS isn't brain damaged!) */
  907. char *dot = strchr(orig_canon, '.');
  908. if (dot && !strncasecmp(dest_canon, orig_canon, (dot + 1) - orig_canon)) {
  909. int allocation;
  910. error = xosargs_read_allocation(dw->file,
  911. &allocation);
  912. if (error) {
  913. LOG(("xosargs_read_allocation: 0x%x : %s",
  914. error->errnum, error->errmess));
  915. }
  916. else {
  917. space += allocation;
  918. }
  919. }
  920. if (dest_canon != dest_file) free(dest_canon);
  921. if (orig_canon != orig_file) free(orig_canon);
  922. if (space >= dw->total_size) {
  923. /* OK, renaming should work */
  924. return true;
  925. }
  926. return false;
  927. }
  928. return true;
  929. }
  930. /**
  931. * Move the downloading file to a new location and continue downloading there.
  932. *
  933. * \param dw download window
  934. * \param dest_file new location
  935. * \param src_file old location
  936. * \return error iff failed to move file
  937. */
  938. os_error *ro_gui_download_move(struct gui_download_window *dw,
  939. const char *dest_file, const char *src_file)
  940. {
  941. os_error *error;
  942. /* close temporary file */
  943. if (dw->file) {
  944. error = xosfind_closew(dw->file);
  945. dw->file = 0;
  946. if (error) {
  947. LOG(("xosfind_closew: 0x%x: %s",
  948. error->errnum, error->errmess));
  949. return error;
  950. }
  951. }
  952. /* move or copy temporary file to destination file */
  953. error = xosfscontrol_rename(src_file, dest_file);
  954. /* Errors from a filing system have number 0x1XXnn, where XX is the FS
  955. * number, and nn the error number. 0x9F is "Not same disc". */
  956. if (error && (error->errnum == error_BAD_RENAME ||
  957. (error->errnum & 0xFF00FFu) == 0x1009Fu)) {
  958. /* rename failed: copy with delete */
  959. error = xosfscontrol_copy(src_file, dest_file,
  960. osfscontrol_COPY_FORCE |
  961. osfscontrol_COPY_DELETE |
  962. osfscontrol_COPY_LOOK,
  963. 0, 0, 0, 0, 0);
  964. if (error) {
  965. LOG(("xosfscontrol_copy: 0x%x: %s",
  966. error->errnum, error->errmess));
  967. return error;
  968. }
  969. } else if (error) {
  970. LOG(("xosfscontrol_rename: 0x%x: %s",
  971. error->errnum, error->errmess));
  972. return error;
  973. }
  974. if (dw->ctx) {
  975. /* open new destination file if still fetching */
  976. error = xosfile_write(dest_file, 0xdeaddead, 0xdeaddead,
  977. fileswitch_ATTR_OWNER_READ |
  978. fileswitch_ATTR_OWNER_WRITE);
  979. if (error) {
  980. LOG(("xosfile_write: 0x%x: %s",
  981. error->errnum, error->errmess));
  982. warn_user("SaveError", error->errmess);
  983. }
  984. error = xosfind_openupw(osfind_NO_PATH | osfind_ERROR_IF_DIR,
  985. dest_file, 0, &dw->file);
  986. if (error) {
  987. LOG(("xosfind_openupw: 0x%x: %s",
  988. error->errnum, error->errmess));
  989. return error;
  990. }
  991. error = xosargs_set_ptrw(dw->file, dw->received);
  992. if (error) {
  993. LOG(("xosargs_set_ptrw: 0x%x: %s",
  994. error->errnum, error->errmess));
  995. return error;
  996. }
  997. } else {
  998. /* otherwise just set the file type */
  999. error = xosfile_set_type(dest_file,
  1000. dw->file_type);
  1001. if (error) {
  1002. LOG(("xosfile_set_type: 0x%x: %s",
  1003. error->errnum, error->errmess));
  1004. warn_user("SaveError", error->errmess);
  1005. }
  1006. }
  1007. /* success */
  1008. return NULL;
  1009. }
  1010. /**
  1011. * Remember the directory containing the given file,
  1012. * for use in further downloads.
  1013. *
  1014. * \param path pathname of downloaded file
  1015. * \return none
  1016. */
  1017. void ro_gui_download_remember_dir(const char *path)
  1018. {
  1019. const char *lastdot = NULL;
  1020. const char *p = path;
  1021. while (*p >= 0x20) {
  1022. if (*p == '.') {
  1023. /* don't remember the directory if it's a temporary file */
  1024. if (!lastdot && p == path + 12 &&
  1025. !memcmp(path, "<Wimp$Scrap>", 12)) break;
  1026. lastdot = p;
  1027. }
  1028. p++;
  1029. }
  1030. if (lastdot) {
  1031. /* remember the directory */
  1032. char *new_dir = realloc(download_dir, (lastdot+1)-path);
  1033. if (new_dir) {
  1034. download_dir_len = lastdot - path;
  1035. memcpy(new_dir, path, download_dir_len);
  1036. new_dir[download_dir_len] = '\0';
  1037. download_dir = new_dir;
  1038. }
  1039. }
  1040. }
  1041. /**
  1042. * Start of save operation, user has specified where the file should be saved.
  1043. *
  1044. * \param dw download window
  1045. * \param file_name pathname of destination file
  1046. & \param force_overwrite true iff required to overwrite without prompting
  1047. * \return true iff save successfully initiated
  1048. */
  1049. bool ro_gui_download_save(struct gui_download_window *dw,
  1050. const char *file_name, bool force_overwrite)
  1051. {
  1052. fileswitch_object_type obj_type;
  1053. const char *temp_name;
  1054. os_error *error;
  1055. if (dw->saved || dw->error)
  1056. return true;
  1057. temp_name = ro_gui_download_temp_name(dw);
  1058. /* does the user want to check for collisions when saving? */
  1059. if (!force_overwrite) {
  1060. /* check whether the destination file/dir already exists */
  1061. error = xosfile_read_stamped(file_name, &obj_type,
  1062. NULL, NULL, NULL, NULL, NULL);
  1063. if (error) {
  1064. LOG(("xosfile_read_stamped: 0x%x:%s", error->errnum, error->errmess));
  1065. return false;
  1066. }
  1067. switch (obj_type) {
  1068. case osfile_NOT_FOUND:
  1069. break;
  1070. case osfile_IS_FILE:
  1071. dw->query = query_user("OverwriteFile", NULL, &overwrite_funcs, dw,
  1072. messages_get("Replace"), messages_get("DontReplace"));
  1073. dw->query_rsn = QueryRsn_Overwrite;
  1074. return false;
  1075. default:
  1076. error = xosfile_make_error(file_name, obj_type);
  1077. assert(error);
  1078. warn_user("SaveError", error->errmess);
  1079. return false;
  1080. }
  1081. }
  1082. if (!ro_gui_download_check_space(dw, file_name, temp_name)) {
  1083. warn_user("SaveError", messages_get("NoDiscSpace"));
  1084. return false;
  1085. }
  1086. error = ro_gui_download_move(dw, file_name, temp_name);
  1087. if (error) {
  1088. warn_user("SaveError", error->errmess);
  1089. /* try to reopen at old location so that the download can continue
  1090. to the temporary file */
  1091. error = xosfind_openupw(osfind_NO_PATH | osfind_ERROR_IF_DIR,
  1092. temp_name, 0, &dw->file);
  1093. if (error) {
  1094. LOG(("xosfind_openupw: 0x%x: %s",
  1095. error->errnum, error->errmess));
  1096. } else {
  1097. error = xosargs_set_ptrw(dw->file, dw->received);
  1098. if (error) {
  1099. LOG(("xosargs_set_ptrw: 0x%x: %s",
  1100. error->errnum, error->errmess));
  1101. }
  1102. }
  1103. if (error) {
  1104. if (dw->ctx)
  1105. download_context_abort(dw->ctx);
  1106. gui_download_window_error(dw, error->errmess);
  1107. }
  1108. return false;
  1109. }
  1110. dw->saved = true;
  1111. strncpy(dw->path, file_name, sizeof dw->path);
  1112. if (!dw->send_dataload || dw->save_message.data.data_xfer.est_size != -1)
  1113. ro_gui_download_remember_dir(file_name);
  1114. /* grey out file icon */
  1115. error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON,
  1116. wimp_ICON_SHADED, wimp_ICON_SHADED);
  1117. if (error) {
  1118. LOG(("xwimp_set_icon_state: 0x%x: %s",
  1119. error->errnum, error->errmess));
  1120. warn_user("WimpError", error->errmess);
  1121. }
  1122. /* hide writeable path icon and show destination icon
  1123. Note: must redraw icon bounding box because the destination icon
  1124. has rounded edges on RISC OS Select/Adjust and doesn't
  1125. completely cover the writeable icon */
  1126. ro_gui_force_redraw_icon(dw->window, ICON_DOWNLOAD_PATH);
  1127. error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_PATH,
  1128. wimp_ICON_DELETED, wimp_ICON_DELETED);
  1129. if (error) {
  1130. LOG(("xwimp_set_icon_state: 0x%x: %s",
  1131. error->errnum, error->errmess));
  1132. warn_user("WimpError", error->errmess);
  1133. }
  1134. error = xwimp_set_icon_state(dw->window,
  1135. ICON_DOWNLOAD_DESTINATION, wimp_ICON_DELETED, 0);
  1136. if (error) {
  1137. LOG(("xwimp_set_icon_state: 0x%x: %s",
  1138. error->errnum, error->errmess));
  1139. warn_user("WimpError", error->errmess);
  1140. }
  1141. ro_gui_download_window_hide_caret(dw);
  1142. return true;
  1143. }
  1144. /**
  1145. * Send DataLoad message in response to DataSaveAck, informing the
  1146. * target application that the transfer is complete.
  1147. *
  1148. * \param dw download window
  1149. */
  1150. void ro_gui_download_send_dataload(struct gui_download_window *dw)
  1151. {
  1152. /* Ack successful save with message_DATA_LOAD */
  1153. wimp_message *message = &dw->save_message;
  1154. os_error *error;
  1155. assert(dw->send_dataload);
  1156. dw->send_dataload = false;
  1157. message->action = message_DATA_LOAD;
  1158. message->your_ref = message->my_ref;
  1159. error = xwimp_send_message_to_window(wimp_USER_MESSAGE, message,
  1160. message->data.data_xfer.w,
  1161. message->data.data_xfer.i, 0);
  1162. /* The window we just attempted to send a message to may
  1163. * have been closed before the message was sent. As we've
  1164. * no clean way of detecting this, we'll just detect the
  1165. * error return from the message send attempt and judiciously
  1166. * ignore it.
  1167. *
  1168. * Ideally, we would have registered to receive Message_WindowClosed
  1169. * and then cleared dw->send_dataload flag for the appropriate
  1170. * window. Unfortunately, however, a long-standing bug in the
  1171. * Pinboard module prevents this from being a viable solution.
  1172. *
  1173. * See http://groups.google.co.uk/group/comp.sys.acorn.tech/msg/e3fbf70d8393e6cf?dmode=source&hl=en
  1174. * for the rather depressing details.
  1175. */
  1176. if (error && error->errnum != error_WIMP_BAD_HANDLE) {
  1177. LOG(("xwimp_set_icon_state: 0x%x: %s",
  1178. error->errnum, error->errmess));
  1179. warn_user("WimpError", error->errmess);
  1180. }
  1181. schedule(200, ro_gui_download_window_destroy_wrapper, dw);
  1182. }
  1183. /**
  1184. * Handle closing of download window
  1185. */
  1186. void ro_gui_download_close(wimp_w w)
  1187. {
  1188. struct gui_download_window *dw;
  1189. dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(w);
  1190. ro_gui_download_window_destroy(dw, false);
  1191. }
  1192. /**
  1193. * Close a download window and free any related resources.
  1194. *
  1195. * \param dw download window
  1196. * \param quit destroying because we're quitting the whole app
  1197. * \return true if window destroyed, not waiting for user confirmation
  1198. */
  1199. bool ro_gui_download_window_destroy(struct gui_download_window *dw, bool quit)
  1200. {
  1201. bool safe = dw->saved && !dw->ctx;
  1202. os_error *error;
  1203. if (!safe && !dw->close_confirmed)
  1204. {
  1205. query_reason rsn = quit ? QueryRsn_Quit : QueryRsn_Abort;
  1206. if (dw->query != QUERY_INVALID) {
  1207. /* can we just reuse the existing query? */
  1208. if (rsn == dw->query_rsn) {
  1209. ro_gui_query_window_bring_to_front(dw->query);
  1210. return false;
  1211. }
  1212. query_close(dw->query);
  1213. dw->query = QUERY_INVALID;
  1214. }
  1215. if (quit) {
  1216. /* bring all download windows to the front of the desktop as
  1217. a convenience if there are lots of windows open */
  1218. struct gui_download_window *d = download_window_list;
  1219. while (d) {
  1220. ro_gui_dialog_open_top(d->window, NULL, 0, 0);
  1221. d = d->next;
  1222. }
  1223. }
  1224. dw->query_rsn = rsn;
  1225. dw->query = query_user(quit ? "QuitDownload" : "AbortDownload",
  1226. NULL, &close_funcs, dw, NULL, NULL);
  1227. return false;
  1228. }
  1229. schedule_remove(ro_gui_download_update_status_wrapper, dw);
  1230. schedule_remove(ro_gui_download_window_destroy_wrapper, dw);
  1231. /* remove from list */
  1232. if (dw->prev)
  1233. dw->prev->next = dw->next;
  1234. else
  1235. download_window_list = dw->next;
  1236. if (dw->next)
  1237. dw->next->prev = dw->prev;
  1238. /* delete window */
  1239. error = xwimp_delete_window(dw->window);
  1240. if (error) {
  1241. LOG(("xwimp_delete_window: 0x%x: %s",
  1242. error->errnum, error->errmess));
  1243. warn_user("WimpError", error->errmess);
  1244. }
  1245. ro_gui_wimp_event_finalise(dw->window);
  1246. /* close download file */
  1247. if (dw->file) {
  1248. error = xosfind_closew(dw->file);
  1249. if (error) {
  1250. LOG(("xosfind_closew: 0x%x: %s",
  1251. error->errnum, error->errmess));
  1252. warn_user("SaveError", error->errmess);
  1253. }
  1254. }
  1255. /* delete temporary file */
  1256. if (!dw->saved) {
  1257. const char *temp_name = ro_gui_download_temp_name(dw);
  1258. error = xosfile_delete(temp_name, 0, 0, 0, 0, 0);
  1259. if (error) {
  1260. LOG(("xosfile_delete: 0x%x: %s",
  1261. error->errnum, error->errmess));
  1262. warn_user("SaveError", error->errmess);
  1263. }
  1264. }
  1265. if (dw->ctx) {
  1266. download_context_abort(dw->ctx);
  1267. download_context_destroy(dw->ctx);
  1268. }
  1269. free(dw);
  1270. return true;
  1271. }
  1272. /**
  1273. * Wrapper for ro_gui_download_window_destroy(), suitable for schedule().
  1274. */
  1275. void ro_gui_download_window_destroy_wrapper(void *p)
  1276. {
  1277. struct gui_download_window *dw = p;
  1278. if (dw->query != QUERY_INVALID)
  1279. query_close(dw->query);
  1280. dw->query = QUERY_INVALID;
  1281. dw->close_confirmed = true;
  1282. ro_gui_download_window_destroy(dw, false);
  1283. }
  1284. /**
  1285. * User has opted to cancel the close, leaving the download to continue.
  1286. */
  1287. void ro_gui_download_close_cancelled(query_id id, enum query_response res, void *p)
  1288. {
  1289. struct gui_download_window *dw = p;
  1290. dw->query = QUERY_INVALID;
  1291. }
  1292. /**
  1293. * Download aborted, close window and tidy up.
  1294. */
  1295. void ro_gui_download_close_confirmed(query_id id, enum query_response res, void *p)
  1296. {
  1297. struct gui_download_window *dw = p;
  1298. dw->query = QUERY_INVALID;
  1299. dw->close_confirmed = true;
  1300. if (dw->query_rsn == QueryRsn_Quit) {
  1301. /* destroy all our downloads */
  1302. while (download_window_list)
  1303. ro_gui_download_window_destroy_wrapper(download_window_list);
  1304. /* and restart the shutdown */
  1305. if (ro_gui_prequit())
  1306. netsurf_quit = true;
  1307. }
  1308. else
  1309. ro_gui_download_window_destroy(dw, false);
  1310. }
  1311. /**
  1312. * User has opted not to overwrite the existing file.
  1313. */
  1314. void ro_gui_download_overwrite_cancelled(query_id id, enum query_response res, void *p)
  1315. {
  1316. struct gui_download_window *dw = p;
  1317. dw->query = QUERY_INVALID;
  1318. }
  1319. /**
  1320. * Overwrite of existing file confirmed, proceed with the save.
  1321. */
  1322. void ro_gui_download_overwrite_confirmed(query_id id, enum query_response res, void *p)
  1323. {
  1324. struct gui_download_window *dw = p;
  1325. dw->query = QUERY_INVALID;
  1326. if (!ro_gui_download_save(dw, dw->save_message.data.data_xfer.file_name, true))
  1327. return;
  1328. if (!dw->ctx) {
  1329. /* Ack successful completed save with message_DATA_LOAD immediately
  1330. to reduce the chance of the target app getting confused by it
  1331. being delayed */
  1332. ro_gui_download_send_dataload(dw);
  1333. schedule(200, ro_gui_download_window_destroy_wrapper, dw);
  1334. }
  1335. }
  1336. /**
  1337. * Respond to PreQuit message, displaying a prompt message if we need
  1338. * the user to confirm the shutdown.
  1339. *
  1340. * \return true if we can shutdown straightaway
  1341. */
  1342. bool ro_gui_download_prequit(void)
  1343. {
  1344. while (download_window_list)
  1345. {
  1346. if (!ro_gui_download_window_destroy(download_window_list, true))
  1347. return false; /* awaiting user confirmation */
  1348. }
  1349. return true;
  1350. }