UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2024 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "download.h" 30 31 #include "davcontroller.h" 32 #include "window.h" 33 34 #include <cx/printf.h> 35 36 #include "config.h" 37 38 #include "system.h" 39 #include "common/context.h" 40 41 #include <libidav/config.h> 42 #include <libidav/utils.h> 43 44 45 static int uithr_download_update_progress(void *data) { 46 DavFileDownload *download = data; 47 if(download->cancel) { 48 return 1; 49 } 50 51 time_t t = time(NULL); 52 53 char *sz_total = util_size_str(FALSE, download->progress.total_bytes); 54 char *sz_downloaded = util_size_str2(FALSE, download->progress.transferred_bytes, download->progress.total_bytes, 2); 55 char *sz_downloaded_end = strchr(sz_downloaded, ' '); 56 if (sz_downloaded_end) { 57 *sz_downloaded_end = 0; 58 } 59 60 if (download->progress.total_bytes > 0) { 61 double progress = ((double)download->progress.transferred_bytes / (double)download->progress.total_bytes)*100; 62 ui_set(download->progressbar, progress); 63 download->trans.progress = progress; 64 /* 65 if(t > download->trans.last_update + 2) { 66 snprintf(download->trans.label+download->trans.label_len, 12, " %d%%", (int)progress); 67 application_update_transferlist(); 68 download->trans.last_update = t; 69 } 70 */ 71 } 72 73 74 cxmutstr label1; 75 if (download->progress.total_files + download->progress.total_directories > 1) { 76 label1 = cx_asprintf( 77 "%s/%s %zu/%zu files", 78 sz_downloaded, 79 sz_total, 80 download->progress.transferred_files+download->progress.transferred_directories, 81 download->progress.total_files+download->progress.total_directories); 82 } else { 83 label1 = cx_asprintf( 84 "%s/%s", 85 sz_downloaded, 86 sz_total); 87 } 88 ui_set(download->label_top_left, label1.ptr); 89 90 free(sz_total); 91 free(label1.ptr); 92 93 time_t start = download->progress.speedtest_start; 94 if(t >= download->progress.speedtest_start + 4) { 95 uint64_t bytesPerSeconds = dav_transfer_speed(&download->progress, t); 96 if(start > 0) { 97 char *szps = util_size_str(FALSE, bytesPerSeconds); 98 cxmutstr label3 = cx_asprintf("%s/s", szps); 99 free(szps); 100 ui_set(download->label_bottom_left, label3.ptr); 101 free(label3.ptr); 102 } 103 } 104 105 return 1; 106 } 107 108 static size_t ddfile_write(const void *buf, size_t size, size_t count, void *stream) { 109 DDFile *file = stream; 110 if(file->download->cancel) { 111 return 0; 112 } 113 114 size_t w = fwrite(buf, size, count, file->fd); 115 file->download->progress.current_file_transferred += w; 116 117 file->download->progress.transferred_bytes += w; 118 119 if (file->download->progress.current_file_transferred > file->download->progress.current_file_size) { 120 size_t diff = file->download->progress.current_file_transferred - file->download->progress.current_file_size; 121 file->download->progress.current_file_size = file->download->progress.current_file_transferred; 122 file->download->progress.total_bytes += diff; 123 } 124 125 ui_call_mainthread(uithr_download_update_progress, file->download); 126 127 return w; 128 } 129 130 static int qthr_download_resource(void *data) { 131 DDFile *file = data; 132 if(file->download->cancel) { 133 return 0; 134 } 135 136 file->download->progress.current_file_transferred = 0; 137 file->download->progress.current_file_size = file->size; 138 139 FILE *f = sys_fopen(file->to, "wb"); 140 if (!f) { 141 return 0; 142 } 143 file->fd = f; 144 145 DavResource *res = dav_resource_new(file->download->download_sn, file->path); 146 dav_get_content(res, file, (dav_write_func)ddfile_write); 147 148 file->download->progress.transferred_files++; 149 150 ui_call_mainthread(uithr_download_update_progress, file->download); 151 152 dav_resource_free(res); 153 154 fclose(f); 155 156 free(file->path); 157 free(file->to); 158 free(file); 159 160 return 0; 161 } 162 163 static int qthr_download_finished(void *data) { 164 return 0; 165 } 166 167 static void uithr_download_finished(UiEvent *event, void *data) { 168 DavFileDownload *download = data; 169 if(download->cancel) { 170 ui_set(download->label_bottom_left, "Canceled"); 171 } 172 if(download->dialog->ref > 1) { 173 ui_close(download->dialog); 174 } 175 ui_object_unref(download->dialog); 176 } 177 178 179 typedef struct DlStackElm { 180 DavResource *resource; 181 char *sub_path; 182 } DlStackElm; 183 184 static int jobthr_download_scan(void *data) { 185 DavFileDownload *download = data; 186 187 // check if the specified local location is a directory 188 SYS_STAT s; 189 if (!sys_stat(download->local_path, &s)) { 190 if (S_ISDIR(s.st_mode)) { 191 download->isdirectory = TRUE; 192 } 193 } 194 195 CxList *stack = cxLinkedListCreateSimple(sizeof(DlStackElm)); 196 197 // add selected files to the download queue 198 DavResource *res = download->reslist; 199 while (res) { 200 DlStackElm elm; 201 elm.resource = res; 202 elm.sub_path = strdup(res->name); 203 cxListAdd(stack, &elm); 204 205 res = res->next; 206 } 207 208 while (cxListSize(stack) > 0 && !download->cancel) { 209 DlStackElm *elm = cxListAt(stack, 0); 210 DavResource *res = elm->resource; 211 char *sub_path = elm->sub_path; 212 cxListRemove(stack, 0); 213 214 if (res->iscollection) { 215 if (dav_load(res)) { 216 // TODO: handle error 217 continue; 218 } 219 220 // update ui 221 ui_call_mainthread(uithr_download_update_progress, download); 222 223 char *path = util_concat_path(download->local_path, sub_path); 224 int err = sys_mkdir(path); 225 free(path); 226 if (err) { 227 // TODO: handle error 228 } 229 230 DavResource *child = res->children; 231 while (child) { 232 char *child_path = util_concat_path(sub_path, child->name); 233 DlStackElm childelm; 234 childelm.resource = child; 235 childelm.sub_path = child_path; 236 cxListAdd(stack, &childelm); 237 238 child = child->next; 239 } 240 } else { 241 // add the file to the download queue 242 DDFile *file = malloc(sizeof(DDFile)); 243 file->download = download; 244 file->path = strdup(res->path); 245 file->size = res->contentlength; 246 if (download->isdirectory) { 247 file->to = util_concat_path(download->local_path, sub_path); 248 } else { 249 file->to = strdup(download->local_path); 250 } 251 252 // stats 253 download->progress.total_files++; 254 download->progress.total_bytes += res->contentlength; 255 256 // update ui 257 ui_call_mainthread(uithr_download_update_progress, download); 258 259 ui_threadpool_job(download->queue, download->dialog, qthr_download_resource, file, NULL, NULL); 260 } 261 } 262 263 ui_threadpool_job(download->queue, download->dialog, qthr_download_finished, download, uithr_download_finished, download); 264 265 cxListDestroy(stack); 266 267 return 0; 268 } 269 270 static void uithr_download_scan_finished(UiEvent *event, void *data) { 271 DavFileDownload *download = data; 272 273 } 274 275 void action_download_cancel(UiEvent *event, void *data) { 276 DavFileDownload *download = event->window; 277 if(!download->cancel) { 278 ui_set(download->label_bottom_left, "Cancel..."); 279 download->cancel = TRUE; 280 } 281 } 282 283 static void dav_file_download_cleanup(DavFileDownload *download) { 284 application_remove_transfer(&download->trans); 285 ui_object_unref(download->browser->window); 286 } 287 288 DavFileDownload* dav_download_create(DavBrowser *browser, UiObject *dialog, DavResource *reslist, const char *local_path) { 289 UiContext *ctx = dialog->ctx; 290 CxMempool *mp = ui_cx_mempool(ctx); 291 DavFileDownload *download = ui_malloc(ctx, sizeof(DavFileDownload)); 292 memset(download, 0, sizeof(DavFileDownload)); 293 download->dialog = dialog; 294 dialog->window = download; 295 ui_object_ref(dialog); 296 ui_object_ref(browser->window); 297 298 size_t label_len = strlen(reslist->name) + 16; 299 download->trans.label = cxCalloc(mp->allocator, label_len, 1); 300 download->trans.label_len = snprintf(download->trans.label, label_len, "< %s%s", reslist->name, reslist->next ? " ..." : ""); 301 download->trans.window = dialog; 302 303 download->browser = browser; 304 download->sn = reslist->session; 305 download->download_sn = dav_session_clone(download->sn); 306 download->reslist = reslist; // TODO: is this safe or do we need a copy? 307 download->local_path = ui_strdup(dialog->ctx, local_path); 308 309 download->queue = ui_threadpool_create(1); 310 311 cxMempoolRegister(mp, download->download_sn, (cx_destructor_func)dav_session_destroy); 312 cxMempoolRegister(mp, download->queue, (cx_destructor_func)ui_threadpool_destroy); 313 cxMempoolSetDestructor(download, (cx_destructor_func)dav_file_download_cleanup); 314 315 download->progressbar = ui_double_new(ctx, "progressbar"); 316 download->label_top_left = ui_string_new(ctx, "label_top_left"); 317 download->label_top_right = ui_string_new(ctx, "label_top_right"); 318 download->label_bottom_left = ui_string_new(ctx, "label_bottom_left"); 319 download->label_bottom_right = ui_string_new(ctx, "label_bottom_right"); 320 321 ui_set(download->label_top_left, ""); 322 ui_set(download->label_top_right, ""); 323 ui_set(download->label_bottom_left, ""); 324 ui_set(download->label_bottom_right, ""); 325 ui_set(download->progressbar, 0); 326 327 return download; 328 } 329 330 void dav_download_start(DavFileDownload *download) { 331 ui_show(download->dialog); 332 ui_job(download->dialog, jobthr_download_scan, download, uithr_download_scan_finished, download); 333 } 334