dav/sync.c

changeset 490
d94c4fd35c21
parent 479
6a198e156278
child 491
fdc2fb090cc7
equal deleted inserted replaced
489:fb69eae42ef0 490:d94c4fd35c21
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");
541 localres_keep(db, res->path); 546 localres_keep(db, res->path);
542 continue; 547 continue;
543 } 548 }
544 549
545 // download the resource 550 // download the resource
546 if(!sync_shutdown && sync_get_resource(a, dir, res, db, &sync_success)) { 551 if(!sync_shutdown && sync_get_resource(a, dir, res, db, FALSE, &sync_success)) {
547 fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path); 552 fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path);
548 sync_error++; 553 sync_error++;
549 } 554 }
550 555
551 // add every resource from the server to svrres 556 // add every resource from the server to svrres
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);

mercurial