191 // download the resource |
191 // download the resource |
192 if(sync_get_resource(a, dir, res, db)) { |
192 if(sync_get_resource(a, dir, res, db)) { |
193 fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path); |
193 fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path); |
194 } |
194 } |
195 |
195 |
|
196 // add every resource from the server to svrres |
|
197 // then db-resources only contains resources which are not on the |
|
198 // server |
196 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
199 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
197 ucx_map_cstr_put(svrres, res->path, local); |
200 ucx_map_cstr_put(svrres, res->path, local); |
198 ucx_map_cstr_remove(db->resources, res->path); |
201 ucx_map_cstr_remove(db->resources, res->path); |
199 |
202 |
200 if(res->children) { |
203 if(res->children) { |
202 } |
205 } |
203 res = res->next; |
206 res = res->next; |
204 } |
207 } |
205 } |
208 } |
206 |
209 |
|
210 // delete every remotely removed resource |
207 UcxMapIterator i = ucx_map_iterator(db->resources); |
211 UcxMapIterator i = ucx_map_iterator(db->resources); |
208 LocalResource *local; |
212 LocalResource *local; |
209 UCX_MAP_FOREACH(key, local, i) { |
213 UCX_MAP_FOREACH(key, local, i) { |
210 if (res_matches_filter(dir, local->path)) { |
214 if (res_matches_filter(dir, local->path)) { |
211 continue; |
215 continue; |
212 } |
216 } |
|
217 // sync_remove_resource does all necessary tests |
213 sync_remove_resource(dir, local); |
218 sync_remove_resource(dir, local); |
214 } |
219 } |
215 ucx_map_free(db->resources); |
220 ucx_map_free(db->resources); |
216 db->resources = svrres; |
221 db->resources = svrres; |
217 |
222 |
229 int sync_get_resource(CmdArgs *a, SyncDirectory *dir, DavResource *res, SyncDatabase *db) { |
234 int sync_get_resource(CmdArgs *a, SyncDirectory *dir, DavResource *res, SyncDatabase *db) { |
230 LocalResource *removed = ucx_map_cstr_get(db->remove, res->path); |
235 LocalResource *removed = ucx_map_cstr_get(db->remove, res->path); |
231 if(removed) { |
236 if(removed) { |
232 return 0; |
237 return 0; |
233 } |
238 } |
234 int cdt = cmd_getoption(a, "conflict") ? 0 : 1; |
239 int cdt = cmd_getoption(a, "conflict") ? 0 : 1; // conflict detection |
235 |
240 |
236 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
241 LocalResource *local = ucx_map_cstr_get(db->resources, res->path); |
237 char *local_path = util_concat_path(dir->path, res->path); |
242 char *local_path = util_concat_path(dir->path, res->path); |
238 |
243 |
239 char *etag = dav_get_property(res, "D:getetag"); |
244 char *etag = dav_get_property(res, "D:getetag"); |
240 struct stat s; |
245 struct stat s; |
241 if(local) { |
246 if(local) { |
242 if(stat(local_path, &s)) { |
247 if(stat(local_path, &s)) { |
243 if(errno == ENOENT) { |
248 // Ignore the fact, that the file is locally removed. If the |
244 printf("removed: %s\n", res->path); |
249 // server has an updated version, we readd the file or the |
245 // the file is in the database, but doesn't exists |
250 // next push will delete it on the server. |
246 // mark the file as removed to delete it on next push |
251 if(errno != ENOENT) { |
247 ucx_map_cstr_remove(db->resources, local->path); |
|
248 ucx_map_cstr_put(db->remove, local->path, local); |
|
249 return 0; |
|
250 } else { |
|
251 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
252 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
252 free(local_path); |
253 free(local_path); |
253 return -1; |
254 return -1; |
254 } |
255 } |
255 } |
256 } |
272 } else { |
273 } else { |
273 if(stat(local_path, &s)) { |
274 if(stat(local_path, &s)) { |
274 if(errno != ENOENT) { |
275 if(errno != ENOENT) { |
275 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
276 fprintf(stderr, "Cannot stat file: %s\n", local_path); |
276 } |
277 } |
277 } else if(cdt && !S_ISDIR(s.st_mode)) { |
278 } else if(S_ISDIR(s.st_mode)) { |
|
279 //fprintf(stderr, "Error: file %s is a directory\n", local_path); |
|
280 } else if(cdt) { |
|
281 // rename file on conflict |
278 rename_local_file(dir, db, res->path); |
282 rename_local_file(dir, db, res->path); |
279 } |
283 } |
280 } |
284 } |
281 |
285 |
282 int ret = 0; |
286 int ret = 0; |
421 |
425 |
422 // upload all changed files |
426 // upload all changed files |
423 UcxList *resources = cmd_getoption(a, "read") ? |
427 UcxList *resources = cmd_getoption(a, "read") ? |
424 read_changes(dir, db) : local_scan(dir, db); |
428 read_changes(dir, db) : local_scan(dir, db); |
425 |
429 |
|
430 UcxMap *lclres = ucx_map_new(db->resources->count); |
426 UCX_FOREACH(elm, resources) { |
431 UCX_FOREACH(elm, resources) { |
427 LocalResource *local_res = elm->data; |
432 LocalResource *local_res = elm->data; |
428 if (!res_matches_filter(dir, local_res->path+1)) { |
433 if (!res_matches_filter(dir, local_res->path+1)) { |
429 printf("put: %s\n", local_res->path); |
434 // upload every changed file |
430 ucx_map_cstr_put(db->resources, local_res->path, local_res); |
435 if (local_resource_is_changed(dir, db, local_res)) { |
431 DavResource *res = dav_resource_new(sn, local_res->path); |
436 printf("put: %s\n", local_res->path); |
432 if(sync_put_resource(dir, res, db)) { |
437 DavResource *res = dav_resource_new(sn, local_res->path); |
433 ucx_map_cstr_remove(db->resources, local_res->path); |
438 if(sync_put_resource(dir, res, local_res)) { |
434 } |
439 // TODO: I don't know what to do now |
435 dav_resource_free(res); |
440 } |
|
441 dav_resource_free(res); |
|
442 } |
|
443 |
|
444 // remove every locally available resource from db->resource |
|
445 // the remaining elements are all deleted files |
|
446 ucx_map_cstr_put(lclres, local_res->path, local_res); |
|
447 ucx_map_cstr_remove(db->resources, local_res->path); // TODO: element leaked |
436 } |
448 } |
437 } |
449 } |
438 ucx_list_free(resources); |
450 ucx_list_free(resources); |
439 |
451 |
440 // delete all removed files |
452 // delete all removed files |
441 UcxMapIterator i = ucx_map_iterator(db->remove); |
453 UcxMapIterator i = ucx_map_iterator(db->resources); |
442 LocalResource *local; |
454 LocalResource *local; |
443 UCX_MAP_FOREACH(key, local, i) { |
455 UCX_MAP_FOREACH(key, local, i) { |
|
456 // TODO: save deletion: check for remote changes |
|
457 |
444 DavResource *res = dav_resource_new(sn, local->path); |
458 DavResource *res = dav_resource_new(sn, local->path); |
445 printf("delete: %s\n", res->path); |
459 printf("delete: %s\n", res->path); |
446 if(dav_delete(res)) { |
460 if(dav_delete(res)) { |
447 if(sn->error != DAV_NOT_FOUND) { |
461 if(sn->error != DAV_NOT_FOUND) { |
448 fprintf(stderr, "Cannot delete resource %s\n", res->path); |
462 fprintf(stderr, "Cannot delete resource %s\n", res->path); |
470 char *path = strdup("/"); |
486 char *path = strdup("/"); |
471 UcxList *stack = ucx_list_prepend(NULL, path); |
487 UcxList *stack = ucx_list_prepend(NULL, path); |
472 while(stack) { |
488 while(stack) { |
473 // get a directory path from the stack and read all entries |
489 // get a directory path from the stack and read all entries |
474 // if an entry is a directory, put it on the stack |
490 // if an entry is a directory, put it on the stack |
475 // otherwise compare the metadata with the db content |
|
476 |
491 |
477 char *p = stack->data; |
492 char *p = stack->data; |
478 stack = ucx_list_remove(stack, stack); |
493 stack = ucx_list_remove(stack, stack); |
479 char *local_path = util_concat_path(dir->path, p); |
494 char *local_path = util_concat_path(dir->path, p); |
480 DIR *local_dir = opendir(local_path); |
495 DIR *local_dir = opendir(local_path); |
538 } |
553 } |
539 value = sstrdup(value); |
554 value = sstrdup(value); |
540 |
555 |
541 if(!sstrcmp(name, S("put"))) { |
556 if(!sstrcmp(name, S("put"))) { |
542 int isdir; |
557 int isdir; |
543 LocalResource *res = path_to_local_resource(dir, db, value.ptr, &isdir); |
558 LocalResource *res = local_resource_new(dir, db, value.ptr, &isdir); |
544 if(res) { |
559 if(res) { |
545 resources = ucx_list_append(resources, res); |
560 resources = ucx_list_append(resources, res); |
546 } |
561 } |
547 } else if(!sstrcmp(name, S("remove"))) { |
562 } else if(!sstrcmp(name, S("remove"))) { |
548 LocalResource *res = calloc(1, sizeof(LocalResource)); |
563 LocalResource *res = calloc(1, sizeof(LocalResource)); |
560 ucx_properties_free(parser); |
575 ucx_properties_free(parser); |
561 |
576 |
562 return resources; |
577 return resources; |
563 } |
578 } |
564 |
579 |
565 LocalResource* path_to_local_resource(SyncDirectory *dir, SyncDatabase *db, char *path, int *isdir) { |
580 LocalResource* local_resource_new(SyncDirectory *dir, SyncDatabase *db, char *path, int *isdir) { |
566 |
|
567 char *file_path = util_concat_path(dir->path, path); |
581 char *file_path = util_concat_path(dir->path, path); |
568 struct stat s; |
582 struct stat s; |
569 if(stat(file_path, &s)) { |
583 if(stat(file_path, &s)) { |
570 fprintf(stderr, "Cannot stat file %s\n", file_path); |
584 fprintf(stderr, "Cannot stat file %s\n", file_path); |
571 free(file_path); |
585 free(file_path); |
573 } |
587 } |
574 free(file_path); |
588 free(file_path); |
575 |
589 |
576 if(!S_ISDIR(s.st_mode)) { |
590 if(!S_ISDIR(s.st_mode)) { |
577 *isdir = 0; |
591 *isdir = 0; |
578 LocalResource *res = ucx_map_cstr_get(db->resources, path); |
592 LocalResource *res = calloc(1, sizeof(LocalResource)); |
579 if(res) { |
593 res->path = strdup(path); |
580 // the file is already in the database |
594 res->etag = NULL; |
581 // compare length and lastmodified date |
595 res->last_modified = s.st_mtime; |
582 |
596 res->size = s.st_size; |
583 if(res->last_modified == s.st_mtime |
597 return res; |
584 && res->size == s.st_size) |
|
585 { |
|
586 // file unchanged |
|
587 return NULL; |
|
588 } else { |
|
589 // update db entries |
|
590 res->size = s.st_size; |
|
591 res->last_modified = s.st_mtime; |
|
592 |
|
593 return res; |
|
594 } |
|
595 } else { |
|
596 LocalResource *res = calloc(1, sizeof(LocalResource)); |
|
597 res->path = strdup(path); |
|
598 res->etag = NULL; |
|
599 res->last_modified = s.st_mtime; |
|
600 res->size = s.st_size; |
|
601 return res; |
|
602 } |
|
603 } else { |
598 } else { |
604 *isdir = 1; |
599 *isdir = 1; |
605 } |
600 } |
606 return NULL; |
601 return NULL; |
607 } |
602 } |
608 |
603 |
609 int sync_put_resource(SyncDirectory *dir, DavResource *res, SyncDatabase *db) { |
604 int local_resource_is_changed(SyncDirectory *dir, SyncDatabase *db, LocalResource *res) { |
|
605 LocalResource *db_res = ucx_map_cstr_get(db->resources, res->path); |
|
606 if(db_res) { |
|
607 if(db_res->etag) { |
|
608 res->etag = strdup(db_res->etag); |
|
609 } |
|
610 |
|
611 if(db_res->last_modified == res->last_modified && db_res->size == res->size) { |
|
612 return 0; |
|
613 } |
|
614 } |
|
615 return 1; |
|
616 } |
|
617 |
|
618 |
|
619 int sync_put_resource(SyncDirectory *dir, DavResource *res, LocalResource *local) { |
610 char *local_path = util_concat_path(dir->path, res->path); |
620 char *local_path = util_concat_path(dir->path, res->path); |
611 FILE *in = fopen(local_path, "r"); |
621 FILE *in = fopen(local_path, "r"); |
612 if(!in) { |
622 if(!in) { |
613 fprintf(stderr, "Cannot open file %s\n", local_path); |
623 fprintf(stderr, "Cannot open file %s\n", local_path); |
614 free(local_path); |
624 free(local_path); |
628 } |
638 } |
629 ret = 0; |
639 ret = 0; |
630 break; |
640 break; |
631 } |
641 } |
632 |
642 |
633 if(ret == 0) { |
643 if(ret == 0) { |
634 LocalResource *local_res = ucx_map_cstr_get(db->resources, res->path); |
644 // get new etag |
635 if(local_res->etag) { |
|
636 free(local_res->etag); |
|
637 } |
|
638 |
|
639 DavResource *up_res = dav_get(res->session, res->path, "D:getetag"); |
645 DavResource *up_res = dav_get(res->session, res->path, "D:getetag"); |
640 char *etag = dav_get_property(up_res, "D:getetag"); |
646 char *etag = dav_get_property(up_res, "D:getetag"); |
641 if(etag) { |
647 if(etag) { |
642 if(strlen(etag) > 2 && etag[0] == 'W' && etag[1] == '/') { |
648 if(strlen(etag) > 2 && etag[0] == 'W' && etag[1] == '/') { |
643 etag = etag + 2; |
649 etag = etag + 2; |
644 } |
650 } |
645 } |
651 } |
646 |
652 |
|
653 if(local->etag) { |
|
654 free(local->etag); |
|
655 } |
647 |
656 |
648 if(etag) { |
657 if(etag) { |
649 local_res->etag = strdup(etag); |
658 local->etag = strdup(etag); |
650 } else { |
659 } else { |
651 local_res->etag = NULL; |
660 local->etag = NULL; |
652 } |
661 } |
|
662 dav_resource_free(up_res); |
653 } |
663 } |
654 |
664 |
655 fclose(in); |
665 fclose(in); |
656 |
666 |
657 return 0; |
667 return ret; |
658 } |
668 } |
659 |
669 |
660 |
670 |
661 |
671 |
662 int cmd_sync(CmdArgs *a) { |
672 int cmd_sync(CmdArgs *a) { |