Back to index

courier  0.68.2
pcpdir.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2001-2006 Double Precision, Inc.  See COPYING for
00003 ** distribution information.
00004 */
00005 
00006 
00007 #include "config.h"
00008 #include <stdio.h>
00009 #include <stdlib.h>
00010 #include <string.h>
00011 #include <errno.h>
00012 #include <signal.h>
00013 #include <fcntl.h>
00014 #include <unistd.h>
00015 #include <ctype.h>
00016 #include <sys/types.h>
00017 #include <sys/stat.h>
00018 #include <sys/time.h>
00019 #include <rfc822/rfc822hdr.h>
00020 #include "pcp.h"
00021 
00022 #if HAVE_DIRENT_H
00023 #include <dirent.h>
00024 #else
00025 #define dirent direct
00026 #if HAVE_SYS_NDIR_H
00027 #include <sys/ndir.h>
00028 #endif
00029 #if HAVE_SYS_DIR_H
00030 #include <sys/dir.h>
00031 #endif
00032 #if HAVE_NDIR_H
00033 #include <ndir.h>
00034 #endif
00035 #endif
00036 
00037 /* PCP driver for filesystem-based calendar */
00038 
00039 #define HOSTNAMELEN 256
00040 
00041 struct PCPdir {
00042        struct PCP pcp;
00043        char *username;
00044        char *dirname;
00045        char *indexname;
00046        char *newindexname;
00047        char hostname_buf[HOSTNAMELEN];
00048        char unique_filename_buf[256];
00049        unsigned uniq_cnt;
00050 } ;
00051 
00052 struct PCPdir_event_participant {
00053        char *address;
00054        char *eventid;
00055 } ;
00056 
00057 struct PCPdir_new_eventid {
00058        struct PCP_new_eventid eventid;
00059        char *newfile;
00060        char *tmpfile;
00061 
00062        char *oldeventid;
00063 
00064        struct PCPdir_event_participant *event_participants;
00065        unsigned n_event_participants;
00066 
00067        int booked;
00068 } ;
00069 
00070 static void pcp_close_dir(struct PCPdir *);
00071 static int cleanup(struct PCPdir *);
00072 
00073 static struct PCPdir_new_eventid *neweventid(struct PCPdir *,
00074                                         const char *,
00075                                         struct PCP_save_event *);
00076 static void destroyeventid(struct PCPdir *, struct PCPdir_new_eventid *);
00077 
00078 static int saveevent(struct PCPdir *, struct PCPdir_new_eventid *,
00079                    struct PCP_save_event *);
00080 
00081 static int commitevent(struct PCPdir *, struct PCPdir_new_eventid *,
00082                      struct PCP_commit *);
00083 static int bookevent(struct PCPdir *, struct PCPdir_new_eventid *,
00084                    struct PCP_commit *);
00085 
00086 static int listallevents(struct PCPdir *, struct PCP_list_all *);
00087 
00088 static int cancelevent(struct PCPdir *, const char *, int *);
00089 static int uncancelevent(struct PCPdir *, const char *,
00090                       int, struct PCP_uncancel *);
00091 static int deleteevent(struct PCPdir *, struct PCP_delete *);
00092 static int retrevent(struct PCPdir *, struct PCP_retr *);
00093 static int setacl(struct PCPdir *, const char *, int);
00094 static int listacl(struct PCPdir *,
00095                  int (*)(const char *, int, void *),
00096                  void *);
00097 
00098 static const char *errmsg(struct PCP *pcp)
00099 {
00100        return (strerror(errno));
00101 }
00102 
00103 static void noop(struct PCP *pcp)
00104 {
00105 }
00106 
00107 static const char *getauthtoken(struct PCP *pcp)
00108 {
00109        return (NULL);
00110 }
00111 
00112 struct PCP *pcp_open_dir(const char *dirname, const char *username)
00113 {
00114        struct PCPdir *pd=(struct PCPdir *)malloc(sizeof(struct PCPdir));
00115 
00116        if (!pd)
00117               return (NULL);
00118 
00119        memset(pd, 0, sizeof(*pd));
00120 
00121        pd->username=strdup(username);
00122        if (!pd->username)
00123        {
00124               free(pd);
00125               return (NULL);
00126        }
00127 
00128        pd->dirname=strdup(dirname);
00129 
00130        if (!pd->dirname)
00131        {
00132               free(pd);
00133               return (NULL);
00134        }
00135 
00136        pd->indexname=malloc(strlen(dirname)+sizeof("/index"));
00137        if (!pd->indexname)
00138        {
00139               free(pd->username);
00140               free(pd->dirname);
00141               free(pd);
00142               return (NULL);
00143        }
00144        strcat(strcpy(pd->indexname, dirname), "/index");
00145 
00146        pd->newindexname=malloc(strlen(dirname)+sizeof("/.index.tmp"));
00147        if (!pd->newindexname)
00148        {
00149               free(pd->username);
00150               free(pd->indexname);
00151               free(pd->dirname);
00152               free(pd);
00153               return (NULL);
00154        }
00155        strcat(strcpy(pd->newindexname, dirname), "/.index.tmp");
00156 
00157        pd->hostname_buf[sizeof(pd->hostname_buf)-1]=0;
00158        pd->hostname_buf[0]=0;
00159        if (gethostname(pd->hostname_buf, sizeof(pd->hostname_buf)-1) < 0
00160            || pd->hostname_buf[0] == 0)
00161               strcpy(pd->hostname_buf, "localhost");
00162 
00163 
00164        pd->pcp.authtoken_func=getauthtoken;
00165        pd->pcp.close_func= (void (*)(struct PCP *)) pcp_close_dir;
00166        pd->pcp.cleanup_func= (int (*)(struct PCP *)) cleanup;
00167 
00168        pd->pcp.create_new_eventid_func=
00169               (struct PCP_new_eventid *(*)(struct PCP *, const char *,
00170                                         struct PCP_save_event *))
00171               neweventid;
00172 
00173        pd->pcp.destroy_new_eventid_func=
00174               (void (*)(struct PCP *, struct PCP_new_eventid *))
00175               destroyeventid;
00176 
00177        pd->pcp.commit_func=
00178               (int (*)(struct PCP *, struct PCP_new_eventid *,
00179                       struct PCP_commit *))
00180               commitevent;
00181 
00182        pd->pcp.book_func=
00183               (int (*)(struct PCP *, struct PCP_new_eventid *,
00184                       struct PCP_commit *))
00185               bookevent;
00186 
00187        pd->pcp.list_all_func=
00188               (int (*)(struct PCP *, struct PCP_list_all *))
00189               listallevents;
00190 
00191        pd->pcp.cancel_func=
00192               (int (*)(struct PCP *, const char *, int *))
00193               cancelevent;
00194 
00195        pd->pcp.uncancel_func=
00196               (int (*)(struct PCP *, const char *, int,
00197                       struct PCP_uncancel *))
00198               uncancelevent;
00199 
00200        pd->pcp.delete_func=
00201               (int (*)(struct PCP *, struct PCP_delete *))
00202               deleteevent;
00203 
00204        pd->pcp.retr_func=
00205               (int (*)(struct PCP *, struct PCP_retr *))
00206               retrevent;
00207 
00208        pd->pcp.errmsg_func=errmsg;
00209 
00210        pd->pcp.noop_func=noop;
00211 
00212        pd->pcp.acl_func=
00213               (int (*)(struct PCP *, const char *, int))setacl;
00214        pd->pcp.listacl_func=
00215               (int (*)(struct PCP *, int (*)(const char *, int, void *),
00216                       void *))listacl;
00217 
00218        return ((struct PCP *)pd);
00219 }
00220 
00221 static void pcp_close_dir(struct PCPdir *pd)
00222 {
00223        free(pd->newindexname);
00224        free(pd->indexname);
00225        free(pd->dirname);
00226        free(pd->username);
00227        free(pd);
00228 }
00229 
00230 
00231 static void mkunique(struct PCPdir *pd)
00232 {
00233        struct timeval tv;
00234 
00235        gettimeofday(&tv, NULL);
00236 
00237        sprintf(pd->unique_filename_buf, "%lu.%lu.%lu_%u.",
00238               (unsigned long)tv.tv_sec,
00239               (unsigned long)tv.tv_usec,
00240               (unsigned long)getpid(),
00241               ++pd->uniq_cnt);
00242 
00243        strncat(pd->unique_filename_buf, pd->hostname_buf,
00244               sizeof(pd->unique_filename_buf)-1
00245               -strlen(pd->unique_filename_buf));
00246 }
00247 
00248 static char *dotlockname(struct PCPdir *pd)
00249 {
00250        char *p=malloc(strlen(pd->dirname)+sizeof("/.lock"));
00251 
00252        if (!p)
00253               return (NULL);
00254 
00255        return (strcat(strcpy(p, pd->dirname), "/.lock"));
00256 }
00257 
00258 /*
00259 ** Mark calendar as updated
00260 */
00261 
00262 static char *changedname(struct PCPdir *pd)
00263 {
00264        char *p=malloc(strlen(pd->dirname)+sizeof("/changed"));
00265 
00266        if (!p)
00267        {
00268               fprintf(stderr, "CRIT: out of memory.\n");
00269               return (NULL);
00270        }
00271 
00272        return (strcat(strcpy(p, pd->dirname), "/changed"));
00273 }
00274 
00275 static void markchanged(struct PCPdir *pd)
00276 {
00277        char *p=changedname(pd);
00278        int n;
00279 
00280        if (!p) return;
00281 
00282        n=open(p, O_RDWR|O_CREAT, 0600);
00283        if (n >= 0)
00284               close(n);
00285 }
00286 
00287 /*
00288 ** Attempt to get rid of a stale dot-lock file.
00289 **
00290 ** Return code: 0 - no dot-lock exists
00291 **              1 - transient race condition, try again later
00292 **              2 - valid dot-lock exists
00293 **             <0 - permanent error.
00294 */
00295 
00296 #define TRY_SLEEP 3
00297 #define TRY_MAX 3
00298 
00299 static int kill_stale_lock(struct PCPdir *pd, const char *stale_lock,
00300                         time_t *tm)
00301 {
00302        int fd;
00303        char buffer[HOSTNAMELEN+256];
00304        int n;
00305        struct stat stat_buf;
00306 
00307        fd=open(stale_lock, O_RDONLY);
00308        if (fd < 0)
00309        {
00310               if (errno == ENOENT)
00311                      return (0);
00312               return (-1);
00313        }
00314 
00315        n=read(fd, buffer, sizeof(buffer)-1);
00316 
00317        if (n < 0 || fstat(fd, &stat_buf) < 0)
00318        {
00319               close(fd);
00320               return (-1);
00321        }
00322 
00323        *tm=stat_buf.st_mtime;
00324        close(fd);
00325 
00326        if (n > 0 && buffer[n-1] == '\n')
00327        {
00328               char *p;
00329 
00330               buffer[n-1]=0;
00331 
00332               p=strchr(buffer, ' ');
00333               if (p)
00334               {
00335                      *p++=0;
00336                      if (strcmp(p, pd->hostname_buf) == 0)
00337                             /* Dot-lock created on this server */
00338                      {
00339                             pid_t pn=atol(buffer);
00340 
00341                             if (pn > 1)
00342                             {
00343                                    if (kill(pn, 0) == 0 ||
00344                                        errno != ESRCH)
00345                                    {
00346                                           return (2);
00347                                    }
00348                                    /* Process doesn't exist no more */
00349 
00350                                    if (unlink(stale_lock) == 0
00351                                        || errno == ENOENT)
00352                                    {
00353                                           return (1);
00354                                    }
00355                                    return (-1);
00356                             }
00357 
00358                             fprintf(stderr, "INFO: "
00359                                    "Corrupted dot-lock - removing %s\n",
00360                                    stale_lock);
00361 
00362                             if (unlink(stale_lock) == 0
00363                                 || errno == ENOENT)
00364                             {
00365                                    return (1);
00366                             }
00367                             return (-1);
00368                      }
00369 
00370                      return (1);
00371               }
00372        }
00373 
00374        fprintf(stderr, "INFO: Corrupted dot-lock - removing %s\n", stale_lock);
00375 
00376        if (unlink(stale_lock) == 0
00377            || errno == ENOENT)
00378        {
00379               return (1);
00380        }
00381 
00382        return (-1);
00383 }
00384 
00385 static char *acquire_dotlock(struct PCPdir *pd)
00386 {
00387        char *n=dotlockname(pd);
00388        char *tmpname;
00389        FILE *fp;
00390        unsigned try_cnt;
00391        time_t last_time;
00392 
00393        if (!n)
00394               return (NULL);
00395 
00396        mkunique(pd);
00397 
00398        tmpname=malloc(strlen(pd->dirname)+strlen(pd->unique_filename_buf)
00399                      +20);
00400 
00401        if (!tmpname)
00402        {
00403               free(n);
00404               fprintf(stderr, "ALERT: Failed to create dotlock: %s - %s\n",
00405                      n, strerror(errno));
00406               return (NULL);
00407        }
00408        strcat(strcat(strcat(strcpy(tmpname, pd->dirname), "/."),
00409                     pd->unique_filename_buf), ".lock");
00410 
00411        fp=fopen(tmpname, "w");
00412 
00413        if (!fp)
00414        {
00415               free(tmpname);
00416               free(n);
00417               fprintf(stderr, "ALERT: Failed to create dotlock: %s - %s\n",
00418                      n, strerror(errno));
00419               return (NULL);
00420        }
00421 
00422        if (fprintf(fp, "%lu %s\n", (unsigned long)getpid(), pd->hostname_buf)
00423            < 0 || fflush(fp))
00424        {
00425               fclose(fp);
00426               unlink(tmpname);
00427               free(tmpname);
00428               free(n);
00429               fprintf(stderr, "ALERT: Failed to create dotlock: %s - %s\n",
00430                      n, strerror(errno));
00431               return (NULL);
00432        }
00433 
00434        if (fclose(fp) < 0)
00435        {
00436               unlink(tmpname);
00437               free(tmpname);
00438               free(n);
00439               fprintf(stderr, "ALERT: Failed to create dotlock: %s - %s\n",
00440                      n, strerror(errno));
00441               return (NULL);
00442        }
00443 
00444        try_cnt=0;
00445        last_time=0;
00446        for (;;)
00447        {
00448               time_t timestamp;
00449               int rc=kill_stale_lock(pd, n, &timestamp);
00450 
00451               if (rc > 0)
00452               {
00453                      ++try_cnt;
00454                      if (last_time == 0)
00455                             last_time=timestamp;
00456 
00457                      if (timestamp != last_time)
00458                             try_cnt=0;    /* Dot-lock was modified,
00459                                           ** reset.
00460                                           */
00461 
00462                      if (try_cnt == TRY_MAX)     /* Time to kill dot lock */
00463                      {
00464                             if (rc > 1 || (unlink(n) < 0
00465                                           && errno != ENOENT))
00466                             {
00467                                    fprintf(stderr, "ALERT: "
00468                                           "Failed to obtain dotlock: %s\n",
00469                                           n);
00470 
00471                                    unlink(tmpname);
00472                                    free(tmpname);
00473                                    free(n);
00474                                    return (NULL);
00475                             }
00476                             fprintf(stderr, "ALERT: Removed stale dotlock: %s\n",
00477                                    n);
00478 
00479                      }
00480                      else if (try_cnt > TRY_MAX)
00481                      {
00482                             fprintf(stderr, "ALERT: "
00483                                    "Failed to obtain dotlock: %s\n",
00484                                    n);
00485 
00486                             unlink(tmpname);
00487                             free(tmpname);
00488                             free(n);
00489                             return (NULL);
00490                      }
00491                      last_time=timestamp;
00492                      sleep(TRY_SLEEP);
00493                      continue;
00494               }
00495 
00496               if (rc < 0)
00497               {
00498                      fprintf(stderr, "ALERT: Failed to obtain dotlock: %s\n",
00499                             n);
00500                      unlink(tmpname);
00501                      free(tmpname);
00502                      free(n);
00503                      return (NULL);
00504               }
00505 
00506               if (link(tmpname, n) == 0)
00507                      break;
00508 
00509               if (try_cnt > TRY_MAX)
00510               {
00511                      fprintf(stderr, "ALERT: Failed to obtain dotlock: %s\n",
00512                             n);
00513 
00514                      unlink(tmpname);
00515                      free(tmpname);
00516                      free(n);
00517                      return (NULL);
00518               }
00519               ++try_cnt;
00520               sleep(TRY_SLEEP);
00521        }
00522 
00523        unlink(tmpname);
00524        free(tmpname);
00525        return (n);
00526 }
00527 
00528 #if 0
00529 static void renew_dotlock(struct PCPdir *pd, char *n)
00530 {
00531        FILE *fp;
00532        char buf[HOSTNAMELEN*2];
00533 
00534        fp=fopen(n, "r");
00535 
00536        if (fp && fgets(buf, sizeof(buf), fp) != NULL)
00537        {
00538               char *p=strchr(buf, '\n');
00539 
00540               if (p)
00541                      *p=0;
00542 
00543               p=strchr(buf, ' ');
00544               if (p)
00545               {
00546                      *p++=0;
00547 
00548                      if (atol(buf) == getpid() &&
00549                          strcmp(p, pd->hostname_buf) == 0)
00550                      {
00551                             fclose(fp);
00552                             fp=fopen(n, "r+");
00553                             if (fp)
00554                             {
00555                                    if (fseek(fp, -1, SEEK_END) >= 0)
00556                                           putc('\n', fp);
00557                                    fflush(fp);
00558                                    fclose(fp);
00559                             }
00560                             return;
00561                      }
00562               }
00563        }
00564        if (fp)
00565               fclose(fp);
00566 }
00567 #endif
00568 
00569 static void release_dotlock(struct PCPdir *pd, char *n)
00570 {
00571        FILE *fp;
00572        char buf[HOSTNAMELEN*2];
00573 
00574        fp=fopen(n, "r");
00575 
00576        if (fp && fgets(buf, sizeof(buf), fp) != NULL)
00577        {
00578               char *p=strchr(buf, '\n');
00579 
00580               if (p)
00581                      *p=0;
00582 
00583               p=strchr(buf, ' ');
00584               if (p)
00585               {
00586                      *p++=0;
00587 
00588                      if (atol(buf) == getpid() &&
00589                          strcmp(p, pd->hostname_buf) == 0)
00590                      {
00591                             fclose(fp);
00592                             unlink(n);
00593                             free(n);
00594                             return;
00595                      }
00596               }
00597        }
00598        if (fp)
00599               fclose(fp);
00600        fprintf(stderr, "ALERT: Dotlock unexpectedly gone: %s\n", n);
00601        free(n);
00602 }
00603 
00604 static struct PCPdir_new_eventid *neweventid(struct PCPdir *pd,
00605                                         const char *oldeventid_arg,
00606                                         struct PCP_save_event *se)
00607 {
00608        unsigned ut;
00609 
00610        struct PCPdir_new_eventid *p=(struct PCPdir_new_eventid *)
00611               malloc(sizeof(struct PCPdir_new_eventid));
00612 
00613        if (!p)
00614               return (NULL);
00615 
00616        mkunique(pd);
00617 
00618        p->eventid.eventid=malloc(strlen(pd->unique_filename_buf)+
00619                               strlen(pd->username)+2);
00620        if (!p->eventid.eventid)
00621        {
00622               free(p);
00623               return (NULL);
00624        }
00625 
00626        strcat(strcat(strcpy(p->eventid.eventid, pd->unique_filename_buf),
00627                     "@"), pd->username);
00628 
00629        p->tmpfile=malloc(strlen(pd->unique_filename_buf)
00630                        +strlen(pd->dirname)+10);
00631        if (!p->tmpfile)
00632        {
00633               free(p->eventid.eventid);
00634               free(p);
00635               return (NULL);
00636        }
00637        strcat(strcat(strcat(strcpy(p->tmpfile, pd->dirname),
00638                           "/."), pd->unique_filename_buf), ".tmp");
00639 
00640        p->newfile=malloc(strlen(pd->unique_filename_buf)
00641                        +strlen(pd->dirname)+2);
00642        if (!p->newfile)
00643        {
00644               free(p->tmpfile);
00645               free(p->eventid.eventid);
00646               free(p);
00647               return (NULL);
00648        }
00649        strcat(strcat(strcpy(p->newfile, pd->dirname),
00650                     "/"), pd->unique_filename_buf);
00651 
00652        if (oldeventid_arg)
00653        {
00654               if ((p->oldeventid=strdup(oldeventid_arg)) == NULL)
00655               {
00656                      free(p->newfile);
00657                      free(p->tmpfile);
00658                      free(p->eventid.eventid);
00659                      free(p);
00660                      return (NULL);
00661               }
00662        }
00663        else
00664               p->oldeventid=0;
00665        p->booked=0;
00666 
00667        p->event_participants=NULL;
00668        p->n_event_participants=0;
00669 
00670        for (ut=0; ut<se->n_event_participants; ut++)
00671        {
00672               const char *q;
00673 
00674               for (q=se->event_participants[ut].address; *q; q++)
00675                      if (isspace((int)(unsigned char)*q))
00676                      {
00677                             errno=EINVAL;
00678                             destroyeventid(pd, p);
00679                             return (NULL);
00680                      }
00681 
00682               for (q=se->event_participants[ut].eventid; q && *q; q++)
00683                      if (isspace((int)(unsigned char)*q))
00684                      {
00685                             errno=EINVAL;
00686                             destroyeventid(pd, p);
00687                             return (NULL);
00688                      }
00689        }
00690 
00691        if (se->n_event_participants > 0)
00692        {
00693               if ((p->event_participants=
00694                    malloc( sizeof (*p->event_participants)
00695                           * se->n_event_participants)) == 0)
00696               {
00697                      destroyeventid(pd, p);
00698                      return (NULL);
00699               }
00700 
00701               for ( ; p->n_event_participants < se->n_event_participants;
00702                     ++p->n_event_participants)
00703               {
00704                      p->event_participants[p->n_event_participants]
00705                             .eventid=NULL;
00706 
00707                      if (se->event_participants[p->n_event_participants]
00708                          .eventid
00709                          && (p->event_participants[p->n_event_participants]
00710                             .eventid=strdup(se->event_participants
00711                                           [p->n_event_participants]
00712                                           .eventid))
00713                           == NULL)
00714                      {
00715                             destroyeventid(pd, p);
00716                             return (NULL);
00717                      }
00718 
00719                      if ( (p->event_participants[p->n_event_participants]
00720                            .address=strdup(se->event_participants
00721                                          [p->n_event_participants]
00722                                          .address))
00723                           == NULL)
00724                      {
00725                             if (p->event_participants
00726                                 [p->n_event_participants].eventid)
00727                                    free(p->event_participants
00728                                         [p->n_event_participants]
00729                                         .eventid);
00730                             destroyeventid(pd, p);
00731                             return (NULL);
00732                      }
00733               }
00734        }
00735 
00736        if (saveevent(pd, p, se))
00737        {
00738               destroyeventid(pd, p);
00739               p=NULL;
00740        }
00741 
00742        return (p);
00743 }
00744 
00745 static void destroyeventid(struct PCPdir *pd, struct PCPdir_new_eventid *p)
00746 {
00747        unsigned i;
00748 
00749        for (i=0; i<p->n_event_participants; i++)
00750        {
00751               if (p->event_participants[i].eventid)
00752                      free(p->event_participants[i].eventid);
00753               if (p->event_participants[i].address)
00754                      free(p->event_participants[i].address);
00755        }
00756        if (p->event_participants)
00757               free(p->event_participants);
00758 
00759        if (p->booked && p->eventid.eventid)
00760        {
00761               struct PCP_delete del;
00762 
00763               memset(&del, 0, sizeof(del));
00764               del.id=p->eventid.eventid;
00765 
00766               deleteevent(pd, &del);
00767        }
00768 
00769        if (p->oldeventid)
00770               free(p->oldeventid);
00771        free(p->newfile);
00772 
00773        if (p->tmpfile)
00774        {
00775               unlink(p->tmpfile);
00776               free(p->tmpfile);
00777               p->tmpfile=NULL;
00778        }
00779 
00780        free(p->eventid.eventid);
00781        free(p);
00782 }             
00783 
00784 static int saveevent(struct PCPdir *pd, struct PCPdir_new_eventid *ne,
00785                    struct PCP_save_event *si)
00786 {
00787        char buf[BUFSIZ];
00788        int n;
00789 
00790        FILE *nf=fopen(ne->tmpfile, "w");
00791 
00792        if (!nf)
00793               return (-1);
00794 
00795        while ((n=pcp_read_saveevent(si, buf, sizeof(buf))) > 0)
00796        {
00797               if (fwrite(buf, n, 1, nf) != 1)
00798               {
00799                      fclose(nf);
00800                      return (-1);
00801               }
00802        }
00803 
00804        if (fflush(nf) || ferror(nf))
00805               n= -1;
00806        if (fclose(nf))
00807               n= -1;
00808 
00809        if (n < 0)
00810               return (-1);
00811 
00812        return (0);
00813 }
00814 
00815 /*
00816 ** Structure of the index file:
00817 **
00818 ** eventid<TAB>filename<TAB>field<TAB>field<TAB>field...
00819 **
00820 ** field is name=value
00821 **
00822 ** Fields:
00823 */
00824 
00825 #define TIME_FIELD   "t"           /* start,end */
00826 #define CANCELLED_FIELD "c"        /* This event has been cancelled */
00827 #define BOOKED_FIELD "b"           /* This is a booked event */
00828 #define PENDEL_FIELD "d"           /* This event is pending to be deleted */
00829 #define PROXY_FIELD  "P"           /* This event was placed by proxy */
00830 #define PARTICIPANT_FIELD "p"             /* event participant */
00831 
00832 /* The line_buffer struct is a line's worth of a buffer */
00833 
00834 struct line_buffer {
00835        int bufsiz;
00836        char *buffer; /* Also the eventid */
00837 
00838        char *next_field;    /* Iterator through the fields */
00839 } ;
00840 
00841 static int lb_init(struct line_buffer *pi)
00842 {
00843        pi->bufsiz=BUFSIZ;
00844        pi->buffer=malloc(pi->bufsiz);
00845        if (!pi->buffer)
00846               return (-1);
00847        return (0);
00848 }
00849 
00850 static void lb_destroy(struct line_buffer *pi)
00851 {
00852        free(pi->buffer);
00853 }
00854 
00855 static int lb_read(FILE *fp, struct line_buffer *pi)
00856 {
00857        int n=0;
00858        int c;
00859 
00860        for (;;)
00861        {
00862               if (n >= pi->bufsiz)
00863               {
00864                      int news=pi->bufsiz + BUFSIZ;
00865                      char *newp=realloc(pi->buffer, news);
00866 
00867                      if (!newp)
00868                             return (-1);
00869 
00870                      pi->buffer=newp;
00871                      pi->bufsiz=news;
00872               }
00873 
00874               c=getc(fp);
00875               if (c == EOF)
00876                      return (-1);
00877 
00878               if (c == '\n')
00879               {
00880                      pi->buffer[n]=0;
00881                      break;
00882               }
00883               pi->buffer[n++]=c;
00884        }
00885 
00886        return (0);
00887 }
00888 
00889 static int lb_is_eventid(struct line_buffer *pi, const char *ei)
00890 {
00891        int l=strlen(ei);
00892        const char *p=pi->buffer;
00893 
00894        return (p && strncmp(p, ei, l) == 0 && p[l] == '\t');
00895 }
00896 
00897 static char *lb_event_id(struct line_buffer *pi)
00898 {
00899        char *p=strchr(pi->buffer, '\t');
00900        char *q;
00901 
00902        if (!p)
00903               return (strdup("")); /* Corrupted index file */
00904 
00905        q=malloc(p-pi->buffer+1);
00906 
00907        if (!q)
00908               return (NULL);
00909 
00910        memcpy(q, pi->buffer, p-pi->buffer);
00911        q[p-pi->buffer]=0;
00912        return (q);
00913 }
00914 
00915 static char *lb_filename(struct PCPdir *pd, struct line_buffer *pi)
00916 {
00917        char *p=strchr(pi->buffer, '\t');
00918        char *q, *r;
00919 
00920        if (!p)
00921               return (strdup("")); /* Corrupted index file */
00922 
00923        ++p;
00924        r=strchr(p, '\t');
00925        if (!r)
00926               r=p+strlen(p);
00927 
00928        q=malloc(strlen(pd->dirname)+2+ (r-p));
00929 
00930        if (!q)
00931               return (NULL);
00932 
00933        strncat(strcat(strcpy(q, pd->dirname), "/"), p, r-p);
00934        return (q);
00935 }
00936 
00937 static char *lb_filename_nodir(struct line_buffer *pi)
00938 {
00939        char *p=strchr(pi->buffer, '\t');
00940        char *q, *r;
00941 
00942        if (!p)
00943               return (strdup("")); /* Corrupted index file */
00944 
00945        ++p;
00946        r=strchr(p, '\t');
00947        if (!r)
00948               r=p+strlen(p);
00949 
00950        q=malloc(1+(r-p));
00951 
00952        if (!q)
00953               return (NULL);
00954 
00955        *q=0;
00956        strncat(q, p, r-p);
00957        return (q);
00958 }
00959 
00960 static const char *lb_first_field(struct line_buffer *pi)
00961 {
00962        char *p;
00963 
00964        p=pi->buffer ? strchr(pi->buffer, '\t'):NULL;
00965 
00966        if (p)
00967        {
00968               ++p;   /* p now is the filename */
00969               p=strchr(p, '\t');
00970               if (p)
00971                      ++p;
00972        }
00973 
00974        return (pi->next_field=p);
00975 }
00976 
00977 static const char *lb_next_field(struct line_buffer *pi)
00978 {
00979        char *p=pi->next_field ? strchr(pi->next_field, '\t'):NULL;
00980 
00981        if (p)
00982               ++p;
00983        return (pi->next_field=p);
00984 }
00985 
00986 static int lb_is_field(const char *p, const char *n)
00987 {
00988        if (p)
00989        {
00990               int l=strlen(n);
00991 
00992               if (memcmp(p, n, l) == 0 &&
00993                   (p[l] == '=' || p[l] == '\t' || p[l] == 0))
00994                      return (1);
00995        }
00996        return (0);
00997 }
00998 
00999 static int lb_is_cancelled(struct line_buffer *pi)
01000 {
01001        const char *p;
01002 
01003        for (p=lb_first_field(pi); p; p=lb_next_field(pi))
01004               if (lb_is_field(p, CANCELLED_FIELD))
01005                      return (1);
01006        return (0);
01007 }
01008 
01009 static int lb_is_proxy(struct line_buffer *pi)
01010 {
01011        const char *p;
01012 
01013        for (p=lb_first_field(pi); p; p=lb_next_field(pi))
01014               if (lb_is_field(p, PROXY_FIELD))
01015                      return (1);
01016        return (0);
01017 }
01018 
01019 static const char *lb_field_value(const char *p)
01020 {
01021        while (p && *p && *p != '\t')
01022               if ( *p++ == '=')
01023                      return (p);
01024        return (NULL);
01025 }
01026 
01027 static int lb_is_booked(struct line_buffer *pi, time_t *t)
01028 {
01029        const char *p;
01030 
01031        for (p=lb_first_field(pi); p; p=lb_next_field(pi))
01032               if (lb_is_field(p, BOOKED_FIELD))
01033               {
01034                      const char *q=lb_field_value(p);
01035                      unsigned long ul;
01036 
01037                      if (q && sscanf(q, "%lu", &ul) == 1)
01038                      {
01039                             if (t)
01040                                    *t= (time_t)ul;
01041                             return (1);
01042                      }
01043               }
01044        return (0);
01045 }
01046 
01047 static char *lb_field_value_buf(const char *p)
01048 {
01049        const char *q;
01050        char *r;
01051 
01052        while (p && *p && *p != '\t')
01053               if ( *p++ == '=')
01054                      break;
01055 
01056        if (!p)
01057               return (strdup(""));
01058 
01059        q=strchr(p, '\t');
01060        if (!q)
01061               return (strdup(p));
01062 
01063        r=malloc(q-p+1);
01064 
01065        if (!r)
01066               return (NULL);
01067 
01068        memcpy(r, p, q-p);
01069        r[q-p]=0;
01070        return (r);
01071 }
01072 
01073 static const char *lb_is_pendel(struct line_buffer *pi)
01074 {
01075        const char *p;
01076 
01077        for (p=lb_first_field(pi); p; p=lb_next_field(pi))
01078               if (lb_is_field(p, PENDEL_FIELD))
01079                      return (p);
01080        return (0);
01081 }
01082 
01083 static int lb_remove_field(struct line_buffer *pi,
01084                         FILE *nfp,
01085                         const char *fieldname,
01086                         const char *newfield,
01087                         const char *newvalue)
01088 {
01089        char *q;
01090        const char *cp;
01091 
01092        q=lb_event_id(pi);
01093        if (!q)
01094               return (-1);
01095 
01096        fprintf(nfp, "%s\t", q);
01097        free(q);
01098        q=lb_filename_nodir(pi);
01099        if (!q)
01100               return (-1);
01101        fprintf(nfp, "%s", q);
01102        free(q);
01103 
01104        for (cp=lb_first_field(pi); cp; cp=lb_next_field(pi))
01105        {
01106               const char *cc;
01107 
01108               if (lb_is_field(cp, fieldname))
01109                   continue;
01110 
01111               cc=strchr(cp, '\t');
01112               if (!cc) cc=cp+strlen(cp);
01113 
01114               putc('\t', nfp);
01115               if (fwrite(cp, cc-cp, 1, nfp) != 1)
01116                      break;
01117        }
01118        if (newfield)
01119        {
01120               fprintf(nfp, "\t%s", newfield);
01121               if (newvalue)
01122                      fprintf(nfp, "=%s", newvalue);
01123        }
01124        putc('\n', nfp);
01125        return (0);
01126 }
01127 
01128 static int docommitevent(struct PCPdir *, struct PCPdir_new_eventid *,
01129                       int,
01130                       struct PCP_commit *);
01131 
01132 static int commitevent(struct PCPdir *pd, struct PCPdir_new_eventid *ae,
01133                      struct PCP_commit *ci)
01134 {
01135        return (docommitevent(pd, ae, 0, ci));
01136 }
01137 
01138 static int bookevent(struct PCPdir *pd, struct PCPdir_new_eventid *ae,
01139                    struct PCP_commit *ci)
01140 {
01141        return (docommitevent(pd, ae, 1, ci));
01142 }
01143 
01144 static int pcp_is_conflict(struct line_buffer *,
01145                         const struct PCP_event_time **, unsigned,
01146                         struct PCP_commit *,
01147                         struct PCP_uncancel *);
01148 
01149 static int eventexists(struct PCPdir *pd, const char *e, int *flag)
01150 {
01151        FILE *fp=fopen(pd->indexname, "r");
01152        struct line_buffer pi;
01153 
01154        if (!fp)
01155               return (0);
01156 
01157        if (fseek(fp, 0L, SEEK_SET) < 0)
01158               return (-1);
01159 
01160 
01161        if (lb_init(&pi))
01162        {
01163               fclose(fp);
01164               return (-1);
01165        }
01166 
01167        *flag=0;
01168 
01169        while (lb_read(fp, &pi) == 0)
01170        {
01171               if (lb_is_eventid(&pi, e))
01172               {
01173                      *flag=1;
01174                      break;
01175               }
01176        }
01177        lb_destroy(&pi);
01178        if (ferror(fp))
01179        {
01180               fclose(fp);
01181               return (-1);
01182        }
01183 
01184        if (fclose(fp))
01185               return (-1);
01186        return (0);
01187 }
01188 
01189 
01190 static int docommitevent(struct PCPdir *pd, struct PCPdir_new_eventid *ae,
01191                       int bookmode,
01192                       struct PCP_commit *ci)
01193 {
01194        const struct PCP_event_time **times;
01195        char *dotlock;
01196        FILE *fp;
01197        FILE *nfp;
01198        int rc;
01199        unsigned ut;
01200 
01201        char *deleted_event=0;      /* Filename of deleted event */
01202        int is_cancelled=0;
01203        int is_booked=0;
01204        int was_booked=0;
01205 
01206        ci->errcode=0;
01207 
01208 
01209        if (ae->tmpfile == NULL)    /* Already commited */
01210        {
01211               ci->errcode=PCP_ERR_EVENTNOTFOUND;
01212               return (-1);
01213        }
01214 
01215        if (ci->n_event_times <= 0)
01216        {
01217               ci->errcode=PCP_ERR_CONFLICT;
01218               return (-1);
01219        }
01220 
01221        /* Sort event times in chronological order */
01222 
01223        times=pcp_add_sort_times(ci->event_times,
01224                                  ci->n_event_times);
01225        if (!times)
01226               return (-1);
01227 
01228        /* Use a dotlock to protect: reading old index + checking for
01229        ** conflicts, writing a new index, updating everything */
01230 
01231        dotlock=acquire_dotlock(pd);
01232 
01233        if (!dotlock)
01234        {
01235               ci->errcode=PCP_ERR_LOCK;
01236               free(times);
01237               return (-1);
01238        }
01239 
01240        rc= -1;
01241        fp=fopen(pd->indexname, "a+");
01242        if (fp && fseek(fp, 0L, SEEK_SET) >= 0)
01243        {
01244               nfp=fopen(pd->newindexname, "w");
01245               if (nfp)
01246               {
01247                      struct line_buffer pi;
01248 
01249                      if (lb_init(&pi) == 0)
01250                      {
01251                             rc= 0;
01252                             while (lb_read(fp, &pi) == 0)
01253                             {
01254                                    if (ae->booked &&
01255                                        lb_is_eventid(&pi,
01256                                                    ae->eventid.eventid))
01257                                    {
01258                                           was_booked=1;
01259                                           if (bookmode)
01260                                                  continue;
01261                                           /* Drop old booking */
01262 
01263                                           if (lb_remove_field(&pi, nfp,
01264                                                             BOOKED_FIELD,
01265                                                             NULL,
01266                                                             NULL))
01267                                                  rc= -1;
01268                                           is_booked=1;
01269                                           continue;
01270                                    }
01271                                    if (ae->oldeventid
01272                                        && lb_is_eventid(&pi,
01273                                                       ae->oldeventid))
01274                                    {
01275                                           if (lb_is_cancelled(&pi))
01276                                                  is_cancelled=1;
01277                                           if (deleted_event) /* ??? */
01278                                                  free(deleted_event);
01279                                           deleted_event=lb_filename
01280                                                  (pd, &pi);
01281                                           if (!deleted_event)
01282                                                  rc= -1;
01283                                           if (bookmode)
01284                                           {
01285                                                  const char *pendel=
01286                                                         lb_is_pendel(&pi);
01287                                                  if (pendel)
01288                                                  {
01289                                                         /* Event is locked by another book/update? */
01290 
01291                                                         char *delid=lb_field_value_buf(pendel);
01292 
01293                                                         if (!delid)
01294                                                                rc= -1;
01295                                                         else if (strcmp(delid, ae->eventid.eventid))
01296                                                         {
01297                                                                int flag=0;
01298                                                                /*
01299                                                                ** Possibly, but make sure the locking
01300                                                                ** event actually exists.
01301                                                                */
01302                                                                if (eventexists(pd, delid, &flag))
01303                                                                       rc= -1;
01304                                                                else if (flag)
01305                                                                {
01306                                                                       rc= -1;
01307                                                                       ci->errcode=PCP_ERR_EVENTLOCKED;
01308                                                                }
01309                                                         }
01310                                                         fprintf(nfp, "%s\n",
01311                                                                pi.buffer);
01312                                                         free(delid);
01313                                                  }
01314                                                  else
01315                                                  {
01316                                                         lb_remove_field(&pi,
01317                                                                       nfp,
01318                                                                       PENDEL_FIELD,
01319                                                                       PENDEL_FIELD,
01320                                                                       ae->eventid.eventid);
01321                                                  }
01322                                                  /* Dont del just yet */
01323                                           }
01324                                           continue;
01325                                    }
01326                                    fprintf(nfp, "%s\n", pi.buffer);
01327 
01328                                    /* Check for conflicts UNLESS */
01329 
01330                                    if ((ci->flags & PCP_OK_CONFLICT)
01331                                        == 0 &&
01332                                        /* Conflics are OK */
01333 
01334                                        (!ae->booked || bookmode) &&
01335                                        /*
01336                                        ** We're not committing a booked
01337                                        ** event (check for conflicts at
01338                                        ** time of booking).
01339                                        */
01340 
01341                                        !lb_is_cancelled(&pi) &&
01342                                        /* This event is cancelled */
01343 
01344                                        pcp_is_conflict(&pi,
01345                                                      times,
01346                                                      ci->n_event_times,
01347                                                      ci,
01348                                                      NULL)
01349                                        )
01350                                    {
01351                                           rc= -1;
01352                                    }
01353                             }
01354 
01355                             if (rc == 0 && !ferror(fp) && !ferror(nfp)
01356                                 && !is_booked)
01357                             {
01358                                    /* Add a new index record */
01359 
01360                                    const char *p=
01361                                           strrchr(ae->newfile, '/');
01362 
01363                                    if (p)
01364                                           ++p;
01365                                    else
01366                                           p=ae->newfile;
01367 
01368                                    fprintf(nfp, "%s\t%s",
01369                                           ae->eventid.eventid, p);
01370 
01371                                    /*
01372                                    ** If older event is cancelled, so
01373                                    ** is this event.
01374                                    */
01375 
01376                                    if (is_cancelled)
01377                                    {
01378                                           fprintf(nfp, "\t"
01379                                                  CANCELLED_FIELD);
01380                                    }
01381 
01382                                    if (ci->flags & PCP_BYPROXY)
01383                                           fprintf(nfp, "\t"
01384                                                  PROXY_FIELD);
01385 
01386                                    if (bookmode)
01387                                    {
01388                                           time_t book_time;
01389 
01390                                           time(&book_time);
01391                                           fprintf(nfp, "\t" BOOKED_FIELD
01392                                                  "=%lu",
01393                                                  (unsigned long)
01394                                                  book_time);
01395                                    }
01396 
01397                                    for (ut=0; ut<ci->n_event_times; ut++)
01398                                           fprintf(nfp, "\t"
01399                                                  TIME_FIELD
01400                                                  "=%lu,%lu",
01401                                                  (unsigned long)
01402                                                  times[ut]->start,
01403                                                  (unsigned long)
01404                                                  times[ut]->end);
01405 
01406 
01407                                    for (ut=0; ut<ae->n_event_participants;
01408                                         ut++)
01409                                    {
01410                                           const char *p=
01411                                                  ae->event_participants
01412                                                  [ut].address;
01413                                           const char *q=
01414                                                  ae->event_participants
01415                                                  [ut].eventid;
01416 
01417                                           if (!p)
01418                                                  continue;
01419 
01420                                           fprintf(nfp,
01421                                                  "\t"
01422                                                  PARTICIPANT_FIELD
01423                                                  "=%s%s%s", p,
01424                                                  q ? " ":"", q ? q:"");
01425 
01426                                           /* Sanity check: */
01427 
01428                                           while (*p)
01429                                           {
01430                                                  if ((int)
01431                                                      (unsigned char)*p
01432                                                      <= ' ')
01433                                                  {
01434                                                         rc= -1;
01435                                                         errno=EINVAL;
01436                                                  }
01437                                                  ++p;
01438                                           }
01439                                    }
01440 
01441                                    fprintf(nfp, "\n");
01442                             }
01443                             if (ae->booked && !was_booked)
01444                             {
01445                                    rc= -1;
01446                                    ci->errcode=PCP_ERR_EVENTNOTFOUND;
01447                                    ae->booked=0;
01448                                    /* Booked event disappeared */
01449                             }
01450 
01451                             if (rc == 0 && 
01452                                 (fflush(nfp) < 0 || ferror(nfp)))
01453                                    rc= -1;
01454 
01455                             lb_destroy(&pi);
01456                      }
01457                      if (fclose(nfp))
01458                             rc= -1;
01459               }
01460               if (fclose(fp))
01461                      rc= -1;
01462 
01463               if (rc == 0 && !bookmode)
01464               {
01465                      if (rename(ae->tmpfile, ae->newfile))
01466                             rc= -1;
01467               }
01468 
01469               if (rc == 0)
01470               {
01471                      if (rename(pd->newindexname, pd->indexname))
01472                             rc=-1;
01473               }
01474 
01475               if (rc == 0 && !bookmode)
01476               {
01477                      if (deleted_event)
01478                             unlink(deleted_event);
01479                      free(ae->tmpfile);
01480                      ae->tmpfile=0;
01481                      ae->booked=0;
01482               }
01483               if (rc == 0 && bookmode)
01484                      ae->booked=1;
01485        }
01486        else if (fp)
01487               fclose(fp);
01488 
01489        if (deleted_event)
01490               free(deleted_event);
01491        markchanged(pd);
01492        release_dotlock(pd, dotlock);
01493        free(times);
01494        return (rc);
01495 }
01496 
01497 /*
01498 ** Spring cleaning.
01499 */
01500 
01501 struct cleanup_filename_list {
01502        struct cleanup_filename_list *next;
01503        char *filename;
01504 } ;
01505 
01506 static int event_expired(struct line_buffer *, time_t);
01507 
01508 static int cleanup(struct PCPdir *pd)
01509 {
01510        char *dotlock;
01511        FILE *fp;
01512        FILE *nfp;
01513        int rc;
01514        struct stat stat_buf;
01515        struct cleanup_filename_list *list=NULL;
01516        time_t now;
01517 
01518        dotlock=acquire_dotlock(pd);
01519 
01520        if (!dotlock)
01521               return (-1);
01522 
01523        /*
01524        ** Read the current index.
01525        ** Remove entries for events whose files don't exist.
01526        **
01527        ** Save list of all files that are indexed.
01528        */
01529 
01530        time(&now);
01531        rc= -1;
01532        fp=fopen(pd->indexname, "a+");
01533        if (fp && fseek(fp, 0L, SEEK_SET) >= 0)
01534        {
01535               nfp=fopen(pd->newindexname, "w");
01536               if (nfp)
01537               {
01538                      struct line_buffer pi;
01539 
01540                      if (lb_init(&pi) == 0)
01541                      {
01542                             rc= 0;
01543                             while (lb_read(fp, &pi) == 0)
01544                             {
01545                                    char *filename=lb_filename(pd, &pi);
01546                                    struct cleanup_filename_list *l;
01547                                    time_t booked_time;
01548 
01549                                    if (!filename)
01550                                    {
01551                                           rc= -1;
01552                                           break;
01553                                    }
01554 
01555                                    if (!*filename)
01556                                    {
01557                                           free(filename);
01558                                           continue;
01559                                    }
01560 
01561                                    if (lb_is_booked(&pi, &booked_time))
01562                                    {
01563                                           /* Expire books after 1 hr */
01564 
01565                                           if (booked_time < now - 60*60)
01566                                           {
01567                                                  free(filename);
01568                                                  continue;
01569                                           }
01570                                    }
01571                                    else if (stat(filename, &stat_buf))
01572                                    {
01573                                           if (errno != ENOENT)
01574                                           {
01575                                                  free(filename);
01576                                                  rc= -1;
01577                                                  break;
01578                                           }
01579                                           free(filename);
01580                                           continue;
01581                                    }
01582                                    else if ( event_expired(&pi,
01583                                                         now -
01584                                                         CALENDARPURGE *
01585                                                         60 * 60 * 24))
01586                                    {
01587                                           free(filename);
01588                                           continue;
01589                                           /*
01590                                           ** cleanup loop below will
01591                                           ** take care of the event file
01592                                           */
01593                                    }
01594 
01595                                    l=malloc(sizeof(*list));
01596                                    l->next=list;
01597                                    l->filename=filename;
01598                                    list=l;
01599 
01600                                    fprintf(nfp, "%s\n", pi.buffer);
01601                             }
01602                             lb_destroy(&pi);
01603                      }
01604 
01605                      if (rc == 0 && (ferror(fp) || ferror(nfp)
01606                                    || fflush(nfp)))
01607                             rc= -1;
01608 
01609                      if (fclose(nfp))
01610                             rc= -1;
01611               }
01612               if (fclose(fp))
01613                      rc= -1;
01614 
01615               if (rename(pd->newindexname, pd->indexname))
01616                      rc= -1;
01617        }
01618        else if (fp)
01619               fclose(fp);
01620 
01621        if (rc == 0)  /* Time to scan the directory */
01622        {
01623               DIR *dirp=opendir(pd->dirname);
01624               struct dirent *de;
01625               const char *p;
01626               time_t now;
01627 
01628               time(&now);
01629 
01630               while (dirp && (de=readdir(dirp)) != NULL)
01631               {
01632                      char *filename=malloc(strlen(pd->dirname)+2+
01633                                          strlen(de->d_name));
01634                      if (!filename)
01635                      {
01636                             rc= -1;
01637                             break;
01638                      }
01639                      strcat(strcat(strcpy(filename, pd->dirname),
01640                                   "/"), de->d_name);
01641                      if (isdigit((int)(unsigned char)de->d_name[0]))
01642                      {
01643                             struct cleanup_filename_list *l;
01644                             for (l=list; l; l=l->next)
01645                                    if (strcmp(l->filename, filename) == 0)
01646                                           break;
01647 
01648                             if (!l)
01649                             {
01650                                    unlink(filename);
01651                             }
01652                      }
01653                      else
01654                      {
01655                             p=strrchr(filename, '.');
01656                             if (p && (strcmp(p, ".tmp") == 0 ||
01657                                      strcmp(p, ".lock") == 0) &&
01658                                 stat(filename, &stat_buf) == 0 &&
01659                                 stat_buf.st_mtime < now - 36 * 60 * 60)
01660                                    unlink(filename);
01661                      }
01662                      free(filename);
01663               }
01664               if (dirp)
01665                      closedir(dirp);
01666        }
01667        release_dotlock(pd, dotlock);
01668 
01669        while (list)
01670        {
01671               struct cleanup_filename_list *l=list;
01672 
01673               list=l->next;
01674               free(l->filename);
01675               free(l);
01676        }
01677        return (rc);
01678 }
01679 
01680 static int parse_time_field(const char *, time_t *, time_t *);
01681 
01682 static int event_expired(struct line_buffer *pi, time_t when)
01683 {
01684        const char *p;
01685 
01686        for (p=lb_first_field(pi); p; p=lb_next_field(pi))
01687        {
01688               time_t start_time;
01689               time_t end_time;
01690 
01691               if (!lb_is_field(p, TIME_FIELD))
01692                      continue;
01693 
01694               if (parse_time_field(lb_field_value(p),
01695                                  &start_time, &end_time))
01696                      continue;
01697               if (end_time > when)
01698                      return (0);
01699        }
01700        return (1);
01701 }
01702 
01703 /* Check if existing event conflicts with new event */
01704 
01705 static int parse_time_field(const char *p, time_t *start_time,
01706                          time_t *end_time)
01707 {
01708        if (!p)
01709               return (-1);
01710 
01711        *start_time=0;
01712        *end_time=0;
01713 
01714        while (*p && isdigit((int)(unsigned char)*p))
01715        {
01716               *start_time=*start_time * 10 + (*p-'0');
01717               ++p;
01718        }
01719        if (*p != ',')
01720               return (-1);
01721 
01722        ++p;
01723        while (*p && isdigit((int)(unsigned char)*p))
01724        {
01725               *end_time=*end_time * 10 + (*p-'0');
01726               ++p;
01727        }
01728        return (0);
01729 }
01730 
01731 static int pcp_is_conflict(struct line_buffer *lb,
01732                         const struct PCP_event_time **t, unsigned n,
01733                         struct PCP_commit *ae,
01734                         struct PCP_uncancel *un)
01735 {
01736        unsigned i;
01737        const char *p;
01738        int rc=0;
01739 
01740        char *event_id=lb_event_id(lb);
01741 
01742        if (!event_id)
01743               return (-1);
01744 
01745        for (p=lb_first_field(lb); p; p=lb_next_field(lb))
01746        {
01747               time_t start_time;
01748               time_t end_time;
01749 
01750               if (!lb_is_field(p, TIME_FIELD))
01751                      continue;
01752 
01753               if (parse_time_field(lb_field_value(p),
01754                                  &start_time, &end_time))
01755                      continue;
01756 
01757               for (i=0; i<n; i++)
01758               {
01759                      time_t com_start;
01760                      time_t com_end;
01761 
01762                      if (start_time >= t[i]->end)
01763                             continue;
01764                      if (t[i]->start >= end_time)
01765                             continue;
01766 
01767                      com_start=start_time;
01768                      com_end=end_time;
01769                      if (t[i]->start > com_start)
01770                             com_start=t[i]->start;
01771                      if (t[i]->end < com_end)
01772                             com_end=t[i]->end;
01773 
01774                      rc= -1;
01775                      if (ae)
01776                      {
01777                             ae->errcode=PCP_ERR_CONFLICT;
01778                             if (ae->add_conflict_callback)
01779                             {
01780                                    rc=(*ae->add_conflict_callback)
01781                                           (event_id, com_start,
01782                                            com_end,
01783                                            "@",
01784                                            ae->add_conflict_callback_ptr
01785                                            );
01786                                    if (rc)
01787                                    {
01788                                           ae->errcode=0;
01789                                           break;
01790                                    }
01791                                    rc= -1;
01792                             }
01793                      }
01794 
01795                      if (un)
01796                      {
01797                             un->errcode=PCP_ERR_CONFLICT;
01798                             if (un->uncancel_conflict_callback)
01799                             {
01800                                    rc=(*un->uncancel_conflict_callback)
01801                                           (event_id, com_start,
01802                                            com_end, "@",
01803                                            un->uncancel_conflict_callback_ptr
01804                                            );
01805                                    if (rc)
01806                                    {
01807                                           un->errcode=0;
01808                                           break;
01809                                    }
01810                                    rc= -1;
01811                             }
01812                      }
01813               }
01814 
01815        }
01816        free(event_id);
01817        return (rc);
01818 }
01819 
01820 static int listallevents(struct PCPdir *pd, struct PCP_list_all *li)
01821 {
01822        FILE *fp;
01823        struct line_buffer lb;
01824        int rc=0;
01825        const char *p;
01826 
01827        if ((fp=fopen(pd->indexname, "r")) == NULL)
01828               return (0);   /* Empty calendar */
01829 
01830        if (lb_init(&lb) == 0)
01831        {
01832               while (lb_read(fp, &lb) == 0)
01833               {
01834                      char *event_id=lb_event_id(&lb);
01835 
01836                      if (!event_id)
01837                      {
01838                             rc= -1;
01839                             break;
01840                      }
01841 
01842                      li->event_id=event_id;
01843 
01844                      for (p=lb_first_field(&lb); p; p=lb_next_field(&lb))
01845                      {
01846                             if (lb_is_field(p, TIME_FIELD))
01847                             {
01848                                    time_t start_time;
01849                                    time_t end_time;
01850 
01851                                    if (parse_time_field(lb_field_value(p),
01852                                                       &start_time,
01853                                                       &end_time))
01854                                           continue;
01855 
01856                                    if (li->list_from
01857                                        && li->list_from == li->list_to)
01858                                    {
01859                                           if (li->list_from < start_time
01860                                               || li->list_to >= end_time)
01861                                                  continue;
01862                                    }
01863                                    else
01864                                    {
01865                                           if (li->list_from &&
01866                                               end_time <= li->list_from)
01867                                                  continue;
01868 
01869                                           if (li->list_to &&
01870                                               start_time >= li->list_to)
01871                                                  continue;
01872                                    }
01873                                    li->event_from=start_time;
01874                                    li->event_to=end_time;
01875 
01876                                    if ((rc= (*li->callback_func)
01877                                         (li, li->callback_arg)) != 0)
01878                                           break;
01879                             }
01880                             if (rc) break;
01881 
01882                      }
01883 
01884                      free(event_id);
01885                      if (rc)
01886                             break;
01887               }
01888               lb_destroy(&lb);
01889        }
01890        fclose(fp);
01891        return (rc);
01892 }
01893 
01894 static int read_event_times(struct line_buffer *pi,
01895                          struct PCP_event_time **time_ret,
01896                          unsigned *time_n_ret)
01897 {
01898        const char *c;
01899        unsigned n;
01900        time_t start_time, end_time;
01901 
01902        *time_n_ret=0;
01903        *time_ret=NULL;
01904 
01905        for (c=lb_first_field(pi); c; c=lb_next_field(pi))
01906               if (lb_is_field(c, TIME_FIELD) &&
01907                   parse_time_field(lb_field_value(c),
01908                                  &start_time, &end_time) == 0)
01909                      ++ *time_n_ret;
01910 
01911        if (!*time_n_ret)
01912               return (0);
01913 
01914        if ( (*time_ret=(struct PCP_event_time *)malloc
01915              (sizeof(struct PCP_event_time)* *time_n_ret)) == NULL)
01916               return (-1);
01917 
01918        n=0;
01919        for (c=lb_first_field(pi); c; c=lb_next_field(pi))
01920               if (lb_is_field(c, TIME_FIELD) &&
01921                   parse_time_field(lb_field_value(c),
01922                                  &start_time, &end_time) == 0)
01923               {
01924                      (*time_ret)[n].start=start_time;
01925                      (*time_ret)[n].end=end_time;
01926                      ++n;
01927               }
01928        return (0);
01929 }
01930 
01931 static int cancelevent(struct PCPdir *pd, const char *event, int *errcode)
01932 {
01933        char *dotlock;
01934        FILE *fp;
01935        FILE *nfp;
01936        int rc;
01937        int found=0;
01938 
01939        if (errcode) *errcode=0;
01940        dotlock=acquire_dotlock(pd);
01941 
01942        if (!dotlock)
01943               return (-1);
01944 
01945        rc= -1;
01946        fp=fopen(pd->indexname, "a+");
01947        if (fp && fseek(fp, 0L, SEEK_SET) >= 0)
01948        {
01949               nfp=fopen(pd->newindexname, "w");
01950               if (nfp)
01951               {
01952                      struct line_buffer pi;
01953 
01954                      if (lb_init(&pi) == 0)
01955                      {
01956                             rc= 0;
01957                             while (lb_read(fp, &pi) == 0)
01958                             {
01959                                    if (!lb_is_eventid(&pi, event))
01960                                    {
01961                                           fprintf(nfp, "%s\n",
01962                                                  pi.buffer);
01963                                           continue;
01964                                    }
01965                                    found=1;
01966                                    if (lb_is_cancelled(&pi))
01967                                    {
01968                                           /* Already canned */
01969                                           fprintf(nfp, "%s\n",
01970                                                  pi.buffer);
01971                                           continue;
01972                                    }
01973 
01974                                    fprintf(nfp, "%s\t"
01975                                           CANCELLED_FIELD
01976                                           "\n", pi.buffer);
01977                             }
01978                             lb_destroy(&pi);
01979                             if (!found)
01980                             {
01981                                    if (errcode)
01982                                           *errcode=PCP_ERR_EVENTNOTFOUND;
01983                                    errno=ENOENT;
01984                                    rc= -1;
01985                             }
01986                      }
01987                      if (ferror(nfp) || fflush(nfp))
01988                             rc= -1;
01989                      if (fclose(nfp))
01990                             rc= -1;
01991               }
01992               fclose(fp);
01993 
01994               if (rc == 0 && rename(pd->newindexname, pd->indexname))
01995                      rc= -1;
01996        }
01997        else if (fp)
01998               fclose(fp);
01999        release_dotlock(pd, dotlock);
02000        return (rc);
02001 }
02002 
02003 static int uncancelevent(struct PCPdir *pd, const char *event, int flags,
02004                       struct PCP_uncancel *info)
02005 {
02006        char *dotlock;
02007        FILE *fp;
02008        FILE *nfp;
02009        int rc;
02010        int found=0;
02011        struct PCP_event_time *old_times=NULL;
02012        const struct PCP_event_time **old_times_sorted=NULL;
02013        unsigned n_old_times=0;
02014 
02015        if (info) info->errcode=0;
02016        dotlock=acquire_dotlock(pd);
02017 
02018        if (!dotlock)
02019               return (-1);
02020 
02021        rc= -1;
02022        fp=fopen(pd->indexname, "a+");
02023        if (fp && fseek(fp, 0L, SEEK_SET) >= 0)
02024        {
02025               struct line_buffer pi;
02026 
02027               /* First, find the existing booked times */
02028 
02029               if (lb_init(&pi) == 0)
02030               {
02031                      if (info)
02032                             info->errcode=PCP_ERR_EVENTNOTFOUND;
02033                      while (lb_read(fp, &pi) == 0)
02034                      {
02035                             if (!lb_is_eventid(&pi, event))
02036                                    continue;
02037                             if (read_event_times(&pi, &old_times,
02038                                                &n_old_times) == 0)
02039                             {
02040                                    if (fseek(fp, 0L, SEEK_SET) >= 0)
02041                                           rc=0;
02042                                    break;
02043                             }
02044                      }
02045                      lb_destroy(&pi);
02046 
02047                      if (rc == 0 && info)
02048                             info->errcode=0;
02049               }
02050 
02051               if (rc == 0 && n_old_times &&
02052                   (old_times_sorted=
02053                    pcp_add_sort_times(old_times, n_old_times)) == NULL)
02054                      rc= -1;
02055 
02056               nfp=rc == 0 ? fopen(pd->newindexname, "w"):NULL;
02057               if (nfp)
02058               {
02059                      struct line_buffer pi;
02060 
02061                      if (lb_init(&pi) == 0)
02062                      {
02063                             rc= 0;
02064                             while (lb_read(fp, &pi) == 0)
02065                             {
02066                                    char *id=lb_event_id(&pi);
02067 
02068                                    if (!id)
02069                                    {
02070                                           rc= -1;
02071                                           break;
02072                                    }
02073 
02074                                    if (!lb_is_eventid(&pi, event))
02075                                    {
02076                                           if ((flags & PCP_OK_CONFLICT)
02077                                               == 0 &&
02078                                               !lb_is_cancelled(&pi))
02079                                           {
02080                                                  rc= pcp_is_conflict
02081                                                         (&pi,
02082                                                          old_times_sorted,
02083                                                          n_old_times,
02084                                                          NULL,
02085                                                          info);
02086                                           }
02087                                           fprintf(nfp, "%s\n",
02088                                                  pi.buffer);
02089                                           continue;
02090                                    }
02091                                    found=1;
02092                                    if (!lb_is_cancelled(&pi))
02093                                    {
02094                                           /* Already uncanned */
02095                                           fprintf(nfp, "%s\n",
02096                                                  pi.buffer);
02097                                           continue;
02098                                    }
02099 
02100                                    if (lb_remove_field(&pi, nfp,
02101                                                      CANCELLED_FIELD,
02102                                                      NULL,
02103                                                      NULL))
02104                                           rc= -1;
02105                             }
02106                             lb_destroy(&pi);
02107                             if (!found)
02108                             {
02109                                    if (info)
02110                                           info->errcode=PCP_ERR_EVENTNOTFOUND;
02111                                    errno=ENOENT;
02112                                    rc= -1;
02113                             }
02114                      }
02115                      if (ferror(nfp) || fflush(nfp))
02116                             rc= -1;
02117                      if (fclose(nfp))
02118                             rc= -1;
02119               }
02120               fclose(fp);
02121 
02122               if (rc == 0 && rename(pd->newindexname, pd->indexname))
02123                      rc= -1;
02124        }
02125        else if (fp)
02126               fclose(fp);
02127        if (old_times_sorted)
02128               free (old_times_sorted);
02129        if (old_times)
02130               free (old_times);
02131        release_dotlock(pd, dotlock);
02132        return (rc);
02133 }
02134 
02135 static int deleteevent(struct PCPdir *pd, struct PCP_delete *del)
02136 {
02137        char *dotlock;
02138        FILE *fp;
02139        FILE *nfp;
02140        int rc;
02141        char *deleted_event=0;
02142 
02143        del->errcode=0;
02144        dotlock=acquire_dotlock(pd);
02145 
02146        if (!dotlock)
02147               return (-1);
02148 
02149        rc= -1;
02150        fp=fopen(pd->indexname, "a+");
02151        if (fp && fseek(fp, 0L, SEEK_SET) >= 0)
02152        {
02153               nfp=fopen(pd->newindexname, "w");
02154               if (nfp)
02155               {
02156                      struct line_buffer pi;
02157 
02158                      if (lb_init(&pi) == 0)
02159                      {
02160                             rc= 0;
02161                             while (lb_read(fp, &pi) == 0)
02162                             {
02163                                    if (!lb_is_eventid(&pi, del->id))
02164                                    {
02165                                           fprintf(nfp, "%s\n",
02166                                                  pi.buffer);
02167                                           continue;
02168                                    }
02169 
02170                                    if (deleted_event) /* ??? */
02171                                           free(deleted_event);
02172                                    deleted_event=lb_filename(pd, &pi);
02173                                    if (!deleted_event)
02174                                           rc= -1;
02175                             }
02176                             lb_destroy(&pi);
02177                             if (!deleted_event)
02178                             {
02179                                    del->errcode=PCP_ERR_EVENTNOTFOUND;
02180                                    errno=ENOENT;
02181                                    rc= -1;
02182                             }
02183                      }
02184                      if (ferror(nfp) || fflush(nfp))
02185                             rc= -1;
02186                      if (fclose(nfp))
02187                             rc= -1;
02188               }
02189               fclose(fp);
02190 
02191               if (rc == 0 && rename(pd->newindexname, pd->indexname))
02192                      rc= -1;
02193        }
02194        else if (fp)
02195               fclose(fp);
02196        if (deleted_event && rc == 0)
02197               unlink(deleted_event);
02198        if (deleted_event)
02199               free(deleted_event);
02200        markchanged(pd);
02201        release_dotlock(pd, dotlock);
02202        return (rc);
02203 }
02204 
02205 static int retrheaders(struct PCPdir *, struct PCP_retr *, const char *);
02206 static int retrrfc822(struct PCPdir *, struct PCP_retr *, const char *);
02207 
02208 static int retrevent(struct PCPdir *pd, struct PCP_retr *ri)
02209 {
02210        FILE *fp;
02211        int rc;
02212 
02213        rc= -1;
02214 
02215        fp=fopen(pd->indexname, "r");
02216        if (fp && fseek(fp, 0L, SEEK_SET) >= 0)
02217        {
02218               struct line_buffer pi;
02219 
02220               if (lb_init(&pi) == 0)
02221               {
02222                      rc= 0;
02223                      while (lb_read(fp, &pi) == 0)
02224                      {
02225                             const char *c;
02226                             char *filename;
02227                             unsigned x;
02228                             int status;
02229 
02230                             for (x=0; ri->event_id_list[x]; x++)
02231                                    if (lb_is_eventid(&pi,
02232                                                    ri->event_id_list[x])
02233                                        )
02234                                           break;
02235                             if (!(ri->event_id=ri->event_id_list[x]))
02236                                    continue;
02237 
02238                             filename=lb_filename(pd, &pi);
02239                             if (!filename)
02240                             {
02241                                    rc= -1;
02242                                    break;
02243                             }
02244                             if (!*filename)
02245                             {
02246                                    free(filename);
02247                                    continue;
02248                             }
02249 
02250                             status=0;
02251                             if (lb_is_cancelled(&pi))
02252                                    status |= LIST_CANCELLED;
02253                             if (lb_is_booked(&pi, NULL))
02254                                    status |= LIST_BOOKED;
02255                             if (lb_is_proxy(&pi))
02256                                    status |= LIST_PROXY;
02257 
02258                             if (ri->callback_retr_status)
02259                             {
02260                                    rc= (*ri->callback_retr_status)
02261                                           (ri, status,
02262                                            ri->callback_arg);
02263                                    if (rc)
02264                                           break;
02265                             }
02266 
02267                             for (c=lb_first_field(&pi); c;
02268                                  c=lb_next_field(&pi))
02269                             {
02270                                    if (lb_is_field(c, TIME_FIELD))
02271                                    {
02272                                           time_t from, to;
02273 
02274                                           if (!ri->callback_retr_date)
02275                                                  continue;
02276 
02277                                           if (parse_time_field
02278                                               (lb_field_value(c),
02279                                                &from, &to))
02280                                                  continue;
02281 
02282                                           rc=(*ri->
02283                                               callback_retr_date)
02284                                                  (ri, from, to,
02285                                                   ri->callback_arg);
02286                                           if (rc)
02287                                                  break;
02288                                           continue;
02289                                    }
02290 
02291                                    if (lb_is_field(c, PARTICIPANT_FIELD))
02292                                    {
02293                                           char *pp, *qq;;
02294                                           if (ri->
02295                                               callback_retr_participants
02296                                               == NULL)
02297                                                  continue;
02298 
02299                                           pp=lb_field_value_buf(c);
02300 
02301                                           if (!pp)
02302                                           {
02303                                                  rc= -1;
02304                                                  break;
02305                                           }
02306 
02307                                           if ((qq=strchr(pp, ' ')) != 0)
02308                                                  *qq++=0;
02309 
02310                                           rc=(*ri->
02311                                               callback_retr_participants)
02312                                                  (ri, pp, qq,
02313                                                   ri->callback_arg);
02314                                           free(pp);
02315                                           if (rc)
02316                                                  break;
02317                                           continue;
02318                                    }
02319                             }
02320 
02321                             if (rc)
02322                             {
02323                                    free(filename);
02324                                    break;
02325                             }
02326 
02327                             if (ri->callback_headers_func == NULL &&
02328                                 ri->callback_rfc822_func == NULL)
02329                             {
02330                                    free(filename);
02331                                    continue;
02332                             }
02333 
02334                             if (ri->callback_begin_func)
02335                             {
02336                                    rc= (*ri->callback_begin_func)
02337                                           (ri, ri->callback_arg);
02338                                    if (rc)
02339                                    {
02340                                           free(filename);
02341                                           break;
02342                                    }
02343                             }
02344 
02345                             if (ri->callback_rfc822_func)
02346                                    rc=retrrfc822(pd, ri, filename);
02347                             else if (ri->callback_headers_func)
02348                                    rc=retrheaders(pd, ri, filename);
02349 
02350                             if (rc == 0 && ri->callback_end_func)
02351                                    rc= (*ri->callback_end_func)
02352                                           (ri, ri->callback_arg);
02353 
02354                             free(filename);
02355                             if (rc)
02356                                    break;
02357                      }
02358                      lb_destroy(&pi);
02359               }
02360               fclose(fp);
02361        }
02362        else if (fp)
02363               fclose(fp);
02364        return (rc);
02365 }
02366 
02367 static int retrheaders(struct PCPdir *pd, struct PCP_retr *ri,
02368                      const char *filename)
02369 {
02370        FILE *fp=fopen(filename, "r");
02371        struct rfc822hdr h;
02372        int rc=0;
02373 
02374        if (!fp)
02375               return (errno == ENOENT ? 0:-1);
02376 
02377        rfc822hdr_init(&h, 8192);
02378 
02379        while (rfc822hdr_read(&h, fp, NULL, 0) == 0)
02380        {
02381               if ((rc= (*ri->callback_headers_func)(ri, h.header,
02382                                                 h.value,
02383                                                 ri->callback_arg)) != 0)
02384                      break;
02385        }
02386        if (rc == 0 && ferror(fp))
02387               rc= -1;
02388        rfc822hdr_free(&h);
02389        fclose(fp);
02390        return (0);
02391 }
02392 
02393 static int retrrfc822(struct PCPdir *pd, struct PCP_retr *ri,
02394                     const char *filename)
02395 {
02396        int fd=open(filename, O_RDONLY);
02397        char buf[BUFSIZ];
02398        int n;
02399        int rc=0;
02400 
02401        if (fd < 0)
02402               return (errno == ENOENT ? 0:-1);
02403 
02404        while ((n=read(fd, buf, sizeof(buf))) > 0)
02405               if ((rc= (*ri->callback_rfc822_func)(ri, buf, n,
02406                                                ri->callback_arg)) != 0)
02407                      break;
02408        if (rc == 0 && n < 0)
02409               rc= -1;
02410        close(fd);
02411        return (rc);
02412 }
02413 
02414 static int setacl(struct PCPdir *pd, const char *who, int flags)
02415 {
02416        char *dotlock;
02417        FILE *fp;
02418        FILE *nfp;
02419        int rc;
02420        char *aclname;
02421        char *newaclname;
02422        char buf[1024];
02423 
02424        if (strchr(who, '\r') || strchr(who, '\n') || strlen(who) > 512)
02425        {
02426               errno=EINVAL;
02427               return (-1);
02428        }
02429 
02430        aclname=malloc(strlen(pd->dirname)+sizeof("/acl"));
02431 
02432        if (!aclname)
02433               return (-1);
02434 
02435        newaclname=malloc(strlen(pd->dirname)+sizeof("/acl.new"));
02436 
02437        if (!newaclname)
02438        {
02439               free(aclname);
02440               return (-1);
02441        }
02442 
02443        dotlock=acquire_dotlock(pd);
02444 
02445        if (!dotlock)
02446        {
02447               free(newaclname);
02448               free(aclname);
02449               return (-1);
02450        }
02451 
02452        strcat(strcpy(aclname, pd->dirname), "/acl");
02453        strcat(strcpy(newaclname, pd->dirname), "/acl.new");
02454 
02455        rc= -1;
02456        nfp=fopen(newaclname, "w");
02457        if (nfp)
02458        {
02459               int l=strlen(who);
02460 
02461               fp=fopen(aclname, "r");
02462               if (fp)
02463               {
02464                      while (fgets(buf, sizeof(buf), fp))
02465                      {
02466                             if (strncmp(buf, who, l) == 0 &&
02467                                 isspace((int)(unsigned char)buf[l]))
02468                                    continue;
02469                             fprintf(nfp, "%s", buf);
02470                      }
02471                      fclose(fp);
02472               }
02473               if (flags)
02474                      fprintf(nfp, "%s\t%d\n", who, flags);
02475               rc=0;
02476               if (fflush(nfp) || ferror(nfp))
02477                      rc= -1;
02478               if (fclose(nfp) || rename(newaclname, aclname))
02479                      rc= -1;
02480        }
02481        release_dotlock(pd, dotlock);
02482        free(newaclname);
02483        free(aclname);
02484        return (rc);
02485 }
02486 
02487 static int listacl(struct PCPdir *pd, int (*func)(const char *, int, void *),
02488                  void *arg)
02489 {
02490        char *aclname;
02491        char buf[1024];
02492        FILE *fp;
02493 
02494        aclname=malloc(strlen(pd->dirname)+sizeof("/acl"));
02495 
02496        if (!aclname)
02497               return (-1);
02498 
02499        strcat(strcpy(aclname, pd->dirname), "/acl");
02500 
02501        fp=fopen(aclname, "r");
02502        free(aclname);
02503 
02504        if (fp)
02505        {
02506               while (fgets(buf, sizeof(buf), fp))
02507               {
02508                      char *p;
02509                      int rc;
02510 
02511                      for (p=buf; *p; p++)
02512                             if (isspace((int)(unsigned char)*p))
02513                             {
02514                                    *p++=0;
02515                                    break;
02516                             }
02517 
02518                      rc= (*func)(buf, atoi(p), arg);
02519                      if (rc)
02520                      {
02521                             fclose(fp);
02522                             return (rc);
02523                      }
02524               }
02525               fclose(fp);
02526        }
02527        return (0);
02528 }