"Fossies" - the Fresh Open Source Software Archive

Member "jpilot-2_0_1/sync.c" (3 Apr 2021, 128907 Bytes) of package /linux/privat/jpilot-2_0_1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sync.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.8.2_vs_2_0_1.

    1 /*******************************************************************************
    2  * sync.c
    3  * A module of J-Pilot http://jpilot.org
    4  *
    5  * Copyright (C) 1999-2014 by Judd Montgomery
    6  *
    7  * This program is free software; you can redistribute it and/or modify
    8  * it under the terms of the GNU General Public License as published by
    9  * the Free Software Foundation; version 2 of the License.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  * GNU General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU General Public License
   17  * along with this program; if not, write to the Free Software
   18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   19  ******************************************************************************/
   20 
   21 /********************************* Includes ***********************************/
   22 #include "config.h"
   23 #include <stdlib.h>
   24 #include <stdio.h>
   25 #include <ctype.h>
   26 #include <string.h>
   27 #include <errno.h>
   28 #include <signal.h>
   29 #include <utime.h>
   30 #include <dirent.h>
   31 #include <sys/wait.h>
   32 #include <sys/types.h>
   33 #include <sys/stat.h>
   34 #include <sys/socket.h>
   35 #ifdef USE_FLOCK
   36 #  include <sys/file.h>
   37 #else
   38 #  include <fcntl.h>
   39 #endif
   40 
   41 #include <pi-socket.h>
   42 #include <pi-dlp.h>
   43 #include <pi-header.h>
   44 #include <pi-file.h>
   45 #include <pi-version.h>
   46 #include <pi-error.h>
   47 #include <pi-macros.h>
   48 
   49 #include "i18n.h"
   50 #include "utils.h"
   51 #include "sync.h"
   52 #include "log.h"
   53 #include "prefs.h"
   54 #include "datebook.h"
   55 #include "plugins.h"
   56 #include "libplugin.h"
   57 #include "password.h"
   58 
   59 /********************************* Constants **********************************/
   60 #define FD_ERROR 1001
   61 
   62 #define MAX_DBNAME 50
   63 
   64 #ifndef min
   65 #  define min(a,b) (((a) < (b)) ? (a) : (b))
   66 #endif
   67 
   68 #define USE_LOCKING
   69 
   70 /* #define PIPE_DEBUG */
   71 /* #define JPILOT_DEBUG */
   72 /* #define SYNC_CAT_DEBUG */
   73 
   74 /******************************* Global vars **********************************/
   75 extern int pipe_to_parent, pipe_from_parent;
   76 extern pid_t glob_child_pid;
   77 
   78 /****************************** Prototypes ************************************/
   79 /* From jpilot.c for restoring sync icon after successful sync */
   80 extern void cb_cancel_sync(GtkWidget *widget, unsigned int flags);
   81 
   82 static int pi_file_install_VFS(const int fd, const char *basename, const int socket, const char *vfspath, progress_func f);
   83 static int findVFSPath(int sd, const char *path, long *volume, char *rpath, int *rpathlen);
   84 
   85 /****************************** Main Code *************************************/
   86 
   87 static void sig_handler(int sig)
   88 {
   89    int status = 0;
   90 
   91    jp_logf(JP_LOG_DEBUG, "caught signal SIGCHLD\n");
   92 
   93    /* wait for any child processes */
   94    waitpid(-1, &status, WNOHANG);
   95 
   96    /* SIGCHLD status is 0 for innocuous events like suspend/resume. */
   97    /* We specifically exit with return code 255 to trigger this cleanup */
   98    if (status > 0) {
   99       glob_child_pid = 0;
  100       cb_cancel_sync(NULL, 0);
  101    }
  102 }
  103 
  104 #ifdef USE_LOCKING
  105 static int sync_lock(int *fd)
  106 {
  107    pid_t pid;
  108    char lock_file[FILENAME_MAX];
  109    int r;
  110    char str[12];
  111 #ifndef USE_FLOCK
  112    struct flock lock;
  113 #endif
  114 
  115    get_home_file_name("sync_pid", lock_file, sizeof(lock_file));
  116    *fd = open(lock_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  117    if (*fd<0) {
  118       jp_logf(JP_LOG_WARN, _("open lock file failed\n"));
  119       return EXIT_FAILURE;
  120    }
  121 #ifndef USE_FLOCK
  122    lock.l_type = F_WRLCK;
  123    lock.l_start = 0;
  124    lock.l_whence = SEEK_SET;
  125    lock.l_len = 0; /* Lock to the end of file */
  126    r = fcntl(*fd, F_SETLK, &lock);
  127 #else
  128    r = flock(*fd, LOCK_EX | LOCK_NB);
  129 #endif
  130    if (r == -1){
  131       jp_logf(JP_LOG_WARN, _("lock failed\n"));
  132       if (read(*fd, str, 10) < 0) {
  133          jp_logf(JP_LOG_WARN, "fread failed %s %d\n", __FILE__, __LINE__);
  134       }
  135       pid = atoi(str);
  136       jp_logf(JP_LOG_FATAL, _("sync file is locked by pid %d\n"), pid);
  137       close(*fd);
  138       return EXIT_FAILURE;
  139    } else {
  140       jp_logf(JP_LOG_DEBUG, "lock succeeded\n");
  141       pid=getpid();
  142       sprintf(str, "%d\n", pid);
  143       if (write(*fd, str, strlen(str)+1) < 0) {
  144          jp_logf(JP_LOG_WARN, "write failed %s %d\n", __FILE__, __LINE__);
  145       }
  146       if (ftruncate(*fd, strlen(str)+1) == -1) {
  147          jp_logf(JP_LOG_WARN, "ftruncate failed %s %d\n", __FILE__, __LINE__);
  148       }
  149    }
  150    return EXIT_SUCCESS;
  151 }
  152 
  153 static int sync_unlock(int fd)
  154 {
  155    pid_t pid;
  156    char lock_file[FILENAME_MAX];
  157    int r;
  158    char str[12];
  159 #ifndef USE_FLOCK
  160    struct flock lock;
  161 #endif
  162 
  163    get_home_file_name("sync_pid", lock_file, sizeof(lock_file));
  164 
  165 #ifndef USE_FLOCK
  166    lock.l_type = F_UNLCK;
  167    lock.l_start = 0;
  168    lock.l_whence = SEEK_SET;
  169    lock.l_len = 0;
  170    r = fcntl(fd, F_SETLK, &lock);
  171 #else
  172    r = flock(fd, LOCK_UN | LOCK_NB);
  173 #endif
  174    if (r == -1) {
  175       jp_logf(JP_LOG_WARN, _("unlock failed\n"));
  176       if (read(fd, str, 10) < 0) {
  177          jp_logf(JP_LOG_WARN, "fread failed %s %d\n", __FILE__, __LINE__);
  178       }
  179       pid = atoi(str);
  180       jp_logf(JP_LOG_WARN, _("sync is locked by pid %d\n"), pid);
  181       close(fd);
  182       return EXIT_FAILURE;
  183    } else {
  184       jp_logf(JP_LOG_DEBUG, "unlock succeeded\n");
  185       if (ftruncate(fd, 0) == -1) {
  186          jp_logf(JP_LOG_WARN, "ftruncate failed %s %d\n", __FILE__, __LINE__);
  187       }
  188       close(fd);
  189    }
  190    return EXIT_SUCCESS;
  191 }
  192 #endif
  193 
  194 static const char *get_error_str(int error)
  195 {
  196    static char buf[10];
  197 
  198    switch (error) {
  199     case SYNC_ERROR_BIND:
  200       return "SYNC_ERROR_BIND";
  201     case SYNC_ERROR_LISTEN:
  202       return "SYNC_ERROR_LISTEN";
  203     case SYNC_ERROR_OPEN_CONDUIT:
  204       return "SYNC_ERROR_OPEN_CONDUIT";
  205     case SYNC_ERROR_PI_ACCEPT:
  206       return "SYNC_ERROR_PI_ACCEPT";
  207     case SYNC_ERROR_READSYSINFO:
  208       return "SYNC_ERROR_PI_CONNECT";
  209     case SYNC_ERROR_PI_CONNECT:
  210       return "SYNC_ERROR_READSYSINFO";
  211     case SYNC_ERROR_NOT_SAME_USER:
  212       return "SYNC_ERROR_NOT_SAME_USER";
  213     case SYNC_ERROR_NOT_SAME_USERID:
  214       return "SYNC_ERROR_NOT_SAME_USERID";
  215     case SYNC_ERROR_NULL_USERID:
  216       return "SYNC_ERROR_NULL_USERID";
  217     default:
  218       sprintf(buf, "%d", error);
  219       return NULL;
  220    }
  221 }
  222 
  223 /* Attempt to match records
  224  *
  225  * Ideally, one would have comparison routines on a per DB_name basis.
  226  * This involves a lot of overhead and packing/unpacking of records
  227  * and the routines are not written.
  228  *
  229  * A simpler way to compare records is to use memcmp. Some databases, 
  230  * however, do not pack tightly into memory and have gaps which are 
  231  * do-not-cares during comparison. These gaps can assume any value
  232  * but for comparison they are zeroed out, the same value that 
  233  * pilot-link uses.
  234  *
  235  * For databases that we have no knowledge of only simple comparisons
  236  * such as record length are possible.  This is almost always good 
  237  * enough but single character changes will not be caught. */ 
  238 static int match_records(char *DB_name,
  239                   void *rrec, int rrec_len, int rattr, int rcategory,
  240                   void *lrec, int lrec_len, int lattr, int lcategory)
  241 {
  242 
  243    if (!rrec || !lrec)         return FALSE;
  244    if (rrec_len != lrec_len)   return FALSE;
  245    if (rcategory != lcategory) return FALSE;
  246    if ((rattr & dlpRecAttrSecret) != (lattr & dlpRecAttrSecret)) {
  247       return FALSE;
  248    }
  249 
  250    /* memcmp works for a few specific databases */
  251    if (!strcmp(DB_name,"DatebookDB") ||
  252        !strcmp(DB_name,"CalendarDB-PDat")) {
  253       /* Hack for gapfill byte */
  254       set_byte(rrec+7,0);
  255       return !(memcmp(lrec, rrec, lrec_len));
  256    }
  257 
  258    if (!strcmp(DB_name,"AddressDB"))
  259       return !(memcmp(lrec, rrec, lrec_len));
  260 
  261    if (!strcmp(DB_name,"ContactsDB-PAdd")) {
  262       /* Hack for gapfill bytes */
  263       set_byte(rrec+4,(get_byte(rrec+4)) & 0x0F);
  264       set_byte(rrec+6,0);
  265       set_byte(lrec+16,0);
  266       set_byte(rrec+16,0);
  267       return !(memcmp(lrec, rrec, lrec_len));
  268    }
  269 
  270    if (!strcmp(DB_name,"ToDoDB"))
  271       return !(memcmp(lrec, rrec, lrec_len));
  272 
  273    if (!strcmp(DB_name,"MemoDB")   || 
  274        !strcmp(DB_name,"Memo32DB") ||
  275        !strcmp(DB_name,"MemosDB-PMem")) {
  276       return !(memcmp(lrec, rrec, lrec_len));
  277    }
  278 
  279    if (!strcmp(DB_name,"ExpenseDB")) {
  280       /* Hack for gapfill byte */
  281       set_byte(rrec+5,0);
  282       return !(memcmp(lrec, rrec, lrec_len));
  283    }
  284 
  285    if (!strcmp(DB_name,"Keys-Gtkr"))
  286       return !(memcmp(lrec, rrec, lrec_len));
  287 
  288    /* Lengths match and no other checks possible */
  289    return TRUE;
  290 
  291 }
  292 
  293 static void filename_make_legal(char *s)
  294 {
  295    char *p;
  296 
  297    for (p=s; *p; p++) {
  298       if (*p=='/') {
  299          *p='?';
  300       }
  301    }
  302 }
  303 
  304 static int wait_for_response(int sd)
  305 {
  306    int i;
  307    char buf[1024];
  308    int buf_len, ret;
  309    fd_set fds;
  310    struct timeval tv;
  311    int command;
  312 
  313 #ifdef PIPE_DEBUG
  314    printf("child: wait_for_response()\n");
  315 #endif
  316 
  317    /* For jpilot-sync */
  318    /* We should never get to this function, but just in case. */
  319    if (pipe_to_parent==STDOUT_FILENO) {
  320       return PIPE_SYNC_CANCEL;
  321    }
  322 
  323    /* Prevent the palm from timing out */
  324    pi_watchdog(sd, 7);
  325    /* 120 iterations is 2 minutes */
  326    for (i=0; i<120; i++) {
  327 #ifdef PIPE_DEBUG
  328       printf("child wait_for_response() for\n");
  329       printf("pipe_from_parent = %d\n", pipe_from_parent);
  330 #endif
  331       /* Linux modifies tv in the select call */
  332       tv.tv_sec=1;
  333       tv.tv_usec=0;
  334       FD_ZERO(&fds);
  335       FD_SET(pipe_from_parent, &fds);
  336       ret=select(pipe_from_parent+1, &fds, NULL, NULL, &tv);
  337       /* if (ret<0) {
  338          int err=errno;
  339          jp_logf(JP_LOG_WARN, "sync select %s\n", strerror(err));
  340       }*/
  341       if (ret==0) continue;
  342       /* this happens when waiting, probably a signal in pilot-link */
  343 
  344       if (!FD_ISSET(pipe_from_parent, &fds)) {
  345 #ifdef PIPE_DEBUG
  346          printf("sync !FD_ISSET\n");
  347 #endif
  348          continue;
  349       }
  350       buf[0]='\0';
  351       /* Read until newline, null, or end */
  352       buf_len=0;
  353       for (i=0; i<1022; i++) {
  354          ret = read(pipe_from_parent, &(buf[i]), 1);
  355          /* Error */
  356          if (ret<1) {
  357             int err=errno;
  358             printf("ret<1\n");
  359             printf("read from parent: %s\n", strerror(err));
  360             jp_logf(JP_LOG_WARN, "read from parent %s\n", strerror(err));
  361             break;
  362          }
  363 #ifdef PIPE_DEBUG
  364          printf("ret=%d read %d[%d]\n", ret, buf[i], buf[i]);
  365 #endif
  366          /* EOF */
  367 #ifdef PIPE_DEBUG
  368          if (ret==0) {
  369             printf("ret==0\n");
  370             break;
  371          }
  372 #endif
  373          buf_len++;
  374          if ((buf[i]=='\n')) break;
  375       }
  376       if (buf_len >= 1022) {
  377          buf[1022] = '\0';
  378       } else {
  379          if (buf_len > 0) {
  380             buf[buf_len]='\0';
  381          }
  382       }
  383 
  384       /* Look for the command */
  385       sscanf(buf, "%d:", &command);
  386 #ifdef PIPE_DEBUG
  387       printf("command from parent=%d\n", command);
  388       printf("buf=[%s]\n", buf);
  389 #endif
  390       break;
  391    }
  392 
  393    /* Back to normal */
  394    pi_watchdog(sd, 0);
  395 
  396    return command;
  397 }
  398 
  399 static int jp_pilot_connect(int *Psd, const char *device)
  400 {
  401    int sd;
  402    int ret;
  403    struct  SysInfo sys_info;
  404 
  405    *Psd=0;
  406 
  407    sd = pi_socket(PI_AF_PILOT, PI_SOCK_STREAM, PI_PF_DLP);
  408    if (sd < 0) {
  409       int err = errno;
  410       perror("pi_socket");
  411       jp_logf(JP_LOG_WARN, "pi_socket %s\n", strerror(err));
  412       return EXIT_FAILURE;
  413    }
  414 
  415    ret = pi_bind(sd, device);
  416    if (ret < 0) {
  417       jp_logf(JP_LOG_WARN, "pi_bind error: %s %s\n", device, strerror(errno));
  418       jp_logf(JP_LOG_WARN, _("Check your sync port and settings\n"));
  419       pi_close(sd);
  420       return SYNC_ERROR_BIND;
  421    }
  422 
  423    ret = pi_listen(sd, 1);
  424    if (ret < 0) {
  425       perror("pi_listen");
  426       jp_logf(JP_LOG_WARN, "pi_listen %s\n", strerror(errno));
  427       pi_close(sd);
  428       return SYNC_ERROR_LISTEN;
  429    }
  430 
  431    sd = pi_accept(sd, 0, 0);
  432    if(sd < 0) {
  433       perror("pi_accept");
  434       jp_logf(JP_LOG_WARN, "pi_accept %s\n", strerror(errno));
  435       pi_close(sd);
  436       return SYNC_ERROR_PI_ACCEPT;
  437    }
  438 
  439    /* We must do this to take care of the password being required to sync
  440     * on Palm OS 4.x */
  441    if (dlp_ReadSysInfo(sd, &sys_info) < 0) {
  442       jp_logf(JP_LOG_WARN, "dlp_ReadSysInfo error\n");
  443       pi_close(sd);
  444       return SYNC_ERROR_READSYSINFO;
  445    }
  446 
  447    *Psd=sd;
  448 
  449    return EXIT_SUCCESS;
  450 }
  451 
  452 static void free_file_name_list(GList **Plist)
  453 {
  454    GList *list, *temp_list;
  455 
  456    if (!Plist) return;
  457    list = *Plist;
  458 
  459    for (temp_list = list; temp_list; temp_list = temp_list->next) {
  460       if (temp_list->data) {
  461          free(temp_list->data);
  462       }
  463    }
  464    g_list_free(list);
  465    *Plist=NULL;
  466 }
  467 
  468 static void move_removed_apps(GList *file_list)
  469 {
  470    DIR *dir;
  471    struct dirent *dirent;
  472    char full_backup_path[FILENAME_MAX];
  473    char full_remove_path[FILENAME_MAX];
  474    char full_backup_file[FILENAME_MAX];
  475    char full_remove_file[FILENAME_MAX];
  476    char home_dir[FILENAME_MAX];
  477    GList *list, *temp_list;
  478    int found;
  479 
  480    list = file_list;
  481 
  482 #ifdef JPILOT_DEBUG
  483    printf("printing file list\n");
  484    for (temp_list = file_list; temp_list; temp_list = temp_list->next) {
  485       if (temp_list->data) {
  486          printf("File list [%s]\n", (char *)temp_list->data);
  487       }
  488    }
  489 #endif
  490 
  491    get_home_file_name("", home_dir, sizeof(home_dir));
  492 
  493    /* Make sure the removed directory exists */
  494    g_snprintf(full_remove_path, sizeof(full_remove_path), "%s/backup_removed", home_dir);
  495    mkdir(full_remove_path, 0700);
  496 
  497 
  498    g_snprintf(full_backup_path, sizeof(full_backup_path), "%s/backup/", home_dir);
  499    jp_logf(JP_LOG_DEBUG, "opening [%s]\n", full_backup_path);
  500    dir = opendir(full_backup_path);
  501    if (dir) {
  502       while ((dirent = readdir(dir))) {
  503          jp_logf(JP_LOG_DEBUG, "dirent->d_name = [%s]\n", dirent->d_name);
  504          found=FALSE;
  505          if (!strcmp(dirent->d_name, ".")) continue;
  506          if (!strcmp(dirent->d_name, "..")) continue;
  507          for (temp_list = list; temp_list; temp_list = temp_list->next) {
  508             if (temp_list->data) {
  509                if (!strcmp((char *)temp_list->data, dirent->d_name)) {
  510                   found=TRUE;
  511                   break;
  512                }
  513             }
  514          }
  515          if (!found) {
  516             g_snprintf(full_backup_file, sizeof(full_backup_file), "%s/backup/%s", home_dir, dirent->d_name);
  517             g_snprintf(full_remove_file, sizeof(full_remove_file), "%s/backup_removed/%s", home_dir, dirent->d_name);
  518             jp_logf(JP_LOG_DEBUG, "[%s] not found\n", dirent->d_name);
  519             jp_logf(JP_LOG_DEBUG, "  moving [%s]\n  to [%s]\n", full_backup_file, full_remove_file);
  520             rename(full_backup_file, full_remove_file);
  521          }
  522       }
  523       closedir(dir);
  524    }
  525 }
  526 
  527 static int is_backup_dir(char *name)
  528 {
  529    int i;
  530 
  531    /* backup dirs are of the form backupMMDDHHMM */
  532    if (strncmp(name, "backup", 6)) {
  533       return FALSE;
  534    }
  535    for (i=6; i<14; i++) {
  536       if (name[i]=='\0') {
  537          return FALSE;
  538       }
  539       if (!isdigit(name[i])) {
  540          return FALSE;
  541       }
  542    }
  543    if (name[i]!='\0') {
  544       return FALSE;
  545    }
  546    return TRUE;
  547 }
  548 
  549 static int compare_back_dates(char *s1, char *s2)
  550 {
  551    /* backupMMDDhhmm */
  552    int i1, i2;
  553 
  554    if ((strlen(s1) < 8) || (strlen(s2) < 8)) {
  555       return 0;
  556    }
  557    i1 = atoi(&s1[6]);
  558    i2 = atoi(&s2[6]);
  559    /* Try to guess the year crossover with a 6 month window */
  560    if (((i1/1000000) <= 3) && ((i2/1000000) >= 10)) {
  561       return 1;
  562    }
  563    if (((i1/1000000) >= 10) && ((i2/1000000) <= 3)) {
  564       return 2;
  565    }
  566    if (i1>i2) {
  567       return 1;
  568    }
  569    if (i1<i2) {
  570       return 2;
  571    }
  572    return 0;
  573 }
  574 
  575 static int sync_remove_r(char *full_path)
  576 {
  577    DIR *dir;
  578    struct dirent *dirent;
  579    char full_src[300];
  580    char last4[8];
  581    int len;
  582 
  583    dir = opendir(full_path);
  584    if (dir) {
  585       while ((dirent = readdir(dir))) {
  586          sprintf(full_src, "%s/%s", full_path, dirent->d_name);
  587          /* Just to make sure nothing too wrong is deleted */
  588          len = strlen(dirent->d_name);
  589          if (len < 4) {
  590             continue;
  591          }
  592          g_strlcpy(last4, dirent->d_name+len-4, 5);
  593          if ((strcmp(last4, ".pdb")==0) ||
  594              (strcmp(last4, ".prc")==0) ||
  595              (strcmp(last4, ".pqa")==0)) {
  596             unlink(full_src);
  597          }
  598       }
  599       closedir(dir);
  600    }
  601    rmdir(full_path);
  602 
  603    return EXIT_SUCCESS;
  604 }
  605 
  606 static int get_oldest_newest_dir(char *oldest, char *newest, int *count)
  607 {
  608    DIR *dir;
  609    struct dirent *dirent;
  610    char home_dir[FILENAME_MAX];
  611    int r;
  612 
  613    get_home_file_name("", home_dir, sizeof(home_dir));
  614    jp_logf(JP_LOG_DEBUG, "rotate_backups: opening dir %s\n", home_dir);
  615    *count = 0;
  616    oldest[0]='\0';
  617    newest[0]='\0';
  618    dir = opendir(home_dir);
  619    if (!dir) {
  620       return EXIT_FAILURE;
  621    }
  622    *count = 0;
  623    while((dirent = readdir(dir))) {
  624       if (is_backup_dir(dirent->d_name)) {
  625          jp_logf(JP_LOG_DEBUG, "backup dir [%s]\n", dirent->d_name);
  626          (*count)++;
  627          if (oldest[0]=='\0') {
  628             strcpy(oldest, dirent->d_name);
  629             /* jp_logf(JP_LOG_DEBUG, "oldest is now %s\n", oldest);*/
  630          }
  631          if (newest[0]=='\0') {
  632             strcpy(newest, dirent->d_name);
  633             /* jp_logf(JP_LOG_DEBUG, "newest is now %s\n", newest);*/
  634          }
  635          r = compare_back_dates(oldest, dirent->d_name);
  636          if (r==1) {
  637             strcpy(oldest, dirent->d_name);
  638             /* jp_logf(JP_LOG_DEBUG, "oldest is now %s\n", oldest);*/
  639          }
  640          r = compare_back_dates(newest, dirent->d_name);
  641          if (r==2) {
  642             strcpy(newest, dirent->d_name);
  643             /* jp_logf(JP_LOG_DEBUG, "newest is now %s\n", newest);*/
  644          }
  645       }
  646    }
  647    closedir(dir);
  648    return EXIT_SUCCESS;
  649 }
  650 
  651 static int sync_rotate_backups(const int num_backups)
  652 {
  653    DIR *dir;
  654    struct dirent *dirent;
  655    char home_dir[FILENAME_MAX];
  656    char full_name[FILENAME_MAX];
  657    char full_newdir[FILENAME_MAX];
  658    char full_backup[FILENAME_MAX];
  659    char full_oldest[FILENAME_MAX + 22];
  660    char full_src[FILENAME_MAX];
  661    char full_dest[FILENAME_MAX];
  662    int r;
  663    int count, safety;
  664    char oldest[20];
  665    char newest[20];
  666    char newdir[20];
  667    time_t ltime;
  668    struct tm *now;
  669 
  670    get_home_file_name("", home_dir, sizeof(home_dir));
  671 
  672    /* We use safety because if removing the directory fails then we
  673     * will get stuck in an endless loop */
  674    for (safety=100; safety>0; safety--) {
  675       r = get_oldest_newest_dir(oldest, newest, &count);
  676       if (r<0) {
  677          jp_logf(JP_LOG_WARN, _("Unable to read home dir\n"));
  678          break;
  679       }
  680       if (count > num_backups) {
  681          sprintf(full_oldest, "%s/%s", home_dir, oldest);
  682          jp_logf(JP_LOG_DEBUG, "count=%d, num_backups=%d\n", count, num_backups);
  683          jp_logf(JP_LOG_DEBUG, "removing dir [%s]\n", full_oldest);
  684          sync_remove_r(full_oldest);
  685       } else {
  686          break;
  687       }
  688    }
  689 
  690    /* Now we should have the same number of backups (or less) as num_backups */
  691 
  692    time(&ltime);
  693    now = localtime(&ltime);
  694    /* Create the new backup directory */
  695    g_snprintf(newdir, sizeof(newdir), "backup%02d%02d%02d%02d",
  696               now->tm_mon+1, now->tm_mday, now->tm_hour, now->tm_min);
  697    if (strncmp(newdir, newest, sizeof(newdir))) {
  698       g_snprintf(full_newdir, sizeof(full_newdir), "%s/%s", home_dir, newdir);
  699       if (mkdir(full_newdir, 0700)==0) {
  700          count++;
  701       }
  702    }
  703 
  704    /* Copy from the newest backup, if it exists */
  705    if (strncmp(newdir, newest, sizeof(newdir))) {
  706       g_snprintf(full_backup, sizeof(full_backup), "%s/backup", home_dir);
  707       g_snprintf(full_newdir, sizeof(full_newdir), "%s/%s", home_dir, newdir);
  708       dir = opendir(full_backup);
  709       if (dir) {
  710          while ((dirent = readdir(dir))) {
  711             g_snprintf(full_src, sizeof(full_src), "%s/%s", full_backup, dirent->d_name);
  712             g_snprintf(full_dest, sizeof(full_dest), "%s/%s", full_newdir, dirent->d_name);
  713             jp_copy_file(full_src, full_dest);
  714          }
  715          closedir(dir);
  716       }
  717    }
  718 
  719    /* Remove the oldest backup if needed */
  720    if (count > num_backups) {
  721       if ( (oldest[0]!='\0') && (strncmp(newdir, oldest, sizeof(newdir))) ) {
  722          g_snprintf(full_oldest, sizeof(full_oldest), "%s/%s", home_dir, oldest);
  723          jp_logf(JP_LOG_DEBUG, "removing dir [%s]\n", full_oldest);
  724          sync_remove_r(full_oldest);
  725       }
  726    }
  727 
  728    /* Delete the symlink */
  729    g_snprintf(full_name, sizeof(full_name), "%s/backup", home_dir);
  730    unlink(full_name);
  731 
  732    /* Create the symlink */
  733    if (symlink(newdir, full_name) != 0) {
  734       jp_logf(JP_LOG_WARN, "symlink failed %s %d\n", __FILE__, __LINE__);
  735    }
  736 
  737    return EXIT_SUCCESS;
  738 }
  739 
  740 static int unpack_datebook_cai_from_ai(struct CategoryAppInfo *cai, unsigned char *ai_raw, int len)
  741 {
  742 
  743    struct AppointmentAppInfo ai;
  744    int r;
  745 
  746    jp_logf(JP_LOG_DEBUG, "unpack_datebook_cai_from_ai\n");
  747 
  748    memset(&ai, 0, sizeof(ai));
  749 
  750    r = unpack_AppointmentAppInfo(&ai, ai_raw, len);
  751    if ((r <= 0) || (len <= 0)) {
  752       jp_logf(JP_LOG_DEBUG, "jp_unpack_AppointmentAppInfo failed %s %d\n", __FILE__, __LINE__);
  753       return EXIT_FAILURE;
  754    }
  755    memcpy(cai, &(ai.category), sizeof(struct CategoryAppInfo));
  756 
  757    return EXIT_SUCCESS;
  758 }
  759 
  760 static int pack_datebook_cai_into_ai(struct CategoryAppInfo *cai, 
  761                                      unsigned char *ai_raw, int len)
  762 {
  763    struct AppointmentAppInfo ai;
  764    int r;
  765 
  766    jp_logf(JP_LOG_DEBUG, "pack_datebook_cai_into_ai\n");
  767 
  768    r = unpack_AppointmentAppInfo(&ai, ai_raw, len);
  769    if (r <= 0) {
  770       jp_logf(JP_LOG_DEBUG, "unpack_AppointmentAppInfo failed %s %d\n", __FILE__, __LINE__);
  771       return EXIT_FAILURE;
  772    }
  773    memcpy(&(ai.category), cai, sizeof(struct CategoryAppInfo));
  774 
  775    r = pack_AppointmentAppInfo(&ai, ai_raw, len);
  776    if (r <= 0) {
  777       jp_logf(JP_LOG_DEBUG, "pack_AppointmentAppInfo failed %s %d\n", __FILE__, __LINE__);
  778       return EXIT_FAILURE;
  779    }
  780 
  781    return EXIT_SUCCESS;
  782 }
  783 
  784 static int unpack_calendar_cai_from_ai(struct CategoryAppInfo *cai, 
  785                                        unsigned char *ai_raw, int len)
  786 {
  787 
  788    struct CalendarAppInfo ai;
  789    int r;
  790    pi_buffer_t pi_buf;
  791 
  792    jp_logf(JP_LOG_DEBUG, "unpack_calendar_cai_from_ai\n");
  793 
  794    memset(&ai, 0, sizeof(ai));
  795    pi_buf.data = ai_raw;
  796    pi_buf.used = len;
  797    pi_buf.allocated = len;
  798 
  799    r = unpack_CalendarAppInfo(&ai, &pi_buf);
  800    if ((r <= 0) || (len <= 0)) {
  801       jp_logf(JP_LOG_DEBUG, "unpack_CalendarAppInfo failed %s %d\n", __FILE__, __LINE__);
  802       return EXIT_FAILURE;
  803    }
  804    memcpy(cai, &(ai.category), sizeof(struct CategoryAppInfo));
  805 
  806    return EXIT_SUCCESS;
  807 }
  808 
  809 static int pack_calendar_cai_into_ai(struct CategoryAppInfo *cai, 
  810                                      unsigned char *ai_raw, int len)
  811 {
  812    struct CalendarAppInfo ai;
  813    int r;
  814    pi_buffer_t pi_buf;
  815 
  816    jp_logf(JP_LOG_DEBUG, "pack_calendar_cai_into_ai\n");
  817 
  818    pi_buf.data = ai_raw;
  819    pi_buf.used = len;
  820    pi_buf.allocated = len;
  821    r = unpack_CalendarAppInfo(&ai, &pi_buf);
  822    if (r <= 0) {
  823       jp_logf(JP_LOG_DEBUG, "unpack_CalendarAppInfo failed %s %d\n", __FILE__, __LINE__);
  824       return EXIT_FAILURE;
  825    }
  826    memcpy(&(ai.category), cai, sizeof(struct CategoryAppInfo));
  827 
  828    pi_buf.data = NULL;
  829    pi_buf.used = 0;
  830    pi_buf.allocated = 0;
  831    r = pack_CalendarAppInfo(&ai, &pi_buf);
  832    memcpy(ai_raw, pi_buf.data, len);
  833    if (r <= 0) {
  834       jp_logf(JP_LOG_DEBUG, "pack_CalendarAppInfo failed %s %d\n", __FILE__, __LINE__);
  835       return EXIT_FAILURE;
  836    }
  837 
  838    return EXIT_SUCCESS;
  839 }
  840 
  841 static int unpack_address_cai_from_ai(struct CategoryAppInfo *cai, 
  842                                       unsigned char *ai_raw, int len)
  843 {
  844    struct AddressAppInfo ai;
  845    int r;
  846 
  847    jp_logf(JP_LOG_DEBUG, "unpack_address_cai_from_ai\n");
  848 
  849    memset(&ai, 0, sizeof(ai));
  850    r = unpack_AddressAppInfo(&ai, ai_raw, len);
  851    if ((r <= 0) || (len <= 0)) {
  852       jp_logf(JP_LOG_DEBUG, "unpack_AddressAppInfo failed %s %d\n", __FILE__, __LINE__);
  853       return EXIT_FAILURE;
  854    }
  855    memcpy(cai, &(ai.category), sizeof(struct CategoryAppInfo));
  856 
  857    return EXIT_SUCCESS;
  858 }
  859 
  860 static int pack_address_cai_into_ai(struct CategoryAppInfo *cai, 
  861                                     unsigned char *ai_raw, int len)
  862 {
  863    struct AddressAppInfo ai;
  864    int r;
  865 
  866    jp_logf(JP_LOG_DEBUG, "pack_address_cai_into_ai\n");
  867 
  868    r = unpack_AddressAppInfo(&ai, ai_raw, len);
  869    if (r <= 0) {
  870       jp_logf(JP_LOG_DEBUG, "unpack_AddressAppInfo failed %s %d\n", __FILE__, __LINE__);
  871       return EXIT_FAILURE;
  872    }
  873    memcpy(&(ai.category), cai, sizeof(struct CategoryAppInfo));
  874 
  875    r = pack_AddressAppInfo(&ai, ai_raw, len);
  876    if (r <= 0) {
  877       jp_logf(JP_LOG_DEBUG, "pack_AddressAppInfo failed %s %d\n", __FILE__, __LINE__);
  878       return EXIT_FAILURE;
  879    }
  880 
  881    return EXIT_SUCCESS;
  882 }
  883 
  884 static int unpack_contact_cai_from_ai(struct CategoryAppInfo *cai, 
  885                                       unsigned char *ai_raw, int len)
  886 {
  887    struct ContactAppInfo ai;
  888    int r;
  889    pi_buffer_t pi_buf;
  890 
  891    jp_logf(JP_LOG_DEBUG, "unpack_contact_cai_from_ai\n");
  892 
  893    memset(&ai, 0, sizeof(ai));
  894    pi_buf.data = ai_raw;
  895    pi_buf.used = len;
  896    pi_buf.allocated = len;
  897    r = jp_unpack_ContactAppInfo(&ai, &pi_buf);
  898    if ((r <= 0) || (len <= 0)) {
  899       jp_logf(JP_LOG_DEBUG, "jp_unpack_ContactAppInfo failed %s %d\n", __FILE__, __LINE__);
  900       return EXIT_FAILURE;
  901    }
  902    memcpy(cai, &(ai.category), sizeof(struct CategoryAppInfo));
  903 
  904    return EXIT_SUCCESS;
  905 }
  906 
  907 static int pack_contact_cai_into_ai(struct CategoryAppInfo *cai, 
  908                                     unsigned char *ai_raw, int len)
  909 {
  910    struct ContactAppInfo ai;
  911    int r;
  912    pi_buffer_t *pi_buf;
  913 
  914    jp_logf(JP_LOG_DEBUG, "pack_contact_cai_into_ai\n");
  915 
  916    pi_buf = pi_buffer_new(len);
  917    pi_buffer_append(pi_buf, ai_raw, len);
  918 
  919    r = jp_unpack_ContactAppInfo(&ai, pi_buf);
  920    if (r <= 0) {
  921       jp_logf(JP_LOG_DEBUG, "jp_unpack_ContactAppInfo failed %s %d\n", __FILE__, __LINE__);
  922       pi_buffer_free(pi_buf);
  923       return EXIT_FAILURE;
  924    }
  925    memcpy(&(ai.category), cai, sizeof(struct CategoryAppInfo));
  926 
  927    //r = jp_pack_ContactAppInfo(&ai, ai_raw, len);
  928    r = jp_pack_ContactAppInfo(&ai, pi_buf);
  929    //undo check buffer sizes
  930    memcpy(ai_raw, pi_buf->data, pi_buf->used);
  931    pi_buffer_free(pi_buf);
  932 
  933    if (r <= 0) {
  934       jp_logf(JP_LOG_DEBUG, "jp_pack_ContactAppInfo failed %s %d\n", __FILE__, __LINE__);
  935       return EXIT_FAILURE;
  936    }
  937 
  938    return EXIT_SUCCESS;
  939 }
  940 
  941 static int unpack_todo_cai_from_ai(struct CategoryAppInfo *cai, 
  942                                    unsigned char *ai_raw, int len)
  943 {
  944    struct ToDoAppInfo ai;
  945    int r;
  946 
  947    jp_logf(JP_LOG_DEBUG, "unpack_todo_cai_from_ai\n");
  948 
  949    memset(&ai, 0, sizeof(ai));
  950    r = unpack_ToDoAppInfo(&ai, ai_raw, len);
  951    if ((r <= 0) || (len <= 0)) {
  952       jp_logf(JP_LOG_DEBUG, "unpack_ToDoAppInfo failed %s %d\n", __FILE__, __LINE__);
  953       return EXIT_FAILURE;
  954    }
  955    memcpy(cai, &(ai.category), sizeof(struct CategoryAppInfo));
  956 
  957    return EXIT_SUCCESS;
  958 }
  959 
  960 static int pack_todo_cai_into_ai(struct CategoryAppInfo *cai, 
  961                                  unsigned char *ai_raw, int len)
  962 {
  963    struct ToDoAppInfo ai;
  964    int r;
  965 
  966    jp_logf(JP_LOG_DEBUG, "pack_todo_cai_into_ai\n");
  967 
  968    r = unpack_ToDoAppInfo(&ai, ai_raw, len);
  969    if (r <= 0) {
  970       jp_logf(JP_LOG_DEBUG, "unpack_ToDoAppInfo failed %s %d\n", __FILE__, __LINE__);
  971       return EXIT_FAILURE;
  972    }
  973    memcpy(&(ai.category), cai, sizeof(struct CategoryAppInfo));
  974 
  975    r = pack_ToDoAppInfo(&ai, ai_raw, len);
  976    if (r <= 0) {
  977       jp_logf(JP_LOG_DEBUG, "pack_ToDoAppInfo failed %s %d\n", __FILE__, __LINE__);
  978       return EXIT_FAILURE;
  979    }
  980 
  981    return EXIT_SUCCESS;
  982 }
  983 
  984 static int unpack_memo_cai_from_ai(struct CategoryAppInfo *cai, 
  985                                    unsigned char *ai_raw, int len)
  986 {
  987    struct MemoAppInfo ai;
  988    int r;
  989 
  990    jp_logf(JP_LOG_DEBUG, "unpack_memo_cai_from_ai\n");
  991 
  992         /* Bug 1922 in pilot-link-0.12.3 and below.
  993          * unpack_MemoAppInfo does not zero out all bytes of the 
  994          * appinfo struct and so it must be cleared here with a memset.
  995          * Can be removed from this and all other unpack routines when
  996          * Bug 1922 is fixed.
  997          * RW: 6/1/2008 */
  998    memset(&ai, 0, sizeof(ai));
  999    r = unpack_MemoAppInfo(&ai, ai_raw, len);
 1000    if ((r <= 0) || (len <= 0)) {
 1001       jp_logf(JP_LOG_DEBUG, "unpack_MemoAppInfo failed %s %d\n", __FILE__, __LINE__);
 1002       return EXIT_FAILURE;
 1003    }
 1004    memcpy(cai, &(ai.category), sizeof(struct CategoryAppInfo));
 1005 
 1006    return EXIT_SUCCESS;
 1007 }
 1008 
 1009 static int pack_memo_cai_into_ai(struct CategoryAppInfo *cai, 
 1010                                  unsigned char *ai_raw, int len)
 1011 {
 1012    struct MemoAppInfo ai;
 1013    int r;
 1014 
 1015    jp_logf(JP_LOG_DEBUG, "pack_memo_cai_into_ai\n");
 1016 
 1017    r = unpack_MemoAppInfo(&ai, ai_raw, len);
 1018    if (r <= 0) {
 1019       jp_logf(JP_LOG_DEBUG, "unpack_MemoAppInfo failed %s %d\n", __FILE__, __LINE__);
 1020       return EXIT_FAILURE;
 1021    }
 1022    memcpy(&(ai.category), cai, sizeof(struct CategoryAppInfo));
 1023 
 1024    r = pack_MemoAppInfo(&ai, ai_raw, len);
 1025    if (r <= 0) {
 1026       jp_logf(JP_LOG_DEBUG, "pack_MemoAppInfo failed %s %d\n", __FILE__, __LINE__);
 1027       return EXIT_FAILURE;
 1028    }
 1029 
 1030    return EXIT_SUCCESS;
 1031 }
 1032 
 1033 /*
 1034  * Fetch the databases from the palm if modified
 1035  */
 1036 static void fetch_extra_DBs2(int sd, struct DBInfo info, char *palm_dbname[])
 1037 {
 1038    struct pi_file *pi_fp;
 1039    char full_name[FILENAME_MAX];
 1040    struct stat statb;
 1041    struct utimbuf times;
 1042    int i;
 1043    int found;
 1044    char db_copy_name[MAX_DBNAME];
 1045    char creator[5];
 1046 
 1047    found = 0;
 1048    for (i=0; palm_dbname[i]; i++) {
 1049       if (palm_dbname[i]==NULL) break;
 1050       if (!strcmp(info.name, palm_dbname[i])) {
 1051          jp_logf(JP_LOG_DEBUG, "Found extra DB\n");
 1052          found=1;
 1053          break;
 1054       }
 1055    }
 1056 
 1057    if (!found) {
 1058       return;
 1059    }
 1060 
 1061    g_strlcpy(db_copy_name, info.name, MAX_DBNAME-5);
 1062    if (info.flags & dlpDBFlagResource) {
 1063       strcat(db_copy_name,".prc");
 1064    } else if (strncmp(db_copy_name + strlen(db_copy_name) - 4, ".pqa", 4)) {
 1065       strcat(db_copy_name,".pdb");
 1066    }
 1067 
 1068    filename_make_legal(db_copy_name);
 1069 
 1070    get_home_file_name(db_copy_name, full_name, sizeof(full_name));
 1071 
 1072    statb.st_mtime = 0;
 1073 
 1074    stat(full_name, &statb);
 1075 
 1076    creator[0] = (info.creator & 0xFF000000) >> 24;
 1077    creator[1] = (info.creator & 0x00FF0000) >> 16;
 1078    creator[2] = (info.creator & 0x0000FF00) >> 8;
 1079    creator[3] = (info.creator & 0x000000FF);
 1080    creator[4] = '\0';
 1081 
 1082    /* If modification times are the same then we don't need to fetch it */
 1083    if (info.modifyDate == statb.st_mtime) {
 1084       jp_logf(JP_LOG_DEBUG, "%s up to date, modify date (1) %ld\n", info.name, info.modifyDate);
 1085       jp_logf(JP_LOG_GUI, _("%s (Creator ID '%s') is up to date, fetch skipped.\n"), db_copy_name, creator);
 1086       return;
 1087    }
 1088 
 1089    jp_logf(JP_LOG_GUI, _("Fetching '%s' (Creator ID '%s')... "), info.name, creator);
 1090 
 1091    info.flags &= 0xff;
 1092 
 1093    pi_fp = pi_file_create(full_name, &info);
 1094 
 1095    if (pi_fp==0) {
 1096       jp_logf(JP_LOG_WARN, _("Failed, unable to create file %s\n"), full_name);
 1097       return;
 1098    }
 1099    if (pi_file_retrieve(pi_fp, sd, 0, NULL)<0) {
 1100       jp_logf(JP_LOG_WARN, _("Failed, unable to back up database %s\n"), info.name);
 1101       times.actime = 0;
 1102       times.modtime = 0;
 1103    } else {
 1104       jp_logf(JP_LOG_GUI, _("OK\n"));
 1105       times.actime = info.createDate;
 1106       times.modtime = info.modifyDate;
 1107    }
 1108    pi_file_close(pi_fp);
 1109 
 1110    /* Set the create and modify times of local file to same as on palm */
 1111    utime(full_name, &times);
 1112 }
 1113 
 1114 /*
 1115  * Fetch the databases from the palm if modified
 1116  */
 1117 static int fetch_extra_DBs(int sd, char *palm_dbname[])
 1118 {
 1119    int cardno, start;
 1120    struct DBInfo info;
 1121    int dbIndex;
 1122    pi_buffer_t *buffer;
 1123 
 1124    jp_logf(JP_LOG_DEBUG, "fetch_extra_DBs()\n");
 1125 
 1126    start=cardno=0;
 1127 
 1128    buffer = pi_buffer_new(32 * sizeof(struct DBInfo));
 1129 
 1130    /* Pilot-link 0.12 can return multiple db infos if the DLP is 1.2 and above */
 1131    while(dlp_ReadDBList(sd, cardno, dlpDBListRAM | dlpDBListMultiple, start, buffer)>0) {
 1132       for (dbIndex=0; dbIndex < (buffer->used / sizeof(struct DBInfo)); dbIndex++) {
 1133          memcpy(&info, buffer->data + (dbIndex * sizeof(struct DBInfo)), sizeof(struct DBInfo));
 1134          start=info.index+1;
 1135          fetch_extra_DBs2(sd, info, palm_dbname);
 1136       }
 1137    }
 1138    pi_buffer_free(buffer);
 1139 
 1140    return EXIT_SUCCESS;
 1141 }
 1142 
 1143 /*
 1144  * Fetch the databases from the palm if modified
 1145  *
 1146  * Be sure to call free_file_name_list(&file_list); before returning from
 1147  * anywhere in this function.
 1148  */
 1149 static int sync_fetch(int sd, unsigned int flags, 
 1150                       const int num_backups, int fast_sync)
 1151 {
 1152    struct pi_file *pi_fp;
 1153    char full_name[FILENAME_MAX];
 1154    char full_backup_name[FILENAME_MAX];
 1155    char creator[5];
 1156    struct stat statb;
 1157    struct utimbuf times;
 1158    int i, r;
 1159    int main_app;
 1160    int skip_file;
 1161    int cardno, start;
 1162    struct DBInfo info;
 1163    char db_copy_name[MAX_DBNAME];
 1164    GList *file_list;
 1165    int palmos_error;
 1166    int dbIndex;
 1167    pi_buffer_t *buffer;
 1168 #ifdef ENABLE_PLUGINS
 1169    GList *temp_list;
 1170    GList *plugin_list;
 1171    struct plugin_s *plugin;
 1172 #endif
 1173    char *file_name;
 1174    /* rename_dbnames is used to modify this list to newer databases if needed*/
 1175    char palm_dbname[][32]={
 1176       "DatebookDB",
 1177       "AddressDB",
 1178       "ToDoDB",
 1179       "MemoDB",
 1180 #ifdef ENABLE_MANANA
 1181       "MananaDB",
 1182 #endif
 1183       "Saved Preferences",
 1184       ""
 1185    };
 1186    char *extra_dbname[]={
 1187       "Saved Preferences",
 1188       NULL
 1189    };
 1190 
 1191    typedef struct skip_db_t {
 1192       unsigned int flags;
 1193       unsigned int not_flags;
 1194       const char *creator;
 1195       char *dbname;
 1196    } skip_db_t ;
 1197 
 1198    skip_db_t skip_db[] = {
 1199       { 0, dlpDBFlagResource, "AvGo", NULL },
 1200       { 0, dlpDBFlagResource, "psys", "Unsaved Preferences" },
 1201       { 0, 0, "a68k", NULL},
 1202       { 0, 0, "appl", NULL},
 1203       { 0, 0, "boot", NULL},
 1204       { 0, 0, "Fntl", NULL},
 1205       { 0, 0, "PMHa", NULL},
 1206       { 0, 0, "PMNe", NULL},
 1207       { 0, 0, "ppp_", NULL},
 1208       { 0, 0, "u8EZ", NULL},
 1209       { 0, 0, NULL, NULL}
 1210    };
 1211    unsigned int full_backup;
 1212 
 1213    jp_logf(JP_LOG_DEBUG, "sync_fetch flags=0x%x, num_backups=%d, fast=%d\n",
 1214                                              flags, num_backups, fast_sync);
 1215 
 1216    rename_dbnames(palm_dbname);
 1217 
 1218    full_backup = flags & SYNC_FULL_BACKUP;
 1219 
 1220    /* Fast sync still needs to fetch Saved Preferences before exiting */
 1221    if (fast_sync && !full_backup) {
 1222       fetch_extra_DBs(sd, extra_dbname);
 1223       return EXIT_SUCCESS;
 1224    }
 1225 
 1226    if (full_backup) {
 1227       jp_logf(JP_LOG_DEBUG, "Full Backup\n");
 1228       pi_watchdog(sd,10); /* prevent Palm timing out on long disk copy times */
 1229       sync_rotate_backups(num_backups);
 1230       pi_watchdog(sd,0);  /* back to normal behavior */
 1231    }
 1232 
 1233    start=cardno=0;
 1234    file_list=NULL;
 1235 
 1236    buffer = pi_buffer_new(32 * sizeof(struct DBInfo));
 1237 
 1238    while( (r=dlp_ReadDBList(sd, cardno, dlpDBListRAM | dlpDBListMultiple, start, buffer)) > 0) {
 1239       for (dbIndex=0; dbIndex < (buffer->used / sizeof(struct DBInfo)); dbIndex++) {
 1240          memcpy(&info, buffer->data + (dbIndex * sizeof(struct DBInfo)), sizeof(struct DBInfo));
 1241 
 1242       start=info.index+1;
 1243       creator[0] = (info.creator & 0xFF000000) >> 24;
 1244       creator[1] = (info.creator & 0x00FF0000) >> 16;
 1245       creator[2] = (info.creator & 0x0000FF00) >> 8;
 1246       creator[3] = (info.creator & 0x000000FF);
 1247       creator[4] = '\0';
 1248 #ifdef JPILOT_DEBUG
 1249       jp_logf(JP_LOG_DEBUG, "dbname = %s\n",info.name);
 1250       jp_logf(JP_LOG_DEBUG, "exclude from sync = %d\n",info.miscFlags & dlpDBMiscFlagExcludeFromSync);
 1251       jp_logf(JP_LOG_DEBUG, "flag backup = %d\n",info.flags & dlpDBFlagBackup);
 1252       /* jp_logf(JP_LOG_DEBUG, "type = %x\n",info.type);*/
 1253       jp_logf(JP_LOG_DEBUG, "Creator ID = [%s]\n", creator);
 1254 #endif
 1255       if (full_backup) {
 1256          /* Look at the skip list */
 1257          skip_file=0;
 1258          for (i=0; skip_db[i].creator || skip_db[i].dbname; i++) {
 1259             if (skip_db[i].creator &&
 1260                 !strcmp(creator, skip_db[i].creator)) {
 1261                if (skip_db[i].dbname && strcmp(info.name,skip_db[i].dbname)) {
 1262                   continue;   /* Only creator matched, not DBname.  */
 1263                }
 1264                else {
 1265                   if (skip_db[i].flags &&
 1266                       (info.flags & skip_db[i].flags) != skip_db[i].flags) {
 1267                      skip_file=1;
 1268                      break;
 1269                   }
 1270                   else if (skip_db[i].not_flags &&
 1271                            !(info.flags & skip_db[i].not_flags)) {
 1272                      skip_file=1;
 1273                      break;
 1274                   }
 1275                   else if (!skip_db[i].flags && !skip_db[i].not_flags) {
 1276                      skip_file=1;
 1277                      break;
 1278                   }
 1279                }
 1280             }
 1281             if (skip_db[i].dbname &&
 1282                 !strcmp(info.name,skip_db[i].dbname)) {
 1283                if (skip_db[i].flags &&
 1284                    (info.flags & skip_db[i].flags) != skip_db[i].flags) {
 1285                   skip_file=1;
 1286                   break;
 1287                }
 1288                else if (skip_db[i].not_flags &&
 1289                         (info.flags & skip_db[i].not_flags)) {
 1290                   skip_file=1;
 1291                   break;
 1292                }
 1293                else if (!skip_db[i].flags && !skip_db[i].not_flags) {
 1294                   skip_file=1;
 1295                   break;
 1296                }
 1297             }
 1298          }
 1299          if (skip_file) {
 1300             jp_logf(JP_LOG_GUI, _("Skipping %s (Creator ID '%s')\n"), info.name, creator);
 1301             continue;
 1302          }
 1303       }
 1304 
 1305       main_app = 0;
 1306       skip_file = 0;
 1307       for (i=0; palm_dbname[i][0]; i++) {
 1308          if (!strcmp(info.name, palm_dbname[i])) {
 1309             jp_logf(JP_LOG_DEBUG, "Found main app\n");
 1310             main_app = 1;
 1311             /* Skip if conduit is not enabled in preferences */
 1312             if (!full_backup) {
 1313                switch (i) {
 1314                 case 0:
 1315                   if (!get_pref_int_default(PREF_SYNC_DATEBOOK, 1)) {
 1316                      skip_file = 1; 
 1317                   }
 1318                   break;
 1319                 case 1:
 1320                   if (!get_pref_int_default(PREF_SYNC_ADDRESS, 1)) {
 1321                      skip_file = 1; 
 1322                   }
 1323                   break;
 1324                 case 2:
 1325                   if (!get_pref_int_default(PREF_SYNC_TODO, 1)) {
 1326                      skip_file = 1; 
 1327                   }
 1328                   break;
 1329                 case 3:
 1330                   if (!get_pref_int_default(PREF_SYNC_MEMO, 1)) {
 1331                      skip_file = 1; 
 1332                   }
 1333                   break;
 1334 #ifdef ENABLE_MANANA                  
 1335                 case 4:
 1336                   if (!get_pref_int_default(PREF_SYNC_MANANA, 1)) {
 1337                      skip_file = 1; 
 1338                   }
 1339                   break;
 1340 #endif
 1341                } /* end switch */
 1342             } /* end if checking for excluded conduits */
 1343             break; 
 1344          } /* end if checking for main app */
 1345       } /* for loop over main app names */
 1346 
 1347       /* skip main app conduit as necessary */
 1348       if (skip_file) continue;
 1349 
 1350 #ifdef ENABLE_PLUGINS
 1351       plugin_list = get_plugin_list();
 1352 
 1353       if (!main_app) {
 1354          skip_file = 0;
 1355          for (temp_list = plugin_list; temp_list; temp_list = temp_list->next) {
 1356             plugin = (struct plugin_s *)temp_list->data;
 1357             if (!strcmp(info.name, plugin->db_name)) {
 1358                jp_logf(JP_LOG_DEBUG, "Found plugin\n");
 1359                main_app = 1;
 1360                /* Skip if conduit is not enabled */
 1361                if (!full_backup && !plugin->sync_on) {
 1362                  skip_file = 1;
 1363                }
 1364                break;
 1365             }
 1366          }
 1367       }
 1368 #endif
 1369       /* skip plugin conduit as necessary */
 1370       if (skip_file) continue;
 1371       
 1372       g_strlcpy(db_copy_name, info.name, MAX_DBNAME-5);
 1373       if (info.flags & dlpDBFlagResource) {
 1374          strcat(db_copy_name,".prc");
 1375       } else if (strncmp(db_copy_name + strlen(db_copy_name) - 4, ".pqa", 4)) {
 1376          strcat(db_copy_name,".pdb");
 1377       }
 1378 
 1379       filename_make_legal(db_copy_name);
 1380 
 1381       if (!strcmp(db_copy_name, "Graffiti ShortCuts .prc")) {
 1382          /* Make a special exception for the graffiti shortcuts.
 1383           * We want to save it as this to avoid the confusion of
 1384           * having 2 different versions around */
 1385          strcpy(db_copy_name, "Graffiti ShortCuts.prc");
 1386       }
 1387       get_home_file_name(db_copy_name, full_name, sizeof(full_name));
 1388       get_home_file_name("backup/", full_backup_name, sizeof(full_backup_name));
 1389       strcat(full_backup_name, db_copy_name);
 1390 
 1391       /* Add this to our file name list if not manually skipped */
 1392       jp_logf(JP_LOG_DEBUG, "appending [%s]\n", db_copy_name);
 1393       file_list = g_list_prepend(file_list, strdup(db_copy_name));
 1394 
 1395       if ( !fast_sync && !full_backup && !main_app ) {
 1396          continue;
 1397       }
 1398 #ifdef JPILOT_DEBUG
 1399       if (main_app) {
 1400          jp_logf(JP_LOG_DEBUG, "main_app is set\n");
 1401       }
 1402 #endif
 1403 
 1404       if (main_app && !fast_sync) {
 1405          file_name = full_name;
 1406       } else {
 1407          file_name = full_backup_name;
 1408       }
 1409       statb.st_mtime = 0;
 1410       stat(file_name, &statb);
 1411 #ifdef JPILOT_DEBUG
 1412       jp_logf(JP_LOG_GUI, "palm dbtime= %d, local dbtime = %d\n", info.modifyDate, statb.st_mtime);
 1413       jp_logf(JP_LOG_GUI, "flags=0x%x\n", info.flags);
 1414       jp_logf(JP_LOG_GUI, "backup_flag=%d\n", info.flags & dlpDBFlagBackup);
 1415 #endif
 1416       /* If modification times are the same then we don't need to fetch it */
 1417       if (info.modifyDate == statb.st_mtime) {
 1418          jp_logf(JP_LOG_DEBUG, "%s up to date, modify date (2) %ld\n", info.name, info.modifyDate);
 1419          jp_logf(JP_LOG_GUI, _("%s (Creator ID '%s') is up to date, fetch skipped.\n"), db_copy_name, creator);
 1420          continue;
 1421       }
 1422 
 1423       jp_logf(JP_LOG_GUI, _("Fetching '%s' (Creator ID '%s')... "), info.name, creator);
 1424 
 1425       info.flags &= 0xff;
 1426 
 1427       pi_fp = pi_file_create(file_name, &info);
 1428       if (pi_fp==0) {
 1429          jp_logf(JP_LOG_WARN, _("Failed, unable to create file %s\n"),
 1430                 main_app ? full_name : full_backup_name);
 1431          continue;
 1432       }
 1433       if (pi_file_retrieve(pi_fp, sd, 0, NULL)<0) {
 1434          jp_logf(JP_LOG_WARN, _("Failed, unable to back up database %s\n"), info.name);
 1435          times.actime = 0;
 1436          times.modtime = 0;
 1437       } else {
 1438          jp_logf(JP_LOG_GUI, _("OK\n"));
 1439          times.actime = info.createDate;
 1440          times.modtime = info.modifyDate;
 1441       }
 1442       pi_file_close(pi_fp);
 1443 
 1444       /* Set the create and modify times of local file to same as on palm */
 1445       utime(file_name, &times);
 1446 
 1447       /* This call preserves the file times */
 1448       if (main_app && !fast_sync && full_backup) {
 1449          jp_copy_file(full_name, full_backup_name);
 1450       }
 1451    }
 1452    }
 1453    pi_buffer_free(buffer);
 1454    palmos_error = pi_palmos_error(sd);
 1455    if (palmos_error==dlpErrNotFound) {
 1456       jp_logf(JP_LOG_DEBUG, "Good return code (dlpErrNotFound)\n");
 1457       if (full_backup) {
 1458          jp_logf(JP_LOG_DEBUG, "Removing apps not found on the palm\n");
 1459          move_removed_apps(file_list);
 1460       }
 1461    } else {
 1462       jp_logf(JP_LOG_WARN, "ReadDBList returned = %d\n", r);
 1463       jp_logf(JP_LOG_WARN, "palmos_error = %d\n", palmos_error);
 1464       jp_logf(JP_LOG_WARN, "dlp_strerror is %s\n", dlp_strerror(palmos_error));
 1465    }
 1466         
 1467    free_file_name_list(&file_list);
 1468 
 1469    return EXIT_SUCCESS;
 1470 }
 1471 
 1472 static int sync_install(char *filename, int sd, char *vfspath)
 1473 {
 1474    struct pi_file *f;
 1475    struct  DBInfo info;
 1476    char *Pc;
 1477    char log_entry[256];
 1478    int r, try_again;
 1479    long char_set;
 1480    char creator[5];
 1481    int sdcard_install;
 1482 
 1483    get_pref(PREF_CHAR_SET, &char_set, NULL);
 1484 
 1485    Pc = strrchr(filename, '/');
 1486    if (!Pc) {
 1487       Pc = filename;
 1488    } else {
 1489       Pc++;
 1490    }
 1491 
 1492    sdcard_install = (vfspath != NULL);
 1493 
 1494    jp_logf(JP_LOG_GUI, _("Installing %s "), Pc);
 1495    f = pi_file_open(filename);
 1496    if (f==NULL && !sdcard_install) {
 1497       int fd;
 1498 
 1499       if ((fd = open(filename, O_RDONLY)) < 0) {
 1500          jp_logf(JP_LOG_WARN, _("\nUnable to open file: '%s': %s!\n"),
 1501                                                     filename, strerror(errno));
 1502       } else {
 1503          close(fd);
 1504          jp_logf(JP_LOG_WARN, _("\nUnable to sync file: '%s': file corrupted?\n"),
 1505                  filename);
 1506       }
 1507       return EXIT_FAILURE;
 1508    }
 1509    if (f != NULL) {
 1510       memset(&info, 0, sizeof(info));
 1511       pi_file_get_info(f, &info);
 1512       creator[0] = (info.creator & 0xFF000000) >> 24;
 1513       creator[1] = (info.creator & 0x00FF0000) >> 16,
 1514       creator[2] = (info.creator & 0x0000FF00) >> 8,
 1515       creator[3] = (info.creator & 0x000000FF);
 1516       creator[4] = '\0';
 1517    }
 1518 
 1519    if (!sdcard_install) {
 1520       jp_logf(JP_LOG_GUI, _("(Creator ID '%s')... "), creator);
 1521       r = pi_file_install(f, sd, 0, NULL);
 1522    } else {
 1523       if (f != NULL) {
 1524          jp_logf(JP_LOG_GUI, _("(Creator ID '%s') "), creator);
 1525       }
 1526       jp_logf(JP_LOG_GUI, _("(SDcard dir %s)... "), vfspath);
 1527       const char *basename = strrchr(filename, '/');
 1528       if (!basename) {
 1529          basename = filename;
 1530       } else {
 1531          basename++;
 1532       }
 1533       int fd;
 1534       if ((fd = open(filename, O_RDONLY)) < 0)
 1535       {
 1536          jp_logf(JP_LOG_WARN, _("\nUnable to open file: '%s': %s!\n"),
 1537                                                     filename, strerror(errno));
 1538          pi_file_close(f);
 1539          return EXIT_FAILURE;
 1540       }
 1541       r = pi_file_install_VFS(fd, basename, sd, vfspath, NULL);
 1542       close(fd);
 1543    }
 1544 
 1545    if (r<0 && !sdcard_install) {
 1546       try_again = 0;
 1547       /* TODO: make this generic? Not sure it would work 100% of the time */
 1548       /* Here we make a special exception for graffiti */
 1549       if (!strcmp(info.name, "Graffiti ShortCuts")) {
 1550          strcpy(info.name, "Graffiti ShortCuts ");
 1551          /* This requires a reset */
 1552          info.flags |= dlpDBFlagReset;
 1553          info.flags |= dlpDBFlagNewer;
 1554          pi_file_close(f);
 1555          pdb_file_write_dbinfo(filename, &info);
 1556          f = pi_file_open(filename);
 1557          if (f==0) {
 1558             jp_logf(JP_LOG_WARN, _("\nUnable to open file: %s\n"), filename);
 1559             return EXIT_FAILURE;
 1560          }
 1561          try_again = 1;
 1562       } else if (!strcmp(info.name, "Graffiti ShortCuts ")) {
 1563          strcpy(info.name, "Graffiti ShortCuts");
 1564          /* This requires a reset */
 1565          info.flags |= dlpDBFlagReset;
 1566          info.flags |= dlpDBFlagNewer;
 1567          pi_file_close(f);
 1568          pdb_file_write_dbinfo(filename, &info);
 1569          f = pi_file_open(filename);
 1570          if (f==0) {
 1571             jp_logf(JP_LOG_WARN, _("\nUnable to open file: %s\n"), filename);
 1572             return EXIT_FAILURE;
 1573          }
 1574          try_again = 1;
 1575       }
 1576       /* Here we make a special exception for Net Prefs */
 1577       if (!strcmp(info.name, "Net Prefs")) {
 1578          strcpy(info.name, "Net Prefs ");
 1579          /* This requires a reset */
 1580          info.flags |= dlpDBFlagReset;
 1581          info.flags |= dlpDBFlagNewer;
 1582          pi_file_close(f);
 1583          pdb_file_write_dbinfo(filename, &info);
 1584          f = pi_file_open(filename);
 1585          if (f==0) {
 1586             jp_logf(JP_LOG_WARN, _("\nUnable to open file: %s\n"), filename);
 1587             return EXIT_FAILURE;
 1588          }
 1589          try_again = 1;
 1590       } else if (!strcmp(info.name, "Net Prefs ")) {
 1591          strcpy(info.name, "Net Prefs");
 1592          /* This requires a reset */
 1593          info.flags |= dlpDBFlagReset;
 1594          info.flags |= dlpDBFlagNewer;
 1595          pi_file_close(f);
 1596          pdb_file_write_dbinfo(filename, &info);
 1597          f = pi_file_open(filename);
 1598          if (f==0) {
 1599             jp_logf(JP_LOG_WARN, _("\nUnable to open file: %s\n"), filename);
 1600             return EXIT_FAILURE;
 1601          }
 1602          try_again = 1;
 1603       }
 1604       if (try_again) {
 1605          /* Try again */
 1606          r = pi_file_install(f, sd, 0, NULL);
 1607       }
 1608    }
 1609 
 1610    if (r<0) {
 1611       g_snprintf(log_entry, sizeof(log_entry), _("Install %s failed"), Pc);
 1612       charset_j2p(log_entry, sizeof(log_entry), char_set);
 1613       dlp_AddSyncLogEntry(sd, log_entry);
 1614       dlp_AddSyncLogEntry(sd, "\n");;
 1615       jp_logf(JP_LOG_GUI, _("Failed.\n"));
 1616       jp_logf(JP_LOG_WARN, "%s\n", log_entry);
 1617       if (f) pi_file_close(f);
 1618       return EXIT_FAILURE;
 1619    }
 1620    else {
 1621       g_snprintf(log_entry, sizeof(log_entry), _("Installed %s"), Pc);
 1622       charset_j2p(log_entry, sizeof(log_entry), char_set);
 1623       dlp_AddSyncLogEntry(sd, log_entry);
 1624       dlp_AddSyncLogEntry(sd, "\n");;
 1625       jp_logf(JP_LOG_GUI, _("OK\n"));
 1626    }
 1627    if (f) pi_file_close(f);
 1628 
 1629    return EXIT_SUCCESS;
 1630 }
 1631 
 1632 /* file must not be open elsewhere when this is called */
 1633 /* the first line is 0 */
 1634 static int sync_process_install_file(int sd)
 1635 {
 1636    FILE *in;
 1637    FILE *out;
 1638    char line[1002];
 1639    char *Pc;
 1640    int r, line_count;
 1641    char vfsdir[] = "/PALM/Launcher/";   // Install location for SDCARD files
 1642 
 1643    in = jp_open_home_file(EPN".install", "r");
 1644    if (!in) {
 1645       jp_logf(JP_LOG_WARN, _("Unable to open file: %s%s\n"), EPN, ".install");
 1646       return EXIT_FAILURE;
 1647    }
 1648 
 1649    out = jp_open_home_file(EPN".install.tmp", "w");
 1650    if (!out) {
 1651       jp_logf(JP_LOG_WARN, _("Unable to open file: %s%s\n"), EPN, ".install.tmp");
 1652       fclose(in);
 1653       return EXIT_FAILURE;
 1654    }
 1655 
 1656    for (line_count=0; (!feof(in)); line_count++) {
 1657       line[0]='\0';
 1658       Pc = fgets(line, 1000, in);
 1659       if (!Pc) {
 1660          break;
 1661       }
 1662       if (line[strlen(line)-1]=='\n') {
 1663          line[strlen(line)-1]='\0';
 1664       }
 1665       if (line[0] == '\001') {
 1666          /* found SDCARD indicator */
 1667          r = sync_install(&(line[1]), sd, vfsdir);
 1668       } else {
 1669          r = sync_install(line, sd, NULL);
 1670       }
 1671       
 1672       if (r==0) {
 1673          continue;
 1674       }
 1675       fprintf(out, "%s\n", line);
 1676    }
 1677    fclose(in);
 1678    fclose(out);
 1679 
 1680    rename_file(EPN".install.tmp", EPN".install");
 1681 
 1682    return EXIT_SUCCESS;
 1683 }
 1684 
 1685 static int sync_categories(char *DB_name, 
 1686                            int sd,
 1687                            int (*unpack_cai_from_ai)(struct CategoryAppInfo *cai, unsigned char *ai_raw, int len),
 1688                            int (*pack_cai_into_ai)(struct CategoryAppInfo *cai, unsigned char *ai_raw, int len)
 1689 )
 1690 {
 1691    struct CategoryAppInfo local_cai, remote_cai, orig_remote_cai;
 1692    char full_name[FILENAME_MAX];
 1693    char pdb_name[FILENAME_MAX];
 1694    char log_entry[256];
 1695    struct pi_file *pf;
 1696    pi_buffer_t *buffer;
 1697    size_t local_cai_size;
 1698    int remote_cai_size;
 1699    unsigned char buf[65536];
 1700    int db;
 1701    void *Papp_info;
 1702    char tmp_name[18];
 1703    int tmp_int;
 1704    int i, r, Li, Ri;
 1705    int found_name, found_ID;
 1706    int found_name_at, found_ID_at;
 1707    int found_a_slot;
 1708    int move_from_idx[NUM_CATEGORIES];
 1709    int move_to_idx[NUM_CATEGORIES];
 1710    int move_i = 0;
 1711    int loop;
 1712    long char_set;
 1713 
 1714    jp_logf(JP_LOG_DEBUG, "sync_categories for %s\n", DB_name);
 1715 
 1716    get_pref(PREF_CHAR_SET, &char_set, NULL);
 1717 
 1718    g_snprintf(pdb_name, sizeof(pdb_name), "%s%s", DB_name, ".pdb");
 1719    get_home_file_name(pdb_name, full_name, sizeof(full_name));
 1720 
 1721    Papp_info=NULL;
 1722    memset(&local_cai, 0, sizeof(local_cai));
 1723    memset(&remote_cai, 0, sizeof(remote_cai));
 1724 
 1725    pf = pi_file_open(full_name);
 1726    if (!pf) {
 1727       jp_logf(JP_LOG_WARN, _("%s:%d Error reading file: %s\n"), __FILE__, __LINE__, full_name);
 1728       return EXIT_FAILURE;
 1729    }
 1730    pi_file_get_app_info(pf, &Papp_info, &local_cai_size);
 1731    if (local_cai_size <= 0) {
 1732       jp_logf(JP_LOG_WARN, _("%s:%d Error getting app info %s\n"), __FILE__, __LINE__, full_name);
 1733       return EXIT_FAILURE;
 1734    }
 1735 
 1736    r = unpack_cai_from_ai(&local_cai, Papp_info, local_cai_size);
 1737    if (EXIT_SUCCESS != r) {
 1738       jp_logf(JP_LOG_WARN, _("%s:%d Error unpacking app info %s\n"), __FILE__, __LINE__, full_name);
 1739       return EXIT_FAILURE;
 1740    }
 1741 
 1742    pi_file_close(pf);
 1743 
 1744    /* Open the applications database, store access handle in db */
 1745    r = dlp_OpenDB(sd, 0, dlpOpenReadWrite, DB_name, &db);
 1746    if (r < 0) {
 1747       /* File unable to be opened.  
 1748        * Return an error but let the main record sync process 
 1749        * do the logging to the GUI and Palm */
 1750       jp_logf(JP_LOG_DEBUG, "sync_categories: Unable to open file: %s\n", DB_name);
 1751       return EXIT_FAILURE;
 1752    }
 1753 
 1754    buffer = pi_buffer_new(0xFFFF);
 1755    /* buffer size passed in cannot be any larger than 0xffff */
 1756    remote_cai_size = dlp_ReadAppBlock(sd, db, 0, -1, buffer);
 1757    jp_logf(JP_LOG_DEBUG, "readappblock r=%d\n", remote_cai_size);
 1758    if ((remote_cai_size<=0) || (remote_cai_size > sizeof(buf))) {
 1759       jp_logf(JP_LOG_WARN, _("Error reading appinfo block for %s\n"), DB_name);
 1760       dlp_CloseDB(sd, db);
 1761       pi_buffer_free(buffer);
 1762       return EXIT_FAILURE;
 1763    }
 1764    memcpy(buf, buffer->data, remote_cai_size);
 1765    pi_buffer_free(buffer);
 1766    r = unpack_cai_from_ai(&remote_cai, buf, remote_cai_size);
 1767    if (EXIT_SUCCESS != r) {
 1768       jp_logf(JP_LOG_WARN, _("%s:%d Error unpacking app info %s\n"), __FILE__, __LINE__, full_name);
 1769       return EXIT_FAILURE;
 1770    }
 1771    memcpy(&orig_remote_cai, &remote_cai, sizeof(remote_cai));
 1772 
 1773    /* Do a memcmp first to see if nothing has changed, the common case */
 1774    if (!memcmp(&(local_cai), &(remote_cai), sizeof(local_cai))) {
 1775       jp_logf(JP_LOG_DEBUG, "Category app info match, nothing to do %s\n", DB_name);
 1776       dlp_CloseDB(sd, db);
 1777       return EXIT_SUCCESS;
 1778    }
 1779 
 1780 #ifdef SYNC_CAT_DEBUG
 1781    printf("--- pre-sync CategoryAppInfo\n");
 1782    printf("--- DB_name [%s]\n", DB_name);
 1783    printf("--- local: size is %d ---\n", local_cai_size);
 1784    for (i=0; i<NUM_CATEGORIES; i++) {
 1785       printf("local: cat %d [%s] ID %d renamed %d\n", i,
 1786              local_cai.name[i],
 1787              local_cai.ID[i], local_cai.renamed[i]);
 1788    }
 1789    printf("--- remote: size is %d ---\n", remote_cai_size);
 1790    for (i=0; i<NUM_CATEGORIES; i++) {
 1791          printf("remote: cat %d [%s] ID %d renamed %d\n", i,
 1792                 remote_cai.name[i],
 1793                 remote_cai.ID[i], remote_cai.renamed[i]);
 1794    }
 1795 #endif
 1796 
 1797    /* Go through the categories, skipping the reserved 'Unfiled' at index 0,
 1798     * and try to synchronize them.
 1799     * loop variable is to prevent infinite loops */
 1800    for (Li=loop=1; ((Li<NUM_CATEGORIES) && (loop<256)); Li++, loop++) {
 1801       found_name=found_ID=FALSE;
 1802       found_name_at=found_ID_at=0;
 1803 
 1804       /* Blank entry in table */
 1805       if ((local_cai.name[Li][0]==0) && (local_cai.ID[Li]==0)) {
 1806          continue;
 1807       }
 1808       /* 0: Category deleted locally.  Undocumented by Palm */
 1809       if (local_cai.name[Li][0]==0) {
 1810          if ((!remote_cai.renamed[Li]) && (remote_cai.ID[Li]!=0)) {
 1811 #ifdef SYNC_CAT_DEBUG
 1812             printf("cat %d deleted local, del cat on remote\n", Li);
 1813             printf(" remote cat name %s\n", remote_cai.name[Li]);
 1814             printf(" remote rename flag was %d\n", remote_cai.renamed[Li]);
 1815 #endif
 1816             remote_cai.name[Li][0]='\0';
 1817             remote_cai.ID[Li]=0;
 1818             remote_cai.renamed[Li]=0;
 1819             /* This category was deleted.  Move records on Palm to Unfiled */
 1820             jp_logf(JP_LOG_DEBUG, "Moving category %d to unfiled...", Li);
 1821             r = dlp_MoveCategory(sd, db, Li, 0);
 1822             jp_logf(JP_LOG_DEBUG, "dlp_MoveCategory returned %d\n", r);
 1823          }
 1824          continue;
 1825       }
 1826 
 1827       /* Do a search for the local category name and ID on the remote */
 1828       for (Ri = 1; Ri < NUM_CATEGORIES; Ri++) {
 1829          if (! strncmp(local_cai.name[Li], remote_cai.name[Ri], PILOTCAT_NAME_SZ)) {
 1830 #ifdef SYNC_CAT_DEBUG
 1831             if (found_name)
 1832                printf("Found name %s twice at %d and %d\n", 
 1833                                   local_cai.name[Li], found_name_at, Ri);
 1834 #endif
 1835             found_name=TRUE;
 1836             found_name_at=Ri;
 1837          }
 1838          if (local_cai.ID[Li] == remote_cai.ID[Ri]) {
 1839 #ifdef SYNC_CAT_DEBUG
 1840             if (found_ID && (local_cai.ID[Li] != 0))
 1841                printf("Found ID %d twice at %d and %d\n", 
 1842                                 local_cai.ID[Li], found_ID_at, Ri);
 1843 #endif
 1844             found_ID=TRUE;
 1845             found_ID_at=Ri;
 1846          }
 1847       }
 1848 
 1849       /* Process the results of the name and ID search */
 1850       if (found_name) {
 1851          if (Li==found_name_at) {
 1852             /* 1: OK. Index and name match so there is nothing to do. */
 1853 #ifdef SYNC_CAT_DEBUG
 1854             printf("cat index %d ok\n", Li);
 1855 #endif
 1856             continue;
 1857          } else {
 1858             /* 2: Change all local recs to use remote recs ID */
 1859             /* This is where there is a bit of trouble, since we have a pdb
 1860              * file on the local side we don't store the ID and there is no way
 1861              * to do this.
 1862              * Instead, we will swap indexes on the local records.
 1863              */
 1864 #ifdef SYNC_CAT_DEBUG
 1865             printf("cat index %d case 2\n", Li);
 1866             printf("Swapping index %d to %d\n", Li, found_name_at);
 1867 #endif
 1868             r = pdb_file_swap_indexes(DB_name, Li, found_name_at);
 1869             r = edit_cats_swap_cats_pc3(DB_name, Li, found_name_at);
 1870             /* Swap name, ID, and renamed attributes in local table */
 1871             g_strlcpy(tmp_name, local_cai.name[found_name_at], PILOTCAT_NAME_SZ);
 1872             strncpy(local_cai.name[found_name_at],
 1873                     local_cai.name[Li], PILOTCAT_NAME_SZ);
 1874             strncpy(local_cai.name[Li], tmp_name, PILOTCAT_NAME_SZ);
 1875 
 1876             tmp_int = local_cai.ID[found_name_at]; 
 1877             local_cai.ID[found_name_at] = local_cai.ID[Li];
 1878             local_cai.ID[Li] = tmp_int;
 1879 
 1880             tmp_int = local_cai.renamed[found_name_at]; 
 1881             local_cai.renamed[found_name_at] = local_cai.renamed[Li];
 1882             local_cai.renamed[Li] = tmp_int;
 1883 
 1884             if (found_name_at > Li) {
 1885                /* Need to reprocess this Li index because a new one has
 1886                 * has been swapped in. */
 1887                Li--;
 1888             }
 1889             continue;
 1890          }
 1891       }
 1892       if ((!found_name) && (found_ID)) {
 1893          if (local_cai.renamed[Li]) {
 1894             /* 3: Change remote category name to match local at index Li */
 1895 #ifdef SYNC_CAT_DEBUG
 1896             printf("cat index %d case 3\n", Li);
 1897 #endif
 1898             g_strlcpy(remote_cai.name[found_ID_at],
 1899                       local_cai.name[Li], PILOTCAT_NAME_SZ);
 1900             continue;
 1901          } else {
 1902             if (remote_cai.renamed[found_ID_at]) {
 1903                /* 3a1: Category has been renamed on Palm. Undocumented */
 1904 #ifdef SYNC_CAT_DEBUG
 1905                printf("cat index %d case 3a1\n", Li);
 1906 #endif
 1907                continue;
 1908             } else {
 1909                /* 3a2: Category has been deleted on Palm. Undocumented */
 1910 #ifdef SYNC_CAT_DEBUG
 1911                printf("cat index %d case 3a2\n", Li);
 1912                printf("cat %d deleted remote, del cat on local\n", Li);
 1913                printf(" local cat name %s\n", local_cai.name[Li]);
 1914 #endif
 1915                local_cai.renamed[Li]=0;
 1916                local_cai.name[Li][0]='\0';
 1917                local_cai.ID[Li]=0;
 1918 
 1919                remote_cai.name[found_ID_at][0]='\0';
 1920                remote_cai.ID[found_ID_at]=0;
 1921                remote_cai.renamed[found_ID_at]=0;
 1922                jp_logf(JP_LOG_DEBUG, "Moving local recs category %d to Unfiled\n", Li);
 1923                /* Move only changed records(pc3) to Unfiled.  Records in pdb
 1924                 * will be handled by sync record process */
 1925                edit_cats_change_cats_pc3(DB_name, Li, 0);
 1926                continue;
 1927             }
 1928          }
 1929       }
 1930       if ((!found_name) && (!found_ID)) {
 1931          if (remote_cai.name[Li][0]=='\0') {
 1932             /* 4: Add local category to remote */
 1933 #ifdef SYNC_CAT_DEBUG
 1934             printf("cat index %d case 4\n", Li);
 1935 #endif
 1936             g_strlcpy(remote_cai.name[Li],
 1937                       local_cai.name[Li], PILOTCAT_NAME_SZ);
 1938             remote_cai.ID[Li]=local_cai.ID[Li];
 1939             remote_cai.renamed[Li]=0;
 1940             continue;
 1941          } else {
 1942             if (!remote_cai.renamed[Li]) {
 1943                /* 5a: Category was deleted locally and a new one replaced it.
 1944                 *     Undocumented by Palm. */
 1945 #ifdef SYNC_CAT_DEBUG
 1946                printf("cat index %d case 5a\n", Li);
 1947 #endif
 1948                /* Move the old category's records to Unfiled */
 1949                jp_logf(JP_LOG_DEBUG, "Moving category %d to unfiled...", Li);
 1950                r = dlp_MoveCategory(sd, db, Li, 0);
 1951                jp_logf(JP_LOG_DEBUG, "dlp_MoveCategory returned %d\n", r);
 1952 
 1953                /* Rename slot to the new category */
 1954                g_strlcpy(remote_cai.name[Li], local_cai.name[Li], PILOTCAT_NAME_SZ);
 1955                remote_cai.ID[Li]=local_cai.ID[Li];
 1956                remote_cai.renamed[Li]=0;
 1957                continue;
 1958             }
 1959             if (!local_cai.renamed[Li]) {
 1960                /* 5b: Category was deleted on Palm and a new one replaced it.
 1961                 *     Undocumented by Palm. */
 1962 #ifdef SYNC_CAT_DEBUG
 1963                printf("cat index %d case 5b\n", Li);
 1964 #endif
 1965                /* Move the old category's records to Unfiled */
 1966                jp_logf(JP_LOG_DEBUG, "Moving local recs category %d to Unfiled\n", Li);
 1967                /* Move only changed records(pc3) to Unfiled.  Records in pdb
 1968                 * will be handled by sync record process */
 1969                edit_cats_change_cats_pc3(DB_name, Li, 0);
 1970                remote_cai.renamed[Li]=0;
 1971                continue;
 1972             }
 1973 
 1974             /* 5: Add local category to remote in the next available slot.
 1975                   local records are changed to use this index. */
 1976 #ifdef SYNC_CAT_DEBUG
 1977             printf("cat index %d case 5\n", Li);
 1978 #endif
 1979             found_a_slot=FALSE;
 1980             for (i=1; i<NUM_CATEGORIES; i++) {
 1981                if (remote_cai.name[i][0]=='\0') {
 1982                   g_strlcpy(remote_cai.name[i], 
 1983                             local_cai.name[Li], PILOTCAT_NAME_SZ);
 1984                   remote_cai.renamed[i]=1;
 1985                   remote_cai.ID[i]=local_cai.ID[Li];
 1986                   move_from_idx[move_i] = Li;
 1987                   move_to_idx[move_i] = i;
 1988                   if (++move_i >= NUM_CATEGORIES) {
 1989                      move_i = NUM_CATEGORIES-1;
 1990                      jp_logf(JP_LOG_DEBUG, "Exceeded number of categorie for case 5\n");
 1991                   }
 1992                   found_a_slot=TRUE;
 1993                   break;
 1994                }
 1995             }
 1996             if (!found_a_slot) {
 1997                jp_logf(JP_LOG_WARN, _("Could not add category %s to remote.\n"), local_cai.name[Li]);
 1998                jp_logf(JP_LOG_WARN, _("Too many categories on remote.\n"));
 1999                jp_logf(JP_LOG_WARN, _("All records on desktop in %s will be moved to %s.\n"), local_cai.name[Li], local_cai.name[0]);
 2000                /* Fix - need a func for this logging */
 2001                g_snprintf(log_entry, sizeof(log_entry), _("Could not add category %s to remote.\n"), local_cai.name[Li]);
 2002                charset_j2p(log_entry, 255, char_set);
 2003                dlp_AddSyncLogEntry(sd, log_entry);
 2004                g_snprintf(log_entry, sizeof(log_entry), _("Too many categories on remote.\n"));
 2005                charset_j2p(log_entry, sizeof(log_entry), char_set);
 2006                dlp_AddSyncLogEntry(sd, log_entry);
 2007                g_snprintf(log_entry, sizeof(log_entry), _("All records on desktop in %s will be moved to %s.\n"), local_cai.name[Li], local_cai.name[0]);
 2008                charset_j2p(log_entry, sizeof(log_entry), char_set);
 2009                dlp_AddSyncLogEntry(sd, log_entry);
 2010 
 2011                jp_logf(JP_LOG_DEBUG, "Moving local recs category %d to Unfiled...", Li);
 2012                edit_cats_change_cats_pc3(DB_name, Li, 0);
 2013                edit_cats_change_cats_pdb(DB_name, Li, 0);
 2014             }
 2015             continue;
 2016          }
 2017       }
 2018 #ifdef SYNC_CAT_DEBUG
 2019       printf("Error: cat index %d passed through with no processing\n", Li);
 2020 #endif
 2021    }
 2022    /* Move records locally into correct index slots */
 2023    /* Note that this happens in reverse order so that A->B, B->C, does not
 2024     * result in records from A and B in category C */
 2025    for (i=move_i-1; i>=0; i--) {
 2026       if (move_from_idx[i]) {
 2027          pdb_file_change_indexes(DB_name, move_from_idx[i], move_to_idx[i]);
 2028          edit_cats_change_cats_pc3(DB_name, move_from_idx[i], move_to_idx[i]);
 2029       }
 2030    }
 2031 
 2032    /* Clear the rename flags now that sync has occurred */
 2033    for (i=0; i<NUM_CATEGORIES; i++) {
 2034       remote_cai.renamed[i]=0;
 2035    }
 2036 
 2037    /* Clear any ID fields for blank slots */
 2038    for (i=0; i<NUM_CATEGORIES; i++) {
 2039       if (remote_cai.name[i][0]=='\0') {
 2040          remote_cai.ID[i] = 0;
 2041       }
 2042    }
 2043 
 2044 #ifdef SYNC_CAT_DEBUG
 2045    printf("--- post-sync CategoryAppInfo\n");
 2046    printf("--- DB_name [%s]\n", DB_name);
 2047    for (i=0; i<NUM_CATEGORIES; i++) {
 2048       printf("local: cat %d [%s] ID %d renamed %d\n", i,
 2049              local_cai.name[i],
 2050              local_cai.ID[i], local_cai.renamed[i]);
 2051    }
 2052    printf("-----------------------------\n");
 2053    for (i=0; i<NUM_CATEGORIES; i++) {
 2054       printf("remote: cat %d [%s] ID %d renamed %d\n", i,
 2055              remote_cai.name[i],
 2056              remote_cai.ID[i], remote_cai.renamed[i]);
 2057    }
 2058 #endif
 2059 
 2060    pack_cai_into_ai(&remote_cai, buf, remote_cai_size);
 2061 
 2062    jp_logf(JP_LOG_DEBUG, "writing out new categories for %s\n", DB_name);
 2063    dlp_WriteAppBlock(sd, db, buf, remote_cai_size);
 2064    pdb_file_write_app_block(DB_name, buf, remote_cai_size);
 2065 
 2066    dlp_CloseDB(sd, db);
 2067 
 2068    return EXIT_SUCCESS;
 2069 }
 2070 
 2071 static int slow_sync_application(char *DB_name, int sd)
 2072 {
 2073    int db;
 2074    int ret;
 2075    int num;
 2076    FILE *pc_in;
 2077    char pc_filename[FILENAME_MAX];
 2078    PC3RecordHeader header;
 2079    unsigned long new_unique_id;
 2080    /* local (.pc3) record */
 2081    void *lrec;
 2082    int lrec_len;
 2083    /* remote (Palm) record */
 2084    pi_buffer_t *rrec;
 2085    int  rindex, rattr, rcategory;
 2086    size_t rrec_len;
 2087    long char_set;
 2088    char log_entry[256];
 2089    char write_log_message[256];
 2090    char error_log_message_w[256];
 2091    char error_log_message_d[256];
 2092    char delete_log_message[256];
 2093    char conflict_log_message[256];
 2094    int  same;
 2095 
 2096    jp_logf(JP_LOG_DEBUG, "slow_sync_application\n");
 2097 
 2098    if ((DB_name==NULL) || (strlen(DB_name) == 0) || (strlen(DB_name) > 250)) {
 2099       return EXIT_FAILURE;
 2100    }
 2101 
 2102    g_snprintf(log_entry, sizeof(log_entry), _("Syncing %s\n"), DB_name);
 2103    jp_logf(JP_LOG_GUI, log_entry);
 2104 
 2105    get_pref(PREF_CHAR_SET, &char_set, NULL);
 2106 
 2107    /* This is an attempt to use the proper pronoun most of the time */
 2108    if (strchr("aeiou", tolower(DB_name[0]))) {
 2109       g_snprintf(write_log_message, sizeof(write_log_message),
 2110               _("Wrote an %s record."), DB_name);
 2111       g_snprintf(error_log_message_w, sizeof(error_log_message_w),
 2112               _("Writing an %s record failed."), DB_name);
 2113       g_snprintf(error_log_message_d, sizeof(error_log_message_d),
 2114               _("Deleting an %s record failed."), DB_name);
 2115       g_snprintf(delete_log_message, sizeof(delete_log_message),
 2116               _("Deleted an %s record."), DB_name);
 2117       g_snprintf(conflict_log_message, sizeof(conflict_log_message),
 2118               _("Sync Conflict: duplicated an %s record."), DB_name);
 2119    } else {
 2120       g_snprintf(write_log_message, sizeof(write_log_message),
 2121               _("Wrote a %s record."), DB_name);
 2122       g_snprintf(error_log_message_w, sizeof(error_log_message_w),
 2123               _("Writing a %s record failed."), DB_name);
 2124       g_snprintf(error_log_message_d, sizeof(error_log_message_d),
 2125               _("Deleting a %s record failed."), DB_name);
 2126       g_snprintf(delete_log_message, sizeof(delete_log_message),
 2127               _("Deleted a %s record."), DB_name);
 2128       g_snprintf(conflict_log_message, sizeof(conflict_log_message),
 2129               _("Sync Conflict: duplicated a %s record."), DB_name);
 2130    }
 2131 
 2132    g_snprintf(pc_filename, sizeof(pc_filename), "%s.pc3", DB_name);
 2133    pc_in = jp_open_home_file(pc_filename, "r+");
 2134    if (pc_in==NULL) {
 2135       jp_logf(JP_LOG_WARN, _("Unable to open file: %s\n"), pc_filename);
 2136       return EXIT_FAILURE;
 2137    }
 2138    /* Open the applications database, store access handle in db */
 2139    ret = dlp_OpenDB(sd, 0, dlpOpenReadWrite, DB_name, &db);
 2140    if (ret < 0) {
 2141       g_snprintf(log_entry, sizeof(log_entry), _("Unable to open file: %s\n"), DB_name);
 2142       charset_j2p(log_entry, sizeof(log_entry), char_set);
 2143       dlp_AddSyncLogEntry(sd, log_entry);
 2144       jp_logf(JP_LOG_WARN, "slow_sync_application: %s", log_entry);
 2145       fclose(pc_in);
 2146       return EXIT_FAILURE;
 2147    }
 2148 
 2149 #ifdef JPILOT_DEBUG
 2150    dlp_ReadOpenDBInfo(sd, db, &num);
 2151    jp_logf(JP_LOG_GUI , "number of records = %d\n", num);
 2152 #endif
 2153 
 2154    /* Loop over records in .pc3 file */
 2155    while (!feof(pc_in)) {
 2156       num = read_header(pc_in, &header);
 2157       if (num!=1) {
 2158          if (ferror(pc_in)) break;
 2159          if (feof(pc_in))   break;
 2160       }
 2161 
 2162       lrec_len = header.rec_len;
 2163       if (lrec_len > 0x10000) {
 2164          jp_logf(JP_LOG_WARN, _("PC file corrupt?\n"));
 2165          fclose(pc_in);
 2166          dlp_CloseDB(sd, db);
 2167          return EXIT_FAILURE;
 2168       }
 2169 
 2170       /* Case 5: */
 2171       if ((header.rt==NEW_PC_REC) || (header.rt==REPLACEMENT_PALM_REC)) {
 2172          jp_logf(JP_LOG_DEBUG, "Case 5: new pc record\n");
 2173 
 2174          lrec = malloc(lrec_len);
 2175          if (!lrec) {
 2176             jp_logf(JP_LOG_WARN, "slow_sync_application(): %s\n", _("Out of memory"));
 2177             break;
 2178          }
 2179          num = fread(lrec, lrec_len, 1, pc_in);
 2180          if (num != 1) {
 2181             if (ferror(pc_in)) {
 2182                free(lrec);
 2183                break;
 2184             }
 2185          }
 2186 
 2187          if (header.rt==REPLACEMENT_PALM_REC) {
 2188             /* A replacement must be checked against pdb file to make sure 
 2189              * a simultaneous modification on the Palm has not occurred */
 2190             rrec = pi_buffer_new(65536);
 2191             if (!rrec) {
 2192                jp_logf(JP_LOG_WARN, "slow_sync_application(), pi_buffer_new: %s\n",
 2193                                   _("Out of memory"));
 2194                free(lrec);
 2195                break;
 2196             }
 2197 
 2198             ret = dlp_ReadRecordById(sd, db, header.unique_id, rrec,
 2199                                      &rindex, &rattr, &rcategory);
 2200             rrec_len = rrec->used;
 2201 #ifdef JPILOT_DEBUG
 2202             if (ret>=0 ) {
 2203                printf("read record by id %s returned %d\n", DB_name, ret);
 2204                printf("id %ld, index %d, size %d, attr 0x%x, category %d\n",
 2205                       header.unique_id, rindex, rrec_len, rattr, rcategory);
 2206             } else {
 2207                printf("Case 5: read record by id failed\n");
 2208             }
 2209 #endif
 2210             if ((ret >=0) && !(dlpRecAttrDeleted & rattr)) {
 2211                /* Modified record was also found on Palm. */
 2212                /* Compare records but ignore category changes */
 2213                /* For simultaneous category changes PC wins */
 2214                same = match_records(DB_name, 
 2215                                     rrec->data, rrec_len, rattr, 0,
 2216                                     lrec, lrec_len, header.attrib&0xF0, 0);
 2217 #ifdef JPILOT_DEBUG
 2218                printf("Same is %d\n", same);
 2219 #endif
 2220                if (same && (header.unique_id != 0)) {
 2221                   /* Records are the same. Add pc3 record over Palm one */
 2222                   jp_logf(JP_LOG_DEBUG, "Case 5: lrec & rrec match, keeping Jpilot version\n");
 2223                } else {
 2224                   /* Record has been changed on the palm as well as the PC. 
 2225                    *
 2226                    * The changed record has already been transferred to the 
 2227                    * local pdb file from the Palm. It must be copied to the 
 2228                    * Palm and to the local pdb file under a new unique ID 
 2229                    * in order to prevent overwriting by the modified PC record.
 2230                    * The record is placed in the Unfiled category so it can 
 2231                    * be quickly located. */
 2232                   jp_logf(JP_LOG_DEBUG, "Case 5: duplicating record\n");
 2233                   jp_logf(JP_LOG_GUI, _("Sync Conflict: a %s record must be manually merged\n"), DB_name);
 2234 
 2235                   /* Write record to Palm and get new unique ID */
 2236                   jp_logf(JP_LOG_DEBUG, "Duplicating PC record to palm\n");
 2237                   ret = dlp_WriteRecord(sd, db, rattr & dlpRecAttrSecret,
 2238                                         0, 0,
 2239                                         rrec->data, rrec_len, &new_unique_id);
 2240 
 2241                   if (ret < 0) {
 2242                      jp_logf(JP_LOG_WARN, "dlp_WriteRecord failed\n");
 2243                      charset_j2p(error_log_message_w,255,char_set);
 2244                      dlp_AddSyncLogEntry(sd, error_log_message_w);
 2245                      dlp_AddSyncLogEntry(sd, "\n");
 2246                   } else {
 2247                      charset_j2p(conflict_log_message,255,char_set);
 2248                      dlp_AddSyncLogEntry(sd, conflict_log_message);
 2249                      dlp_AddSyncLogEntry(sd, "\n");
 2250                   }
 2251                }
 2252             }
 2253 
 2254             pi_buffer_free(rrec);
 2255 
 2256          } /* endif REPLACEMENT_PALM_REC */
 2257 
 2258          jp_logf(JP_LOG_DEBUG, "Writing PC record to palm\n");
 2259 
 2260          if (header.rt==REPLACEMENT_PALM_REC) {
 2261             ret = dlp_WriteRecord(sd, db, header.attrib & dlpRecAttrSecret,
 2262                                   header.unique_id, header.attrib & 0x0F,
 2263                                   lrec, lrec_len, &header.unique_id);
 2264          } else {
 2265             ret = dlp_WriteRecord(sd, db, header.attrib & dlpRecAttrSecret,
 2266                                   0, header.attrib & 0x0F,
 2267                                   lrec, lrec_len, &header.unique_id);
 2268          }
 2269 
 2270          if (lrec) {
 2271             free(lrec);
 2272             lrec = NULL;
 2273          }
 2274 
 2275          if (ret < 0) {
 2276             jp_logf(JP_LOG_WARN, "dlp_WriteRecord failed\n");
 2277             charset_j2p(error_log_message_w,255,char_set);
 2278             dlp_AddSyncLogEntry(sd, error_log_message_w);
 2279             dlp_AddSyncLogEntry(sd, "\n");
 2280          } else {
 2281             charset_j2p(write_log_message,255,char_set);
 2282             dlp_AddSyncLogEntry(sd, write_log_message);
 2283             dlp_AddSyncLogEntry(sd, "\n");
 2284             /* mark the record as deleted in the pc file */
 2285             if (fseek(pc_in, -(header.header_len+lrec_len), SEEK_CUR)) {
 2286                jp_logf(JP_LOG_WARN, _("fseek failed - fatal error\n"));
 2287                fclose(pc_in);
 2288                dlp_CloseDB(sd, db);
 2289                return EXIT_FAILURE;
 2290             }
 2291             header.rt=DELETED_PC_REC;
 2292             write_header(pc_in, &header);
 2293          }
 2294       } /* endif Case 5 */
 2295 
 2296       /* Case 3 & 4: */
 2297       if ((header.rt==DELETED_PALM_REC) || (header.rt==MODIFIED_PALM_REC)) {
 2298          jp_logf(JP_LOG_DEBUG, "Case 3&4: deleted or modified pc record\n");
 2299          lrec = malloc(lrec_len);
 2300          if (!lrec) {
 2301             jp_logf(JP_LOG_WARN, "slow_sync_application(): %s\n", 
 2302                                _("Out of memory"));
 2303             break;
 2304          }
 2305          num = fread(lrec, lrec_len, 1, pc_in);
 2306          if (num != 1) {
 2307             if (ferror(pc_in)) {
 2308                free(lrec);
 2309                break;
 2310             }
 2311          }
 2312 
 2313          rrec = pi_buffer_new(65536);
 2314          if (!rrec) {
 2315             jp_logf(JP_LOG_WARN, "slow_sync_application(), pi_buffer_new: %s\n",
 2316                                _("Out of memory"));
 2317             free(lrec);
 2318             break;
 2319          }
 2320 
 2321          ret = dlp_ReadRecordById(sd, db, header.unique_id, rrec,
 2322                                   &rindex, &rattr, &rcategory);
 2323          rrec_len = rrec->used;
 2324 #ifdef JPILOT_DEBUG
 2325          if (ret>=0 ) {
 2326             printf("read record by id %s returned %d\n", DB_name, ret);
 2327             printf("id %ld, index %d, size %d, attr 0x%x, category %d\n",
 2328                    header.unique_id, rindex, rrec_len, rattr, rcategory);
 2329          } else {
 2330             printf("Case 3&4: read record by id failed\n");
 2331          }
 2332 #endif
 2333 
 2334          if ((ret < 0) || (dlpRecAttrDeleted & rattr)) {
 2335             /* lrec can't be found which means it has already 
 2336              * been deleted from the Palm side.
 2337              * Mark the local record as deleted */
 2338             jp_logf(JP_LOG_DEBUG, "Case 3&4: no remote record found, must have been deleted on the Palm\n");
 2339             if (fseek(pc_in, -(header.header_len+lrec_len), SEEK_CUR)) {
 2340                jp_logf(JP_LOG_WARN, _("fseek failed - fatal error\n"));
 2341                fclose(pc_in);
 2342                dlp_CloseDB(sd, db);
 2343                free(lrec);
 2344                pi_buffer_free(rrec);
 2345                return EXIT_FAILURE;
 2346             }
 2347             header.rt=DELETED_DELETED_PALM_REC;
 2348             write_header(pc_in, &header);
 2349          } else {
 2350             /* Record exists on the palm and has been deleted from PC 
 2351              * If the two records are the same, then no changes have
 2352              * occurred on the Palm and the deletion should occur */
 2353             same = match_records(DB_name, 
 2354                                  rrec->data, rrec_len, rattr, rcategory,
 2355                                  lrec, lrec_len, header.attrib&0xF0,
 2356                                                  header.attrib&0x0F);
 2357 #ifdef JPILOT_DEBUG
 2358             printf("Same is %d\n", same);
 2359 #endif
 2360             if (same && (header.unique_id != 0)) {
 2361                jp_logf(JP_LOG_DEBUG, "Case 3&4: lrec & rrec match, deleting\n");
 2362                ret = dlp_DeleteRecord(sd, db, 0, header.unique_id);
 2363                if (ret < 0) {
 2364                   jp_logf(JP_LOG_WARN, _("dlp_DeleteRecord failed\n"\
 2365                   "This could be because the record was already deleted on the Palm\n"));
 2366                   charset_j2p(error_log_message_d,255,char_set);
 2367                   dlp_AddSyncLogEntry(sd, error_log_message_d);
 2368                   dlp_AddSyncLogEntry(sd, "\n");
 2369                } else {
 2370                   charset_j2p(delete_log_message,255,char_set);
 2371                   dlp_AddSyncLogEntry(sd, delete_log_message);
 2372                   dlp_AddSyncLogEntry(sd, "\n");
 2373                }
 2374                
 2375                /* Now mark the record in pc3 file as deleted */
 2376                if (fseek(pc_in, -(header.header_len+lrec_len), SEEK_CUR)) {
 2377                   jp_logf(JP_LOG_WARN, _("fseek failed - fatal error\n"));
 2378                   fclose(pc_in);
 2379                   dlp_CloseDB(sd, db);
 2380                   free(lrec);
 2381                   pi_buffer_free(rrec);
 2382                   return EXIT_FAILURE;
 2383                }
 2384                header.rt=DELETED_DELETED_PALM_REC;
 2385                write_header(pc_in, &header);
 2386 
 2387             } else {
 2388                /* Record has been changed on the palm and deletion can't occur
 2389                 * Mark the pc3 record as having been dealt with */
 2390                jp_logf(JP_LOG_DEBUG, "Case 3: skipping PC deleted record\n");
 2391                if (fseek(pc_in, -(header.header_len+lrec_len), SEEK_CUR)) {
 2392                   jp_logf(JP_LOG_WARN, _("fseek failed - fatal error\n"));
 2393                   fclose(pc_in);
 2394                   dlp_CloseDB(sd, db);
 2395                   free(lrec);
 2396                   pi_buffer_free(rrec);
 2397                   return EXIT_FAILURE;
 2398                }
 2399                header.rt=DELETED_PC_REC;
 2400                write_header(pc_in, &header);
 2401             } /* end if checking whether old & new records are the same */
 2402 
 2403             /* free buffers */
 2404             if (lrec) {
 2405                free(lrec);
 2406                lrec = NULL;
 2407             }
 2408 
 2409             pi_buffer_free(rrec);
 2410 
 2411          } /* record found on Palm */
 2412       } /* end if Case 3&4 */
 2413 
 2414       /* move to next record in .pc3 file */
 2415       if (fseek(pc_in, lrec_len, SEEK_CUR)) {
 2416          jp_logf(JP_LOG_WARN, _("fseek failed - fatal error\n"));
 2417          fclose(pc_in);
 2418          dlp_CloseDB(sd, db);
 2419          return EXIT_FAILURE;
 2420       }
 2421 
 2422    } /* end while on feof(pc_in) */
 2423 
 2424    fclose(pc_in);
 2425 #ifdef JPILOT_DEBUG
 2426    dlp_ReadOpenDBInfo(sd, db, &num);
 2427    jp_logf(JP_LOG_WARN ,"number of records = %d\n", num);
 2428 #endif
 2429    dlp_ResetSyncFlags(sd, db);
 2430    dlp_CleanUpDatabase(sd, db);
 2431    dlp_CloseDB(sd, db);
 2432 
 2433    return EXIT_SUCCESS;
 2434 }
 2435 
 2436 static int fast_sync_local_recs(char *DB_name, int sd, int db)
 2437 {
 2438    int ret;
 2439    int num;
 2440    FILE *pc_in;
 2441    char pc_filename[FILENAME_MAX];
 2442    PC3RecordHeader header;
 2443    unsigned long orig_unique_id, new_unique_id;
 2444    void *lrec;  /* local (.pc3) record */
 2445    int  lrec_len;
 2446    void *rrec;  /* remote (Palm) record */
 2447    int  rindex, rattr, rcategory;
 2448    size_t rrec_len;
 2449    long char_set;
 2450    char write_log_message[256];
 2451    char error_log_message_w[256];
 2452    char error_log_message_d[256];
 2453    char delete_log_message[256];
 2454    char conflict_log_message[256];
 2455    int same;
 2456 
 2457    jp_logf(JP_LOG_DEBUG, "fast_sync_local_recs\n");
 2458    get_pref(PREF_CHAR_SET, &char_set, NULL);
 2459 
 2460    /* This is an attempt to use the proper pronoun most of the time */
 2461    if (strchr("aeiou", tolower(DB_name[0]))) {
 2462       g_snprintf(write_log_message, sizeof(write_log_message),
 2463               _("Wrote an %s record."), DB_name);
 2464       g_snprintf(error_log_message_w, sizeof(error_log_message_w),
 2465               _("Writing an %s record failed."), DB_name);
 2466       g_snprintf(error_log_message_d, sizeof(error_log_message_d),
 2467               _("Deleting an %s record failed."), DB_name);
 2468       g_snprintf(delete_log_message, sizeof(delete_log_message),
 2469               _("Deleted an %s record."), DB_name);
 2470       g_snprintf(conflict_log_message, sizeof(conflict_log_message),
 2471               _("Sync Conflict: duplicated an %s record."), DB_name);
 2472    } else {
 2473       g_snprintf(write_log_message, sizeof(write_log_message),
 2474               _("Wrote a %s record."), DB_name);
 2475       g_snprintf(error_log_message_w, sizeof(error_log_message_w),
 2476               _("Writing a %s record failed."), DB_name);
 2477       g_snprintf(error_log_message_d, sizeof(error_log_message_d),
 2478               _("Deleting a %s record failed."), DB_name);
 2479       g_snprintf(delete_log_message, sizeof(delete_log_message),
 2480               _("Deleted a %s record."), DB_name);
 2481       g_snprintf(conflict_log_message, sizeof(conflict_log_message),
 2482               _("Sync Conflict: duplicated a %s record."), DB_name);
 2483    }
 2484    g_snprintf(pc_filename, sizeof(pc_filename), "%s.pc3", DB_name);
 2485    pc_in = jp_open_home_file(pc_filename, "r+");
 2486    if (pc_in==NULL) {
 2487       jp_logf(JP_LOG_WARN, _("Unable to open file: %s\n"), pc_filename);
 2488       return EXIT_FAILURE;
 2489    }
 2490 
 2491    /* Loop over records in .pc3 file */
 2492    while (!feof(pc_in)) {
 2493       num = read_header(pc_in, &header);
 2494       if (num!=1) {
 2495          if (ferror(pc_in)) break;
 2496          if (feof(pc_in))   break;
 2497       }
 2498 
 2499       lrec_len = header.rec_len;
 2500       if (lrec_len > 0x10000) {
 2501          jp_logf(JP_LOG_WARN, _("PC file corrupt?\n"));
 2502          fclose(pc_in);
 2503          return EXIT_FAILURE;
 2504       }
 2505 
 2506       /* Case 5: */
 2507       if ((header.rt==NEW_PC_REC) || (header.rt==REPLACEMENT_PALM_REC)) {
 2508          jp_logf(JP_LOG_DEBUG, "Case 5: new pc record\n");
 2509 
 2510          lrec = malloc(lrec_len);
 2511          if (!lrec) {
 2512             jp_logf(JP_LOG_WARN, "fast_sync_local_recs(): %s\n", 
 2513                                _("Out of memory"));
 2514             break;
 2515          }
 2516          num = fread(lrec, lrec_len, 1, pc_in);
 2517          if (num != 1) {
 2518             if (ferror(pc_in)) {
 2519                free(lrec);
 2520                break;
 2521             }
 2522          }
 2523 
 2524          if (header.rt==REPLACEMENT_PALM_REC) {
 2525             /* A replacement must be checked against pdb file to make sure 
 2526              * a simultaneous modification on the Palm has not occurred */
 2527             ret = pdb_file_read_record_by_id(DB_name,
 2528                                              header.unique_id,
 2529                                              &rrec, &rrec_len, &rindex,
 2530                                              &rattr, &rcategory);
 2531 #ifdef JPILOT_DEBUG
 2532             if (ret>=0 ) {
 2533                printf("read record by id %s returned %d\n", DB_name, ret);
 2534                printf("id %ld, index %d, size %d, attr 0x%x, category %d\n",
 2535                       header.unique_id, rindex, rrec_len, rattr, rcategory);
 2536             } else {
 2537                printf("Case 5: read record by id failed\n");
 2538             }
 2539 #endif
 2540             if (ret >=0) {
 2541                /* Modified record was also found on Palm. */
 2542                /* Compare records but ignore category changes */
 2543                /* For simultaneous category changes PC wins */
 2544                same = match_records(DB_name, 
 2545                                     rrec, rrec_len, rattr, 0,
 2546                                     lrec, lrec_len, header.attrib&0xF0, 0);
 2547    #ifdef JPILOT_DEBUG
 2548                printf("Same is %d\n", same);
 2549    #endif
 2550                if (same && (header.unique_id != 0)) {
 2551                   /* Records are the same. Add pc3 record over Palm one */
 2552                   jp_logf(JP_LOG_DEBUG, "Case 5: lrec & rrec match, keeping Jpilot version\n");
 2553                } else {
 2554                   /* Record has been changed on the palm as well as the PC. 
 2555                    *
 2556                    * The changed record has already been transferred to the 
 2557                    * local pdb file from the Palm. It must be copied to the 
 2558                    * Palm and to the local pdb file under a new unique ID 
 2559                    * in order to prevent overwriting by the modified PC record.
 2560                    * The record is placed in the Unfiled category so it can 
 2561                    * be quickly located. */
 2562                   jp_logf(JP_LOG_DEBUG, "Case 5: duplicating record\n");
 2563                   jp_logf(JP_LOG_GUI, _("Sync Conflict: a %s record must be manually merged\n"), DB_name);
 2564 
 2565                   /* Write record to Palm and get new unique ID */
 2566                   jp_logf(JP_LOG_DEBUG, "Duplicating PC record to palm\n");
 2567                   ret = dlp_WriteRecord(sd, db, rattr & dlpRecAttrSecret,
 2568                                         0, 0,
 2569                                         rrec, rrec_len, &new_unique_id);
 2570 
 2571                   /* Write record to local pdb file */
 2572                   jp_logf(JP_LOG_DEBUG, "Duplicating PC record to local\n");
 2573                   if (ret >=0) {
 2574                      pdb_file_modify_record(DB_name, rrec, rrec_len,
 2575                                             rattr & dlpRecAttrSecret,
 2576                                             0, new_unique_id);
 2577                   }
 2578 
 2579                   if (ret < 0) {
 2580                      jp_logf(JP_LOG_WARN, "dlp_WriteRecord failed\n");
 2581                      charset_j2p(error_log_message_w,255,char_set);
 2582                      dlp_AddSyncLogEntry(sd, error_log_message_w);
 2583                      dlp_AddSyncLogEntry(sd, "\n");
 2584                   } else {
 2585                      charset_j2p(conflict_log_message,255,char_set);
 2586                      dlp_AddSyncLogEntry(sd, conflict_log_message);
 2587                      dlp_AddSyncLogEntry(sd, "\n");
 2588                   }
 2589                }
 2590             }
 2591          } /* endif REPLACEMENT_PALM_REC */
 2592 
 2593          jp_logf(JP_LOG_DEBUG, "Writing PC record to palm\n");
 2594        
 2595          orig_unique_id = header.unique_id;
 2596 
 2597          if (header.rt==REPLACEMENT_PALM_REC) {
 2598             ret = dlp_WriteRecord(sd, db, header.attrib & dlpRecAttrSecret,
 2599                                   header.unique_id, header.attrib & 0x0F,
 2600                                   lrec, lrec_len, &header.unique_id);
 2601          } else {
 2602             ret = dlp_WriteRecord(sd, db, header.attrib & dlpRecAttrSecret,
 2603                                   0, header.attrib & 0x0F,
 2604                                   lrec, lrec_len, &header.unique_id);
 2605          }
 2606 
 2607          jp_logf(JP_LOG_DEBUG, "Writing PC record to local\n");
 2608          if (ret >=0) {
 2609             if ((header.rt==REPLACEMENT_PALM_REC) &&
 2610                 (orig_unique_id != header.unique_id)) {
 2611                /* There is a possibility that the palm handed back a unique ID
 2612                 * other than the one we requested */
 2613                pdb_file_delete_record_by_id(DB_name, orig_unique_id);
 2614             }
 2615             pdb_file_modify_record(DB_name, lrec, lrec_len,
 2616                                    header.attrib & dlpRecAttrSecret,
 2617                                    header.attrib & 0x0F, header.unique_id);
 2618          }
 2619 
 2620          if (lrec) {
 2621             free(lrec);
 2622             lrec = NULL;
 2623          }
 2624 
 2625          if (ret < 0) {
 2626             jp_logf(JP_LOG_WARN, "dlp_WriteRecord failed\n");
 2627             charset_j2p(error_log_message_w,255,char_set);
 2628             dlp_AddSyncLogEntry(sd, error_log_message_w);
 2629             dlp_AddSyncLogEntry(sd, "\n");
 2630          } else {
 2631             charset_j2p(write_log_message,255,char_set);
 2632             dlp_AddSyncLogEntry(sd, write_log_message);
 2633             dlp_AddSyncLogEntry(sd, "\n");
 2634             /* mark the record as deleted in the pc file */
 2635             if (fseek(pc_in, -(header.header_len+lrec_len), SEEK_CUR)) {
 2636                jp_logf(JP_LOG_WARN, _("fseek failed - fatal error\n"));
 2637                fclose(pc_in);
 2638                return EXIT_FAILURE;
 2639             }
 2640             header.rt=DELETED_PC_REC;
 2641             write_header(pc_in, &header);
 2642          }
 2643 
 2644       } /* endif Case 5 */
 2645 
 2646       /* Case 3 & 4: */
 2647       if ((header.rt==DELETED_PALM_REC) || (header.rt==MODIFIED_PALM_REC)) {
 2648          jp_logf(JP_LOG_DEBUG, "Case 3&4: deleted or modified pc record\n");
 2649          lrec = malloc(lrec_len);
 2650          if (!lrec) {
 2651             jp_logf(JP_LOG_WARN, "fast_sync_local_recs(): %s\n", 
 2652                                _("Out of memory"));
 2653             break;
 2654          }
 2655          num = fread(lrec, lrec_len, 1, pc_in);
 2656          if (num != 1) {
 2657             if (ferror(pc_in)) {
 2658                free(lrec);
 2659                break;
 2660             }
 2661          }
 2662          ret = pdb_file_read_record_by_id(DB_name,
 2663                                           header.unique_id,
 2664                                           &rrec, &rrec_len, &rindex,
 2665                                           &rattr, &rcategory);
 2666 #ifdef JPILOT_DEBUG
 2667          if (ret>=0 ) {
 2668             printf("read record by id %s returned %d\n", DB_name, ret);
 2669             printf("id %ld, index %d, size %d, attr 0x%x, category %d\n",
 2670                    header.unique_id, rindex, rrec_len, rattr, rcategory);
 2671          } else {
 2672             printf("Case 3&4: read record by id failed\n");
 2673          }
 2674 #endif
 2675          if (ret < 0) {
 2676             /* lrec can't be found in pdb file which means it
 2677              * has already been deleted from the Palm side.
 2678              * Mark the local record as deleted */
 2679             jp_logf(JP_LOG_DEBUG, "Case 3&4: no remote record found, must have been deleted on the Palm\n");
 2680             if (fseek(pc_in, -(header.header_len+lrec_len), SEEK_CUR)) {
 2681                jp_logf(JP_LOG_WARN, _("fseek failed - fatal error\n"));
 2682                fclose(pc_in);
 2683                free(lrec);
 2684                free(rrec);
 2685                return EXIT_FAILURE;
 2686             }
 2687             header.rt=DELETED_DELETED_PALM_REC;
 2688             write_header(pc_in, &header);
 2689          } else {
 2690             /* Record exists on the palm and has been deleted from PC 
 2691              * If the two records are the same, then no changes have
 2692              * occurred on the Palm and the deletion should occur */
 2693             same = match_records(DB_name, 
 2694                                  rrec, rrec_len, rattr, rcategory,
 2695                                  lrec, lrec_len, header.attrib&0xF0,
 2696                                                  header.attrib&0x0F);
 2697 #ifdef JPILOT_DEBUG
 2698             printf("Same is %d\n", same);
 2699 #endif
 2700             if (same && (header.unique_id != 0)) {
 2701                jp_logf(JP_LOG_DEBUG, "Case 3&4: lrec & rrec match, deleting\n");
 2702                ret = dlp_DeleteRecord(sd, db, 0, header.unique_id);
 2703                if (ret < 0) {
 2704                   jp_logf(JP_LOG_WARN, _("dlp_DeleteRecord failed\n"
 2705                                          "This could be because the record was already deleted on the Palm\n"));
 2706                   charset_j2p(error_log_message_d,255,char_set);
 2707                   dlp_AddSyncLogEntry(sd, error_log_message_d);
 2708                   dlp_AddSyncLogEntry(sd, "\n");
 2709                } else {
 2710                   charset_j2p(delete_log_message,255,char_set);
 2711                   dlp_AddSyncLogEntry(sd, delete_log_message);
 2712                   dlp_AddSyncLogEntry(sd, "\n");
 2713                   pdb_file_delete_record_by_id(DB_name, header.unique_id);
 2714                }
 2715                
 2716                /* Now mark the record in pc3 file as deleted */
 2717                if (fseek(pc_in, -(header.header_len+lrec_len), SEEK_CUR)) {
 2718                   jp_logf(JP_LOG_WARN, _("fseek failed - fatal error\n"));
 2719                   fclose(pc_in);
 2720                   free(lrec);
 2721                   free(rrec);
 2722                   return EXIT_FAILURE;
 2723                }
 2724                header.rt=DELETED_DELETED_PALM_REC;
 2725                write_header(pc_in, &header);
 2726             } else {
 2727                /* Record has been changed on the palm and deletion can't occur
 2728                 * Mark the pc3 record as having been dealt with */
 2729                jp_logf(JP_LOG_DEBUG, "Case 3: skipping PC deleted record\n");
 2730                if (fseek(pc_in, -(header.header_len+lrec_len), SEEK_CUR)) {
 2731                   jp_logf(JP_LOG_WARN, _("fseek failed - fatal error\n"));
 2732                   fclose(pc_in);
 2733                   free(lrec);
 2734                   free(rrec);
 2735                   return EXIT_FAILURE;
 2736                }
 2737                header.rt=DELETED_PC_REC;
 2738                write_header(pc_in, &header);
 2739             } /* end if checking whether old & new records are the same */
 2740 
 2741             /* free buffers */
 2742             if (lrec) {
 2743                free(lrec);
 2744                lrec = NULL;
 2745             }
 2746 
 2747             if (rrec) {
 2748                free(rrec);
 2749                rrec = NULL;
 2750             }
 2751          } /* record found on Palm */
 2752       } /* end if Case 3&4 */
 2753 
 2754       /* move to next record in .pc3 file */
 2755       if (fseek(pc_in, lrec_len, SEEK_CUR)) {
 2756          jp_logf(JP_LOG_WARN, _("fseek failed - fatal error\n"));
 2757          fclose(pc_in);
 2758          return EXIT_FAILURE;
 2759       }
 2760 
 2761    } /* end while on feof(pc_in) */
 2762 
 2763    fclose(pc_in);
 2764 
 2765    return EXIT_SUCCESS;
 2766 }
 2767 
 2768 
 2769 /*
 2770  * This code does not do archiving.
 2771  *
 2772  * For each remote record (RR):
 2773  *   Case 1:
 2774  *   if RR deleted or archived
 2775  *     remove local record (LR)
 2776  *   Case 2:
 2777  *   if RR changed
 2778  *     change LR, If LR doesn't exist then add it
 2779  *
 2780  * For each local record (LR):
 2781  *   Case 3:
 2782  *   if LR deleted
 2783  *     if RR==OLR (Original LR) remove both RR and LR
 2784  *   Case 4:
 2785  *   if LR changed
 2786  *     We have a new local record (NLR) and a
 2787  *     modified (deleted) local record (MLR)
 2788  *     if NLR==RR then do nothing (either both were changed equally, or
 2789  *                                 local was changed and changed back)
 2790  *     otherwise,
 2791  *       add NLR to remote
 2792  *       if RR==LR remove RR
 2793  *   Case 5:
 2794  *   if new LR
 2795  *     add LR to remote
 2796  */
 2797 static int fast_sync_application(char *DB_name, int sd)
 2798 {
 2799    int db;
 2800    int ret;
 2801    long char_set;
 2802    char log_entry[256];
 2803    char write_log_message[256];
 2804    char error_log_message_w[256];
 2805    char error_log_message_d[256];
 2806    char delete_log_message[256];
 2807    /* remote (Palm) record */
 2808    pi_buffer_t *rrec;
 2809    recordid_t rid=0;
 2810    int rindex, rrec_len, rattr, rcategory;
 2811    int num_local_recs, num_palm_recs;
 2812    char *extra_dbname[2];
 2813 
 2814    jp_logf(JP_LOG_DEBUG, "fast_sync_application %s\n", DB_name);
 2815 
 2816    if ((DB_name==NULL) || (strlen(DB_name) == 0) || (strlen(DB_name) > 250)) {
 2817       return EXIT_FAILURE;
 2818    }
 2819 
 2820    g_snprintf(log_entry, sizeof(log_entry), _("Syncing %s\n"), DB_name);
 2821    jp_logf(JP_LOG_GUI, log_entry);
 2822 
 2823    get_pref(PREF_CHAR_SET, &char_set, NULL);
 2824 
 2825    /* This is an attempt to use the proper pronoun most of the time */
 2826    if (strchr("aeiou", tolower(DB_name[0]))) {
 2827       g_snprintf(write_log_message, sizeof(write_log_message),
 2828               _("Wrote an %s record."),  DB_name);
 2829       g_snprintf(error_log_message_w, sizeof(error_log_message_w),
 2830               _("Writing an %s record failed."), DB_name);
 2831       g_snprintf(error_log_message_d, sizeof(error_log_message_d),
 2832               _("Deleting an %s record failed."), DB_name);
 2833       g_snprintf(delete_log_message, sizeof(delete_log_message),
 2834               _("Deleted an %s record."),  DB_name);
 2835    } else {
 2836       g_snprintf(write_log_message, sizeof(write_log_message),
 2837               _("Wrote a %s record."),  DB_name);
 2838       g_snprintf(error_log_message_w, sizeof(error_log_message_w),
 2839               _("Writing a %s record failed."), DB_name);
 2840       g_snprintf(error_log_message_d, sizeof(error_log_message_d),
 2841               _("Deleting a %s record failed."), DB_name);
 2842       g_snprintf(delete_log_message, sizeof(delete_log_message),
 2843               _("Deleted a %s record."),  DB_name);
 2844    }
 2845    /* Open the applications database, store access handle in db */
 2846    ret = dlp_OpenDB(sd, 0, dlpOpenReadWrite|dlpOpenSecret, DB_name, &db);
 2847    if (ret < 0) {
 2848       g_snprintf(log_entry, sizeof(log_entry), _("Unable to open file: %s\n"), DB_name);
 2849       charset_j2p(log_entry, sizeof(log_entry), char_set);
 2850       dlp_AddSyncLogEntry(sd, log_entry);
 2851       jp_logf(JP_LOG_WARN, "fast_sync_application: %s", log_entry);
 2852       return EXIT_FAILURE;
 2853    }
 2854 
 2855    /* I can't get the appinfodirty flag to work, so I do this for now */
 2856    /* ret = dlp_ReadAppBlock(sd, db, 0, rrec, 65535);
 2857    jp_logf(JP_LOG_DEBUG, "readappblock ret=%d\n", ret);
 2858    if (ret>0) {
 2859       pdb_file_write_app_block(DB_name, rrec, ret);
 2860    }*/
 2861 
 2862    /* Loop over all Palm records with dirty bit set */
 2863    while(1) {
 2864       rrec = pi_buffer_new(0);
 2865       ret = dlp_ReadNextModifiedRec(sd, db, rrec,
 2866                                     &rid, &rindex, &rattr, &rcategory);
 2867       rrec_len = rrec->used;
 2868       if (ret>=0 ) {
 2869          jp_logf(JP_LOG_DEBUG, "read next record for %s returned %d\n", DB_name, ret);
 2870          jp_logf(JP_LOG_DEBUG, "id %ld, index %d, size %d, attr 0x%x, category %d\n",rid, rindex, rrec_len, rattr, rcategory);
 2871       } else {
 2872          pi_buffer_free(rrec);
 2873          break;
 2874       }
 2875 
 2876       /* Case 1: */
 2877       if ((rattr & dlpRecAttrDeleted) || (rattr & dlpRecAttrArchived)) {
 2878          jp_logf(JP_LOG_DEBUG, "Case 1: found a deleted record on palm\n");
 2879          pdb_file_delete_record_by_id(DB_name, rid);
 2880          pi_buffer_free(rrec);
 2881          continue;
 2882       }
 2883 
 2884       /* Case 2: */
 2885       if (rattr & dlpRecAttrDirty) {
 2886          jp_logf(JP_LOG_DEBUG, "Case 2: found a dirty record on palm\n");
 2887          pdb_file_modify_record(DB_name, rrec->data, rrec->used, rattr, rcategory, rid);
 2888          pi_buffer_free(rrec);
 2889          continue;
 2890       }
 2891 
 2892       pi_buffer_free(rrec);
 2893    } /* end while over Palm records */
 2894 
 2895    fast_sync_local_recs(DB_name, sd, db);
 2896 
 2897    dlp_ResetSyncFlags(sd, db);
 2898    dlp_CleanUpDatabase(sd, db);
 2899 
 2900    /* Count the number of records, should be equal, may not be */
 2901    dlp_ReadOpenDBInfo(sd, db, &num_palm_recs);
 2902    pdb_file_count_recs(DB_name, &num_local_recs);
 2903 
 2904    dlp_CloseDB(sd, db);
 2905 
 2906    if (num_local_recs != num_palm_recs) {
 2907       extra_dbname[0] = DB_name;
 2908       extra_dbname[1] = NULL;
 2909       jp_logf(JP_LOG_DEBUG, "fetch_extra_DBs() [%s]\n", extra_dbname[0]);
 2910       jp_logf(JP_LOG_DEBUG, "palm: number of records = %d\n", num_palm_recs);
 2911       jp_logf(JP_LOG_DEBUG, "disk: number of records = %d\n", num_local_recs);
 2912       fetch_extra_DBs(sd, extra_dbname);
 2913    }
 2914 
 2915    return EXIT_SUCCESS;
 2916 }
 2917 
 2918 static int jp_install_user(const char *device, int sd, 
 2919                            struct my_sync_info *sync_info)
 2920 {
 2921    struct PilotUser U;
 2922 
 2923    U.userID=sync_info->userID;
 2924    U.viewerID=0;
 2925    U.lastSyncPC=0;
 2926    strncpy(U.username, sync_info->username, sizeof(U.username));
 2927 
 2928    dlp_WriteUserInfo(sd, &U);
 2929 
 2930    dlp_EndOfSync(sd, 0);
 2931    pi_close(sd);
 2932 
 2933    jp_logf(JP_LOG_GUI, _("Finished installing user information.\n"));
 2934    return EXIT_SUCCESS;
 2935 }
 2936 
 2937 static int jp_sync(struct my_sync_info *sync_info)
 2938 {
 2939    int sd;
 2940    int ret;
 2941    struct PilotUser U;
 2942    const char *device;
 2943    char default_device[]="/dev/pilot";
 2944    int found=0, fast_sync=0;
 2945 #ifdef ENABLE_PLUGINS
 2946    GList *plugin_list, *temp_list;
 2947    struct plugin_s *plugin;
 2948 #endif
 2949 #ifdef JPILOT_DEBUG
 2950    int start;
 2951    struct DBInfo info;
 2952 #endif
 2953 #ifdef ENABLE_PRIVATE
 2954    char hex_password[PASSWD_LEN*2+4];
 2955 #endif
 2956    char buf[1024];
 2957    long char_set;
 2958 #ifdef JPILOT_DEBUG
 2959    pi_buffer_t *buffer;
 2960 #endif
 2961    int i;
 2962    /* rename_dbnames is used to modify this list to newer databases if needed */
 2963    char dbname[][32]={
 2964       "DatebookDB",
 2965       "AddressDB",
 2966       "ToDoDB",
 2967       "MemoDB",
 2968 #ifdef ENABLE_MANANA
 2969       "MananaDB",
 2970 #endif
 2971       ""
 2972    };
 2973    int pref_sync_array[]={
 2974       PREF_SYNC_DATEBOOK,
 2975       PREF_SYNC_ADDRESS,
 2976       PREF_SYNC_TODO,
 2977       PREF_SYNC_MEMO,
 2978 #ifdef ENABLE_MANANA
 2979       PREF_SYNC_MANANA,
 2980 #endif
 2981       0
 2982    };
 2983 
 2984    /* 
 2985     * This is pretty confusing, but necessary.
 2986     * This is an array of pointers to functions and will need to be changed
 2987     * to point to calendar, contacts, tasks, memos, etc. as preferences
 2988     * dictate.
 2989     */
 2990    int (*unpack_cai_from_buf[])(struct CategoryAppInfo *cai, unsigned char *ai_raw, int len) = {
 2991       NULL,
 2992       unpack_address_cai_from_ai,
 2993       unpack_todo_cai_from_ai,
 2994       unpack_memo_cai_from_ai,
 2995       unpack_memo_cai_from_ai,
 2996 #ifdef ENABLE_MANANA
 2997       unpack_todo_cai_from_ai,
 2998 #endif
 2999       NULL
 3000    };
 3001    int (*pack_cai_into_buf[])(struct CategoryAppInfo *cai, unsigned char *ai_raw, int len) = {
 3002       NULL,
 3003       pack_address_cai_into_ai,
 3004       pack_todo_cai_into_ai,
 3005       pack_memo_cai_into_ai,
 3006       pack_memo_cai_into_ai,
 3007 #ifdef ENABLE_MANANA
 3008       pack_todo_cai_into_ai,
 3009 #endif
 3010       NULL
 3011    };
 3012 
 3013    long datebook_version, address_version, todo_version, memo_version;
 3014 
 3015    /* Convert to new database names if prefs set */
 3016    rename_dbnames(dbname);
 3017 
 3018    /* Change unpack/pack function pointers if needed */
 3019    get_pref(PREF_DATEBOOK_VERSION, &datebook_version, NULL);
 3020    get_pref(PREF_ADDRESS_VERSION, &address_version, NULL);
 3021    get_pref(PREF_TODO_VERSION, &todo_version, NULL);
 3022    get_pref(PREF_MEMO_VERSION, &memo_version, NULL);
 3023    if (datebook_version==0) {
 3024       unpack_cai_from_buf[0]=unpack_datebook_cai_from_ai;
 3025       pack_cai_into_buf[0]=pack_datebook_cai_into_ai;
 3026    }
 3027    if (datebook_version==1) {
 3028       unpack_cai_from_buf[0]=unpack_calendar_cai_from_ai;
 3029       pack_cai_into_buf[0]=pack_calendar_cai_into_ai;
 3030    }
 3031    if (address_version==1) {
 3032       unpack_cai_from_buf[1]=unpack_contact_cai_from_ai;
 3033       pack_cai_into_buf[1]=pack_contact_cai_into_ai;
 3034    }
 3035    if (todo_version==1) {
 3036       /* FIXME: Uncomment when support for Task has been added
 3037       unpack_cai_from_buf[2]=unpack_task_cai_from_ai;
 3038       pack_cai_into_buf[2]=pack_task_cai_into_ai;
 3039       */
 3040       ;
 3041    }
 3042    if (memo_version==1) {
 3043       /* No change necessary between Memo and Memos databases */
 3044       ;
 3045    }
 3046 
 3047    /* Load the plugins for a forked process */
 3048 #ifdef ENABLE_PLUGINS
 3049    if (!(SYNC_NO_FORK & sync_info->flags) &&
 3050        !(SYNC_NO_PLUGINS & sync_info->flags)) {
 3051       jp_logf(JP_LOG_DEBUG, "sync:calling load_plugins\n");
 3052       load_plugins();
 3053    }
 3054 #endif
 3055 #ifdef ENABLE_PLUGINS
 3056    /* Do the plugin_pre_sync_pre_connect calls */
 3057    plugin_list = NULL;
 3058    plugin_list = get_plugin_list();
 3059 
 3060    for (temp_list = plugin_list; temp_list; temp_list = temp_list->next) {
 3061       plugin = (struct plugin_s *)temp_list->data;
 3062       if (plugin) {
 3063          if (plugin->sync_on) {
 3064             if (plugin->plugin_pre_sync_pre_connect) {
 3065                jp_logf(JP_LOG_DEBUG, "sync:calling plugin_pre_sync_pre_connect for [%s]\n", plugin->name);
 3066                plugin->plugin_pre_sync_pre_connect();
 3067             }
 3068          }
 3069       }
 3070    }
 3071 #endif
 3072 
 3073    device = NULL;
 3074    if (sync_info->port) {
 3075       if (sync_info->port[0]) {
 3076          /* A port was passed in to use */
 3077          device=sync_info->port;
 3078          found = 1;
 3079       }
 3080    }
 3081    if (!found) {
 3082       /* No port was passed in, look in env */
 3083       device = getenv("PILOTPORT");
 3084       if (device == NULL) {
 3085          device = default_device;
 3086       }
 3087    }
 3088 
 3089    jp_logf(JP_LOG_GUI, "****************************************\n");
 3090    jp_logf(JP_LOG_GUI, _(" Syncing on device %s\n"), device);
 3091    jp_logf(JP_LOG_GUI, _(" Press the HotSync button now\n"));
 3092    jp_logf(JP_LOG_GUI, "****************************************\n");
 3093 
 3094    ret = jp_pilot_connect(&sd, device);
 3095    if (ret) {
 3096       return ret;
 3097    }
 3098 
 3099    if (SYNC_INSTALL_USER & sync_info->flags) {
 3100       ret = jp_install_user(device, sd, sync_info);
 3101       write_to_parent(PIPE_FINISHED, "\n");
 3102       return ret;
 3103    }
 3104 
 3105    /* The connection has been established here */
 3106    /* Plugins should call pi_watchdog(); if they are going to be a while */
 3107 #ifdef ENABLE_PLUGINS
 3108    /* Do the pre_sync plugin calls */
 3109    plugin_list=NULL;
 3110 
 3111    plugin_list = get_plugin_list();
 3112 
 3113    for (temp_list = plugin_list; temp_list; temp_list = temp_list->next) {
 3114       plugin = (struct plugin_s *)temp_list->data;
 3115       if (plugin) {
 3116          if (plugin->sync_on) {
 3117             if (plugin->plugin_pre_sync) {
 3118                jp_logf(JP_LOG_DEBUG, "sync:calling plugin_pre_sync for [%s]\n", plugin->name);
 3119                plugin->plugin_pre_sync();
 3120             }
 3121          }
 3122       }
 3123    }
 3124 #endif
 3125 
 3126    U.username[0]='\0';
 3127    ret = dlp_ReadUserInfo(sd, &U);
 3128 
 3129    /* Do some checks to see if this is the same palm that was synced
 3130     * the last time */
 3131    if ( (U.userID == 0) &&
 3132       (!(SYNC_RESTORE & sync_info->flags)) ) {
 3133       jp_logf(JP_LOG_GUI, _("Last Synced Username-->\"%s\"\n"), sync_info->username);
 3134       jp_logf(JP_LOG_GUI, _("Last Synced UserID-->\"%d\"\n"), sync_info->userID);
 3135       jp_logf(JP_LOG_GUI, _(" This Username-->\"%s\"\n"), U.username);
 3136       jp_logf(JP_LOG_GUI, _(" This User ID-->%d\n"), U.userID);
 3137 
 3138       if (SYNC_NO_FORK & sync_info->flags) {
 3139          return SYNC_ERROR_NULL_USERID;
 3140       } else {
 3141          write_to_parent(PIPE_WAITING_ON_USER, "%d:\n", SYNC_ERROR_NULL_USERID);
 3142          ret = wait_for_response(sd);
 3143       }
 3144 
 3145       if (ret != PIPE_SYNC_CONTINUE) {
 3146          dlp_EndOfSync(sd, 0);
 3147          pi_close(sd);
 3148          return SYNC_ERROR_NULL_USERID;
 3149       }
 3150    }
 3151    if ((sync_info->userID != U.userID) &&
 3152        (sync_info->userID != 0) &&
 3153        (!(SYNC_OVERRIDE_USER & sync_info->flags)) &&
 3154        (!(SYNC_RESTORE & sync_info->flags))) {
 3155       jp_logf(JP_LOG_GUI, _("Last Synced Username-->\"%s\"\n"), sync_info->username);
 3156       jp_logf(JP_LOG_GUI, _("Last Synced UserID-->\"%d\"\n"), sync_info->userID);
 3157       jp_logf(JP_LOG_GUI, _(" This Username-->\"%s\"\n"), U.username);
 3158       jp_logf(JP_LOG_GUI, _(" This User ID-->%d\n"), U.userID);
 3159 
 3160       if (SYNC_NO_FORK & sync_info->flags) {
 3161          return SYNC_ERROR_NOT_SAME_USERID;
 3162       } else {
 3163          write_to_parent(PIPE_WAITING_ON_USER, "%d:\n", SYNC_ERROR_NOT_SAME_USERID);
 3164          ret = wait_for_response(sd);
 3165       }
 3166 
 3167       if (ret != PIPE_SYNC_CONTINUE) {
 3168          dlp_EndOfSync(sd, 0);
 3169          pi_close(sd);
 3170          return SYNC_ERROR_NOT_SAME_USERID;
 3171       }
 3172    } else if ((strcmp(sync_info->username, U.username)) &&
 3173               (sync_info->username[0]!='\0') &&
 3174               (!(SYNC_OVERRIDE_USER & sync_info->flags)) &&
 3175               (!(SYNC_RESTORE & sync_info->flags))) {
 3176       jp_logf(JP_LOG_GUI, _("Last Synced Username-->\"%s\"\n"), sync_info->username);
 3177       jp_logf(JP_LOG_GUI, _("Last Synced UserID-->\"%d\"\n"), sync_info->userID);
 3178       jp_logf(JP_LOG_GUI, _(" This Username-->\"%s\"\n"), U.username);
 3179       jp_logf(JP_LOG_GUI, _(" This User ID-->%d\n"), U.userID);
 3180       write_to_parent(PIPE_WAITING_ON_USER, "%d:\n", SYNC_ERROR_NOT_SAME_USER);
 3181       if (SYNC_NO_FORK & sync_info->flags) {
 3182          return SYNC_ERROR_NOT_SAME_USER;
 3183       } else {
 3184          ret = wait_for_response(sd);
 3185       }
 3186 
 3187       if (ret != PIPE_SYNC_CONTINUE) {
 3188          dlp_EndOfSync(sd, 0);
 3189          pi_close(sd);
 3190          return SYNC_ERROR_NOT_SAME_USER;
 3191       }
 3192    }
 3193 
 3194    /* User name and User ID is read by the parent process and stored
 3195     * in the preferences.
 3196     * So, this is more than just displaying it to the user */
 3197    if (!(SYNC_RESTORE & sync_info->flags)) {
 3198       write_to_parent(PIPE_USERNAME, "\"%s\"\n", U.username);
 3199       write_to_parent(PIPE_USERID, "%d", U.userID);
 3200       jp_logf(JP_LOG_GUI, _("Username is \"%s\"\n"), U.username);
 3201       jp_logf(JP_LOG_GUI, _("User ID is %d\n"), U.userID);
 3202    }
 3203    jp_logf(JP_LOG_GUI, _("lastSyncPC = %d\n"), U.lastSyncPC);
 3204    jp_logf(JP_LOG_GUI, _("This PC = %lu\n"), sync_info->PC_ID);
 3205    jp_logf(JP_LOG_GUI, "****************************************\n");
 3206 
 3207    jp_logf(JP_LOG_DEBUG, "Last Username = [%s]\n", sync_info->username);
 3208    jp_logf(JP_LOG_DEBUG, "Last UserID = %d\n", sync_info->userID);
 3209    jp_logf(JP_LOG_DEBUG, "Username = [%s]\n", U.username);
 3210    jp_logf(JP_LOG_DEBUG, "userID = %d\n", U.userID);
 3211    jp_logf(JP_LOG_DEBUG, "lastSyncPC = %d\n", U.lastSyncPC);
 3212 
 3213 #ifdef ENABLE_PRIVATE
 3214    if (U.passwordLength > 0) {
 3215       bin_to_hex_str((unsigned char *)U.password, hex_password,
 3216                      ((U.passwordLength > 0)&&(U.passwordLength < PASSWD_LEN))
 3217                      ? U.passwordLength : PASSWD_LEN);
 3218    } else {
 3219       strcpy(hex_password, "09021345070413440c08135a3215135dd217ead3b5df556322e9a14a994b0f88");
 3220    }
 3221    jp_logf(JP_LOG_DEBUG, "passwordLength = %d\n", U.passwordLength);
 3222    jp_logf(JP_LOG_DEBUG, "userPassword = [%s]\n", hex_password);
 3223    write_to_parent(PIPE_PASSWORD, "\"%s\"\n", hex_password);
 3224 #endif
 3225 
 3226    if (dlp_OpenConduit(sd)<0) {
 3227       jp_logf(JP_LOG_WARN, "dlp_OpenConduit() failed\n");
 3228       jp_logf(JP_LOG_WARN, _("Sync canceled\n"));
 3229 #ifdef ENABLE_PLUGINS
 3230       if (!(SYNC_NO_FORK & sync_info->flags)) 
 3231          free_plugin_list(&plugin_list);
 3232 #endif
 3233       dlp_EndOfSync(sd, 0);
 3234       pi_close(sd);
 3235       return SYNC_ERROR_OPEN_CONDUIT;
 3236    }
 3237 
 3238    sync_process_install_file(sd);
 3239 
 3240    if ((SYNC_RESTORE & sync_info->flags)) {
 3241       U.userID=sync_info->userID;
 3242       U.viewerID=0;
 3243       U.lastSyncPC=0;
 3244       strncpy(U.username, sync_info->username, sizeof(U.username));
 3245 
 3246       dlp_WriteUserInfo(sd, &U);
 3247 
 3248       dlp_EndOfSync(sd, 0);
 3249       pi_close(sd);
 3250 
 3251       jp_logf(JP_LOG_GUI, _("Finished restoring handheld.\n"));
 3252       jp_logf(JP_LOG_GUI, _("You may need to sync to update J-Pilot.\n"));
 3253       write_to_parent(PIPE_FINISHED, "\n");
 3254       return EXIT_SUCCESS;
 3255    }
 3256 
 3257 #ifdef JPILOT_DEBUG
 3258    start=0;
 3259    buffer = pi_buffer_new(sizeof(struct DBInfo));
 3260    while(dlp_ReadDBList(sd, 0, dlpDBListRAM, start, buffer)>0) {
 3261       memcpy(&info, buffer->data, sizeof(struct DBInfo));
 3262       start=info.index+1;
 3263       if (info.flags & dlpDBFlagAppInfoDirty) {
 3264          printf("appinfo dirty for %s\n", info.name);
 3265       }
 3266    }
 3267    pi_buffer_free(buffer);
 3268 #endif
 3269 
 3270    /* Do a fast, or a slow sync on each application in the arrays */
 3271    if ( (!(SYNC_OVERRIDE_USER & sync_info->flags)) &&
 3272         (U.lastSyncPC == sync_info->PC_ID) ) {
 3273       fast_sync=1;
 3274       jp_logf(JP_LOG_GUI, _("Doing a fast sync.\n"));
 3275       for (i=0; dbname[i][0]; i++) {
 3276          if (get_pref_int_default(pref_sync_array[i], 1)) {
 3277             if (unpack_cai_from_buf[i] && pack_cai_into_buf[i]) {
 3278                sync_categories(dbname[i], sd,
 3279                                unpack_cai_from_buf[i],
 3280                                pack_cai_into_buf[i]);
 3281             }
 3282             fast_sync_application(dbname[i], sd);
 3283          }
 3284       }
 3285    } else {
 3286       fast_sync=0;
 3287       jp_logf(JP_LOG_GUI, _("Doing a slow sync.\n"));
 3288       for (i=0; dbname[i][0]; i++) {
 3289          if (get_pref_int_default(pref_sync_array[i], 1)) {
 3290             if (unpack_cai_from_buf[i] && pack_cai_into_buf[i]) {
 3291                sync_categories(dbname[i], sd,
 3292                                unpack_cai_from_buf[i],
 3293                                pack_cai_into_buf[i]);
 3294             }
 3295             slow_sync_application(dbname[i], sd);
 3296          }
 3297       }
 3298    }
 3299 
 3300 
 3301 #ifdef ENABLE_PLUGINS
 3302    plugin_list = get_plugin_list();
 3303 
 3304    for (temp_list = plugin_list; temp_list; temp_list = temp_list->next) {
 3305       plugin = (struct plugin_s *)temp_list->data;
 3306       jp_logf(JP_LOG_DEBUG, "syncing plugin name: [%s]\n", plugin->name);
 3307       if ((plugin->db_name==NULL) || (plugin->db_name[0]=='\0')) {
 3308          jp_logf(JP_LOG_DEBUG, "not syncing plugin DB: [%s]\n", plugin->db_name);
 3309          continue;
 3310       }
 3311       jp_logf(JP_LOG_DEBUG, "syncing plugin DB: [%s]\n", plugin->db_name);
 3312       if (fast_sync) {
 3313          if (plugin->sync_on) {
 3314             if (plugin->plugin_unpack_cai_from_ai &&
 3315                 plugin->plugin_pack_cai_into_ai) {
 3316                sync_categories(plugin->db_name, sd,
 3317                                plugin->plugin_unpack_cai_from_ai,
 3318                                plugin->plugin_pack_cai_into_ai);
 3319             }
 3320             fast_sync_application(plugin->db_name, sd);
 3321          }
 3322       } else {
 3323          if (plugin->sync_on) {
 3324             if (plugin->plugin_unpack_cai_from_ai &&
 3325                 plugin->plugin_pack_cai_into_ai) {
 3326                sync_categories(plugin->db_name, sd,
 3327                                plugin->plugin_unpack_cai_from_ai,
 3328                                plugin->plugin_pack_cai_into_ai);
 3329             }
 3330             slow_sync_application(plugin->db_name, sd);
 3331          }
 3332       }
 3333    }
 3334 #endif
 3335 
 3336 #ifdef ENABLE_PLUGINS
 3337    /* Do the sync plugin calls */
 3338    plugin_list=NULL;
 3339 
 3340    plugin_list = get_plugin_list();
 3341 
 3342    for (temp_list = plugin_list; temp_list; temp_list = temp_list->next) {
 3343       plugin = (struct plugin_s *)temp_list->data;
 3344       if (plugin) {
 3345          if (plugin->sync_on) {
 3346             if (plugin->plugin_sync) {
 3347                jp_logf(JP_LOG_DEBUG, "calling plugin_sync for [%s]\n", plugin->name);
 3348                plugin->plugin_sync(sd);
 3349             }
 3350          }
 3351       }
 3352    }
 3353 #endif
 3354 
 3355    sync_fetch(sd, sync_info->flags, sync_info->num_backups, fast_sync);
 3356 
 3357    /* Tell the user who it is, with this PC id. */
 3358    U.lastSyncPC = sync_info->PC_ID;
 3359    U.successfulSyncDate = time(NULL);
 3360    U.lastSyncDate = U.successfulSyncDate;
 3361    dlp_WriteUserInfo(sd, &U);
 3362    if (strncpy(buf,_("Thank you for using J-Pilot."),1024) == NULL) {
 3363       jp_logf(JP_LOG_DEBUG, "memory allocation internal error\n");
 3364       dlp_EndOfSync(sd, 0);
 3365       pi_close(sd);
 3366 #ifdef ENABLE_PLUGINS
 3367       if (!(SYNC_NO_FORK & sync_info->flags)) 
 3368          free_plugin_list(&plugin_list);
 3369 #endif
 3370       return 0;
 3371    }
 3372    get_pref(PREF_CHAR_SET, &char_set, NULL);
 3373    charset_j2p(buf,1023,char_set);
 3374 
 3375    dlp_AddSyncLogEntry(sd, buf);
 3376    dlp_AddSyncLogEntry(sd, "\n");
 3377 
 3378    dlp_EndOfSync(sd, 0);
 3379    pi_close(sd);
 3380 
 3381    cleanup_pc_files();
 3382 
 3383 #ifdef ENABLE_PLUGINS
 3384    /* Do the sync plugin calls */
 3385    plugin_list=NULL;
 3386 
 3387    plugin_list = get_plugin_list();
 3388 
 3389    for (temp_list = plugin_list; temp_list; temp_list = temp_list->next) {
 3390       plugin = (struct plugin_s *)temp_list->data;
 3391       if (plugin) {
 3392          if (plugin->sync_on) {
 3393             if (plugin->plugin_post_sync) {
 3394                jp_logf(JP_LOG_DEBUG, "calling plugin_post_sync for [%s]\n", plugin->name);
 3395                plugin->plugin_post_sync();
 3396             }
 3397          }
 3398       }
 3399    }
 3400 
 3401    if (!(SYNC_NO_FORK & sync_info->flags)) {
 3402       jp_logf(JP_LOG_DEBUG, "freeing plugin list\n");
 3403       free_plugin_list(&plugin_list);
 3404    }
 3405 #endif
 3406 
 3407    jp_logf(JP_LOG_GUI, _("Finished.\n"));
 3408    write_to_parent(PIPE_FINISHED, "\n");
 3409 
 3410    return EXIT_SUCCESS;
 3411 }
 3412 
 3413 int sync_once(struct my_sync_info *sync_info)
 3414 {
 3415 #ifdef USE_LOCKING
 3416    int fd;
 3417 #endif
 3418    int r;
 3419    struct my_sync_info sync_info_copy;
 3420    pid_t pid;
 3421 
 3422 #ifdef __APPLE__
 3423    /* bug 1924 */
 3424    sync_info->flags |= SYNC_NO_FORK;
 3425 #endif
 3426 
 3427 #ifdef USE_LOCKING
 3428    r = sync_lock(&fd);
 3429    if (r) {
 3430       jp_logf(JP_LOG_DEBUG, "Child cannot lock file\n");
 3431       if (!(SYNC_NO_FORK & sync_info->flags)) {
 3432          _exit(255);
 3433       } else {
 3434          return EXIT_FAILURE;
 3435       }
 3436    }
 3437 #endif
 3438 
 3439    /* This should never be reached with new cancel sync code
 3440     * Although, it can be reached through a remote sync. */
 3441    if (glob_child_pid) {
 3442       jp_logf(JP_LOG_WARN, _("%s: sync process already in progress (process ID = %d)\n"), PN, glob_child_pid);
 3443       jp_logf(JP_LOG_WARN, _("%s: press the HotSync button on the cradle\n"
 3444                              "         or stop the sync by using the cancel sync button\n"
 3445                              "         or stop the sync by typing \"kill %d\" at the command line\n"), PN, glob_child_pid);
 3446       return EXIT_FAILURE;
 3447    }
 3448 
 3449    /* Make a copy of the sync info for the forked process */
 3450    memcpy(&sync_info_copy, sync_info, sizeof(struct my_sync_info));
 3451 
 3452    if (!(SYNC_NO_FORK & sync_info->flags)) {
 3453       jp_logf(JP_LOG_DEBUG, "forking sync process\n");
 3454       signal(SIGCHLD, sig_handler);
 3455       glob_child_pid = -1;
 3456       pid = fork();
 3457       switch (pid){
 3458        case -1:
 3459          perror("fork");
 3460          return 0;
 3461        case 0:
 3462          /* child continues sync */
 3463          break;
 3464        default:
 3465          /* parent stores child pid and goes back to GUI */
 3466          if (-1 == glob_child_pid)
 3467             glob_child_pid = pid;
 3468          return EXIT_SUCCESS;
 3469       }
 3470    }
 3471 
 3472    r = jp_sync(&sync_info_copy);
 3473    if (r) {
 3474       jp_logf(JP_LOG_WARN, _("Exiting with status %s\n"), get_error_str(r));
 3475       jp_logf(JP_LOG_WARN, _("Finished.\n"));
 3476    }
 3477 #ifdef USE_LOCKING
 3478    sync_unlock(fd);
 3479 #endif
 3480    jp_logf(JP_LOG_DEBUG, "sync child exiting\n");
 3481    if (!(SYNC_NO_FORK & sync_info->flags)) {
 3482       _exit(255);
 3483    } else {
 3484       return r;
 3485    }
 3486 }
 3487 
 3488 /***********************************************************************/
 3489 /* Imported from pilot-xfer.c, pilot-link-0.12.5, 2010-10-31 */
 3490 /***********************************************************************/
 3491 
 3492 /***********************************************************************
 3493  *
 3494  * Function:    pi_file_install_VFS
 3495  *
 3496  * Summary:     Push file(s) to the Palm's VFS (parameters intentionally
 3497  *              similar to pi_file_install).
 3498  *
 3499  * Parameters:  fd       --> open file descriptor for file
 3500  *              basename --> filename or description of file
 3501  *              socket   --> sd, connection to Palm
 3502  *              vfspath  --> target in VFS, may be dir or filename
 3503  *              f        --> progress function, in the style of pi_file_install
 3504  *
 3505  * Returns:     -1 on bad parameters
 3506  *              -2 on cancelled sync
 3507  *              -3 on bad vfs path
 3508  *              -4 on bad local file
 3509  *              -5 on insufficient VFS space for the file
 3510  *              -6 on memory allocation error
 3511  *              >=0 if all went well (size of installed file)
 3512  *
 3513  * Note:        Should probably return an ssize_t and refuse to do files >
 3514  *              2Gb, due to signedness.
 3515  *
 3516  ***********************************************************************/
 3517 static int pi_file_install_VFS(const int fd, const char *basename, const int socket, const char *vfspath, progress_func f)
 3518 {
 3519     enum { bad_parameters=-1,
 3520            cancel=-2,
 3521            bad_vfs_path=-3,
 3522            bad_local_file=-4,
 3523            insufficient_space=-5,
 3524            internal_=-6
 3525     } ;
 3526 
 3527     char        rpath[vfsMAXFILENAME];
 3528     int         rpathlen = vfsMAXFILENAME;
 3529     FileRef     file;
 3530     unsigned long attributes;
 3531     char        *filebuffer = NULL;
 3532     long        volume = -1;
 3533     long        used,
 3534                 total,
 3535                 freespace;
 3536     int
 3537                 writesize,
 3538                 offset;
 3539     size_t      readsize;
 3540     size_t      written_so_far = 0;
 3541     enum { no_path=0, appended_filename=1, retried=2, done=3 } path_steps;
 3542     struct stat sbuf;
 3543     pi_progress_t progress;
 3544 
 3545     if (fstat(fd,&sbuf) < 0) {
 3546         fprintf(stderr,"   ERROR: Cannot stat '%s'.\n",basename);
 3547         return bad_local_file;
 3548     }
 3549 
 3550     if (findVFSPath(socket,vfspath,&volume,rpath,&rpathlen) < 0)
 3551     {
 3552         fprintf(stderr,"\n   VFS path '%s' does not exist.\n\n", vfspath);
 3553         return bad_vfs_path;
 3554     }
 3555 
 3556     if (dlp_VFSVolumeSize(socket,volume,&used,&total)<0)
 3557     {
 3558         fprintf(stderr,"   Unable to get volume size.\n");
 3559         return bad_vfs_path;
 3560     }
 3561 
 3562     /* Calculate free space but leave last 64k free on card */
 3563     freespace  = total - used - 65536 ;
 3564 
 3565     if ((unsigned long)sbuf.st_size > freespace)
 3566     {
 3567         fprintf(stderr, "\n\n");
 3568         fprintf(stderr, "   Insufficient space to install this file on your Palm.\n");
 3569         fprintf(stderr, "   We needed %lu and only had %lu available..\n\n",
 3570                 (unsigned long)sbuf.st_size, freespace);
 3571         return insufficient_space;
 3572     }
 3573 #define APPEND_BASENAME path_steps-=1; \
 3574             if (rpath[rpathlen-1] != '/') { \
 3575                 rpath[rpathlen++]='/'; \
 3576                     rpath[rpathlen]=0; \
 3577             } \
 3578             strncat(rpath,basename,vfsMAXFILENAME-rpathlen-1); \
 3579             rpathlen = strlen(rpath);
 3580 
 3581     path_steps = no_path;
 3582 
 3583     while (path_steps<retried)
 3584     {
 3585         /* Don't retry by default. APPEND_BASENAME changes
 3586            the file being tries, so it decrements fd again.
 3587            Because we add _two_ here, (two steps fwd, one step back)
 3588            we try at most twice anyway.
 3589               no_path->retried
 3590               appended_filename -> done
 3591            Note that APPEND_BASENAME takes one off, so
 3592               retried->appended_basename
 3593         */
 3594         path_steps+=2;
 3595 
 3596         if (dlp_VFSFileOpen(socket,volume,rpath,dlpVFSOpenRead,&file) < 0)
 3597         {
 3598             /* Target doesn't exist. If it ends with a /, try to
 3599             create the directory and then act as if the existing
 3600             directory was given as target. If it doesn't, carry
 3601             on, it's a regular file to create. */
 3602             if ('/' == rpath[rpathlen-1])
 3603             {
 3604                 /* directory, doesn't exist. Don't try to mkdir /. */
 3605                 if ((rpathlen > 1)
 3606                         && (dlp_VFSDirCreate(socket,volume,rpath) < 0))
 3607                 {
 3608                     fprintf(stderr,"   Could not create destination directory.\n");
 3609                     return bad_vfs_path;
 3610                 }
 3611                 APPEND_BASENAME
 3612             }
 3613             if (dlp_VFSFileCreate(socket,volume,rpath) < 0)
 3614             {
 3615                 fprintf(stderr,"   Cannot create destination file '%s'.\n",
 3616                         rpath);
 3617                 return bad_vfs_path;
 3618             }
 3619         }
 3620         else
 3621         {
 3622             /* Exists, and may be a directory, or a filename. If it's
 3623             a filename, that's fine as long as we're installing
 3624             just a single file. */
 3625             if (dlp_VFSFileGetAttributes(socket,file,&attributes) < 0)
 3626             {
 3627                 fprintf(stderr,"   Could not get attributes for destination.\n");
 3628                 (void) dlp_VFSFileClose(socket,file);
 3629                 return bad_vfs_path;
 3630             }
 3631 
 3632             if (attributes & vfsFileAttrDirectory)
 3633             {
 3634                 APPEND_BASENAME
 3635                 dlp_VFSFileClose(socket,file);
 3636                 /* Now for sure it's a filename in a directory. */
 3637             } else {
 3638                 dlp_VFSFileClose(socket,file);
 3639                 if ('/' == rpath[rpathlen-1])
 3640                 {
 3641                     /* was expecting a directory */
 3642                     fprintf(stderr,"   Destination is not a directory.\n");
 3643                     return bad_vfs_path;
 3644                 }
 3645             }
 3646         }
 3647     }
 3648 #undef APPEND_BASENAME
 3649 
 3650     if (dlp_VFSFileOpen(socket,volume,rpath,0x7,&file) < 0)
 3651     {
 3652         fprintf(stderr,"   Cannot open destination file '%s'.\n",rpath);
 3653         return bad_vfs_path;
 3654     }
 3655 
 3656     /* If the file already exists we want to truncate it so if we write a smaller file
 3657      * the tail of the previous file won't show */
 3658     if (dlp_VFSFileResize(socket, file, 0) < 0)
 3659     {
 3660         fprintf(stderr,"   Cannot truncate file size to 0 '%s'.\n",rpath);
 3661         /* Non-fatal error, continue */
 3662     }
 3663 
 3664 #define FBUFSIZ 65536
 3665     filebuffer = (char *)malloc(FBUFSIZ);
 3666     if (NULL == filebuffer)
 3667     {
 3668         fprintf(stderr,"   Cannot allocate memory for file copy.\n");
 3669         dlp_VFSFileClose(socket,file);
 3670         close(fd);
 3671         return internal_;
 3672     }
 3673 
 3674     memset(&progress, 0, sizeof(progress));
 3675     progress.type = PI_PROGRESS_SEND_VFS;
 3676     progress.data.vfs.path = (char *) basename;
 3677     progress.data.vfs.total_bytes = sbuf.st_size;
 3678 
 3679     writesize = 0;
 3680     written_so_far = 0;
 3681     while (writesize >= 0)
 3682     {
 3683         readsize = read(fd,filebuffer,FBUFSIZ);
 3684         if (readsize <= 0) break;
 3685         offset=0;
 3686         while (readsize > 0)
 3687         {
 3688             writesize = dlp_VFSFileWrite(socket,file,filebuffer+offset,readsize);
 3689             if (writesize < 0)
 3690             {
 3691                 fprintf(stderr,"   Error while writing file.\n");
 3692                 break;
 3693             }
 3694             readsize -= writesize;
 3695             offset += writesize;
 3696             written_so_far += writesize;
 3697             progress.transferred_bytes += writesize;
 3698 
 3699             if ((writesize>0) || (readsize > 0)) {
 3700                 if (f && (f(socket, &progress) == PI_TRANSFER_STOP)) {
 3701                     sbuf.st_size = 0;
 3702                     pi_set_error(socket,PI_ERR_FILE_ABORTED);
 3703                     goto cleanup;
 3704                 }
 3705             }
 3706         }
 3707     }
 3708 
 3709 cleanup:
 3710     free(filebuffer);
 3711     dlp_VFSFileClose(socket,file);
 3712    
 3713     close(fd);
 3714     return sbuf.st_size;
 3715 }
 3716 
 3717 /***********************************************************************
 3718  *
 3719  * Function:    findVFSRoot_clumsy
 3720  *
 3721  * Summary:     For internal use only. May contain live weasels.
 3722  *
 3723  * Parameters:  root_component --> root path to search for.
 3724  *              match          <-> volume matching root_component.
 3725  *
 3726  * Returns:     -2 on VFSVolumeEnumerate error,
 3727  *              -1 if no match was found,
 3728  *              0 if a match was found and @p match is set,
 3729  *              1 if no match but only one VFS volume exists and
 3730  *                match is set.
 3731  *
 3732  ***********************************************************************/
 3733 static int
 3734 findVFSRoot_clumsy(int sd, const char *root_component, long *match)
 3735 {
 3736     int             volume_count         = 16;
 3737     int             volumes[16];
 3738     struct VFSInfo  info;
 3739     int             i;
 3740     int             buflen;
 3741     char            buf[vfsMAXFILENAME];
 3742     long            matched_volume       = -1;
 3743 
 3744     if (dlp_VFSVolumeEnumerate(sd,&volume_count,volumes) < 0)
 3745     {
 3746         return -2;
 3747     }
 3748 
 3749     /* Here we scan the "root directory" of the Pilot.  We will fake out
 3750        a bunch of directories pointing to the various "cards" on the
 3751        device. If we're listing, print everything out, otherwise remain
 3752        silent and just set matched_volume if there's a match in the
 3753        first filename component. */
 3754     for (i = 0; i<volume_count; ++i)
 3755     {
 3756         if (dlp_VFSVolumeInfo(sd,volumes[i],&info) < 0)
 3757             continue;
 3758 
 3759         buflen=vfsMAXFILENAME;
 3760         buf[0]=0;
 3761         (void) dlp_VFSVolumeGetLabel(sd,volumes[i],&buflen,buf);
 3762 
 3763         /* Not listing, so just check matches and continue. */
 3764         if (0 == strcmp(root_component,buf)) {
 3765             matched_volume = volumes[i];
 3766             break;
 3767         }
 3768         /* volume label no longer important, overwrite */
 3769         sprintf(buf,"card%d",info.slotRefNum);
 3770 
 3771         if (0 == strcmp(root_component,buf)) {
 3772             matched_volume = volumes[i];
 3773             break;
 3774         }
 3775     }
 3776 
 3777     if (matched_volume >= 0) {
 3778         *match = matched_volume;
 3779         return 0;
 3780     }
 3781 
 3782     if ((matched_volume < 0) && (1 == volume_count)) {
 3783         /* Assume that with one card, just go look there. */
 3784         *match = volumes[0];
 3785         return 1;
 3786     }
 3787     return -1;
 3788 }
 3789 
 3790 /***********************************************************************
 3791  *
 3792  * Function:    findVFSPath
 3793  *
 3794  * Summary:     Search the VFS volumes for @p path. Sets @p volume
 3795  *              equal to the VFS volume matching @p path (if any) and
 3796  *              fills buffer @p rpath with the path to the file relative
 3797  *              to the volume.
 3798  *
 3799  *              Acceptable root components are /cardX/ for card indicators
 3800  *              or /volumename/ for for identifying VFS volumes by their
 3801  *              volume name. In the special case that there is only one
 3802  *              VFS volume, no root component need be specified, and
 3803  *              "/DCIM/" will map to "/card1/DCIM/".
 3804  *
 3805  * Parameters:  path           --> path to search for.
 3806  *              volume         <-> volume containing path.
 3807  *              rpath          <-> buffer for path relative to volume.
 3808  *              rpathlen       <-> in: length of buffer; out: length of
 3809  *                                 relative path.
 3810  *
 3811  * Returns:     -2 on VFSVolumeEnumerate error,
 3812  *              -1 if no match was found,
 3813  *              0 if a match was found.
 3814  *
 3815  ***********************************************************************/
 3816 static int
 3817 findVFSPath(int sd, const char *path, long *volume, char *rpath, int *rpathlen)
 3818 {
 3819     char *s;
 3820     int   r;
 3821 
 3822     if ((NULL == path) || (NULL == rpath) || (NULL == rpathlen))
 3823         return -1;
 3824     if (*rpathlen < strlen(path))
 3825         return -1;
 3826 
 3827     memset(rpath,0,*rpathlen);
 3828     if ('/'==path[0])
 3829         strncpy(rpath,path+1,*rpathlen-1);
 3830     else
 3831         strncpy(rpath,path,*rpathlen-1);
 3832     s = strchr(rpath,'/');
 3833     if (NULL != s)
 3834         *s=0;
 3835 
 3836 
 3837     r = findVFSRoot_clumsy(sd, rpath,volume);
 3838     if (r < 0)
 3839         return r;
 3840 
 3841     if (0 == r)
 3842     {
 3843         /* Path includes card/volume label. */
 3844         r = strlen(rpath);
 3845         if ('/'==path[0])
 3846             ++r; /* adjust for stripped / */
 3847         memset(rpath,0,*rpathlen);
 3848         strncpy(rpath,path+r,*rpathlen-1);
 3849     } else {
 3850         /* Path without card label */
 3851         memset(rpath,0,*rpathlen);
 3852         strncpy(rpath,path,*rpathlen-1);
 3853     }
 3854 
 3855     if (!rpath[0])
 3856     {
 3857         rpath[0]='/';
 3858         rpath[1]=0;
 3859     }
 3860     *rpathlen = strlen(rpath);
 3861     return 0;
 3862 }