0e80efd108b27b67acab33cdbc6e53a353f5fef4
[sdk/target/sdbd.git] / src / file_sync_client.c
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <errno.h>
21 #include <sys/stat.h>
22 #include <sys/time.h>
23 #include <time.h>
24 #include <dirent.h>
25 #include <limits.h>
26 #include <sys/types.h>
27 // tizen specific #include <zipfile/zipfile.h>
28
29 #include "sysdeps.h"
30 #include "sdb.h"
31 #include "sdb_client.h"
32 #include "file_sync_service.h"
33
34
35 static unsigned total_bytes;
36 static long long start_time;
37 extern const char* get_basename(const char* filename);
38
39 static long long NOW()
40 {
41     struct timeval tv;
42     gettimeofday(&tv, 0);
43     return ((long long) tv.tv_usec) +
44         1000000LL * ((long long) tv.tv_sec);
45 }
46
47 static void BEGIN()
48 {
49     total_bytes = 0;
50     start_time = NOW();
51 }
52
53 static void END(const char* filename)
54 {
55     long long t = NOW() - start_time;
56     if(total_bytes == 0) return;
57
58     if (t == 0)  /* prevent division by 0 :-) */
59         t = 1000000;
60
61     fprintf(stderr,"%-30s   %lld KB/s (%lld bytes in %lld.%03llds)\n",
62             filename,
63             ((((long long) total_bytes) * 1000000LL) / t) / 1024LL,
64             (long long) total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
65 }
66
67
68 void sync_quit(int fd)
69 {
70     syncmsg msg;
71
72     msg.req.id = ID_QUIT;
73     msg.req.namelen = 0;
74
75     writex(fd, &msg.req, sizeof(msg.req));
76 }
77
78 typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie);
79
80 int sync_ls(int fd, const char *path, sync_ls_cb func, void *cookie)
81 {
82     syncmsg msg;
83     char buf[257];
84     int len;
85
86     len = strlen(path);
87     if(len > 1024) goto fail;
88
89     msg.req.id = ID_LIST;
90     msg.req.namelen = htoll(len);
91
92     if(writex(fd, &msg.req, sizeof(msg.req)) ||
93        writex(fd, path, len)) {
94         goto fail;
95     }
96
97     for(;;) {
98         if(readx(fd, &msg.dent, sizeof(msg.dent))) break;
99         if(msg.dent.id == ID_DONE) return 0;
100         if(msg.dent.id != ID_DENT) break;
101
102         len = ltohl(msg.dent.namelen);
103         if(len > 256) break;
104
105         if(readx(fd, buf, len)) break;
106         buf[len] = 0;
107
108         func(ltohl(msg.dent.mode),
109              ltohl(msg.dent.size),
110              ltohl(msg.dent.time),
111              buf, cookie);
112     }
113
114 fail:
115     sdb_close(fd);
116     return -1;
117 }
118
119 typedef struct syncsendbuf syncsendbuf;
120
121 struct syncsendbuf {
122     unsigned id;
123     unsigned size;
124     char data[SYNC_DATA_MAX];
125 };
126
127 static syncsendbuf send_buffer;
128
129 int sync_readtime(int fd, const char *path, unsigned *timestamp)
130 {
131     syncmsg msg;
132     int len = strlen(path);
133
134     msg.req.id = ID_STAT;
135     msg.req.namelen = htoll(len);
136
137     if(writex(fd, &msg.req, sizeof(msg.req)) ||
138        writex(fd, path, len)) {
139         return -1;
140     }
141
142     if(readx(fd, &msg.stat, sizeof(msg.stat))) {
143         return -1;
144     }
145
146     if(msg.stat.id != ID_STAT) {
147         return -1;
148     }
149
150     *timestamp = ltohl(msg.stat.time);
151     return 0;
152 }
153
154 static int sync_start_readtime(int fd, const char *path)
155 {
156     syncmsg msg;
157     int len = strlen(path);
158
159     msg.req.id = ID_STAT;
160     msg.req.namelen = htoll(len);
161
162     if(writex(fd, &msg.req, sizeof(msg.req)) ||
163        writex(fd, path, len)) {
164         return -1;
165     }
166
167     return 0;
168 }
169
170 static int sync_finish_readtime(int fd, unsigned int *timestamp,
171                                 unsigned int *mode, unsigned int *size)
172 {
173     syncmsg msg;
174
175     if(readx(fd, &msg.stat, sizeof(msg.stat)))
176         return -1;
177
178     if(msg.stat.id != ID_STAT)
179         return -1;
180
181     *timestamp = ltohl(msg.stat.time);
182     *mode = ltohl(msg.stat.mode);
183     *size = ltohl(msg.stat.size);
184
185     return 0;
186 }
187
188 int sync_readmode(int fd, const char *path, unsigned *mode)
189 {
190     syncmsg msg;
191     int len = strlen(path);
192
193     msg.req.id = ID_STAT;
194     msg.req.namelen = htoll(len);
195
196     if(writex(fd, &msg.req, sizeof(msg.req)) ||
197        writex(fd, path, len)) {
198         return -1;
199     }
200
201     if(readx(fd, &msg.stat, sizeof(msg.stat))) {
202         return -1;
203     }
204
205     if(msg.stat.id != ID_STAT) {
206         return -1;
207     }
208
209     *mode = ltohl(msg.stat.mode);
210     return 0;
211 }
212
213 static int write_data_file(int fd, const char *path, syncsendbuf *sbuf)
214 {
215     int lfd, err = 0;
216
217     lfd = sdb_open(path, O_RDONLY);
218     if(lfd < 0) {
219         fprintf(stderr,"cannot open '%s': %s\n", path, strerror(errno));
220         return -1;
221     }
222
223     sbuf->id = ID_DATA;
224     for(;;) {
225         int ret;
226
227         ret = sdb_read(lfd, sbuf->data, SYNC_DATA_MAX);
228         if(!ret)
229             break;
230
231         if(ret < 0) {
232             if(errno == EINTR)
233                 continue;
234             fprintf(stderr,"cannot read '%s': %s\n", path, strerror(errno));
235             break;
236         }
237
238         sbuf->size = htoll(ret);
239         if(writex(fd, sbuf, sizeof(unsigned) * 2 + ret)){
240             err = -1;
241             break;
242         }
243         total_bytes += ret;
244     }
245
246     sdb_close(lfd);
247     return err;
248 }
249
250 static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf)
251 {
252     int err = 0;
253     int total = 0;
254
255     sbuf->id = ID_DATA;
256     while (total < size) {
257         int count = size - total;
258         if (count > SYNC_DATA_MAX) {
259             count = SYNC_DATA_MAX;
260         }
261
262         memcpy(sbuf->data, &file_buffer[total], count);
263         sbuf->size = htoll(count);
264         if(writex(fd, sbuf, sizeof(unsigned) * 2 + count)){
265             err = -1;
266             break;
267         }
268         total += count;
269         total_bytes += count;
270     }
271
272     return err;
273 }
274
275 #ifdef HAVE_SYMLINKS
276 static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
277 {
278     int len, ret;
279
280     len = readlink(path, sbuf->data, SYNC_DATA_MAX-1);
281     if(len < 0) {
282         fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
283         return -1;
284     }
285     sbuf->data[len] = '\0';
286
287     sbuf->size = htoll(len + 1);
288     sbuf->id = ID_DATA;
289
290     ret = writex(fd, sbuf, sizeof(unsigned) * 2 + len + 1);
291     if(ret)
292         return -1;
293
294     total_bytes += len + 1;
295
296     return 0;
297 }
298 #endif
299
300 static int sync_send(int fd, const char *lpath, const char *rpath,
301                      unsigned mtime, mode_t mode, int verifyApk)
302 {
303     syncmsg msg;
304     int len, r;
305     syncsendbuf *sbuf = &send_buffer;
306     char* file_buffer = NULL;
307     int size = 0;
308     char tmp[64];
309
310     len = strlen(rpath);
311     if(len > 1024) goto fail;
312
313     snprintf(tmp, sizeof(tmp), ",%d", mode);
314     r = strlen(tmp);
315 #if 0 /* tizen specific */
316     if (verifyApk) {
317         int lfd;
318         zipfile_t zip;
319         zipentry_t entry;
320         int amt;
321
322         // if we are transferring an APK file, then sanity check to make sure
323         // we have a real zip file that contains an AndroidManifest.xml
324         // this requires that we read the entire file into memory.
325         lfd = sdb_open(lpath, O_RDONLY);
326         if(lfd < 0) {
327             fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
328             return -1;
329         }
330
331         size = sdb_lseek(lfd, 0, SEEK_END);
332         if (size == -1 || -1 == sdb_lseek(lfd, 0, SEEK_SET)) {
333             fprintf(stderr, "error seeking in file '%s'\n", lpath);
334             sdb_close(lfd);
335             return 1;
336         }
337
338         file_buffer = (char *)malloc(size);
339         if (file_buffer == NULL) {
340             fprintf(stderr, "could not allocate buffer for '%s'\n",
341                     lpath);
342             sdb_close(lfd);
343             return 1;
344         }
345         amt = sdb_read(lfd, file_buffer, size);
346         if (amt != size) {
347             fprintf(stderr, "error reading from file: '%s'\n", lpath);
348             sdb_close(lfd);
349             free(file_buffer);
350             return 1;
351         }
352
353         sdb_close(lfd);
354
355         zip = init_zipfile(file_buffer, size);
356         if (zip == NULL) {
357             fprintf(stderr, "file '%s' is not a valid zip file\n",
358                     lpath);
359             free(file_buffer);
360             return 1;
361         }
362
363         entry = lookup_zipentry(zip, "AndroidManifest.xml");
364         release_zipfile(zip);
365         if (entry == NULL) {
366             fprintf(stderr, "file '%s' does not contain AndroidManifest.xml\n",
367                     lpath);
368             free(file_buffer);
369             return 1;
370         }
371     }
372 #endif
373     msg.req.id = ID_SEND;
374     msg.req.namelen = htoll(len + r);
375
376     if(writex(fd, &msg.req, sizeof(msg.req)) ||
377        writex(fd, rpath, len) || writex(fd, tmp, r)) {
378         free(file_buffer);
379         goto fail;
380     }
381
382     if (file_buffer) {
383         write_data_buffer(fd, file_buffer, size, sbuf);
384         free(file_buffer);
385     } else if (S_ISREG(mode))
386         write_data_file(fd, lpath, sbuf);
387 #ifdef HAVE_SYMLINKS
388     else if (S_ISLNK(mode))
389         write_data_link(fd, lpath, sbuf);
390 #endif
391     else
392         goto fail;
393
394     msg.data.id = ID_DONE;
395     msg.data.size = htoll(mtime);
396     if(writex(fd, &msg.data, sizeof(msg.data)))
397         goto fail;
398
399     if(readx(fd, &msg.status, sizeof(msg.status)))
400         return -1;
401
402     if(msg.status.id != ID_OKAY) {
403         if(msg.status.id == ID_FAIL) {
404             len = ltohl(msg.status.msglen);
405             if(len > 256) len = 256;
406             if(readx(fd, sbuf->data, len)) {
407                 return -1;
408             }
409             sbuf->data[len] = 0;
410         } else
411             strcpy(sbuf->data, "unknown reason");
412
413         fprintf(stderr,"failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
414         return -1;
415     }
416
417     return 0;
418
419 fail:
420     fprintf(stderr,"protocol failure\n");
421     sdb_close(fd);
422     return -1;
423 }
424
425 static int mkdirs(char *name)
426 {
427     int ret;
428     char *x = name + 1;
429
430     for(;;) {
431         x = sdb_dirstart(x);
432         if(x == 0) return 0;
433         *x = 0;
434         ret = sdb_mkdir(name, 0775);
435         *x = OS_PATH_SEPARATOR;
436         if((ret < 0) && (errno != EEXIST)) {
437             return ret;
438         }
439         x++;
440     }
441     return 0;
442 }
443
444 int sync_recv(int fd, const char *rpath, const char *lpath)
445 {
446     syncmsg msg;
447     int len;
448     int lfd = -1;
449     char *buffer = send_buffer.data;
450     unsigned id;
451
452     len = strlen(rpath);
453     if(len > 1024) return -1;
454
455     msg.req.id = ID_RECV;
456     msg.req.namelen = htoll(len);
457     if(writex(fd, &msg.req, sizeof(msg.req)) ||
458        writex(fd, rpath, len)) {
459         return -1;
460     }
461
462     if(readx(fd, &msg.data, sizeof(msg.data))) {
463         return -1;
464     }
465     id = msg.data.id;
466
467     if((id == ID_DATA) || (id == ID_DONE)) {
468         sdb_unlink(lpath);
469         mkdirs((char *)lpath);
470         lfd = sdb_creat(lpath, 0644);
471         if(lfd < 0) {
472             fprintf(stderr,"cannot create '%s': %s\n", lpath, strerror(errno));
473             return -1;
474         }
475         goto handle_data;
476     } else {
477         goto remote_error;
478     }
479
480     for(;;) {
481         if(readx(fd, &msg.data, sizeof(msg.data))) {
482             return -1;
483         }
484         id = msg.data.id;
485
486     handle_data:
487         len = ltohl(msg.data.size);
488         if(id == ID_DONE) break;
489         if(id != ID_DATA) goto remote_error;
490         if(len > SYNC_DATA_MAX) {
491             fprintf(stderr,"data overrun\n");
492             sdb_close(lfd);
493             return -1;
494         }
495
496         if(readx(fd, buffer, len)) {
497             sdb_close(lfd);
498             return -1;
499         }
500
501         if(writex(lfd, buffer, len)) {
502             fprintf(stderr,"cannot write '%s': %s\n", rpath, strerror(errno));
503             sdb_close(lfd);
504             return -1;
505         }
506
507         total_bytes += len;
508     }
509
510     sdb_close(lfd);
511     return 0;
512
513 remote_error:
514     sdb_close(lfd);
515     sdb_unlink(lpath);
516
517     if(id == ID_FAIL) {
518         len = ltohl(msg.data.size);
519         if(len > 256) len = 256;
520         if(readx(fd, buffer, len)) {
521             return -1;
522         }
523         buffer[len] = 0;
524     } else {
525         memcpy(buffer, &id, 4);
526         buffer[4] = 0;
527 //        strcpy(buffer,"unknown reason");
528     }
529     fprintf(stderr,"failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer);
530     return 0;
531 }
532
533
534
535 /* --- */
536
537
538 static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
539                           const char *name, void *cookie)
540 {
541     printf("%08x %08x %08x %s\n", mode, size, time, name);
542 }
543
544 int do_sync_ls(const char *path)
545 {
546     int fd = sdb_connect("sync:");
547     if(fd < 0) {
548         fprintf(stderr,"error: %s\n", sdb_error());
549         return 1;
550     }
551
552     if(sync_ls(fd, path, do_sync_ls_cb, 0)) {
553         return 1;
554     } else {
555         sync_quit(fd);
556         return 0;
557     }
558 }
559
560 typedef struct copyinfo copyinfo;
561
562 struct copyinfo
563 {
564     copyinfo *next;
565     const char *src;
566     const char *dst;
567     unsigned int time;
568     unsigned int mode;
569     unsigned int size;
570     int flag;
571     //char data[0];
572 };
573
574 copyinfo *mkcopyinfo(const char *spath, const char *dpath,
575                      const char *name, int isdir)
576 {
577     int slen = strlen(spath);
578     int dlen = strlen(dpath);
579     int nlen = strlen(name);
580     int ssize = slen + nlen + 2;
581     int dsize = dlen + nlen + 2;
582
583     copyinfo *ci = malloc(sizeof(copyinfo) + ssize + dsize);
584     if(ci == 0) {
585         fprintf(stderr,"out of memory\n");
586         abort();
587     }
588
589     ci->next = 0;
590     ci->time = 0;
591     ci->mode = 0;
592     ci->size = 0;
593     ci->flag = 0;
594     ci->src = (const char*)(ci + 1);
595     ci->dst = ci->src + ssize;
596     snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name);
597     snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name);
598
599 //    fprintf(stderr,"mkcopyinfo('%s','%s')\n", ci->src, ci->dst);
600     return ci;
601 }
602
603
604 static int local_build_list(copyinfo **filelist,
605                             const char *lpath, const char *rpath)
606 {
607     DIR *d;
608     struct dirent *de;
609     struct stat st;
610     copyinfo *dirlist = 0;
611     copyinfo *ci, *next;
612
613 //    fprintf(stderr,"local_build_list('%s','%s')\n", lpath, rpath);
614
615     d = opendir(lpath);
616     if(d == 0) {
617         fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
618         return -1;
619     }
620
621     while((de = readdir(d))) {
622         char stat_path[PATH_MAX];
623         char *name = de->d_name;
624
625         if(name[0] == '.') {
626             if(name[1] == 0) continue;
627             if((name[1] == '.') && (name[2] == 0)) continue;
628         }
629
630         /*
631          * We could use d_type if HAVE_DIRENT_D_TYPE is defined, but reiserfs
632          * always returns DT_UNKNOWN, so we just use stat() for all cases.
633          */
634         if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path))
635             continue;
636         strcpy(stat_path, lpath);
637         strcat(stat_path, de->d_name);
638         stat(stat_path, &st);
639
640         if (S_ISDIR(st.st_mode)) {
641             ci = mkcopyinfo(lpath, rpath, name, 1);
642             ci->next = dirlist;
643             dirlist = ci;
644         } else {
645             ci = mkcopyinfo(lpath, rpath, name, 0);
646             if(lstat(ci->src, &st)) {
647                 fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
648                 closedir(d);
649
650                 return -1;
651             }
652             if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
653                 fprintf(stderr, "skipping special file\n");
654                 free(ci);
655             } else {
656                 ci->time = st.st_mtime;
657                 ci->mode = st.st_mode;
658                 ci->size = st.st_size;
659                 ci->next = *filelist;
660                 *filelist = ci;
661             }
662         }
663     }
664
665     closedir(d);
666
667     for(ci = dirlist; ci != 0; ci = next) {
668         next = ci->next;
669         local_build_list(filelist, ci->src, ci->dst);
670         free(ci);
671     }
672
673     return 0;
674 }
675
676
677 static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps, int listonly)
678 {
679     copyinfo *filelist = 0;
680     copyinfo *ci, *next;
681     int pushed = 0;
682     int skipped = 0;
683
684     if((lpath[0] == 0) || (rpath[0] == 0)) return -1;
685     if(lpath[strlen(lpath) - 1] != '/') {
686         int  tmplen = strlen(lpath)+2;
687         char *tmp = malloc(tmplen);
688         if(tmp == 0) return -1;
689         snprintf(tmp, tmplen, "%s/",lpath);
690         lpath = tmp;
691     }
692     if(rpath[strlen(rpath) - 1] != '/') {
693         int tmplen = strlen(rpath)+2;
694         char *tmp = malloc(tmplen);
695         if(tmp == 0) return -1;
696         snprintf(tmp, tmplen, "%s/",rpath);
697         rpath = tmp;
698     }
699
700     if(local_build_list(&filelist, lpath, rpath)) {
701         return -1;
702     }
703
704     if(checktimestamps){
705         for(ci = filelist; ci != 0; ci = ci->next) {
706             if(sync_start_readtime(fd, ci->dst)) {
707                 return 1;
708             }
709         }
710         for(ci = filelist; ci != 0; ci = ci->next) {
711             unsigned int timestamp, mode, size;
712             if(sync_finish_readtime(fd, &timestamp, &mode, &size))
713                 return 1;
714             if(size == ci->size) {
715                 /* for links, we cannot update the atime/mtime */
716                 if((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
717                     (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
718                     ci->flag = 1;
719             }
720         }
721     }
722     for(ci = filelist; ci != 0; ci = next) {
723         next = ci->next;
724         if(ci->flag == 0) {
725             fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
726             if(!listonly &&
727                sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */)){
728                 return 1;
729             }
730             pushed++;
731         } else {
732             skipped++;
733         }
734         free(ci);
735     }
736
737     fprintf(stderr,"%d file%s pushed. %d file%s skipped.\n",
738             pushed, (pushed == 1) ? "" : "s",
739             skipped, (skipped == 1) ? "" : "s");
740
741     return 0;
742 }
743
744
745 int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int isUtf8)
746 {
747     struct stat st;
748     unsigned mode;
749     int fd;
750     char *tmp = NULL;
751     char *utf8 = NULL;
752     int ret = 0;
753
754     fd = sdb_connect("sync:");
755     if(fd < 0) {
756         fprintf(stderr,"error: %s\n", sdb_error());
757         return 1;
758     }
759
760     if(stat(lpath, &st)) {
761         fprintf(stderr,"cannot stat '%s': %s\n", lpath, strerror(errno));
762         sync_quit(fd);
763         return 1;
764     }
765
766     if(S_ISDIR(st.st_mode)) {
767         BEGIN();
768         if(copy_local_dir_remote(fd, lpath, rpath, 0, 0)) {
769             return 1;
770         } else {
771             END(get_basename(lpath));
772             sync_quit(fd);
773         }
774     } else {
775         if(sync_readmode(fd, rpath, &mode)) {
776             return 1;
777         }
778
779         if((mode != 0) && S_ISDIR(mode)) {
780                 /* if we're copying a local file to a remote directory,
781                 ** we *really* want to copy to remotedir + "/" + localfilename
782                 */
783             const char *name = sdb_dirstop(lpath);
784             if(name == 0) {
785                 name = lpath;
786             } else {
787                 name++;
788             }
789             int  tmplen = strlen(name) + strlen(rpath) + 2;
790             tmp = malloc(strlen(name) + strlen(rpath) + 2);
791             if(tmp == 0) return 1;
792             snprintf(tmp, tmplen, "%s/%s", rpath, name);
793             if (isUtf8 != 0) { //ansi to utf8
794                 utf8 = ansi_to_utf8(tmp);
795                 rpath = utf8;
796             } else {
797                 rpath = tmp;
798             }
799         }
800         BEGIN();
801         if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk)) {
802             ret = 1;
803             goto cleanup;
804         } else {
805             END(get_basename(lpath));
806             sync_quit(fd);
807             ret = 0;
808             goto cleanup;
809         }
810     }
811     return 0;
812 cleanup:
813     if (tmp != NULL) {
814         free(tmp);
815     }
816     if (utf8 != NULL) {
817         free(utf8);
818     }
819     return ret;
820 }
821
822
823 typedef struct {
824     copyinfo **filelist;
825     copyinfo **dirlist;
826     const char *rpath;
827     const char *lpath;
828 } sync_ls_build_list_cb_args;
829
830 void
831 sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
832                       const char *name, void *cookie)
833 {
834     sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie;
835     copyinfo *ci;
836
837     if (S_ISDIR(mode)) {
838         copyinfo **dirlist = args->dirlist;
839
840         /* Don't try recursing down "." or ".." */
841         if (name[0] == '.') {
842             if (name[1] == '\0') return;
843             if ((name[1] == '.') && (name[2] == '\0')) return;
844         }
845
846         ci = mkcopyinfo(args->rpath, args->lpath, name, 1);
847         ci->next = *dirlist;
848         *dirlist = ci;
849     } else if (S_ISREG(mode) || S_ISLNK(mode)) {
850         copyinfo **filelist = args->filelist;
851
852         ci = mkcopyinfo(args->rpath, args->lpath, name, 0);
853         ci->time = time;
854         ci->mode = mode;
855         ci->size = size;
856         ci->next = *filelist;
857         *filelist = ci;
858     } else {
859         fprintf(stderr, "skipping special file '%s'\n", name);
860     }
861 }
862
863 static int remote_build_list(int syncfd, copyinfo **filelist,
864                              const char *rpath, const char *lpath)
865 {
866     copyinfo *dirlist = NULL;
867     sync_ls_build_list_cb_args args;
868
869     args.filelist = filelist;
870     args.dirlist = &dirlist;
871     args.rpath = rpath;
872     args.lpath = lpath;
873
874     /* Put the files/dirs in rpath on the lists. */
875     if (sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) {
876         return 1;
877     }
878
879     /* Recurse into each directory we found. */
880     while (dirlist != NULL) {
881         copyinfo *next = dirlist->next;
882         if (remote_build_list(syncfd, filelist, dirlist->src, dirlist->dst)) {
883             return 1;
884         }
885         free(dirlist);
886         dirlist = next;
887     }
888
889     return 0;
890 }
891
892 static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath,
893                                  int checktimestamps)
894 {
895     copyinfo *filelist = 0;
896     copyinfo *ci, *next;
897     int pulled = 0;
898     int skipped = 0;
899
900     /* Make sure that both directory paths end in a slash. */
901     if (rpath[0] == 0 || lpath[0] == 0) return -1;
902     if (rpath[strlen(rpath) - 1] != '/') {
903         int  tmplen = strlen(rpath) + 2;
904         char *tmp = malloc(tmplen);
905         if (tmp == 0) return -1;
906         snprintf(tmp, tmplen, "%s/", rpath);
907         rpath = tmp;
908     }
909     if (lpath[strlen(lpath) - 1] != '/') {
910         int  tmplen = strlen(lpath) + 2;
911         char *tmp = malloc(tmplen);
912         if (tmp == 0) return -1;
913         snprintf(tmp, tmplen, "%s/", lpath);
914         lpath = tmp;
915     }
916
917     fprintf(stderr, "pull: building file list...\n");
918     /* Recursively build the list of files to copy. */
919     if (remote_build_list(fd, &filelist, rpath, lpath)) {
920         return -1;
921     }
922
923 #if 0
924     if (checktimestamps) {
925         for (ci = filelist; ci != 0; ci = ci->next) {
926             if (sync_start_readtime(fd, ci->dst)) {
927                 return 1;
928             }
929         }
930         for (ci = filelist; ci != 0; ci = ci->next) {
931             unsigned int timestamp, mode, size;
932             if (sync_finish_readtime(fd, &timestamp, &mode, &size))
933                 return 1;
934             if (size == ci->size) {
935                 /* for links, we cannot update the atime/mtime */
936                 if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
937                     (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
938                     ci->flag = 1;
939             }
940         }
941     }
942 #endif
943     for (ci = filelist; ci != 0; ci = next) {
944         next = ci->next;
945         if (ci->flag == 0) {
946             fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
947             if (sync_recv(fd, ci->src, ci->dst)) {
948                 return 1;
949             }
950             pulled++;
951         } else {
952             skipped++;
953         }
954         free(ci);
955     }
956
957     fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
958             pulled, (pulled == 1) ? "" : "s",
959             skipped, (skipped == 1) ? "" : "s");
960
961     return 0;
962 }
963
964 int do_sync_pull(const char *rpath, const char *lpath)
965 {
966     unsigned mode;
967     struct stat st;
968
969     int fd;
970
971     fd = sdb_connect("sync:");
972     if(fd < 0) {
973         fprintf(stderr,"error: %s\n", sdb_error());
974         return 1;
975     }
976
977     if(sync_readmode(fd, rpath, &mode)) {
978         return 1;
979     }
980     if(mode == 0) {
981         fprintf(stderr,"'%s': No such file or directory\n", rpath);
982         sync_quit(fd);
983         return 1;
984     }
985
986     if(S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
987         if(stat(lpath, &st) == 0) {
988             if(S_ISDIR(st.st_mode)) {
989                     /* if we're copying a remote file to a local directory,
990                     ** we *really* want to copy to localdir + "/" + remotefilename
991                     */
992                 const char *name = sdb_dirstop(rpath);
993                 if(name == 0) {
994                     name = rpath;
995                 } else {
996                     name++;
997                 }
998                 int  tmplen = strlen(name) + strlen(lpath) + 2;
999                 char *tmp = malloc(tmplen);
1000                 if(tmp == 0) return 1;
1001                 snprintf(tmp, tmplen, "%s/%s", lpath, name);
1002                 lpath = tmp;
1003             }
1004         }
1005         BEGIN();
1006         if(sync_recv(fd, rpath, lpath)) {
1007             return 1;
1008         } else {
1009             END(get_basename(rpath));
1010             sync_quit(fd);
1011             return 0;
1012         }
1013     } else if(S_ISDIR(mode)) {
1014         BEGIN();
1015         if (copy_remote_dir_local(fd, rpath, lpath, 0)) {
1016             return 1;
1017         } else {
1018             END(get_basename(rpath));
1019             sync_quit(fd);
1020             return 0;
1021         }
1022     } else {
1023         fprintf(stderr,"'%s': No such file or directory\n", rpath);
1024         return 1;
1025     }
1026 }
1027
1028 int do_sync_sync(const char *lpath, const char *rpath, int listonly)
1029 {
1030     fprintf(stderr,"syncing %s...\n",rpath);
1031
1032     int fd = sdb_connect("sync:");
1033     if(fd < 0) {
1034         fprintf(stderr,"error: %s\n", sdb_error());
1035         return 1;
1036     }
1037
1038     BEGIN();
1039     if(copy_local_dir_remote(fd, lpath, rpath, 1, listonly)){
1040         return 1;
1041     } else {
1042         END(get_basename(lpath));
1043         sync_quit(fd);
1044         return 0;
1045     }
1046 }