440 ui_set(browser->path, nav_url); |
442 ui_set(browser->path, nav_url); |
441 } |
443 } |
442 } |
444 } |
443 |
445 |
444 |
446 |
445 // ------------------------------------- File Upload ------------------------------------- |
|
446 |
|
447 typedef struct DavFileUpload { |
|
448 UiObject *ui; |
|
449 DavBrowser *browser; |
|
450 DavSession *sn; |
|
451 UiFileList files; |
|
452 char *base_path; |
|
453 UiThreadpool *queue; |
|
454 |
|
455 size_t total_bytes; |
|
456 size_t total_files; |
|
457 size_t total_directories; |
|
458 size_t uploaded_bytes; |
|
459 size_t uploaded_files; |
|
460 size_t uploaded_directories; |
|
461 |
|
462 DavBool upload_file; |
|
463 size_t current_file_size; |
|
464 size_t current_file_upload; |
|
465 |
|
466 char *current_file_name; |
|
467 |
|
468 UiObject *dialog; |
|
469 UiDouble *progress; |
|
470 UiString *label_top_left; |
|
471 UiString *label_top_right; |
|
472 UiString *label_bottom_left; |
|
473 UiString *label_bottom_right; |
|
474 |
|
475 // The collection, the files are uploaded to |
|
476 // It is only safe to access the collection ptr, if |
|
477 // collection == browser->current && collection_ctn == browser->res_counter |
|
478 // and obviously it can only be accessed from the UI thread |
|
479 DavResource *collection; |
|
480 |
|
481 // copy of browser->res_counter, used for integrity check |
|
482 int64_t collection_ctn; |
|
483 |
|
484 // current uploaded resource, created as part of the browser session |
|
485 // only if collection == browser->current && collection_ctn == browser->res_counter |
|
486 DavResource *current_resource; |
|
487 } DavFileUpload; |
|
488 |
|
489 typedef struct DUFile { |
|
490 char *path; |
|
491 char *upload_path; |
|
492 size_t bytes; |
|
493 DavBool isdirectory; |
|
494 DavFileUpload *upload; |
|
495 DavError error; |
|
496 } DUFile; |
|
497 |
|
498 static double upload_progress(DavFileUpload *upload) { |
|
499 return ((double)upload->uploaded_bytes / (double)upload->total_bytes) * 100; |
|
500 } |
|
501 |
|
502 static void update_upload_labels(DavFileUpload *upload) { |
|
503 char *sz_total = util_size_str(FALSE, upload->total_bytes); |
|
504 char *sz_uploaded = util_size_str2(FALSE, upload->uploaded_bytes, upload->total_bytes, 2); |
|
505 char *sz_uploaded_end = strchr(sz_uploaded, ' '); |
|
506 if (sz_uploaded_end) { |
|
507 *sz_uploaded_end = 0; |
|
508 } |
|
509 |
|
510 double progress = upload_progress(upload); |
|
511 ui_set(upload->progress, upload_progress(upload)); |
|
512 |
|
513 cxmutstr label1; |
|
514 if (upload->total_files + upload->total_directories > 1) { |
|
515 label1 = cx_asprintf( |
|
516 "%s/%s %zu/%zu files", |
|
517 sz_uploaded, |
|
518 sz_total, |
|
519 upload->uploaded_files+upload->uploaded_directories, |
|
520 upload->total_files+upload->total_directories); |
|
521 } else { |
|
522 label1 = cx_asprintf( |
|
523 "%s/%s", |
|
524 sz_uploaded, |
|
525 sz_total); |
|
526 } |
|
527 ui_set(upload->label_top_left, label1.ptr); |
|
528 |
|
529 free(sz_total); |
|
530 free(label1.ptr); |
|
531 |
|
532 if (upload->current_file_size > 0) { |
|
533 cxmutstr file_label = cx_asprintf("%s (%.0f%%)", upload->current_file_name, ((float)upload->current_file_upload/(float)upload->current_file_size)*100); |
|
534 ui_set(upload->label_top_right, file_label.ptr); |
|
535 free(file_label.ptr); |
|
536 } |
|
537 } |
|
538 |
|
539 static int uithr_update_upload_labels(void *data) { |
|
540 update_upload_labels(data); |
|
541 return 0; |
|
542 } |
|
543 |
|
544 static void upload_dav_progress(DavResource *res, int64_t total, int64_t now, void *data) { |
|
545 DavFileUpload *upload = data; |
|
546 if (upload->upload_file) { |
|
547 if (now > upload->current_file_size) { |
|
548 // current_file_size is not accurate (either the file was changed after the last stat |
|
549 // or we have some extra bytes because of encryption |
|
550 // adjust current_file_size and the total upload size |
|
551 int64_t extra = now - upload->current_file_size; |
|
552 upload->current_file_size += extra; |
|
553 upload->total_bytes += extra; |
|
554 } |
|
555 |
|
556 int64_t new_progress = now - upload->current_file_upload; |
|
557 upload->uploaded_bytes += new_progress; |
|
558 upload->current_file_upload = now; |
|
559 |
|
560 ui_call_mainthread(uithr_update_upload_labels, upload); |
|
561 } |
|
562 } |
|
563 |
|
564 |
|
565 typedef struct FileNameUpdate { |
|
566 DavFileUpload *upload; |
|
567 char *name; |
|
568 char *path; |
|
569 DavBool iscollection; |
|
570 } FileNameUpdate; |
|
571 |
|
572 static int uithr_update_file_label(FileNameUpdate *update) { |
|
573 // replace upload->current_filename with update->name |
|
574 if (update->upload->current_file_name) { |
|
575 free(update->upload->current_file_name); |
|
576 } |
|
577 update->upload->current_file_name = update->name; |
|
578 |
|
579 ui_set(update->upload->label_top_right, update->name); |
|
580 |
|
581 DavFileUpload *upload = update->upload; |
|
582 DavBrowser *browser = upload->browser; |
|
583 // update the resource list in the browser, if the current collection has not changed |
|
584 if (upload->collection == browser->current && upload->collection_ctn == browser->res_counter) { |
|
585 char *parent = util_parent_path(update->path); |
|
586 cxstring parent_s = cx_str(parent); |
|
587 cxstring colpath_s = cx_str(upload->collection->path); |
|
588 if (parent_s.length > 0 && parent_s.ptr[parent_s.length - 1] == '/') { |
|
589 parent_s.length--; |
|
590 } |
|
591 if (colpath_s.length > 0 && colpath_s.ptr[colpath_s.length - 1] == '/') { |
|
592 colpath_s.length--; |
|
593 } |
|
594 |
|
595 // only update, if the added resource has the current collection as parent |
|
596 if (!cx_strcmp(parent_s, colpath_s)) { |
|
597 DavResource *ui_res = dav_resource_new(upload->collection->session, update->path); |
|
598 ui_res->iscollection = update->iscollection; |
|
599 ui_res->lastmodified = time(NULL); |
|
600 ui_res->creationdate = time(NULL); |
|
601 upload->current_resource = ui_res; |
|
602 |
|
603 ui_list_append(browser->resources, ui_res); |
|
604 browser->resources->update(browser->resources, 0); |
|
605 } else { |
|
606 upload->current_resource = NULL; // maybe not necessary |
|
607 } |
|
608 free(parent); |
|
609 } |
|
610 |
|
611 free(update->path); |
|
612 free(update); |
|
613 return 0; |
|
614 } |
|
615 |
|
616 static int qthr_file_upload(void *data) { |
|
617 DUFile *f = data; |
|
618 DavFileUpload *upload = f->upload; |
|
619 DavSession *sn = upload->sn; |
|
620 |
|
621 FILE *in = sys_fopen(f->path, "rb"); |
|
622 if (!in) { |
|
623 // TODO: error msg |
|
624 return 0; |
|
625 } |
|
626 |
|
627 upload->upload_file = TRUE; |
|
628 upload->current_file_size = f->bytes; |
|
629 upload->current_file_upload = 0; |
|
630 |
|
631 DavResource *res = dav_resource_new(sn, f->upload_path); |
|
632 |
|
633 FileNameUpdate *ui_update = malloc(sizeof(FileNameUpdate)); |
|
634 ui_update->upload = upload; |
|
635 ui_update->name = strdup(res->name); |
|
636 ui_update->path = strdup(res->path); |
|
637 ui_update->iscollection = FALSE; |
|
638 ui_call_mainthread((ui_threadfunc)uithr_update_file_label, ui_update); |
|
639 |
|
640 dav_set_content(res, in, (dav_read_func)fread, (dav_seek_func)fseek); |
|
641 if (dav_store(res)) { |
|
642 f->error = sn->error; |
|
643 } |
|
644 dav_resource_free(res); |
|
645 |
|
646 fclose(in); |
|
647 |
|
648 upload->upload_file = FALSE; |
|
649 |
|
650 return 0; |
|
651 } |
|
652 |
|
653 static void uithr_file_uploaded(UiEvent *event, void *data) { |
|
654 DUFile *file = data; |
|
655 DavFileUpload *upload = file->upload; |
|
656 |
|
657 upload->uploaded_files++; |
|
658 //upload->uploaded_bytes += file->bytes; |
|
659 |
|
660 double progress = upload_progress(upload); |
|
661 ui_set(upload->progress, upload_progress(upload)); |
|
662 |
|
663 update_upload_labels(upload); |
|
664 |
|
665 // update resource content length in the browser |
|
666 DavBrowser *browser = upload->browser; |
|
667 if (upload->collection == browser->current && upload->collection_ctn == browser->res_counter) { |
|
668 if (upload->current_resource) { |
|
669 upload->current_resource->contentlength = upload->current_file_upload; |
|
670 browser->resources->update(browser->resources, 0); |
|
671 } |
|
672 } |
|
673 upload->current_resource = NULL; |
|
674 |
|
675 free(file->path); |
|
676 free(file->upload_path); |
|
677 } |
|
678 |
|
679 static int qthr_dir_upload(void *data) { |
|
680 DUFile *f = data; |
|
681 DavFileUpload *upload = f->upload; |
|
682 DavSession *sn = upload->sn; |
|
683 |
|
684 DavResource *res = dav_resource_new(sn, f->upload_path); |
|
685 res->iscollection = TRUE; |
|
686 |
|
687 FileNameUpdate *ui_update = malloc(sizeof(FileNameUpdate)); |
|
688 ui_update->upload = upload; |
|
689 ui_update->name = strdup(res->name); |
|
690 ui_update->path = strdup(res->path); |
|
691 ui_update->iscollection = TRUE; |
|
692 ui_call_mainthread((ui_threadfunc)uithr_update_file_label, ui_update); |
|
693 |
|
694 if (dav_create(res)) { |
|
695 f->error = sn->error; |
|
696 } |
|
697 |
|
698 dav_resource_free(res); |
|
699 |
|
700 return 0; |
|
701 } |
|
702 |
|
703 static void uithr_dir_uploaded(UiEvent *event, void *data) { |
|
704 DUFile *file = data; |
|
705 DavFileUpload *upload = file->upload; |
|
706 |
|
707 upload->uploaded_directories++; |
|
708 |
|
709 update_upload_labels(upload); |
|
710 |
|
711 upload->current_resource = NULL; |
|
712 |
|
713 free(file->path); |
|
714 free(file->upload_path); |
|
715 } |
|
716 |
|
717 static int qthr_upload_finished(void *data) { |
|
718 return 0; |
|
719 } |
|
720 |
|
721 static void uithr_upload_finished(UiEvent *event, void *data) { |
|
722 DavFileUpload *upload = data; |
|
723 ui_threadpool_destroy(upload->queue); |
|
724 |
|
725 free(upload->base_path); |
|
726 dav_session_destroy(upload->sn); |
|
727 } |
|
728 |
|
729 static int jobthr_upload_scan(void *data) { |
|
730 DavFileUpload *upload = data; |
|
731 |
|
732 CxList *stack = cxLinkedListCreateSimple(CX_STORE_POINTERS); |
|
733 for (int i = 0; i < upload->files.nfiles; i++) { |
|
734 DUFile *f = malloc(sizeof(DUFile)); |
|
735 f->path = strdup(upload->files.files[i]); |
|
736 f->upload_path = util_concat_path(upload->base_path, util_path_file_name(f->path)); |
|
737 f->isdirectory = FALSE; |
|
738 f->bytes = 0; |
|
739 f->upload = upload; |
|
740 f->error = 0; |
|
741 cxListInsert(stack, 0, f); |
|
742 } |
|
743 |
|
744 while (cxListSize(stack) > 0) { |
|
745 DUFile *f = cxListAt(stack, 0); |
|
746 |
|
747 char *path = util_concat_path(upload->base_path, f->upload_path); |
|
748 cxListRemove(stack, 0); |
|
749 |
|
750 SYS_STAT s; |
|
751 if (!sys_stat(f->path, &s)) { |
|
752 if (S_ISDIR(s.st_mode)) { |
|
753 f->isdirectory = TRUE; |
|
754 upload->total_directories++; |
|
755 ui_threadpool_job(upload->queue, upload->ui, qthr_dir_upload, f, uithr_dir_uploaded, f); |
|
756 |
|
757 SYS_DIR dir = sys_opendir(f->path); |
|
758 if (dir) { |
|
759 SysDirEnt *entry; |
|
760 int nument = 0; |
|
761 while((entry = sys_readdir(dir)) != NULL) { |
|
762 if(!strcmp(entry->name, ".") || !strcmp(entry->name, "..")) { |
|
763 continue; |
|
764 } |
|
765 |
|
766 cxmutstr newpath = util_concat_sys_path(cx_str(f->path), cx_str(entry->name)); |
|
767 char *new_upload_path = util_concat_path(f->upload_path, entry->name); |
|
768 |
|
769 DUFile *child = malloc(sizeof(DUFile)); |
|
770 child->path = newpath.ptr; |
|
771 child->upload_path = new_upload_path; |
|
772 child->isdirectory = FALSE; |
|
773 child->bytes = 0; |
|
774 child->upload = upload; |
|
775 child->error = 0; |
|
776 cxListAdd(stack, child); |
|
777 } |
|
778 |
|
779 sys_closedir(dir); |
|
780 } |
|
781 } else if (S_ISREG(s.st_mode)) { |
|
782 f->isdirectory = FALSE; |
|
783 f->bytes = s.st_size; |
|
784 upload->total_files++; |
|
785 upload->total_bytes += s.st_size; |
|
786 ui_threadpool_job(upload->queue, upload->ui, qthr_file_upload, f, uithr_file_uploaded, f); |
|
787 } |
|
788 } |
|
789 } // TODO: else error msg |
|
790 |
|
791 ui_threadpool_job(upload->queue, upload->ui, qthr_upload_finished, upload, uithr_upload_finished, upload); |
|
792 |
|
793 ui_filelist_free(upload->files); |
|
794 |
|
795 return 0; |
|
796 } |
|
797 |
|
798 static void uithr_upload_scan_finished(UiEvent *event, void *data) { |
|
799 DavFileUpload *upload = data; |
|
800 |
|
801 update_upload_labels(upload); |
|
802 } |
|
803 |
|
804 static void upload_window_closed(UiEvent *event, void *data) { |
|
805 // noop, prevents context destruction |
|
806 } |
|
807 |
|
808 void davbrowser_upload_files(UiObject *ui, DavBrowser *browser, UiFileList files) { |
447 void davbrowser_upload_files(UiObject *ui, DavBrowser *browser, UiFileList files) { |
809 if (!browser->sn) { |
448 if (!browser->sn) { |
810 return; // TODO: error msg |
449 return; // TODO: error msg |
811 } |
450 } |
812 |
451 |
813 // we need a clone of the current session, because the upload |
|
814 // is done in a separate thread |
|
815 DavSession *upload_session = dav_session_clone(browser->sn); |
|
816 |
|
817 // create upload obj, that contains all relevant data for the upload |
|
818 DavFileUpload *upload = malloc(sizeof(DavFileUpload)); |
|
819 memset(upload, 0, sizeof(DavFileUpload)); |
|
820 |
|
821 dav_session_set_progresscallback(upload_session, NULL, upload_dav_progress, upload); |
|
822 |
|
823 upload->ui = ui; |
|
824 upload->browser = browser; |
|
825 upload->sn = upload_session; |
|
826 upload->files = files; |
|
827 upload->base_path = strdup(browser->current->path); |
|
828 upload->queue = ui_threadpool_create(1); |
|
829 |
|
830 upload->collection = browser->current; |
|
831 upload->collection_ctn = browser->res_counter; |
|
832 |
|
833 // create upload progress window |
|
834 cxmutstr wtitle = cx_asprintf("Upload to: %s", ui_get(browser->path)); |
452 cxmutstr wtitle = cx_asprintf("Upload to: %s", ui_get(browser->path)); |
835 UiObject *dialog = ui_simple_window(wtitle.ptr, upload); |
453 UiObject *dialog = ui_simple_window(wtitle.ptr, NULL); |
836 ui_context_closefunc(dialog->ctx, upload_window_closed, NULL); |
|
837 free(wtitle.ptr); |
454 free(wtitle.ptr); |
838 upload->dialog = dialog; |
455 |
839 ui_window_size(dialog, 550, 120); |
456 DavFileUpload *upload = dav_upload_create(browser, dialog, files); |
840 upload->progress = ui_double_new(dialog->ctx, NULL); |
457 transfer_window_init(dialog, action_upload_cancel); |
841 upload->label_top_left = ui_string_new(dialog->ctx, NULL); |
458 dav_upload_start(upload); |
842 upload->label_top_right = ui_string_new(dialog->ctx, NULL); |
459 } |
843 upload->label_bottom_left = ui_string_new(dialog->ctx, NULL); |
|
844 upload->label_bottom_right = ui_string_new(dialog->ctx, NULL); |
|
845 |
|
846 ui_grid(dialog, .margin = 10, .spacing = 10, .fill = TRUE) { |
|
847 ui_llabel(dialog, .value = upload->label_top_left, .hexpand = TRUE); |
|
848 ui_rlabel(dialog, .value = upload->label_top_right); |
|
849 ui_newline(dialog); |
|
850 |
|
851 ui_progressbar(dialog, .value = upload->progress, .colspan = 2, .hexpand = TRUE); |
|
852 ui_newline(dialog); |
|
853 |
|
854 ui_llabel(dialog, .value = upload->label_bottom_left); |
|
855 ui_rlabel(dialog, .value = upload->label_bottom_right); |
|
856 ui_newline(dialog); |
|
857 } |
|
858 |
|
859 ui_set(upload->label_top_left, ""); |
|
860 ui_set(upload->label_top_right, ""); |
|
861 ui_set(upload->label_bottom_left, ""); |
|
862 ui_set(upload->label_bottom_right, ""); |
|
863 ui_set(upload->progress, 0); |
|
864 |
|
865 ui_show(dialog); |
|
866 |
|
867 // start upload and stat threads |
|
868 ui_job(ui, jobthr_upload_scan, upload, uithr_upload_scan_finished, upload); |
|
869 } |
|
870 |
|
871 |
|
872 // ------------------------------------- File Download ------------------------------------- |
|
873 |
|
874 typedef struct DavFileDownload { |
|
875 UiObject *ui; |
|
876 DavBrowser *browser; |
|
877 |
|
878 DavSession *sn; |
|
879 DavSession *download_sn; |
|
880 DavResource *reslist; |
|
881 char *local_path; |
|
882 DavBool isdirectory; |
|
883 |
|
884 UiThreadpool *queue; |
|
885 |
|
886 size_t total_bytes; |
|
887 size_t total_files; |
|
888 size_t total_directories; |
|
889 size_t downloaded_bytes; |
|
890 size_t downloaded_files; |
|
891 size_t downloaded_directories; |
|
892 |
|
893 size_t current_file_size; |
|
894 size_t current_file_downloaded; |
|
895 |
|
896 UiObject *dialog; |
|
897 UiDouble *progress; |
|
898 UiString *label_top_left; |
|
899 UiString *label_top_right; |
|
900 UiString *label_bottom_left; |
|
901 UiString *label_bottom_right; |
|
902 } DavFileDownload; |
|
903 |
|
904 |
|
905 static int uithr_download_update_progress(void *data) { |
|
906 DavFileDownload *download = data; |
|
907 char *sz_total = util_size_str(FALSE, download->total_bytes); |
|
908 char *sz_downloaded = util_size_str2(FALSE, download->downloaded_bytes, download->total_bytes, 2); |
|
909 char *sz_downloaded_end = strchr(sz_downloaded, ' '); |
|
910 if (sz_downloaded_end) { |
|
911 *sz_downloaded_end = 0; |
|
912 } |
|
913 |
|
914 if (download->total_bytes > 0) { |
|
915 double progress = (double)download->downloaded_bytes / (double)download->total_bytes; |
|
916 ui_set(download->progress, progress*100); |
|
917 } |
|
918 |
|
919 |
|
920 cxmutstr label1; |
|
921 if (download->total_files + download->total_directories > 1) { |
|
922 label1 = cx_asprintf( |
|
923 "%s/%s %zu/%zu files", |
|
924 sz_downloaded, |
|
925 sz_total, |
|
926 download->downloaded_files+download->downloaded_directories, |
|
927 download->total_files+download->total_directories); |
|
928 } else { |
|
929 label1 = cx_asprintf( |
|
930 "%s/%s", |
|
931 sz_downloaded, |
|
932 sz_total); |
|
933 } |
|
934 ui_set(download->label_top_left, label1.ptr); |
|
935 |
|
936 free(sz_total); |
|
937 free(label1.ptr); |
|
938 |
|
939 |
|
940 return 1; |
|
941 } |
|
942 |
|
943 // dav download file |
|
944 typedef struct DDFile { |
|
945 DavFileDownload *download; |
|
946 size_t size; |
|
947 char *path; |
|
948 char *to; |
|
949 FILE *fd; |
|
950 } DDFile; |
|
951 |
|
952 static size_t ddfile_write(const void *buf, size_t size, size_t count, void *stream) { |
|
953 DDFile *file = stream; |
|
954 |
|
955 size_t w = fwrite(buf, size, count, file->fd); |
|
956 file->download->current_file_downloaded += w; |
|
957 |
|
958 file->download->downloaded_bytes += w; |
|
959 |
|
960 if (file->download->current_file_downloaded > file->download->current_file_size) { |
|
961 size_t diff = file->download->current_file_downloaded - file->download->current_file_size; |
|
962 file->download->current_file_size = file->download->current_file_downloaded; |
|
963 file->download->total_bytes += diff; |
|
964 } |
|
965 |
|
966 ui_call_mainthread(uithr_download_update_progress, file->download); |
|
967 |
|
968 return w; |
|
969 } |
|
970 |
|
971 static int qthr_download_resource(void *data) { |
|
972 DDFile *file = data; |
|
973 |
|
974 file->download->current_file_downloaded = 0; |
|
975 file->download->current_file_size = file->size; |
|
976 |
|
977 FILE *f = sys_fopen(file->to, "wb"); |
|
978 if (!f) { |
|
979 return 0; |
|
980 } |
|
981 file->fd = f; |
|
982 |
|
983 DavResource *res = dav_resource_new(file->download->download_sn, file->path); |
|
984 dav_get_content(res, file, (dav_write_func)ddfile_write); |
|
985 |
|
986 file->download->downloaded_files++; |
|
987 |
|
988 ui_call_mainthread(uithr_download_update_progress, file->download); |
|
989 |
|
990 dav_resource_free(res); |
|
991 |
|
992 fclose(f); |
|
993 |
|
994 free(file->path); |
|
995 free(file->to); |
|
996 free(file); |
|
997 |
|
998 return 0; |
|
999 } |
|
1000 |
|
1001 static int qthr_download_finished(void *data) { |
|
1002 return 0; |
|
1003 } |
|
1004 |
|
1005 static void uithr_download_finished(UiEvent *event, void *data) { |
|
1006 DavFileDownload *download = data; |
|
1007 printf("download finished\n"); |
|
1008 ui_object_unref(download->dialog); |
|
1009 } |
|
1010 |
|
1011 |
|
1012 typedef struct DlStackElm { |
|
1013 DavResource *resource; |
|
1014 char *sub_path; |
|
1015 } DlStackElm; |
|
1016 |
|
1017 static int jobthr_download_scan(void *data) { |
|
1018 DavFileDownload *download = data; |
|
1019 DavBrowser *browser = download->browser; |
|
1020 |
|
1021 // check if the specified local location is a directory |
|
1022 SYS_STAT s; |
|
1023 if (!sys_stat(download->local_path, &s)) { |
|
1024 if (S_ISDIR(s.st_mode)) { |
|
1025 download->isdirectory = TRUE; |
|
1026 } |
|
1027 } |
|
1028 |
|
1029 CxList *stack = cxLinkedListCreateSimple(sizeof(DlStackElm)); |
|
1030 |
|
1031 // add selected files to the download queue |
|
1032 DavResource *res = download->reslist; |
|
1033 while (res) { |
|
1034 DlStackElm elm; |
|
1035 elm.resource = res; |
|
1036 elm.sub_path = strdup(res->name); |
|
1037 cxListAdd(stack, &elm); |
|
1038 |
|
1039 res = res->next; |
|
1040 } |
|
1041 |
|
1042 while (cxListSize(stack) > 0) { |
|
1043 DlStackElm *elm = cxListAt(stack, 0); |
|
1044 DavResource *res = elm->resource; |
|
1045 char *sub_path = elm->sub_path; |
|
1046 cxListRemove(stack, 0); |
|
1047 |
|
1048 if (res->iscollection) { |
|
1049 if (dav_load(res)) { |
|
1050 // TODO: handle error |
|
1051 continue; |
|
1052 } |
|
1053 |
|
1054 // update ui |
|
1055 ui_call_mainthread(uithr_download_update_progress, download); |
|
1056 |
|
1057 char *path = util_concat_path(download->local_path, sub_path); |
|
1058 int err = sys_mkdir(path); |
|
1059 free(path); |
|
1060 if (err) { |
|
1061 // TODO: handle error |
|
1062 } |
|
1063 |
|
1064 DavResource *child = res->children; |
|
1065 while (child) { |
|
1066 char *child_path = util_concat_path(sub_path, child->name); |
|
1067 DlStackElm childelm; |
|
1068 childelm.resource = child; |
|
1069 childelm.sub_path = child_path; |
|
1070 cxListAdd(stack, &childelm); |
|
1071 |
|
1072 child = child->next; |
|
1073 } |
|
1074 } else { |
|
1075 // add the file to the download queue |
|
1076 DDFile *file = malloc(sizeof(DDFile)); |
|
1077 file->download = download; |
|
1078 file->path = strdup(res->path); |
|
1079 file->size = res->contentlength; |
|
1080 if (download->isdirectory) { |
|
1081 file->to = util_concat_path(download->local_path, sub_path); |
|
1082 } else { |
|
1083 file->to = strdup(download->local_path); |
|
1084 } |
|
1085 |
|
1086 // stats |
|
1087 download->total_files++; |
|
1088 download->total_bytes += res->contentlength; |
|
1089 |
|
1090 // update ui |
|
1091 ui_call_mainthread(uithr_download_update_progress, download); |
|
1092 |
|
1093 ui_threadpool_job(download->queue, download->ui, qthr_download_resource, file, NULL, NULL); |
|
1094 } |
|
1095 } |
|
1096 |
|
1097 ui_threadpool_job(download->queue, download->ui, qthr_download_finished, download, uithr_download_finished, download); |
|
1098 |
|
1099 cxListDestroy(stack); |
|
1100 |
|
1101 return 0; |
|
1102 } |
|
1103 |
|
1104 static void uithr_download_scan_finished(UiEvent *event, void *data) { |
|
1105 DavFileDownload *download = data; |
|
1106 |
|
1107 } |
|
1108 |
|
1109 static void download_window_closed(UiEvent *event, void *data) { |
|
1110 |
|
1111 } |
|
1112 |
|
1113 |
460 |
1114 void davbrowser_download(UiObject *ui, DavBrowser *browser, DavResource *reslist, const char *local_path) { |
461 void davbrowser_download(UiObject *ui, DavBrowser *browser, DavResource *reslist, const char *local_path) { |
1115 DavFileDownload *download = malloc(sizeof(DavFileDownload)); |
|
1116 memset(download, 0, sizeof(DavFileDownload)); |
|
1117 |
|
1118 download->ui = ui; |
|
1119 download->browser = browser; |
|
1120 download->sn = reslist->session; |
|
1121 download->download_sn = dav_session_clone(download->sn); |
|
1122 download->reslist = reslist; // TODO: is this safe or do we need a copy? |
|
1123 download->local_path = strdup(local_path); |
|
1124 |
|
1125 download->queue = ui_threadpool_create(1); |
|
1126 |
|
1127 // create download progress window |
|
1128 cxmutstr wtitle = cx_asprintf("Download to: %s", local_path); |
462 cxmutstr wtitle = cx_asprintf("Download to: %s", local_path); |
1129 UiObject *dialog = ui_simple_window(wtitle.ptr, download); |
463 UiObject *dialog = ui_simple_window(wtitle.ptr, NULL); |
1130 ui_context_closefunc(dialog->ctx, download_window_closed, NULL); |
|
1131 free(wtitle.ptr); |
464 free(wtitle.ptr); |
1132 download->dialog = dialog; |
465 |
1133 ui_window_size(dialog, 550, 120); |
466 DavFileDownload *download = dav_download_create(browser, dialog, reslist, local_path); |
1134 download->progress = ui_double_new(dialog->ctx, NULL); |
467 transfer_window_init(dialog, action_download_cancel); |
1135 download->label_top_left = ui_string_new(dialog->ctx, NULL); |
468 dav_download_start(download); |
1136 download->label_top_right = ui_string_new(dialog->ctx, NULL); |
|
1137 download->label_bottom_left = ui_string_new(dialog->ctx, NULL); |
|
1138 download->label_bottom_right = ui_string_new(dialog->ctx, NULL); |
|
1139 |
|
1140 ui_grid(dialog, .margin = 10, .spacing = 10, .fill = TRUE) { |
|
1141 ui_llabel(dialog, .value = download->label_top_left, .hexpand = TRUE); |
|
1142 ui_rlabel(dialog, .value = download->label_top_right); |
|
1143 ui_newline(dialog); |
|
1144 |
|
1145 ui_progressbar(dialog, .value = download->progress, .min = 0, .max = 100, .colspan = 2, .hexpand = TRUE); |
|
1146 ui_newline(dialog); |
|
1147 |
|
1148 ui_llabel(dialog, .value = download->label_bottom_left); |
|
1149 ui_rlabel(dialog, .value = download->label_bottom_right); |
|
1150 ui_newline(dialog); |
|
1151 } |
|
1152 |
|
1153 ui_set(download->label_top_left, ""); |
|
1154 ui_set(download->label_top_right, ""); |
|
1155 ui_set(download->label_bottom_left, ""); |
|
1156 ui_set(download->label_bottom_right, ""); |
|
1157 ui_set(download->progress, 0); |
|
1158 |
|
1159 ui_show(dialog); |
|
1160 |
|
1161 ui_object_ref(dialog); |
|
1162 |
|
1163 // start upload and stat threads |
|
1164 ui_job(ui, jobthr_download_scan, download, uithr_download_scan_finished, download); |
|
1165 } |
469 } |
1166 |
470 |
1167 |
471 |
1168 // ------------------------------------- Path Operation (DELETE, MKCOL) ------------------------------------- |
472 // ------------------------------------- Path Operation (DELETE, MKCOL) ------------------------------------- |
1169 |
473 |