569 |
573 |
570 char *status = dav_get_string_property(res, "idav:status"); |
574 char *status = dav_get_string_property(res, "idav:status"); |
571 if(status && !strcmp(status, "broken")) { |
575 if(status && !strcmp(status, "broken")) { |
572 res = res->next; |
576 res = res->next; |
573 localres_keep(db, res->path); |
577 localres_keep(db, res->path); |
574 ls_broken = ucx_list_append(ls_broken, res); |
578 res_broken = ucx_list_append(res_broken, res); |
575 continue; |
579 continue; |
576 } |
580 } |
577 |
581 |
578 // download the resource |
582 // check if a resource has changed on the server |
579 if(!sync_shutdown && sync_get_resource(a, dir, res, db, FALSE, &sync_success)) { |
583 int change = resource_get_remote_change(a, res, dir, db); |
580 fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path); |
584 switch(change) { |
581 sync_error++; |
585 case REMOTE_NO_CHANGE: break; |
|
586 case REMOTE_CHANGE_MODIFIED: { |
|
587 res_modified = ucx_list_append(res_modified, res); |
|
588 break; |
|
589 } |
|
590 case REMOTE_CHANGE_NEW: { |
|
591 res_new = ucx_list_append(res_new, res); |
|
592 break; |
|
593 } |
|
594 case REMOTE_CHANGE_DELETED: break; // never happens |
|
595 case REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED: { |
|
596 res_conflict = ucx_list_append(res_conflict, res); |
|
597 break; |
|
598 } |
|
599 case REMOTE_CHANGE_METADATA: { |
|
600 res_metadata = ucx_list_append(res_metadata, res); |
|
601 break; |
|
602 } |
|
603 case REMOTE_CHANGE_MKDIR: { |
|
604 res_mkdir = ucx_list_append(res_mkdir, res); |
|
605 break; |
|
606 } |
582 } |
607 } |
583 |
608 |
584 // add every resource from the server to svrres |
609 // remove every server resource from dbres |
585 // then db-resources contains only resources which are not on the |
610 // all remaining elements are the resources that are removed |
586 // server |
611 // on the server |
587 LocalResource *local = ucx_map_cstr_remove(db->resources, res->path); |
612 ucx_map_cstr_remove(dbres, res->path); |
588 if(local) { |
|
589 ucx_map_cstr_put(svrres, res->path, local); |
|
590 |
|
591 if(local->last_modified == 0) { |
|
592 // stat this file later (directory) |
|
593 statls = ucx_list_prepend(statls, local); |
|
594 } |
|
595 } // else: sync_shutdown is TRUE |
|
596 |
613 |
597 if(res->children) { |
614 if(res->children) { |
598 stack = ucx_list_prepend(stack, res->children); |
615 stack = ucx_list_prepend(stack, res->children); |
599 } |
616 } |
600 res = res->next; |
617 res = res->next; |
601 } |
618 } |
602 } |
619 } |
603 |
620 |
604 // stat all files with unknown lastmodified date |
621 // find deleted resources |
605 UCX_FOREACH(elm, statls) { |
622 // svrres currently contains all resources from the server |
606 LocalResource *l = elm->data; |
623 // and will replace the current db->resources map later |
607 char *local_path = util_concat_path(dir->path, l->path); |
624 UcxMapIterator i = ucx_map_iterator(dbres); |
608 SYS_STAT s; |
|
609 if(!sys_stat(local_path, &s)) { |
|
610 l->last_modified = s.st_mtime; |
|
611 } |
|
612 free(local_path); |
|
613 } |
|
614 ucx_list_free(statls); |
|
615 |
|
616 // delete every remotely removed resource |
|
617 UcxMapIterator i = ucx_map_iterator(db->resources); |
|
618 LocalResource *local; |
625 LocalResource *local; |
619 UcxList *rmdirs = NULL; |
|
620 UCX_MAP_FOREACH(key, local, i) { |
626 UCX_MAP_FOREACH(key, local, i) { |
621 if (res_matches_filter(dir, local->path)) { |
627 if (res_matches_filter(dir, local->path)) { |
622 continue; |
628 continue; |
623 } |
629 } |
624 |
630 if(!local->keep) { |
625 if(sync_shutdown || local->keep) { |
631 lres_removed = ucx_list_prepend(lres_removed, local); |
626 ucx_map_cstr_put(svrres, local->path, local_resource_copy(local)); |
632 } |
|
633 } |
|
634 |
|
635 // the first thing we need are all directories to put the files in |
|
636 UCX_FOREACH(elm, res_mkdir) { |
|
637 DavResource *res = elm->data; |
|
638 char *local_path = util_concat_path(dir->path, res->path); |
|
639 if(sys_mkdir(local_path) && errno != EEXIST) { |
|
640 fprintf(stderr, |
|
641 "Cannot create directory %s: %s", |
|
642 local_path, strerror(errno)); |
|
643 } |
|
644 free(local_path); |
|
645 } |
|
646 |
|
647 // we need a map for all conflicts for fast lookups |
|
648 UcxMap *conflicts = ucx_map_new(ucx_list_size(res_conflict)+16); |
|
649 UCX_FOREACH(elm, res_conflict) { |
|
650 DavResource *res = elm->data; |
|
651 ucx_map_cstr_put(conflicts, res->path, res); |
|
652 } |
|
653 |
|
654 // download all new, modified and conflict files |
|
655 UcxList *download = ucx_list_concat(res_modified, res_conflict); |
|
656 download = ucx_list_concat(res_new, download); |
|
657 UCX_FOREACH(elm, download) { |
|
658 DavResource *res = elm->data; |
|
659 if(sync_shutdown) { |
|
660 break; |
|
661 } |
|
662 |
|
663 if(ucx_map_cstr_get(conflicts, res->path)) { |
|
664 rename_conflict_file(dir, db, res->path); |
|
665 } |
|
666 |
|
667 // download the resource |
|
668 if(sync_get_resource(a, dir, res, db, &sync_success)) { |
|
669 fprintf(stderr, "resource download failed: %s\n", res->path); |
|
670 sync_error++; |
|
671 } |
|
672 } |
|
673 |
|
674 UCX_FOREACH(elm, res_metadata) { |
|
675 DavResource *res = elm->data; |
|
676 if(sync_shutdown) { |
|
677 break; |
|
678 } |
|
679 |
|
680 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
|
681 if(local) { |
|
682 char *local_path = util_concat_path(dir->path, res->path); |
|
683 if(sync_store_tags(dir, local_path, local, res)) { |
|
684 fprintf(stderr, "Tag update failed: %s\n", res->path); |
|
685 } |
|
686 free(local_path); |
627 } else { |
687 } else { |
628 // sync_remove_resource does all necessary tests |
688 // this should never happen but who knows |
629 int ret = sync_remove_local_resource(dir, local); |
689 fprintf(stderr, |
630 if(ret == -1) { |
690 "Cannot update metadata of file %s: not in database\n", |
631 rmdirs = ucx_list_append(rmdirs, local); |
691 res->path); |
632 } else if(ret == 0) { |
692 } |
633 sync_delete++; |
693 } |
634 } |
694 |
635 } |
695 UcxList *rmdirs = NULL; |
636 } |
696 UCX_FOREACH(elm, lres_removed) { |
|
697 LocalResource *res = elm->data; |
|
698 if(sync_shutdown) { |
|
699 break; |
|
700 } |
|
701 |
|
702 int ret = sync_remove_local_resource(dir, res); |
|
703 if(ret == -1) { |
|
704 rmdirs = ucx_list_append(rmdirs, res); |
|
705 } else if(ret == 0) { |
|
706 LocalResource *local = ucx_map_cstr_remove(db->resources, res->path); |
|
707 if(local) { |
|
708 local_resource_free(local); |
|
709 } |
|
710 sync_delete++; |
|
711 } |
|
712 } |
|
713 |
637 UCX_FOREACH(elm, rmdirs) { |
714 UCX_FOREACH(elm, rmdirs) { |
638 LocalResource *local_dir = elm->data; |
715 LocalResource *local_dir = elm->data; |
639 sync_remove_local_directory(dir, local_dir); |
716 if(!sync_remove_local_directory(dir, local_dir)) { |
640 } |
717 LocalResource *local = ucx_map_cstr_remove(db->resources, local_dir->path); |
641 ucx_map_free_content(db->resources, (ucx_destructor)local_resource_free); |
718 if(local) { |
642 ucx_map_free(db->resources); |
719 local_resource_free(local); |
643 db->resources = svrres; |
720 } |
|
721 sync_delete++; |
|
722 } |
|
723 } |
644 |
724 |
645 // unlock repository |
725 // unlock repository |
646 if(locked) { |
726 if(locked) { |
647 if(dav_unlock(root)) { |
727 if(dav_unlock(root)) { |
648 print_resource_error(sn, "/"); |
728 print_resource_error(sn, "/"); |
677 } |
757 } |
678 |
758 |
679 return ret; |
759 return ret; |
680 } |
760 } |
681 |
761 |
682 int sync_remote_is_changed( |
762 |
|
763 RemoteChangeType resource_get_remote_change( |
683 CmdArgs *a, |
764 CmdArgs *a, |
|
765 DavResource *res, |
684 SyncDirectory *dir, |
766 SyncDirectory *dir, |
685 DavResource *res, |
767 SyncDatabase *db) |
686 SyncDatabase *db, |
|
687 DavBool force, |
|
688 DavBool *conflict, |
|
689 DavBool *metadataupdate) |
|
690 { |
768 { |
691 int cdt = cmd_getoption(a, "conflict") ? 0 : 1; // conflict detection |
769 char *etag = dav_get_string_property(res, "D:getetag"); |
692 *conflict = 0; |
770 if(!etag) { |
693 *metadataupdate = 0; |
771 fprintf(stderr, "Error: resource %s has no etag\n", res->path); |
|
772 return REMOTE_NO_CHANGE; |
|
773 } |
|
774 |
|
775 RemoteChangeType type = cmd_getoption(a, "conflict") ? |
|
776 REMOTE_CHANGE_MODIFIED : REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED; |
694 |
777 |
695 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
778 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
696 char *local_path = util_concat_path(dir->path, res->path); |
779 char *local_path = util_concat_path(dir->path, res->path); |
697 |
780 |
698 char *etag = dav_get_string_property(res, "D:getetag"); |
|
699 SYS_STAT s; |
781 SYS_STAT s; |
700 int ret = 0; |
782 DavBool exists = 1; |
701 |
783 if(sys_stat(local_path, &s)) { |
|
784 if(errno != ENOENT) { |
|
785 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
|
786 free(local_path); |
|
787 return REMOTE_NO_CHANGE; |
|
788 } |
|
789 exists = 0; |
|
790 } |
|
791 |
|
792 RemoteChangeType ret = REMOTE_NO_CHANGE; |
|
793 if(res->iscollection) { |
|
794 if(!exists) { |
|
795 ret = REMOTE_CHANGE_MKDIR; |
|
796 } |
|
797 } else if(local) { |
|
798 DavBool nochange = FALSE; |
|
799 if(local->etag) { |
|
800 sstr_t e = sstr(etag); |
|
801 if(sstrprefix(e, S("W/"))) { |
|
802 e = sstrsubs(e, 2); |
|
803 } |
|
804 if(!strcmp(e.ptr, local->etag)) { |
|
805 // resource is already up-to-date on the client |
|
806 |
|
807 // TODO: detect metadata update |
|
808 //sync_store_tags(dir, local_path, local, res); |
|
809 nochange = TRUE; |
|
810 } |
|
811 } |
|
812 |
|
813 if(!nochange) { |
|
814 if(!(exists && s.st_mtime != local->last_modified)) { |
|
815 type = REMOTE_CHANGE_MODIFIED; |
|
816 } |
|
817 ret = type; |
|
818 } |
|
819 } else if(exists) { |
|
820 ret = type; |
|
821 } else { |
|
822 ret = REMOTE_CHANGE_NEW; |
|
823 } |
702 |
824 |
703 free(local_path); |
825 free(local_path); |
704 return ret; |
826 return ret; |
705 } |
827 } |
706 |
828 |
707 int sync_get_resource( |
829 int sync_get_resource( |
708 CmdArgs *a, |
830 CmdArgs *a, |
709 SyncDirectory *dir, |
831 SyncDirectory *dir, |
710 DavResource *res, |
832 DavResource *res, |
711 SyncDatabase *db, |
833 SyncDatabase *db, |
712 DavBool force, |
|
713 int *counter) |
834 int *counter) |
714 { |
835 { |
715 int cdt = cmd_getoption(a, "conflict") ? 0 : 1; // conflict detection |
|
716 |
|
717 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
836 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
718 char *local_path = util_concat_path(dir->path, res->path); |
837 char *local_path = util_concat_path(dir->path, res->path); |
719 |
838 |
720 char *etag = dav_get_string_property(res, "D:getetag"); |
839 char *etag = dav_get_string_property(res, "D:getetag"); |
721 SYS_STAT s; |
840 SYS_STAT s; |
722 memset(&s, 0, sizeof(SYS_STAT)); |
841 memset(&s, 0, sizeof(SYS_STAT)); |
723 if(!force) { |
|
724 if(local && !res->iscollection) { |
|
725 int exists = 1; |
|
726 if(sys_stat(local_path, &s)) { |
|
727 // Ignore the fact, that the file is locally removed. If the |
|
728 // server has an updated version, we read the file or the |
|
729 // next push will delete it on the server. |
|
730 if(errno != ENOENT) { |
|
731 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
|
732 free(local_path); |
|
733 return -1; |
|
734 } else { |
|
735 exists = 0; |
|
736 } |
|
737 } |
|
738 |
|
739 if(local->etag) { |
|
740 sstr_t e = sstr(etag); |
|
741 if(sstrprefix(e, S("W/"))) { |
|
742 e = sstrsubs(e, 2); |
|
743 } |
|
744 if(!strcmp(e.ptr, local->etag)) { |
|
745 // resource is already up-to-date on the client |
|
746 sync_store_tags(dir, local_path, local, res); |
|
747 free(local_path); |
|
748 return 0; |
|
749 } |
|
750 } |
|
751 |
|
752 if(cdt && exists && s.st_mtime != local->last_modified) { |
|
753 // file modified on the server and on the client |
|
754 rename_conflict_file(dir, db, local->path); |
|
755 } |
|
756 } else { |
|
757 if(sys_stat(local_path, &s)) { |
|
758 if(errno != ENOENT) { |
|
759 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
|
760 } |
|
761 } else if(S_ISDIR(s.st_mode)) { |
|
762 //fprintf(stderr, "Error: file %s is a directory\n", local_path); |
|
763 } else if(cdt) { |
|
764 // rename file on conflict |
|
765 rename_conflict_file(dir, db, res->path); |
|
766 } |
|
767 } |
|
768 } |
|
769 |
842 |
770 int ret = 0; |
843 int ret = 0; |
771 char *tmp_path = create_tmp_download_path(local_path); |
844 char *tmp_path = create_tmp_download_path(local_path); |
772 if(res->iscollection) { |
845 |
773 if(sys_mkdir(local_path) && errno != EEXIST) { |
846 if(!tmp_path) { |
774 ret = -1; |
847 fprintf(stderr, "Cannot create tmp path for %s\n", local_path); |
775 } |
848 free(local_path); |
776 |
849 return -1; |
777 if(ret == 0) { |
850 } |
778 if(!local) { |
851 FILE *out = sys_fopen(tmp_path, "wb"); |
779 // new local resource |
852 if(!out) { |
780 local = calloc(1, sizeof(LocalResource)); |
853 fprintf(stderr, "Cannot open output file: %s\n", local_path); |
781 local->path = util_concat_path(res->path, "/"); |
854 free(local_path); |
782 local->last_modified = 0; |
855 free(tmp_path); |
783 if(local->etag) { |
856 return -1; |
784 free(local->etag); |
857 } |
785 } |
858 printf("get: %s\n", res->path); |
786 local->etag = strdup(etag); |
859 if(dav_get_content(res, out, (dav_write_func)fwrite)) { |
787 ucx_map_cstr_put(db->resources, local->path, local); |
860 ret = -1; |
788 } |
861 } |
789 |
862 fclose(out); |
790 sync_store_tags(dir, local_path, local, res); |
863 |
791 } |
864 if(ret == 0) { |
792 } else { |
865 (*counter)++; |
793 if(!tmp_path) { |
866 |
794 fprintf(stderr, "Cannot create tmp path for %s\n", local_path); |
867 if(dir->trash && dir->backuppull) { |
|
868 move_to_trash(dir, local_path); |
|
869 } |
|
870 if(sys_rename(tmp_path, local_path)) { |
|
871 fprintf( |
|
872 stderr, |
|
873 "Cannot rename file %s to %s\n", |
|
874 tmp_path, |
|
875 local_path); |
|
876 perror(""); |
|
877 free(tmp_path); |
795 free(local_path); |
878 free(local_path); |
796 return -1; |
879 return -1; |
797 } |
880 } |
798 FILE *out = sys_fopen(tmp_path, "wb"); |
881 |
799 if(!out) { |
882 if(sys_stat(local_path, &s)) { |
800 fprintf(stderr, "Cannot open output file: %s\n", local_path); |
883 fprintf(stderr, |
801 free(local_path); |
884 "Cannot stat file %s: %s\n", local_path, strerror(errno)); |
802 free(tmp_path); |
885 } |
803 return -1; |
886 |
804 } |
887 if(!local) { |
805 printf("get: %s\n", res->path); |
888 // new local resource |
806 if(dav_get_content(res, out, (dav_write_func)fwrite)) { |
889 local = calloc(1, sizeof(LocalResource)); |
807 ret = -1; |
890 local->path = strdup(res->path); |
808 } |
891 ucx_map_cstr_put(db->resources, local->path, local); |
809 fclose(out); |
892 } |
|
893 |
|
894 if(local->etag) { |
|
895 free(local->etag); |
|
896 } |
|
897 // set metadata from stat |
|
898 local->etag = strdup(etag); |
|
899 local->last_modified = s.st_mtime; |
|
900 local->size = s.st_size; |
|
901 local->skipped = FALSE; |
810 |
902 |
811 sync_store_tags(dir, tmp_path, local, res); |
903 sync_store_tags(dir, tmp_path, local, res); |
812 |
904 } else { |
813 if(ret == 0) { |
905 if(sys_unlink(tmp_path)) { |
814 (*counter)++; |
906 fprintf(stderr, "Cannot remove tmp file: %s\n", tmp_path); |
815 |
|
816 if(dir->trash && dir->backuppull) { |
|
817 move_to_trash(dir, local_path); |
|
818 } |
|
819 if(sys_rename(tmp_path, local_path)) { |
|
820 fprintf( |
|
821 stderr, |
|
822 "Cannot rename file %s to %s\n", |
|
823 tmp_path, |
|
824 local_path); |
|
825 perror(""); |
|
826 free(tmp_path); |
|
827 free(local_path); |
|
828 return -1; |
|
829 } |
|
830 |
|
831 if(sys_stat(local_path, &s)) { |
|
832 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
|
833 perror(""); |
|
834 } |
|
835 |
|
836 if(!local) { |
|
837 // new local resource |
|
838 local = calloc(1, sizeof(LocalResource)); |
|
839 local->path = strdup(res->path); |
|
840 ucx_map_cstr_put(db->resources, local->path, local); |
|
841 } |
|
842 |
|
843 if(local->etag) { |
|
844 free(local->etag); |
|
845 } |
|
846 // set metadata from stat |
|
847 local->etag = strdup(etag); |
|
848 local->last_modified = s.st_mtime; |
|
849 local->size = s.st_size; |
|
850 local->skipped = FALSE; |
|
851 } else { |
|
852 if(sys_unlink(tmp_path)) { |
|
853 fprintf(stderr, "Cannot remove tmp file: %s\n", tmp_path); |
|
854 } |
|
855 } |
907 } |
856 } |
908 } |
857 |
909 |
858 free(tmp_path); |
910 free(tmp_path); |
859 free(local_path); |
911 free(local_path); |