120 ret = cmd_push(args, FALSE); |
120 ret = cmd_push(args, FALSE); |
121 stop_sighandler(&mutex, tid); |
121 stop_sighandler(&mutex, tid); |
122 } else if(!strcmp(cmd, "archive")) { |
122 } else if(!strcmp(cmd, "archive")) { |
123 tid = start_sighandler(&mutex); |
123 tid = start_sighandler(&mutex); |
124 ret = cmd_push(args, TRUE); |
124 ret = cmd_push(args, TRUE); |
|
125 stop_sighandler(&mutex, tid); |
|
126 } else if(!strcmp(cmd, "restore")) { |
|
127 tid = start_sighandler(&mutex); |
|
128 ret = cmd_restore(args); |
125 stop_sighandler(&mutex, tid); |
129 stop_sighandler(&mutex, tid); |
126 } else if(!strcmp(cmd, "resolve-conflicts")) { |
130 } else if(!strcmp(cmd, "resolve-conflicts")) { |
127 ret = cmd_resolve_conflicts(args); |
131 ret = cmd_resolve_conflicts(args); |
128 } else if(!strcmp(cmd, "delete-conflicts")) { |
132 } else if(!strcmp(cmd, "delete-conflicts")) { |
129 ret = cmd_delete_conflicts(args); |
133 ret = cmd_delete_conflicts(args); |
171 |
175 |
172 fprintf(stderr, "Commands:\n"); |
176 fprintf(stderr, "Commands:\n"); |
173 fprintf(stderr, " pull [-cldr] [-t <tags>] <directory>\n"); |
177 fprintf(stderr, " pull [-cldr] [-t <tags>] <directory>\n"); |
174 fprintf(stderr, " push [-cldrRM] [-t <tags>] <directory>\n"); |
178 fprintf(stderr, " push [-cldrRM] [-t <tags>] <directory>\n"); |
175 fprintf(stderr, " archive [-cldRM] [-t <tags>] <directory>\n"); |
179 fprintf(stderr, " archive [-cldRM] [-t <tags>] <directory>\n"); |
|
180 fprintf(stderr, " restore [-ld] <directory>\n"); |
176 fprintf(stderr, " resolve-conflicts <directory>\n"); |
181 fprintf(stderr, " resolve-conflicts <directory>\n"); |
177 fprintf(stderr, " delete-conflicts <directory>\n"); |
182 fprintf(stderr, " delete-conflicts <directory>\n"); |
178 fprintf(stderr, " trash-info <directory>\n"); |
183 fprintf(stderr, " trash-info <directory>\n"); |
179 fprintf(stderr, " empty-trash <directory>\n"); |
184 fprintf(stderr, " empty-trash <directory>\n"); |
180 fprintf(stderr, " add-tag [-s <syncdir>] <file> <tag>\n"); |
185 fprintf(stderr, " add-tag [-s <syncdir>] <file> <tag>\n"); |
649 int sync_get_resource( |
654 int sync_get_resource( |
650 CmdArgs *a, |
655 CmdArgs *a, |
651 SyncDirectory *dir, |
656 SyncDirectory *dir, |
652 DavResource *res, |
657 DavResource *res, |
653 SyncDatabase *db, |
658 SyncDatabase *db, |
|
659 DavBool force, |
654 int *counter) |
660 int *counter) |
655 { |
661 { |
656 int cdt = cmd_getoption(a, "conflict") ? 0 : 1; // conflict detection |
662 int cdt = cmd_getoption(a, "conflict") ? 0 : 1; // conflict detection |
657 |
663 |
658 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
664 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
659 char *local_path = util_concat_path(dir->path, res->path); |
665 char *local_path = util_concat_path(dir->path, res->path); |
660 |
666 |
661 char *etag = dav_get_string_property(res, "D:getetag"); |
667 char *etag = dav_get_string_property(res, "D:getetag"); |
662 SYS_STAT s; |
668 SYS_STAT s; |
663 memset(&s, 0, sizeof(SYS_STAT)); |
669 memset(&s, 0, sizeof(SYS_STAT)); |
664 if(local && !res->iscollection) { |
670 if(!force) { |
665 int exists = 1; |
671 if(local && !res->iscollection) { |
666 if(sys_stat(local_path, &s)) { |
672 int exists = 1; |
667 // Ignore the fact, that the file is locally removed. If the |
673 if(sys_stat(local_path, &s)) { |
668 // server has an updated version, we read the file or the |
674 // Ignore the fact, that the file is locally removed. If the |
669 // next push will delete it on the server. |
675 // server has an updated version, we read the file or the |
670 if(errno != ENOENT) { |
676 // next push will delete it on the server. |
671 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
677 if(errno != ENOENT) { |
672 free(local_path); |
678 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
673 return -1; |
679 free(local_path); |
674 } else { |
680 return -1; |
675 exists = 0; |
681 } else { |
676 } |
682 exists = 0; |
677 } |
683 } |
678 |
684 } |
679 if(local->etag) { |
685 |
680 sstr_t e = sstr(etag); |
686 if(local->etag) { |
681 if(sstrprefix(e, S("W/"))) { |
687 sstr_t e = sstr(etag); |
682 e = sstrsubs(e, 2); |
688 if(sstrprefix(e, S("W/"))) { |
683 } |
689 e = sstrsubs(e, 2); |
684 if(!strcmp(e.ptr, local->etag)) { |
690 } |
685 // resource is already up-to-date on the client |
691 if(!strcmp(e.ptr, local->etag)) { |
686 sync_store_tags(dir, local_path, local, res); |
692 // resource is already up-to-date on the client |
687 free(local_path); |
693 sync_store_tags(dir, local_path, local, res); |
688 return 0; |
694 free(local_path); |
689 } |
695 return 0; |
690 } |
696 } |
691 |
697 } |
692 if(cdt && exists && s.st_mtime != local->last_modified) { |
698 |
693 // file modified on the server and on the client |
699 if(cdt && exists && s.st_mtime != local->last_modified) { |
694 rename_conflict_file(dir, db, local->path); |
700 // file modified on the server and on the client |
695 } |
701 rename_conflict_file(dir, db, local->path); |
696 } else { |
702 } |
697 if(sys_stat(local_path, &s)) { |
703 } else { |
698 if(errno != ENOENT) { |
704 if(sys_stat(local_path, &s)) { |
699 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
705 if(errno != ENOENT) { |
700 } |
706 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
701 } else if(S_ISDIR(s.st_mode)) { |
707 } |
702 //fprintf(stderr, "Error: file %s is a directory\n", local_path); |
708 } else if(S_ISDIR(s.st_mode)) { |
703 } else if(cdt) { |
709 //fprintf(stderr, "Error: file %s is a directory\n", local_path); |
704 // rename file on conflict |
710 } else if(cdt) { |
705 rename_conflict_file(dir, db, res->path); |
711 // rename file on conflict |
|
712 rename_conflict_file(dir, db, res->path); |
|
713 } |
706 } |
714 } |
707 } |
715 } |
708 |
716 |
709 int ret = 0; |
717 int ret = 0; |
710 char *tmp_path = create_tmp_download_path(local_path); |
718 char *tmp_path = create_tmp_download_path(local_path); |
1287 } |
1295 } |
1288 |
1296 |
1289 return ret; |
1297 return ret; |
1290 } |
1298 } |
1291 |
1299 |
|
1300 static int localres_cmp_path(LocalResource *a, LocalResource *b, void *n) { |
|
1301 return strcmp(a->path, b->path); |
|
1302 } |
|
1303 |
|
1304 int cmd_restore(CmdArgs *a) { |
|
1305 SyncDirectory *dir = scfg_get_dir(a->argv[0]); |
|
1306 if(!dir) { |
|
1307 fprintf(stderr, "Unknown sync dir: %s\n", a->argv[0]); |
|
1308 return -1; |
|
1309 } |
|
1310 if(scfg_check_dir(dir)) { |
|
1311 return -1; |
|
1312 } |
|
1313 |
|
1314 if((dir->allow_cmd & SYNC_CMD_RESTORE) != SYNC_CMD_RESTORE) { |
|
1315 fprintf(stderr, "Command ''restore'' is not allowed for this sync dir\n"); |
|
1316 print_allowed_cmds(dir); |
|
1317 return -1; |
|
1318 } |
|
1319 |
|
1320 SyncDatabase *db = load_db(dir->database); |
|
1321 if(!db) { |
|
1322 fprintf(stderr, "Cannot load database file: %s\n", dir->database); |
|
1323 return -1; |
|
1324 } |
|
1325 remove_deleted_conflicts(dir, db); |
|
1326 |
|
1327 UcxList *modified = NULL; |
|
1328 UcxList *deleted = NULL; |
|
1329 |
|
1330 // iterate over all db resources and check if any resource is |
|
1331 // modified or deleted |
|
1332 UcxMapIterator i = ucx_map_iterator(db->resources); |
|
1333 LocalResource *resource; |
|
1334 UCX_MAP_FOREACH(key, resource, i) { |
|
1335 char *file_path = util_concat_path(dir->path, resource->path); |
|
1336 SYS_STAT s; |
|
1337 if(sys_stat(file_path, &s)) { |
|
1338 if(errno == ENOENT) { |
|
1339 deleted = ucx_list_prepend(deleted, resource); |
|
1340 } else { |
|
1341 fprintf(stderr, "Cannot stat file: %s\n", file_path); |
|
1342 perror(""); |
|
1343 } |
|
1344 } else if(!resource->isdirectory && !S_ISDIR(s.st_mode)) { |
|
1345 if(resource->last_modified != s.st_mtime || resource->size != s.st_size) { |
|
1346 modified = ucx_list_prepend(modified, resource); |
|
1347 } |
|
1348 } |
|
1349 |
|
1350 free(file_path); |
|
1351 } |
|
1352 |
|
1353 int ret = 0; |
|
1354 |
|
1355 // create DavSession |
|
1356 Repository *repo = get_repository(sstr(dir->repository)); |
|
1357 if(!repo) { |
|
1358 fprintf(stderr, "Unkown repository %s\n", dir->name); |
|
1359 return -1; |
|
1360 } |
|
1361 char *new_url = NULL; |
|
1362 if(dir->collection) { |
|
1363 new_url = util_concat_path(repo->url, dir->collection); |
|
1364 } |
|
1365 DavSession *sn = create_session(ctx, repo, new_url ? new_url : repo->url); |
|
1366 ucx_mempool_reg_destr(sn->mp, db, (ucx_destructor)destroy_db); |
|
1367 if(new_url) { |
|
1368 free(new_url); |
|
1369 } |
|
1370 if (cmd_getoption(a, "verbose")) { |
|
1371 curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L); |
|
1372 curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr); |
|
1373 } |
|
1374 |
|
1375 // lock repository |
|
1376 char *locktokenfile = NULL; |
|
1377 DavBool locked = FALSE; |
|
1378 DavResource *root = dav_resource_new(sn, "/"); |
|
1379 root->iscollection = TRUE; |
|
1380 if((dir->lockpush || cmd_getoption(a, "lock")) && !cmd_getoption(a, "nolock")) { |
|
1381 if(dav_lock_t(root, dir->lock_timeout)) { |
|
1382 print_resource_error(sn, "/"); |
|
1383 dav_session_destroy(sn); |
|
1384 fprintf(stderr, "Abort\n"); |
|
1385 return -1; |
|
1386 } |
|
1387 DavLock *lock = dav_get_lock(sn, "/"); |
|
1388 if(lock) { |
|
1389 printf("Lock-Token: %s\n", lock->token); |
|
1390 } |
|
1391 locked = TRUE; |
|
1392 locktokenfile = create_locktoken_file(dir->name, lock->token); |
|
1393 } |
|
1394 |
|
1395 int sync_success = 0; |
|
1396 int sync_error = 0; |
|
1397 |
|
1398 UcxList *resources = ucx_list_concat(modified, deleted); |
|
1399 resources = ucx_list_sort(resources, (cmp_func)localres_cmp_path, NULL); |
|
1400 |
|
1401 UCX_FOREACH(elm, resources) { |
|
1402 LocalResource *resource = elm->data; |
|
1403 |
|
1404 DavResource *res = dav_get(sn, resource->path, "idav:status,D:getetag"); |
|
1405 |
|
1406 char *status = dav_get_string_property(res, "idav:status"); |
|
1407 if(status && !strcmp(status, "broken")) { |
|
1408 continue; |
|
1409 } |
|
1410 |
|
1411 // download the resource |
|
1412 if(!sync_shutdown && sync_get_resource(a, dir, res, db, TRUE, &sync_success)) { |
|
1413 fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path); |
|
1414 sync_error++; |
|
1415 } |
|
1416 } |
|
1417 |
|
1418 // unlock repository |
|
1419 if(locked) { |
|
1420 if(dav_unlock(root)) { |
|
1421 print_resource_error(sn, "/"); |
|
1422 ret = -1; |
|
1423 } else { |
|
1424 locked = FALSE; |
|
1425 } |
|
1426 } |
|
1427 |
|
1428 // store db |
|
1429 if(store_db(db, dir->database)) { |
|
1430 fprintf(stderr, "Cannot store sync db\n"); |
|
1431 ret = -2; |
|
1432 } |
|
1433 |
|
1434 // cleanup |
|
1435 dav_session_destroy(sn); |
|
1436 |
|
1437 if(!locked && locktokenfile) { |
|
1438 remove(locktokenfile); |
|
1439 } |
|
1440 |
|
1441 // Report |
|
1442 if(ret != -2) { |
|
1443 char *str_success = sync_success == 1 ? "file" : "files"; |
|
1444 char *str_error = sync_error == 1 ? "error" : "errors"; |
|
1445 printf("Result: %d %s pulled, %d %s\n", |
|
1446 sync_success, str_success, |
|
1447 sync_error, str_error); |
|
1448 } |
|
1449 |
|
1450 return 0; |
|
1451 } |
|
1452 |
1292 UcxList* local_scan(SyncDirectory *dir, SyncDatabase *db) { |
1453 UcxList* local_scan(SyncDirectory *dir, SyncDatabase *db) { |
1293 UcxList *resources = NULL; |
1454 UcxList *resources = NULL; |
1294 |
1455 |
1295 char *path = strdup("/"); |
1456 char *path = strdup("/"); |
1296 UcxList *stack = ucx_list_prepend(NULL, path); |
1457 UcxList *stack = ucx_list_prepend(NULL, path); |