85 int ret = EXIT_FAILURE; |
85 int ret = EXIT_FAILURE; |
86 if(!strcmp(cmd, "pull")) { |
86 if(!strcmp(cmd, "pull")) { |
87 ret = cmd_pull(args); |
87 ret = cmd_pull(args); |
88 } else if(!strcmp(cmd, "push")) { |
88 } else if(!strcmp(cmd, "push")) { |
89 ret = cmd_push(args); |
89 ret = cmd_push(args); |
90 } else if(!strcmp(cmd, "sync")) { |
|
91 ret = cmd_sync(args); |
|
92 } |
90 } |
93 |
91 |
94 // TODO: cleanup sync config (don't forget to call regfree for regex) |
92 // TODO: cleanup sync config (don't forget to call regfree for regex) |
95 |
93 |
96 return ret; |
94 return ret; |
107 fprintf(stderr, " -c Disable conflict detection\n"); |
105 fprintf(stderr, " -c Disable conflict detection\n"); |
108 fprintf(stderr, " -r Read changes from stdin\n\n"); |
106 fprintf(stderr, " -r Read changes from stdin\n\n"); |
109 } |
107 } |
110 |
108 |
111 static int res_matches_filter(SyncDirectory *dir, char *res_path) { |
109 static int res_matches_filter(SyncDirectory *dir, char *res_path) { |
|
110 // trash filter |
|
111 if (dir->trash) { |
|
112 sstr_t rpath = sstr(util_concat_path(dir->path, res_path)); |
|
113 if (sstrprefix(rpath, sstr(dir->trash))) { |
|
114 free(rpath.ptr); |
|
115 return 1; |
|
116 } |
|
117 free(rpath.ptr); |
|
118 } |
|
119 |
|
120 // include/exclude filter |
112 UCX_FOREACH(inc, dir->include) { |
121 UCX_FOREACH(inc, dir->include) { |
113 regex_t* pattern = (regex_t*) inc->data; |
122 regex_t* pattern = (regex_t*) inc->data; |
114 if (regexec(pattern, res_path, 0, NULL, 0) == 0) { |
123 if (regexec(pattern, res_path, 0, NULL, 0) == 0) { |
115 UCX_FOREACH(exc, dir->exclude) { |
124 UCX_FOREACH(exc, dir->exclude) { |
116 regex_t* pattern = (regex_t*) exc->data; |
125 regex_t* pattern = (regex_t*) exc->data; |
213 UCX_MAP_FOREACH(key, local, i) { |
222 UCX_MAP_FOREACH(key, local, i) { |
214 if (res_matches_filter(dir, local->path)) { |
223 if (res_matches_filter(dir, local->path)) { |
215 continue; |
224 continue; |
216 } |
225 } |
217 // sync_remove_resource does all necessary tests |
226 // sync_remove_resource does all necessary tests |
218 sync_remove_resource(dir, local); |
227 sync_remove_local_resource(dir, local); |
219 } |
228 } |
220 ucx_map_free(db->resources); |
229 ucx_map_free(db->resources); |
221 db->resources = svrres; |
230 db->resources = svrres; |
222 |
231 |
223 // TODO: cleanup - BUT DONT CLEANUP SYNC CONFIG (do this in main!) |
232 // TODO: cleanup - BUT DONT CLEANUP SYNC CONFIG (do this in main!) |
230 |
239 |
231 return 0; |
240 return 0; |
232 } |
241 } |
233 |
242 |
234 int sync_get_resource(CmdArgs *a, SyncDirectory *dir, DavResource *res, SyncDatabase *db) { |
243 int sync_get_resource(CmdArgs *a, SyncDirectory *dir, DavResource *res, SyncDatabase *db) { |
235 LocalResource *removed = ucx_map_cstr_get(db->remove, res->path); |
|
236 if(removed) { |
|
237 return 0; |
|
238 } |
|
239 int cdt = cmd_getoption(a, "conflict") ? 0 : 1; // conflict detection |
244 int cdt = cmd_getoption(a, "conflict") ? 0 : 1; // conflict detection |
240 |
245 |
241 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
246 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
242 char *local_path = util_concat_path(dir->path, res->path); |
247 char *local_path = util_concat_path(dir->path, res->path); |
243 |
248 |
244 char *etag = dav_get_property(res, "D:getetag"); |
249 char *etag = dav_get_property(res, "D:getetag"); |
245 struct stat s; |
250 struct stat s; |
246 if(local) { |
251 if(local) { |
|
252 int exists = 1; |
247 if(stat(local_path, &s)) { |
253 if(stat(local_path, &s)) { |
248 // Ignore the fact, that the file is locally removed. If the |
254 // Ignore the fact, that the file is locally removed. If the |
249 // server has an updated version, we readd the file or the |
255 // server has an updated version, we readd the file or the |
250 // next push will delete it on the server. |
256 // next push will delete it on the server. |
251 if(errno != ENOENT) { |
257 if(errno != ENOENT) { |
252 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
258 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
253 free(local_path); |
259 free(local_path); |
254 return -1; |
260 return -1; |
|
261 } else { |
|
262 exists = 0; |
255 } |
263 } |
256 } |
264 } |
257 |
265 |
258 if(local->etag) { |
266 if(local->etag) { |
259 sstr_t e = sstr(etag); |
267 sstr_t e = sstr(etag); |
264 // resource is already up-to-date on the client |
272 // resource is already up-to-date on the client |
265 return 0; |
273 return 0; |
266 } |
274 } |
267 } |
275 } |
268 |
276 |
269 if(cdt && s.st_mtime != local->last_modified) { |
277 if(cdt && exists && s.st_mtime != local->last_modified) { |
270 // file modified on the server and on the client |
278 // file modified on the server and on the client |
271 rename_local_file(dir, db, local->path); |
279 rename_local_file(dir, db, local->path); |
272 } |
280 } |
273 } else { |
281 } else { |
274 if(stat(local_path, &s)) { |
282 if(stat(local_path, &s)) { |
340 free(local_path); |
348 free(local_path); |
341 return; |
349 return; |
342 } |
350 } |
343 |
351 |
344 printf("delete: %s\n", res->path); |
352 printf("delete: %s\n", res->path); |
345 if(unlink(local_path)) { |
353 |
|
354 if(dir->trash) { |
|
355 move_to_trash(dir, local_path); |
|
356 } else if(unlink(local_path)) { |
346 fprintf(stderr, "Cannot remove file %s\n", local_path); |
357 fprintf(stderr, "Cannot remove file %s\n", local_path); |
347 } |
358 } |
348 free(local_path); |
359 free(local_path); |
349 } |
360 } |
350 |
361 |
367 if(stat(new_path.ptr, &s)) { |
378 if(stat(new_path.ptr, &s)) { |
368 if(errno == ENOENT) { |
379 if(errno == ENOENT) { |
369 loop = 0; |
380 loop = 0; |
370 printf("conflict: %s\n", local_path); |
381 printf("conflict: %s\n", local_path); |
371 if(rename(local_path, new_path.ptr)) { |
382 if(rename(local_path, new_path.ptr)) { |
372 printf("errno: %d\n", errno); |
383 //printf("errno: %d\n", errno); |
373 fprintf( |
384 fprintf( |
374 stderr, |
385 stderr, |
375 "Cannot rename file %s to %s\n", |
386 "Cannot rename file %s to %s\n", |
376 local_path, |
387 local_path, |
377 new_path.ptr); |
388 new_path.ptr); |
380 } |
391 } |
381 rev++; |
392 rev++; |
382 free(new_path.ptr); |
393 free(new_path.ptr); |
383 } while(loop); |
394 } while(loop); |
384 free(parent); |
395 free(parent); |
|
396 } |
|
397 |
|
398 void move_to_trash(SyncDirectory *dir, char *path) { |
|
399 char *new_path = NULL; |
|
400 for (int i=0;;i++) { |
|
401 sstr_t np = ucx_asprintf( |
|
402 ucx_default_allocator(), |
|
403 "%s%d-%s", |
|
404 dir->trash, |
|
405 i, |
|
406 util_resource_name(path)); |
|
407 |
|
408 struct stat s; |
|
409 if(stat(np.ptr, &s)) { |
|
410 if(errno == ENOENT) { |
|
411 new_path = np.ptr; |
|
412 } |
|
413 break; |
|
414 } |
|
415 free(np.ptr); |
|
416 }; |
|
417 |
|
418 if(!new_path) { |
|
419 fprintf(stderr, "Cannot move file %s to trash.\n", path); |
|
420 return; |
|
421 } |
|
422 |
|
423 if(rename(path, new_path)) { |
|
424 //printf("errno: %d\n", errno); |
|
425 fprintf( |
|
426 stderr, |
|
427 "Cannot rename file %s to %s\n", |
|
428 path, |
|
429 new_path); |
|
430 } |
|
431 |
|
432 free(new_path); |
385 } |
433 } |
386 |
434 |
387 int cmd_push(CmdArgs *a) { |
435 int cmd_push(CmdArgs *a) { |
388 if(a->argc != 1) { |
436 if(a->argc != 1) { |
389 fprintf(stderr, "Too %s arguments\n", a->argc < 1 ? "few" : "many"); |
437 fprintf(stderr, "Too %s arguments\n", a->argc < 1 ? "few" : "many"); |
451 |
499 |
452 // delete all removed files |
500 // delete all removed files |
453 UcxMapIterator i = ucx_map_iterator(db->resources); |
501 UcxMapIterator i = ucx_map_iterator(db->resources); |
454 LocalResource *local; |
502 LocalResource *local; |
455 UCX_MAP_FOREACH(key, local, i) { |
503 UCX_MAP_FOREACH(key, local, i) { |
456 // TODO: save deletion: check for remote changes |
504 if(sync_delete_remote_resource(sn, local)) { |
457 |
505 ucx_map_cstr_put(lclres, local->path, local); |
458 DavResource *res = dav_resource_new(sn, local->path); |
506 } |
459 printf("delete: %s\n", res->path); |
|
460 if(dav_delete(res)) { |
|
461 if(sn->error != DAV_NOT_FOUND) { |
|
462 fprintf(stderr, "Cannot delete resource %s\n", res->path); |
|
463 } |
|
464 } |
|
465 dav_resource_free(res); |
|
466 // TODO: free local resource |
|
467 ucx_map_remove(db->remove, key); |
|
468 } |
507 } |
469 ucx_map_free(db->resources); |
508 ucx_map_free(db->resources); |
470 db->resources = lclres; |
509 db->resources = lclres; |
471 |
510 |
472 // TODO: free res |
511 // TODO: free res |
665 fclose(in); |
704 fclose(in); |
666 |
705 |
667 return ret; |
706 return ret; |
668 } |
707 } |
669 |
708 |
670 |
709 int sync_delete_remote_resource(DavSession *sn, LocalResource *local_res) { |
671 |
710 DavResource *res = dav_get(sn, local_res->path, "D:getetag"); |
672 int cmd_sync(CmdArgs *a) { |
711 if(!res) { |
673 return 0; |
712 return sn->error == DAV_NOT_FOUND ? 0 : 1; |
674 } |
713 } |
675 |
714 |
|
715 char *etag = dav_get_property(res, "D:getetag"); |
|
716 if(etag) { |
|
717 if(strlen(etag) > 2 && etag[0] == 'W' && etag[1] == '/') { |
|
718 etag = etag + 2; |
|
719 } |
|
720 } |
|
721 |
|
722 int ret = 0; |
|
723 if(!strcmp(etag, local_res->etag)) { |
|
724 // local resource metadata == remote resource metadata |
|
725 // resource can be deleted |
|
726 printf("delete: %s\n", res->path); |
|
727 if(dav_delete(res)) { |
|
728 if(sn->error != DAV_NOT_FOUND) { |
|
729 fprintf(stderr, "Cannot delete resource %s\n", res->path); |
|
730 } |
|
731 } |
|
732 } else { |
|
733 ret = 1; |
|
734 } |
|
735 |
|
736 // cleanup |
|
737 dav_resource_free(res); |
|
738 |
|
739 return ret; |
|
740 } |
|
741 |
|
742 |
|
743 |