diff options
Diffstat (limited to 'riscos/download.c')
-rw-r--r-- | riscos/download.c | 1629 |
1 files changed, 0 insertions, 1629 deletions
diff --git a/riscos/download.c b/riscos/download.c deleted file mode 100644 index cddb449de..000000000 --- a/riscos/download.c +++ /dev/null @@ -1,1629 +0,0 @@ -/* - * Copyright 2004 James Bursa <bursa@users.sourceforge.net> - * Copyright 2003 Rob Jackson <jacko@xms.ms> - * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net> - * - * This file is part of NetSurf, http://www.netsurf-browser.org/ - * - * NetSurf is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * NetSurf is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * RISC OS download windows implementation. - * - * This file implements the interface given by desktop/gui_download.h - * for download windows. Each download window has an associated - * fetch. Downloads start by writing received data to a temporary - * file. At some point the user chooses a destination (by drag & - * drop), and the temporary file is then moved to the destination and - * the download continues until complete. - */ - -#include <assert.h> -#include <string.h> -#include <time.h> -#include <curl/curl.h> -#include <libwapcaplet/libwapcaplet.h> - -#include "oslib/mimemap.h" -#include "oslib/osargs.h" -#include "oslib/osfile.h" -#include "oslib/osfind.h" -#include "oslib/osfscontrol.h" -#include "oslib/osgbpb.h" -#include "oslib/wimp.h" -#include "oslib/wimpspriteop.h" - -#include "utils/sys_time.h" -#include "utils/nsoption.h" -#include "utils/log.h" -#include "utils/messages.h" -#include "utils/nsurl.h" -#include "utils/utf8.h" -#include "utils/utils.h" -#include "utils/string.h" -#include "utils/corestrings.h" -#include "desktop/gui_download.h" -#include "desktop/download.h" - -#include "riscos/gui.h" -#include "riscos/dialog.h" -#include "riscos/mouse.h" -#include "riscos/save.h" -#include "riscos/query.h" -#include "riscos/wimp.h" -#include "riscos/wimp_event.h" -#include "riscos/ucstables.h" -#include "riscos/filetype.h" - -#define ICON_DOWNLOAD_ICON 0 -#define ICON_DOWNLOAD_URL 1 -#define ICON_DOWNLOAD_PATH 2 -#define ICON_DOWNLOAD_DESTINATION 3 -#define ICON_DOWNLOAD_PROGRESS 5 -#define ICON_DOWNLOAD_STATUS 6 - -#define RO_DOWNLOAD_MAX_PATH_LEN 255 - -typedef enum -{ - QueryRsn_Quit, - QueryRsn_Abort, - QueryRsn_Overwrite -} query_reason; - - -/** Data for a download window. */ -struct gui_download_window { - /** Associated context, or 0 if the fetch has completed or aborted. */ - download_context *ctx; - unsigned int received; /**< Amount of data received so far. */ - unsigned int total_size; /**< Size of resource, or 0 if unknown. */ - - wimp_w window; /**< RISC OS window handle. */ - bits file_type; /**< RISC OS file type. */ - - char url[256]; /**< Buffer for URL icon. */ - char sprite_name[20]; /**< Buffer for sprite icon. */ - char path[RO_DOWNLOAD_MAX_PATH_LEN]; /**< Buffer for pathname icon. */ - char status[256]; /**< Buffer for status icon. */ - - /** User has chosen the destination, and it is being written. */ - bool saved; - bool close_confirmed; - bool error; /**< Error occurred, aborted. */ - - /** RISC OS file handle, of temporary file when !saved, and of - * destination when saved. */ - os_fw file; - - query_id query; - query_reason query_rsn; - - struct timeval start_time; /**< Time download started. */ - struct timeval last_time; /**< Time status was last updated. */ - unsigned int last_received; /**< Value of received at last_time. */ - float average_rate; /**< Moving average download rate. */ - unsigned int average_points; /**< Number of points in the average. */ - - bool send_dataload; /**< Should send DataLoad message when finished */ - wimp_message save_message; /**< Copy of wimp DataSaveAck message */ - - struct gui_download_window *prev; /**< Previous in linked list. */ - struct gui_download_window *next; /**< Next in linked list. */ -}; - - -/** List of all download windows. */ -static struct gui_download_window *download_window_list = 0; -/** Download window with current save operation. */ -static struct gui_download_window *download_window_current = 0; - -/** Template for a download window. */ -static wimp_window *download_template; - -/** Width of progress bar at 100%. */ -static int download_progress_width; -/** Coordinates of progress bar. */ -static int download_progress_x0; -static int download_progress_y0; -static int download_progress_y1; - -/** Current download directory. */ -static char *download_dir = NULL; -static size_t download_dir_len; - -static void ro_gui_download_drag_end(wimp_dragged *drag, void *data); -static const char *ro_gui_download_temp_name(struct gui_download_window *dw); -static void ro_gui_download_update_status(struct gui_download_window *dw); -static void ro_gui_download_update_status_wrapper(void *p); -static void ro_gui_download_window_hide_caret(struct gui_download_window *dw); -static char *ro_gui_download_canonicalise(const char *path); -static bool ro_gui_download_check_space(struct gui_download_window *dw, - const char *dest_file, const char *orig_file); -static os_error *ro_gui_download_move(struct gui_download_window *dw, - const char *dest_file, const char *src_file); -static void ro_gui_download_remember_dir(const char *path); -static bool ro_gui_download_save(struct gui_download_window *dw, - const char *file_name, bool force_overwrite); -static void ro_gui_download_send_dataload(struct gui_download_window *dw); -static void ro_gui_download_window_destroy_wrapper(void *p); -static bool ro_gui_download_window_destroy(struct gui_download_window *dw, bool quit); -static void ro_gui_download_close_confirmed(query_id, enum query_response res, void *p); -static void ro_gui_download_close_cancelled(query_id, enum query_response res, void *p); -static void ro_gui_download_overwrite_confirmed(query_id, enum query_response res, void *p); -static void ro_gui_download_overwrite_cancelled(query_id, enum query_response res, void *p); - -static bool ro_gui_download_click(wimp_pointer *pointer); -static bool ro_gui_download_keypress(wimp_key *key); -static void ro_gui_download_close(wimp_w w); - -static const query_callback close_funcs = -{ - ro_gui_download_close_confirmed, - ro_gui_download_close_cancelled -}; - -static const query_callback overwrite_funcs = -{ - ro_gui_download_overwrite_confirmed, - ro_gui_download_overwrite_cancelled -}; - - -/** - * Load the download window template. - */ - -void ro_gui_download_init(void) -{ - download_template = ro_gui_dialog_load_template("download"); - download_progress_width = - download_template->icons[ICON_DOWNLOAD_STATUS].extent.x1 - - download_template->icons[ICON_DOWNLOAD_STATUS].extent.x0; - download_progress_x0 = - download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.x0; - download_progress_y0 = - download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.y0; - download_progress_y1 = - download_template->icons[ICON_DOWNLOAD_PROGRESS].extent.y1; -} - - -/** - * Returns the pathname of a temporary file for this download. - * - * \param dw download window - * \return ptr to pathname - */ - -const char *ro_gui_download_temp_name(struct gui_download_window *dw) -{ - static char temp_name[40]; - snprintf(temp_name, sizeof temp_name, "<Wimp$ScrapDir>.ns%x", - (unsigned int) dw); - return temp_name; -} - -/** - * Try and find the correct RISC OS filetype from a download context. - */ -static nserror download_ro_filetype(download_context *ctx, bits *ftype_out) -{ - nsurl *url = download_context_get_url(ctx); - bits ftype = 0; - lwc_string *scheme; - - /* If the file is local try and read its filetype */ - scheme = nsurl_get_component(url, NSURL_SCHEME); - if (scheme != NULL) { - bool filescheme; - if (lwc_string_isequal(scheme, - corestring_lwc_file, - &filescheme) != lwc_error_ok) { - filescheme = false; - } - - if (filescheme) { - lwc_string *path = nsurl_get_component(url, NSURL_PATH); - if (path != NULL && lwc_string_length(path) != 0) { - char *raw_path; - raw_path = curl_unescape(lwc_string_data(path), - lwc_string_length(path)); - if (raw_path != NULL) { - ftype = ro_filetype_from_unix_path(raw_path); - curl_free(raw_path); - } - } - } - } - - /* If we still don't have a filetype (i.e. failed reading local - * one or fetching a remote object), then use the MIME type. - */ - if (ftype == 0) { - /* convert MIME type to RISC OS file type */ - os_error *error; - const char *mime_type; - - mime_type = download_context_get_mime_type(ctx); - error = xmimemaptranslate_mime_type_to_filetype(mime_type, &ftype); - if (error) { - LOG("xmimemaptranslate_mime_type_to_filetype: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("MiscError", error->errmess); - ftype = 0xffd; - } - } - - *ftype_out = ftype; - return NSERROR_OK; -} - -/** - * Create and open a download progress window. - * - * \param ctx Download context - * \param gui The RISCOS gui window to download for. - * \return A new gui_download_window structure, or NULL on error and error - * reported - */ - -static struct gui_download_window * -gui_download_window_create(download_context *ctx, struct gui_window *gui) -{ - nsurl *url = download_context_get_url(ctx); - const char *temp_name; - char *filename = NULL; - struct gui_download_window *dw; - bool space_warning = false; - os_error *error; - char *local_path; - nserror err; - size_t i, last_dot; - - dw = malloc(sizeof *dw); - if (!dw) { - ro_warn_user("NoMemory", 0); - return 0; - } - - dw->ctx = ctx; - dw->saved = false; - dw->close_confirmed = false; - dw->error = false; - dw->query = QUERY_INVALID; - dw->received = 0; - dw->total_size = download_context_get_total_length(ctx); - - /** @todo change this to take a reference to the nsurl and use - * that value directly rather than using a fixed buffer. - */ - strncpy(dw->url, nsurl_access(url), sizeof dw->url); - dw->url[sizeof dw->url - 1] = 0; - - dw->status[0] = 0; - gettimeofday(&dw->start_time, 0); - dw->last_time = dw->start_time; - dw->last_received = 0; - dw->file_type = 0; - dw->average_rate = 0; - dw->average_points = 0; - - /* get filetype */ - err = download_ro_filetype(ctx, &dw->file_type); - if (err != NSERROR_OK) { - ro_warn_user(messages_get_errorcode(err), 0); - free(dw); - return 0; - } - - /* open temporary output file */ - temp_name = ro_gui_download_temp_name(dw); - if (!ro_gui_download_check_space(dw, temp_name, NULL)) { - /* issue a warning but continue with the download because the - user can save it to another medium whilst it's downloading */ - space_warning = true; - } - error = xosfind_openoutw(osfind_NO_PATH | osfind_ERROR_IF_DIR, - temp_name, 0, &dw->file); - if (error) { - LOG("xosfind_openoutw: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("SaveError", error->errmess); - free(dw); - return 0; - } - - /* fill in download window icons */ - download_template->icons[ICON_DOWNLOAD_URL].data.indirected_text.text = - dw->url; - download_template->icons[ICON_DOWNLOAD_URL].data.indirected_text.size = - sizeof dw->url; - - download_template->icons[ICON_DOWNLOAD_STATUS].data.indirected_text. - text = dw->status; - download_template->icons[ICON_DOWNLOAD_STATUS].data.indirected_text. - size = sizeof dw->status; - - sprintf(dw->sprite_name, "file_%.3x", dw->file_type); - if (!ro_gui_wimp_sprite_exists(dw->sprite_name)) - strcpy(dw->sprite_name, "file_xxx"); - download_template->icons[ICON_DOWNLOAD_ICON].data.indirected_sprite.id = - (osspriteop_id) dw->sprite_name; - - /* Get a suitable path- and leafname for the download. */ - temp_name = download_context_get_filename(dw->ctx); - - if (temp_name == NULL) - temp_name = messages_get("SaveObject"); - - if (temp_name != NULL) - filename = strdup(temp_name); - - if (filename == NULL) { - LOG("Failed to establish download filename."); - ro_warn_user("SaveError", error->errmess); - free(dw); - return 0; - } - - for (i = 0, last_dot = (size_t) -1; filename[i] != '\0'; i++) { - const char c = filename[i]; - - if (c == '.') { - last_dot = i; - filename[i] = '/'; - } else if (c <= ' ' || strchr(":*#$&@^%\\", c) != NULL) - filename[i] = '_'; - } - - if (nsoption_bool(strip_extensions) && last_dot != (size_t) -1) - filename[last_dot] = '\0'; - - if (download_dir != NULL && strlen(download_dir) > 0) - snprintf(dw->path, RO_DOWNLOAD_MAX_PATH_LEN, "%s.%s", - download_dir, filename); - else - snprintf(dw->path, RO_DOWNLOAD_MAX_PATH_LEN, "%s", - filename); - - free(filename); - - err = utf8_to_local_encoding(dw->path, 0, &local_path); - if (err != NSERROR_OK) { - /* badenc should never happen */ - assert(err !=NSERROR_BAD_ENCODING); - LOG("utf8_to_local_encoding failed"); - ro_warn_user("NoMemory", 0); - free(dw); - return 0; - } - else { - strncpy(dw->path, local_path, sizeof dw->path); - free(local_path); - } - - download_template->icons[ICON_DOWNLOAD_PATH].data.indirected_text.text = - dw->path; - download_template->icons[ICON_DOWNLOAD_PATH].data.indirected_text.size = - sizeof dw->path; - - download_template->icons[ICON_DOWNLOAD_DESTINATION].data. - indirected_text.text = dw->path; - download_template->icons[ICON_DOWNLOAD_DESTINATION].data. - indirected_text.size = sizeof dw->path; - - download_template->icons[ICON_DOWNLOAD_DESTINATION].flags |= - wimp_ICON_DELETED; - - /* create and open the download window */ - error = xwimp_create_window(download_template, &dw->window); - if (error) { - LOG("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - free(dw); - return 0; - } - - dw->prev = 0; - dw->next = download_window_list; - if (download_window_list) - download_window_list->prev = dw; - download_window_list = dw; - - ro_gui_download_update_status(dw); - - ro_gui_dialog_open(dw->window); - - ro_gui_wimp_event_set_user_data(dw->window, dw); - ro_gui_wimp_event_register_mouse_click(dw->window, ro_gui_download_click); - ro_gui_wimp_event_register_keypress(dw->window, ro_gui_download_keypress); - ro_gui_wimp_event_register_close_window(dw->window, ro_gui_download_close); - - /* issue the warning now, so that it appears in front of the download - * window! */ - if (space_warning) - ro_warn_user("DownloadWarn", messages_get("NoDiscSpace")); - - return dw; -} - -/** - * Handle failed downloads. - * - * \param dw download window - * \param error_msg error message - */ - -static void gui_download_window_error(struct gui_download_window *dw, - const char *error_msg) -{ - os_error *error; - - if (dw->ctx != NULL) - download_context_destroy(dw->ctx); - dw->ctx = NULL; - dw->error = true; - - riscos_schedule(-1, ro_gui_download_update_status_wrapper, dw); - - /* place error message in status icon in red */ - strncpy(dw->status, error_msg, sizeof dw->status); - error = xwimp_set_icon_state(dw->window, - ICON_DOWNLOAD_STATUS, - wimp_COLOUR_RED << wimp_ICON_FG_COLOUR_SHIFT, - wimp_ICON_FG_COLOUR); - if (error) { - LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - - /* grey out pathname icon */ - error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_PATH, - wimp_ICON_SHADED, 0); - if (error) { - LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - - /* grey out file icon */ - error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON, - wimp_ICON_SHADED, wimp_ICON_SHADED); - if (error) { - LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - - ro_gui_download_window_hide_caret(dw); -} - -/** - * Handle received download data. - * - * \param dw download window - * \param data pointer to block of data received - * \param size size of data - * \return NSERROR_OK on success, appropriate error otherwise - */ - -static nserror gui_download_window_data(struct gui_download_window *dw, - const char *data, unsigned int size) -{ - while (true) { - const char *msg; - int unwritten; - os_error *error; - - error = xosgbpb_writew(dw->file, (const byte *) data, size, - &unwritten); - if (error) { - LOG("xosgbpb_writew: 0x%x: %s", error->errnum, error->errmess); - msg = error->errmess; - - } else if (unwritten) { - LOG("xosgbpb_writew: unwritten %i", unwritten); - msg = messages_get("Unwritten"); - } - else { - dw->received += size; - return NSERROR_OK; - } - - ro_warn_user("SaveError", msg); - - if (dw->saved) { - /* try to continue with the temporary file */ - const char *temp_name = ro_gui_download_temp_name(dw); - - error = ro_gui_download_move(dw, temp_name, dw->path); - if (!error) { - - /* re-allow saving */ - dw->saved = false; - - error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON, - wimp_ICON_SHADED, 0); - if (error) { - LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - - error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_DESTINATION, - wimp_ICON_DELETED, wimp_ICON_DELETED); - if (error) { - LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - error = xwimp_set_icon_state(dw->window, - ICON_DOWNLOAD_PATH, wimp_ICON_DELETED, 0); - if (error) { - LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - - continue; - } - } - - /* give up then */ - assert(dw->ctx); - download_context_abort(dw->ctx); - gui_download_window_error(dw, msg); - - return NSERROR_SAVE_FAILED; - } -} - - -/** - * Update the status text and progress bar. - * - * \param dw download window - */ - -void ro_gui_download_update_status(struct gui_download_window *dw) -{ - char *total_size; - char *speed; - char time[20] = "?"; - struct timeval t; - float dt; - unsigned int left; - float rate; - os_error *error; - int width; - char *local_status; - nserror err; - - gettimeofday(&t, 0); - dt = (t.tv_sec + 0.000001 * t.tv_usec) - (dw->last_time.tv_sec + - 0.000001 * dw->last_time.tv_usec); - if (dt == 0) - dt = 0.001; - - total_size = human_friendly_bytesize(max(dw->received, dw->total_size)); - - if (dw->ctx) { - char *received; - rate = (dw->received - dw->last_received) / dt; - received = human_friendly_bytesize(dw->received); - /* A simple 'modified moving average' download rate calculation - * to smooth out rate fluctuations: chosen for simplicity. - */ - dw->average_points++; - dw->average_rate = - ((dw->average_points - 1) * - dw->average_rate + rate) / - dw->average_points; - speed = human_friendly_bytesize(dw->average_rate); - if (dw->total_size) { - float f; - - if (dw->average_rate > 0) { - left = (dw->total_size - dw->received) / - dw->average_rate; - sprintf(time, "%u:%.2u", left / 60, left % 60); - } - - /* convert to local encoding */ - err = utf8_to_local_encoding( - messages_get("Download"), 0, &local_status); - if (err != NSERROR_OK) { - /* badenc should never happen */ - assert(err != NSERROR_BAD_ENCODING); - /* hide nomem error */ - snprintf(dw->status, sizeof dw->status, - messages_get("Download"), - received, total_size, speed, time); - } - else { - snprintf(dw->status, sizeof dw->status, - local_status, - received, total_size, speed, time); - free(local_status); - } - - f = (float) dw->received / (float) dw->total_size; - width = download_progress_width * f; - } else { - left = t.tv_sec - dw->start_time.tv_sec; - sprintf(time, "%u:%.2u", left / 60, left % 60); - - err = utf8_to_local_encoding( - messages_get("DownloadU"), 0, &local_status); - if (err != NSERROR_OK) { - /* badenc should never happen */ - assert(err != NSERROR_BAD_ENCODING); - /* hide nomem error */ - snprintf(dw->status, sizeof dw->status, - messages_get("DownloadU"), - received, speed, time); - } - else { - snprintf(dw->status, sizeof dw->status, - local_status, - received, speed, time); - free(local_status); - } - - /* length unknown, stay at 0 til finished */ - width = 0; - } - } else { - left = dw->last_time.tv_sec - dw->start_time.tv_sec; - if (left == 0) - left = 1; - rate = (float) dw->received / (float) left; - sprintf(time, "%u:%.2u", left / 60, left % 60); - speed = human_friendly_bytesize(rate); - - err = utf8_to_local_encoding(messages_get("Downloaded"), 0, - &local_status); - if (err != NSERROR_OK) { - /* badenc should never happen */ - assert(err != NSERROR_BAD_ENCODING); - /* hide nomem error */ - snprintf(dw->status, sizeof dw->status, - messages_get("Downloaded"), - total_size, speed, time); - } - else { - snprintf(dw->status, sizeof dw->status, local_status, - total_size, speed, time); - free(local_status); - } - - /* all done */ - width = download_progress_width; - } - - dw->last_time = t; - dw->last_received = dw->received; - - error = xwimp_resize_icon(dw->window, ICON_DOWNLOAD_PROGRESS, - download_progress_x0, - download_progress_y0, - download_progress_x0 + width, - download_progress_y1); - if (error) { - LOG("xwimp_resize_icon: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - - error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_STATUS, 0, 0); - if (error) { - LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - - if (dw->ctx) { - riscos_schedule(1000, ro_gui_download_update_status_wrapper, dw); - } else { - riscos_schedule(-1, ro_gui_download_update_status_wrapper, dw); - } -} - - -/** - * Wrapper for ro_gui_download_update_status(), suitable for riscos_schedule(). - */ - -void ro_gui_download_update_status_wrapper(void *p) -{ - ro_gui_download_update_status((struct gui_download_window *) p); -} - - - -/** - * Hide the caret but preserve input focus. - * - * \param dw download window - */ - -void ro_gui_download_window_hide_caret(struct gui_download_window *dw) -{ - wimp_caret caret; - os_error *error; - - error = xwimp_get_caret_position(&caret); - if (error) { - LOG("xwimp_get_caret_position: 0x%x : %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - else if (caret.w == dw->window) { - error = xwimp_set_caret_position(dw->window, (wimp_i)-1, 0, 0, 1 << 25, -1); - if (error) { - LOG("xwimp_get_caret_position: 0x%x : %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - } -} - - - - -/** - * Handle completed downloads. - * - * \param dw download window - */ - -static void gui_download_window_done(struct gui_download_window *dw) -{ - os_error *error; - - if (dw->ctx != NULL) - download_context_destroy(dw->ctx); - dw->ctx = NULL; - ro_gui_download_update_status(dw); - - error = xosfind_closew(dw->file); - if (error) { - LOG("xosfind_closew: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("SaveError", error->errmess); - } - dw->file = 0; - - if (dw->saved) { - error = xosfile_set_type(dw->path, - dw->file_type); - if (error) { - LOG("xosfile_set_type: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("SaveError", error->errmess); - } - - if (dw->send_dataload) { - ro_gui_download_send_dataload(dw); - } - - riscos_schedule(2000, ro_gui_download_window_destroy_wrapper, dw); - } -} - - -/** - * Handle Mouse_Click events in a download window. - * - * \param pointer block returned by Wimp_Poll - */ - -bool ro_gui_download_click(wimp_pointer *pointer) -{ - struct gui_download_window *dw; - - dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(pointer->w); - if ((pointer->buttons & (wimp_DRAG_SELECT | wimp_DRAG_ADJUST)) && - pointer->i == ICON_DOWNLOAD_ICON && - !dw->error && !dw->saved) { - const char *sprite = ro_gui_get_icon_string(pointer->w, pointer->i); - int x = pointer->pos.x, y = pointer->pos.y; - wimp_window_state wstate; - wimp_icon_state istate; - /* start the drag from the icon's exact location, rather than the pointer */ - istate.w = wstate.w = pointer->w; - istate.i = pointer->i; - if (!xwimp_get_window_state(&wstate) && !xwimp_get_icon_state(&istate)) { - x = (istate.icon.extent.x1 + istate.icon.extent.x0)/2 + - wstate.visible.x0 - wstate.xscroll; - y = (istate.icon.extent.y1 + istate.icon.extent.y0)/2 + - wstate.visible.y1 - wstate.yscroll; - } - ro_mouse_drag_start(ro_gui_download_drag_end, NULL, NULL, NULL); - download_window_current = dw; - ro_gui_drag_icon(x, y, sprite); - - } else if (pointer->i == ICON_DOWNLOAD_DESTINATION) { - char command[256] = "Filer_OpenDir "; - char *dot; - - strncpy(command + 14, dw->path, 242); - command[255] = 0; - dot = strrchr(command, '.'); - if (dot) { - os_error *error; - *dot = 0; - error = xos_cli(command); - if (error) { - LOG("xos_cli: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("MiscError", error->errmess); - } - } - } - return true; -} - - -/** - * Handler Key_Press events in a download window. - * - * \param key key press returned by Wimp_Poll - * \return true iff key press handled - */ - -bool ro_gui_download_keypress(wimp_key *key) -{ - struct gui_download_window *dw; - - dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(key->w); - switch (key->c) - { - case wimp_KEY_ESCAPE: - ro_gui_download_window_destroy(dw, false); - return true; - - case wimp_KEY_RETURN: { - const char *name = ro_gui_get_icon_string(dw->window, - ICON_DOWNLOAD_PATH); - if (!strrchr(name, '.')) { - ro_warn_user("NoPathError", NULL); - return true; - } - ro_gui_convert_save_path(dw->path, sizeof dw->path, name); - - dw->send_dataload = false; - if (ro_gui_download_save(dw, dw->path, - !nsoption_bool(confirm_overwrite)) && !dw->ctx) - { - /* finished already */ - riscos_schedule(2000, ro_gui_download_window_destroy_wrapper, dw); - } - return true; - } - break; - } - - /* ignore all other keypresses (F12 etc) */ - return false; -} - - -/** - * Handle User_Drag_Box event for a drag from a download window. - * - * \param *drag block returned by Wimp_Poll - * \param *data NULL data to allow use as callback from ro_mouse. - */ - -static void ro_gui_download_drag_end(wimp_dragged *drag, void *data) -{ - wimp_pointer pointer; - wimp_message message; - struct gui_download_window *dw = download_window_current; - const char *leaf; - os_error *error; - - if (dw->saved || dw->error) - return; - - error = xwimp_get_pointer_info(&pointer); - if (error) { - LOG("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - return; - } - - /* ignore drags to the download window itself */ - if (pointer.w == dw->window) return; - - leaf = strrchr(dw->path, '.'); - if (leaf) - leaf++; - else - leaf = dw->path; - ro_gui_convert_save_path(message.data.data_xfer.file_name, 212, leaf); - - message.your_ref = 0; - message.action = message_DATA_SAVE; - message.data.data_xfer.w = pointer.w; - message.data.data_xfer.i = pointer.i; - message.data.data_xfer.pos.x = pointer.pos.x; - message.data.data_xfer.pos.y = pointer.pos.y; - message.data.data_xfer.est_size = dw->total_size ? dw->total_size : - dw->received; - message.data.data_xfer.file_type = dw->file_type; - message.size = 44 + ((strlen(message.data.data_xfer.file_name) + 4) & - (~3u)); - - error = xwimp_send_message_to_window(wimp_USER_MESSAGE, &message, - pointer.w, pointer.i, 0); - if (error) { - LOG("xwimp_send_message_to_window: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - - gui_current_drag_type = GUI_DRAG_DOWNLOAD_SAVE; -} - - -/** - * Handle Message_DataSaveAck for a drag from a download window. - * - * \param message block returned by Wimp_Poll - */ - -void ro_gui_download_datasave_ack(wimp_message *message) -{ - struct gui_download_window *dw = download_window_current; - - dw->send_dataload = true; - memcpy(&dw->save_message, message, sizeof(wimp_message)); - - if (!ro_gui_download_save(dw, message->data.data_xfer.file_name, - !nsoption_bool(confirm_overwrite))) - return; - - if (!dw->ctx) { - /* Ack successful completed save with message_DATA_LOAD immediately - to reduce the chance of the target app getting confused by it - being delayed */ - - ro_gui_download_send_dataload(dw); - - riscos_schedule(2000, ro_gui_download_window_destroy_wrapper, dw); - } -} - - -/** - * Return a pathname in canonical form - * - * \param path pathnamee to be canonicalised - * \return ptr to pathname in malloc block, or NULL - */ - -char *ro_gui_download_canonicalise(const char *path) -{ - os_error *error; - int spare = 0; - char *buf; - - error = xosfscontrol_canonicalise_path(path, NULL, NULL, NULL, 0, &spare); - if (error) { - LOG("xosfscontrol_canonicalise_path: 0x%x: %s", error->errnum, error->errmess); - return NULL; - } - - buf = malloc(1 - spare); - if (buf) { - error = xosfscontrol_canonicalise_path(path, buf, NULL, NULL, - 1 - spare, NULL); - if (error) { - LOG("xosfscontrol_canonicalise_path: 0x%x: %s", error->errnum, error->errmess); - - free(buf); - return NULL; - } - } - - return buf; -} - - -/** - * Check the available space on the medium containing the destination file, - * taking into account any space currently occupied by the file at its - * original location. - * - * \param dw download window - * \param dest_file destination pathname - * \param orig_file current pathname, NULL if no existing file - * \return true iff there's enough space - */ - -bool ro_gui_download_check_space(struct gui_download_window *dw, - const char *dest_file, const char *orig_file) -{ - /* is there enough free space for this file? */ - int dest_len = strlen(dest_file); - os_error *error; - int max_file; - bits free_lo; - int free_hi; - char *dir; - - dir = malloc(dest_len + 1); - if (!dir) return true; - - while (dest_len > 0 && dest_file[--dest_len] != '.'); - - memcpy(dir, dest_file, dest_len); - dir[dest_len] = '\0'; - - /* try the 64-bit variant first (RO 3.6+) */ - error = xosfscontrol_free_space64(dir, &free_lo, &free_hi, - &max_file, NULL, NULL); - if (error) { - LOG("xosfscontrol_free_space64: 0x%x: %s", error->errnum, error->errmess); - - free_hi = 0; - error = xosfscontrol_free_space(dir, (int*)&free_lo, - &max_file, NULL); - if (error) { - LOG("xosfscontrol_free_space: 0x%x: %s", error->errnum, error->errmess); - /* close our eyes and hope */ - free(dir); - return true; - } - } - - free(dir); - - if ((bits)max_file < dw->total_size || (!free_hi && free_lo < dw->total_size)) { - char *dest_canon, *orig_canon; - bits space; - - if (!orig_file || !dw->file) { - /* no original file to take into account */ - return false; - } - - space = min((bits)max_file, free_lo); - - dest_canon = ro_gui_download_canonicalise(dest_file); - if (!dest_canon) dest_canon = (char*)dest_file; - - orig_canon = ro_gui_download_canonicalise(orig_file); - if (!orig_canon) orig_canon = (char*)orig_file; - - /* not enough space; allow for the file's original location - when space is tight by comparing the first part of the two - pathnames (and assuming the FS isn't brain damaged!) */ - - char *dot = strchr(orig_canon, '.'); - if (dot && !strncasecmp(dest_canon, orig_canon, (dot + 1) - orig_canon)) { - int allocation; - - error = xosargs_read_allocation(dw->file, - &allocation); - if (error) { - LOG("xosargs_read_allocation: 0x%x : %s", error->errnum, error->errmess); - } - else { - space += allocation; - } - } - - if (dest_canon != dest_file) free(dest_canon); - if (orig_canon != orig_file) free(orig_canon); - - if (space >= dw->total_size) { - /* OK, renaming should work */ - return true; - } - - return false; - } - return true; -} - -/** - * Move the downloading file to a new location and continue downloading there. - * - * \param dw download window - * \param dest_file new location - * \param src_file old location - * \return error iff failed to move file - */ - -os_error *ro_gui_download_move(struct gui_download_window *dw, - const char *dest_file, const char *src_file) -{ - os_error *error; - - /* close temporary file */ - if (dw->file) { - error = xosfind_closew(dw->file); - dw->file = 0; - if (error) { - LOG("xosfind_closew: 0x%x: %s", error->errnum, error->errmess); - return error; - } - } - - /* move or copy temporary file to destination file */ - error = xosfscontrol_rename(src_file, dest_file); - /* Errors from a filing system have number 0x1XXnn, where XX is the FS - * number, and nn the error number. 0x9F is "Not same disc". */ - if (error && (error->errnum == error_BAD_RENAME || - (error->errnum & 0xFF00FFu) == 0x1009Fu)) { - /* rename failed: copy with delete */ - error = xosfscontrol_copy(src_file, dest_file, - osfscontrol_COPY_FORCE | - osfscontrol_COPY_DELETE | - osfscontrol_COPY_LOOK, - 0, 0, 0, 0, 0); - if (error) { - LOG("xosfscontrol_copy: 0x%x: %s", error->errnum, error->errmess); - return error; - } - } else if (error) { - LOG("xosfscontrol_rename: 0x%x: %s", error->errnum, error->errmess); - return error; - } - - if (dw->ctx) { - /* open new destination file if still fetching */ - error = xosfile_write(dest_file, 0xdeaddead, 0xdeaddead, - fileswitch_ATTR_OWNER_READ | - fileswitch_ATTR_OWNER_WRITE); - if (error) { - LOG("xosfile_write: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("SaveError", error->errmess); - } - - error = xosfind_openupw(osfind_NO_PATH | osfind_ERROR_IF_DIR, - dest_file, 0, &dw->file); - if (error) { - LOG("xosfind_openupw: 0x%x: %s", error->errnum, error->errmess); - return error; - } - - error = xosargs_set_ptrw(dw->file, dw->received); - if (error) { - LOG("xosargs_set_ptrw: 0x%x: %s", error->errnum, error->errmess); - return error; - } - - } else { - /* otherwise just set the file type */ - error = xosfile_set_type(dest_file, - dw->file_type); - if (error) { - LOG("xosfile_set_type: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("SaveError", error->errmess); - } - } - - /* success */ - return NULL; -} - - -/** - * Remember the directory containing the given file, - * for use in further downloads. - * - * \param path pathname of downloaded file - * \return none - */ - -void ro_gui_download_remember_dir(const char *path) -{ - const char *lastdot = NULL; - const char *p = path; - - while (*p >= 0x20) { - if (*p == '.') { - /* don't remember the directory if it's a temporary file */ - if (!lastdot && p == path + 12 && - !memcmp(path, "<Wimp$Scrap>", 12)) break; - lastdot = p; - } - p++; - } - - if (lastdot) { - /* remember the directory */ - char *new_dir = realloc(download_dir, (lastdot+1)-path); - if (new_dir) { - download_dir_len = lastdot - path; - memcpy(new_dir, path, download_dir_len); - new_dir[download_dir_len] = '\0'; - download_dir = new_dir; - } - } -} - -/** - * Start of save operation, user has specified where the file should be saved. - * - * \param dw download window - * \param file_name pathname of destination file - & \param force_overwrite true iff required to overwrite without prompting - * \return true iff save successfully initiated - */ - -bool ro_gui_download_save(struct gui_download_window *dw, - const char *file_name, bool force_overwrite) -{ - fileswitch_object_type obj_type; - const char *temp_name; - os_error *error; - - if (dw->saved || dw->error) - return true; - - temp_name = ro_gui_download_temp_name(dw); - - /* does the user want to check for collisions when saving? */ - if (!force_overwrite) { - /* check whether the destination file/dir already exists */ - error = xosfile_read_stamped(file_name, &obj_type, - NULL, NULL, NULL, NULL, NULL); - if (error) { - LOG("xosfile_read_stamped: 0x%x:%s", error->errnum, error->errmess); - return false; - } - - switch (obj_type) { - case osfile_NOT_FOUND: - break; - - case osfile_IS_FILE: - dw->query = query_user("OverwriteFile", NULL, &overwrite_funcs, dw, - messages_get("Replace"), messages_get("DontReplace")); - dw->query_rsn = QueryRsn_Overwrite; - return false; - - default: - error = xosfile_make_error(file_name, obj_type); - assert(error); - ro_warn_user("SaveError", error->errmess); - return false; - } - } - - if (!ro_gui_download_check_space(dw, file_name, temp_name)) { - ro_warn_user("SaveError", messages_get("NoDiscSpace")); - return false; - } - - error = ro_gui_download_move(dw, file_name, temp_name); - if (error) { - ro_warn_user("SaveError", error->errmess); - - /* try to reopen at old location so that the download can continue - to the temporary file */ - error = xosfind_openupw(osfind_NO_PATH | osfind_ERROR_IF_DIR, - temp_name, 0, &dw->file); - if (error) { - LOG("xosfind_openupw: 0x%x: %s", error->errnum, error->errmess); - - } else { - error = xosargs_set_ptrw(dw->file, dw->received); - if (error) { - LOG("xosargs_set_ptrw: 0x%x: %s", error->errnum, error->errmess); - } - } - - if (error) { - if (dw->ctx) - download_context_abort(dw->ctx); - gui_download_window_error(dw, error->errmess); - } - return false; - } - - dw->saved = true; - strncpy(dw->path, file_name, sizeof dw->path); - - if (!dw->send_dataload || dw->save_message.data.data_xfer.est_size != -1) - ro_gui_download_remember_dir(file_name); - - /* grey out file icon */ - error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON, - wimp_ICON_SHADED, wimp_ICON_SHADED); - if (error) { - LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - - /* hide writeable path icon and show destination icon - Note: must redraw icon bounding box because the destination icon - has rounded edges on RISC OS Select/Adjust and doesn't - completely cover the writeable icon */ - - ro_gui_force_redraw_icon(dw->window, ICON_DOWNLOAD_PATH); - error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_PATH, - wimp_ICON_DELETED, wimp_ICON_DELETED); - if (error) { - LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - error = xwimp_set_icon_state(dw->window, - ICON_DOWNLOAD_DESTINATION, wimp_ICON_DELETED, 0); - if (error) { - LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - - ro_gui_download_window_hide_caret(dw); - - return true; -} - - -/** - * Send DataLoad message in response to DataSaveAck, informing the - * target application that the transfer is complete. - * - * \param dw download window - */ - -void ro_gui_download_send_dataload(struct gui_download_window *dw) -{ - /* Ack successful save with message_DATA_LOAD */ - wimp_message *message = &dw->save_message; - os_error *error; - - assert(dw->send_dataload); - dw->send_dataload = false; - - message->action = message_DATA_LOAD; - message->your_ref = message->my_ref; - error = xwimp_send_message_to_window(wimp_USER_MESSAGE, message, - message->data.data_xfer.w, - message->data.data_xfer.i, 0); - /* The window we just attempted to send a message to may - * have been closed before the message was sent. As we've - * no clean way of detecting this, we'll just detect the - * error return from the message send attempt and judiciously - * ignore it. - * - * Ideally, we would have registered to receive Message_WindowClosed - * and then cleared dw->send_dataload flag for the appropriate - * window. Unfortunately, however, a long-standing bug in the - * Pinboard module prevents this from being a viable solution. - * - * See http://groups.google.co.uk/group/comp.sys.acorn.tech/msg/e3fbf70d8393e6cf?dmode=source&hl=en - * for the rather depressing details. - */ - if (error && error->errnum != error_WIMP_BAD_HANDLE) { - LOG("xwimp_set_icon_state: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - - riscos_schedule(2000, ro_gui_download_window_destroy_wrapper, dw); -} - - -/** - * Handle closing of download window - */ -void ro_gui_download_close(wimp_w w) -{ - struct gui_download_window *dw; - - dw = (struct gui_download_window *)ro_gui_wimp_event_get_user_data(w); - ro_gui_download_window_destroy(dw, false); -} - - -/** - * Close a download window and free any related resources. - * - * \param dw download window - * \param quit destroying because we're quitting the whole app - * \return true if window destroyed, not waiting for user confirmation - */ - -bool ro_gui_download_window_destroy(struct gui_download_window *dw, bool quit) -{ - bool safe = dw->saved && !dw->ctx; - os_error *error; - - if (!safe && !dw->close_confirmed) - { - query_reason rsn = quit ? QueryRsn_Quit : QueryRsn_Abort; - - if (dw->query != QUERY_INVALID) { - - /* can we just reuse the existing query? */ - if (rsn == dw->query_rsn) { - ro_gui_query_window_bring_to_front(dw->query); - return false; - } - - query_close(dw->query); - dw->query = QUERY_INVALID; - } - - if (quit) { - /* bring all download windows to the front of the desktop as - a convenience if there are lots of windows open */ - - struct gui_download_window *d = download_window_list; - while (d) { - ro_gui_dialog_open_top(d->window, NULL, 0, 0); - d = d->next; - } - } - - dw->query_rsn = rsn; - dw->query = query_user(quit ? "QuitDownload" : "AbortDownload", - NULL, &close_funcs, dw, NULL, NULL); - - return false; - } - - riscos_schedule(-1, ro_gui_download_update_status_wrapper, dw); - riscos_schedule(-1, ro_gui_download_window_destroy_wrapper, dw); - - /* remove from list */ - if (dw->prev) - dw->prev->next = dw->next; - else - download_window_list = dw->next; - if (dw->next) - dw->next->prev = dw->prev; - - /* delete window */ - error = xwimp_delete_window(dw->window); - if (error) { - LOG("xwimp_delete_window: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("WimpError", error->errmess); - } - ro_gui_wimp_event_finalise(dw->window); - - /* close download file */ - if (dw->file) { - error = xosfind_closew(dw->file); - if (error) { - LOG("xosfind_closew: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("SaveError", error->errmess); - } - } - - /* delete temporary file */ - if (!dw->saved) { - const char *temp_name = ro_gui_download_temp_name(dw); - - error = xosfile_delete(temp_name, 0, 0, 0, 0, 0); - if (error) { - LOG("xosfile_delete: 0x%x: %s", error->errnum, error->errmess); - ro_warn_user("SaveError", error->errmess); - } - } - - if (dw->ctx) { - download_context_abort(dw->ctx); - download_context_destroy(dw->ctx); - } - - free(dw); - - return true; -} - - -/** - * Wrapper for ro_gui_download_window_destroy(), suitable for riscos_schedule(). - */ - -void ro_gui_download_window_destroy_wrapper(void *p) -{ - struct gui_download_window *dw = p; - if (dw->query != QUERY_INVALID) - query_close(dw->query); - dw->query = QUERY_INVALID; - dw->close_confirmed = true; - ro_gui_download_window_destroy(dw, false); -} - - -/** - * User has opted to cancel the close, leaving the download to continue. - */ - -void ro_gui_download_close_cancelled(query_id id, enum query_response res, void *p) -{ - struct gui_download_window *dw = p; - dw->query = QUERY_INVALID; -} - - -/** - * Download aborted, close window and tidy up. - */ - -void ro_gui_download_close_confirmed(query_id id, enum query_response res, void *p) -{ - struct gui_download_window *dw = p; - dw->query = QUERY_INVALID; - dw->close_confirmed = true; - if (dw->query_rsn == QueryRsn_Quit) { - - /* destroy all our downloads */ - while (download_window_list) - ro_gui_download_window_destroy_wrapper(download_window_list); - - /* and restart the shutdown */ - if (ro_gui_prequit()) - riscos_done = true; - } - else - ro_gui_download_window_destroy(dw, false); -} - - -/** - * User has opted not to overwrite the existing file. - */ - -void ro_gui_download_overwrite_cancelled(query_id id, enum query_response res, void *p) -{ - struct gui_download_window *dw = p; - dw->query = QUERY_INVALID; -} - - -/** - * Overwrite of existing file confirmed, proceed with the save. - */ - -void ro_gui_download_overwrite_confirmed(query_id id, enum query_response res, void *p) -{ - struct gui_download_window *dw = p; - dw->query = QUERY_INVALID; - - if (!ro_gui_download_save(dw, dw->save_message.data.data_xfer.file_name, true)) - return; - - if (!dw->ctx) { - /* Ack successful completed save with message_DATA_LOAD immediately - to reduce the chance of the target app getting confused by it - being delayed */ - - ro_gui_download_send_dataload(dw); - - riscos_schedule(2000, ro_gui_download_window_destroy_wrapper, dw); - } -} - - -/** - * Respond to PreQuit message, displaying a prompt message if we need - * the user to confirm the shutdown. - * - * \return true if we can shutdown straightaway - */ - -bool ro_gui_download_prequit(void) -{ - while (download_window_list) - { - if (!ro_gui_download_window_destroy(download_window_list, true)) - return false; /* awaiting user confirmation */ - } - return true; -} - -static struct gui_download_table download_table = { - .create = gui_download_window_create, - .data = gui_download_window_data, - .error = gui_download_window_error, - .done = gui_download_window_done, -}; - -struct gui_download_table *riscos_download_table = &download_table; |