69 va_list ap; |
69 va_list ap; |
70 va_start(ap, msg); |
70 va_start(ap, msg); |
71 vfprintf(stderr, msg, ap); |
71 vfprintf(stderr, msg, ap); |
72 va_end(ap); |
72 va_end(ap); |
73 } |
73 } |
|
74 |
|
75 static DavPropName defprops[] = { |
|
76 { "DAV:", "getetag" }, |
|
77 { DAV_NS, "status" }, |
|
78 { DAV_NS, "finfo" }, |
|
79 { DAV_NS, "tags" }, |
|
80 { DAV_NS, "xattributes" } |
|
81 }; |
|
82 static size_t numdefprops = 5; |
74 |
83 |
75 /* |
84 /* |
76 * strcmp version that works with NULL pointers |
85 * strcmp version that works with NULL pointers |
77 */ |
86 */ |
78 static int nullstrcmp(const char *s1, const char *s2) { |
87 static int nullstrcmp(const char *s1, const char *s2) { |
192 void print_usage(char *cmd) { |
201 void print_usage(char *cmd) { |
193 fprintf(stderr, "Usage: %s command [options] arguments...\n\n", cmd); |
202 fprintf(stderr, "Usage: %s command [options] arguments...\n\n", cmd); |
194 |
203 |
195 fprintf(stderr, "Commands:\n"); |
204 fprintf(stderr, "Commands:\n"); |
196 fprintf(stderr, " pull [-cldr] [-t <tags>] <directory>\n"); |
205 fprintf(stderr, " pull [-cldr] [-t <tags>] <directory>\n"); |
197 fprintf(stderr, " push [-cldrVRM] [-t <tags>] <directory>\n"); |
206 fprintf(stderr, " push [-cldrSRM] [-t <tags>] <directory>\n"); |
198 fprintf(stderr, " archive [-cldVRM] [-t <tags>] <directory>\n"); |
207 fprintf(stderr, " archive [-cldSRM] [-t <tags>] <directory>\n"); |
199 fprintf(stderr, " restore [-ldRM] [-s <directory>] [file...]\n"); |
208 fprintf(stderr, |
|
209 " restore [-ldRM] [-V <version>] [-s <directory>] [file...]\n"); |
200 fprintf(stderr, " resolve-conflicts <directory>\n"); |
210 fprintf(stderr, " resolve-conflicts <directory>\n"); |
201 fprintf(stderr, " delete-conflicts <directory>\n"); |
211 fprintf(stderr, " delete-conflicts <directory>\n"); |
202 fprintf(stderr, " trash-info <directory>\n"); |
212 fprintf(stderr, " trash-info <directory>\n"); |
203 fprintf(stderr, " empty-trash <directory>\n"); |
213 fprintf(stderr, " empty-trash <directory>\n"); |
204 fprintf(stderr, " add-tag [-s <syncdir>] <file> <tag>\n"); |
214 fprintf(stderr, " add-tag [-s <syncdir>] <file> <tag>\n"); |
212 fprintf(stderr, " -d Don't lock the repository\n"); |
222 fprintf(stderr, " -d Don't lock the repository\n"); |
213 fprintf(stderr, " -t <tags> " |
223 fprintf(stderr, " -t <tags> " |
214 "Only sync files which have the specified tags\n"); |
224 "Only sync files which have the specified tags\n"); |
215 fprintf(stderr, " -r " |
225 fprintf(stderr, " -r " |
216 "Remove resources not matching the tag filter\n"); |
226 "Remove resources not matching the tag filter\n"); |
217 fprintf(stderr, " -V Enable versioning\n"); |
227 fprintf(stderr, " -V <vers> Restore specific version\n"); |
|
228 fprintf(stderr, " -S Save previous file version\n"); |
218 fprintf(stderr, " -R Restore removed files\n"); |
229 fprintf(stderr, " -R Restore removed files\n"); |
219 fprintf(stderr, " -M Restore modified files\n"); |
230 fprintf(stderr, " -M Restore modified files\n"); |
220 fprintf(stderr, " -v Verbose output (all commands)\n\n"); |
231 fprintf(stderr, " -v Verbose output (all commands)\n\n"); |
221 |
232 |
222 fprintf(stderr, "Config commands:\n"); |
233 fprintf(stderr, "Config commands:\n"); |
682 if(ucx_map_cstr_get(conflicts, res->path)) { |
693 if(ucx_map_cstr_get(conflicts, res->path)) { |
683 rename_conflict_file(dir, db, res->path); |
694 rename_conflict_file(dir, db, res->path); |
684 } |
695 } |
685 |
696 |
686 // download the resource |
697 // download the resource |
687 if(sync_get_resource(a, dir, res, db, &sync_success)) { |
698 if(sync_get_resource(a, dir, res->path, res, db, &sync_success)) { |
688 fprintf(stderr, "resource download failed: %s\n", res->path); |
699 fprintf(stderr, "resource download failed: %s\n", res->path); |
689 sync_error++; |
700 sync_error++; |
690 } |
701 } |
691 } |
702 } |
692 |
703 |
906 } |
917 } |
907 |
918 |
908 int sync_get_resource( |
919 int sync_get_resource( |
909 CmdArgs *a, |
920 CmdArgs *a, |
910 SyncDirectory *dir, |
921 SyncDirectory *dir, |
|
922 const char *path, |
911 DavResource *res, |
923 DavResource *res, |
912 SyncDatabase *db, |
924 SyncDatabase *db, |
913 int *counter) |
925 int *counter) |
914 { |
926 { |
915 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
927 LocalResource *local = ucx_map_cstr_get(db->resources, path); |
916 char *local_path = util_concat_path(dir->path, res->path); |
928 char *local_path = util_concat_path(dir->path, path); |
917 |
929 |
918 char *etag = dav_get_string_property(res, "D:getetag"); |
930 char *etag = dav_get_string_property(res, "D:getetag"); |
919 SYS_STAT s; |
931 SYS_STAT s; |
920 memset(&s, 0, sizeof(SYS_STAT)); |
932 memset(&s, 0, sizeof(SYS_STAT)); |
921 |
933 |
932 fprintf(stderr, "Cannot open output file: %s\n", local_path); |
944 fprintf(stderr, "Cannot open output file: %s\n", local_path); |
933 free(local_path); |
945 free(local_path); |
934 free(tmp_path); |
946 free(tmp_path); |
935 return -1; |
947 return -1; |
936 } |
948 } |
937 printf("get: %s\n", res->path); |
949 printf("get: %s\n", path); |
938 if(dav_get_content(res, out, (dav_write_func)fwrite)) { |
950 if(dav_get_content(res, out, (dav_write_func)fwrite)) { |
939 ret = -1; |
951 ret = -1; |
940 } |
952 } |
941 fclose(out); |
953 fclose(out); |
942 |
954 |
943 if(ret == 0) { |
955 if(ret == 0) { |
944 (*counter)++; |
956 (*counter)++; |
945 |
957 |
946 if(sync_store_metadata(dir, tmp_path, local, res)) { |
958 if(sync_store_metadata(dir, tmp_path, local, res)) { |
947 fprintf(stderr, "Cannot store metadata: %s\n", res->path); |
959 fprintf(stderr, "Cannot store metadata: %s\n", path); |
948 } |
960 } |
949 |
961 |
950 if(dir->trash && dir->backuppull) { |
962 if(dir->trash && dir->backuppull) { |
951 move_to_trash(dir, local_path); |
963 move_to_trash(dir, local_path); |
952 } |
964 } |
968 } |
980 } |
969 |
981 |
970 if(!local) { |
982 if(!local) { |
971 // new local resource |
983 // new local resource |
972 local = calloc(1, sizeof(LocalResource)); |
984 local = calloc(1, sizeof(LocalResource)); |
973 local->path = strdup(res->path); |
985 local->path = strdup(path); |
974 ucx_map_cstr_put(db->resources, local->path, local); |
986 ucx_map_cstr_put(db->resources, local->path, local); |
975 } |
987 } |
976 |
988 |
977 if(local->etag) { |
989 if(local->etag) { |
978 free(local->etag); |
990 free(local->etag); |
1175 return -1; |
1187 return -1; |
1176 } |
1188 } |
1177 if(scfg_check_dir(dir)) { |
1189 if(scfg_check_dir(dir)) { |
1178 return -1; |
1190 return -1; |
1179 } |
1191 } |
1180 if(cmd_getoption(a, "versioning")) { |
1192 if(cmd_getoption(a, "snapshot")) { |
1181 if(dir->versioning) { |
1193 if(dir->versioning) { |
1182 dir->versioning->always = TRUE; |
1194 dir->versioning->always = TRUE; |
1183 } else { |
1195 } else { |
1184 fprintf(stderr, "Error: versioning not configured for the sync directory\nAbort.\n"); |
1196 fprintf(stderr, "Error: versioning not configured for the sync directory\nAbort.\n"); |
1185 return -1; |
1197 return -1; |
1427 |
1439 |
1428 // metadata updates |
1440 // metadata updates |
1429 for(UcxList *elm=ls_update;elm && !sync_shutdown;elm=elm->next) { |
1441 for(UcxList *elm=ls_update;elm && !sync_shutdown;elm=elm->next) { |
1430 LocalResource *local_res = elm->data; |
1442 LocalResource *local_res = elm->data; |
1431 |
1443 |
1432 DavResource *res = dav_resource_new(sn, local_res->path); |
1444 DavResource *res = dav_resource_new(sn, local_res->path); |
1433 if(dir->tagconfig) { |
|
1434 DavPropName properties[] = { |
|
1435 {DAV_NS,"tags"}, |
|
1436 }; |
|
1437 if(dav_load_prop(res, properties, 1)) { |
|
1438 sync_error++; |
|
1439 print_resource_error(sn, res->path); |
|
1440 ret = -1; |
|
1441 error = 1; |
|
1442 continue; |
|
1443 } |
|
1444 } |
|
1445 |
|
1446 if(local_res->metadata_updated) { |
1445 if(local_res->metadata_updated) { |
1447 if(!sync_update_metadata(dir, sn, res, local_res)) { |
1446 if(!sync_update_metadata(dir, sn, res, local_res)) { |
1448 LocalResource *dbres = ucx_map_cstr_remove(db->resources, local_res->path); |
1447 LocalResource *dbres = ucx_map_cstr_remove(db->resources, local_res->path); |
1449 ucx_map_cstr_put(db->resources, local_res->path, local_res); |
1448 ucx_map_cstr_put(db->resources, local_res->path, local_res); |
1450 } |
1449 } |
1533 if(!syncdir && a->argc == 0) { |
1532 if(!syncdir && a->argc == 0) { |
1534 fprintf(stderr, "No syncdir or files specified\n"); |
1533 fprintf(stderr, "No syncdir or files specified\n"); |
1535 return -1; |
1534 return -1; |
1536 } |
1535 } |
1537 |
1536 |
|
1537 char *version = cmd_getoption(a, "version"); |
|
1538 if(version) { |
|
1539 if(a->argc != 1) { |
|
1540 fprintf(stderr, "If the -V option is enabled, only one file can be specified\n"); |
|
1541 return -1; |
|
1542 } |
|
1543 } |
|
1544 |
1538 SyncDirectory *dir = NULL; |
1545 SyncDirectory *dir = NULL; |
1539 UcxMap *files = NULL; |
1546 UcxMap *files = NULL; |
1540 if(syncdir) { |
1547 if(syncdir) { |
1541 dir = scfg_get_dir(syncdir); |
1548 dir = scfg_get_dir(syncdir); |
1542 } |
1549 } |
1682 resources = ucx_list_sort(resources, (cmp_func)localres_cmp_path, NULL); |
1689 resources = ucx_list_sort(resources, (cmp_func)localres_cmp_path, NULL); |
1683 |
1690 |
1684 UCX_FOREACH(elm, resources) { |
1691 UCX_FOREACH(elm, resources) { |
1685 LocalResource *resource = elm->data; |
1692 LocalResource *resource = elm->data; |
1686 |
1693 |
1687 DavResource *res = dav_get(sn, resource->path, "D:getetag,idav:status,idav:finfo,idav:xattributes"); |
1694 DavResource *res = dav_get(sn, resource->path, "D:getetag,idav:status,idav:version-collection,idav:finfo,idav:xattributes"); |
1688 if(!res) { |
1695 if(!res) { |
1689 printf("skip: %s\n", resource->path); |
1696 printf("skip: %s\n", resource->path); |
1690 continue; |
1697 continue; |
1691 //continue; |
|
1692 } |
1698 } |
1693 char *status = dav_get_string_property(res, "idav:status"); |
1699 char *status = dav_get_string_property(res, "idav:status"); |
1694 if(status && !strcmp(status, "broken")) { |
1700 if(status && !strcmp(status, "broken")) { |
|
1701 fprintf(stderr, "Resource %s broken\n", res->path); |
1695 continue; |
1702 continue; |
|
1703 } |
|
1704 |
|
1705 DavResource *vres = NULL; |
|
1706 if(version) { |
|
1707 if(dir->versioning->type == VERSIONING_SIMPLE) { |
|
1708 vres = versioning_simple_find(res, version); |
|
1709 } else if(dir->versioning->type == VERSIONING_DELTAV) { |
|
1710 vres = versioning_deltav_find(res, version); |
|
1711 } |
|
1712 if(!vres) { |
|
1713 fprintf(stderr, "Cannot find specified version for resource %s\n", res->path); |
|
1714 ret = 1; |
|
1715 break; |
|
1716 } |
|
1717 } else { |
|
1718 vres = res; |
1696 } |
1719 } |
1697 |
1720 |
1698 // download the resource |
1721 // download the resource |
1699 if(!sync_shutdown) { |
1722 if(!sync_shutdown) { |
1700 if(resource->isdirectory) { |
1723 if(resource->isdirectory) { |
1704 "Cannot create directory %s: %s", |
1727 "Cannot create directory %s: %s", |
1705 local_path, strerror(errno)); |
1728 local_path, strerror(errno)); |
1706 } |
1729 } |
1707 free(local_path); |
1730 free(local_path); |
1708 } else { |
1731 } else { |
1709 if(sync_get_resource(a, dir, res, db, &sync_success)) { |
1732 if(sync_get_resource(a, dir, res->path, vres, db, &sync_success)) { |
1710 fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path); |
1733 fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path); |
1711 sync_error++; |
1734 sync_error++; |
1712 } |
1735 } |
1713 } |
1736 } |
1714 } |
1737 } |
1744 printf("Result: %d %s pulled, %d %s\n", |
1767 printf("Result: %d %s pulled, %d %s\n", |
1745 sync_success, str_success, |
1768 sync_success, str_success, |
1746 sync_error, str_error); |
1769 sync_error, str_error); |
1747 } |
1770 } |
1748 |
1771 |
1749 return 0; |
1772 return ret; |
1750 } |
1773 } |
1751 |
1774 |
1752 UcxList* local_scan(SyncDirectory *dir, SyncDatabase *db) { |
1775 UcxList* local_scan(SyncDirectory *dir, SyncDatabase *db) { |
1753 UcxList *resources = NULL; |
1776 UcxList *resources = NULL; |
1754 |
1777 |
2045 } else { |
2068 } else { |
2046 return 0; |
2069 return 0; |
2047 } |
2070 } |
2048 } |
2071 } |
2049 |
2072 |
|
2073 DavResource *versioning_simple_find(DavResource *res, const char *version) { |
|
2074 char *vcol_href = dav_get_string_property_ns(res, DAV_NS, VERSION_PATH_PROPERTY); |
|
2075 if(!vcol_href) { |
|
2076 return NULL; |
|
2077 } |
|
2078 DavResource *vcol = dav_resource_new_href(res->session, vcol_href); |
|
2079 if(!vcol) { |
|
2080 return NULL; |
|
2081 } |
|
2082 |
|
2083 |
|
2084 if(dav_load_prop(vcol, defprops, numdefprops)) { |
|
2085 print_resource_error(res->session, vcol->path); |
|
2086 dav_resource_free(vcol); |
|
2087 return NULL; |
|
2088 } |
|
2089 |
|
2090 DavResource *ret = NULL; |
|
2091 DavResource *child = vcol->children; |
|
2092 while(child) { |
|
2093 DavResource *next = child->next; |
|
2094 if(!strcmp(child->name, version)) { |
|
2095 ret = child; |
|
2096 } else { |
|
2097 dav_resource_free(child); |
|
2098 } |
|
2099 child = next; |
|
2100 } |
|
2101 dav_resource_free(vcol); |
|
2102 |
|
2103 return ret; |
|
2104 } |
|
2105 |
|
2106 // TODO: remove code dup (main.c: find_version) |
|
2107 DavResource* versioning_deltav_find(DavResource *res, const char *version) { |
|
2108 DavResource *list = dav_versiontree(res, "D:getetag,idav:status,idav:finfo,idav:xattributes,idav:tags"); |
|
2109 DavResource *ret = NULL; |
|
2110 while(list) { |
|
2111 DavResource *next = list->next; |
|
2112 if(!ret) { |
|
2113 char *vname = dav_get_string_property(list, "D:version-name"); |
|
2114 if(vname && !strcmp(vname, version)) { |
|
2115 ret = list; |
|
2116 } |
|
2117 } |
|
2118 if(list != ret) { |
|
2119 dav_resource_free(list); |
|
2120 } |
|
2121 list = next; |
|
2122 } |
|
2123 return ret; |
|
2124 } |
2050 |
2125 |
2051 int sync_set_status(DavResource *res, char *status) { |
2126 int sync_set_status(DavResource *res, char *status) { |
2052 DavResource *resource = dav_resource_new(res->session, res->path); |
2127 DavResource *resource = dav_resource_new(res->session, res->path); |
2053 dav_set_string_property(resource, "idav:status", status); |
2128 dav_set_string_property(resource, "idav:status", status); |
2054 int ret = dav_store(resource); |
2129 int ret = dav_store(resource); |