diff options
Diffstat (limited to 'pcp/pcpdir.c')
| -rw-r--r-- | pcp/pcpdir.c | 2528 |
1 files changed, 2528 insertions, 0 deletions
diff --git a/pcp/pcpdir.c b/pcp/pcpdir.c new file mode 100644 index 0000000..3bfe995 --- /dev/null +++ b/pcp/pcpdir.c @@ -0,0 +1,2528 @@ +/* +** Copyright 2001-2006 Double Precision, Inc. See COPYING for +** distribution information. +*/ + + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <rfc822/rfc822hdr.h> +#include "pcp.h" + +#if HAVE_DIRENT_H +#include <dirent.h> +#else +#define dirent direct +#if HAVE_SYS_NDIR_H +#include <sys/ndir.h> +#endif +#if HAVE_SYS_DIR_H +#include <sys/dir.h> +#endif +#if HAVE_NDIR_H +#include <ndir.h> +#endif +#endif + +/* PCP driver for filesystem-based calendar */ + +#define HOSTNAMELEN 256 + +struct PCPdir { + struct PCP pcp; + char *username; + char *dirname; + char *indexname; + char *newindexname; + char hostname_buf[HOSTNAMELEN]; + char unique_filename_buf[256]; + unsigned uniq_cnt; +} ; + +struct PCPdir_event_participant { + char *address; + char *eventid; +} ; + +struct PCPdir_new_eventid { + struct PCP_new_eventid eventid; + char *newfile; + char *tmpfile; + + char *oldeventid; + + struct PCPdir_event_participant *event_participants; + unsigned n_event_participants; + + int booked; +} ; + +static void pcp_close_dir(struct PCPdir *); +static int cleanup(struct PCPdir *); + +static struct PCPdir_new_eventid *neweventid(struct PCPdir *, + const char *, + struct PCP_save_event *); +static void destroyeventid(struct PCPdir *, struct PCPdir_new_eventid *); + +static int saveevent(struct PCPdir *, struct PCPdir_new_eventid *, + struct PCP_save_event *); + +static int commitevent(struct PCPdir *, struct PCPdir_new_eventid *, + struct PCP_commit *); +static int bookevent(struct PCPdir *, struct PCPdir_new_eventid *, + struct PCP_commit *); + +static int listallevents(struct PCPdir *, struct PCP_list_all *); + +static int cancelevent(struct PCPdir *, const char *, int *); +static int uncancelevent(struct PCPdir *, const char *, + int, struct PCP_uncancel *); +static int deleteevent(struct PCPdir *, struct PCP_delete *); +static int retrevent(struct PCPdir *, struct PCP_retr *); +static int setacl(struct PCPdir *, const char *, int); +static int listacl(struct PCPdir *, + int (*)(const char *, int, void *), + void *); + +static const char *errmsg(struct PCP *pcp) +{ + return (strerror(errno)); +} + +static void noop(struct PCP *pcp) +{ +} + +static const char *getauthtoken(struct PCP *pcp) +{ + return (NULL); +} + +struct PCP *pcp_open_dir(const char *dirname, const char *username) +{ + struct PCPdir *pd=(struct PCPdir *)malloc(sizeof(struct PCPdir)); + + if (!pd) + return (NULL); + + memset(pd, 0, sizeof(*pd)); + + pd->username=strdup(username); + if (!pd->username) + { + free(pd); + return (NULL); + } + + pd->dirname=strdup(dirname); + + if (!pd->dirname) + { + free(pd); + return (NULL); + } + + pd->indexname=malloc(strlen(dirname)+sizeof("/index")); + if (!pd->indexname) + { + free(pd->username); + free(pd->dirname); + free(pd); + return (NULL); + } + strcat(strcpy(pd->indexname, dirname), "/index"); + + pd->newindexname=malloc(strlen(dirname)+sizeof("/.index.tmp")); + if (!pd->newindexname) + { + free(pd->username); + free(pd->indexname); + free(pd->dirname); + free(pd); + return (NULL); + } + strcat(strcpy(pd->newindexname, dirname), "/.index.tmp"); + + pd->hostname_buf[sizeof(pd->hostname_buf)-1]=0; + pd->hostname_buf[0]=0; + if (gethostname(pd->hostname_buf, sizeof(pd->hostname_buf)-1) < 0 + || pd->hostname_buf[0] == 0) + strcpy(pd->hostname_buf, "localhost"); + + + pd->pcp.authtoken_func=getauthtoken; + pd->pcp.close_func= (void (*)(struct PCP *)) pcp_close_dir; + pd->pcp.cleanup_func= (int (*)(struct PCP *)) cleanup; + + pd->pcp.create_new_eventid_func= + (struct PCP_new_eventid *(*)(struct PCP *, const char *, + struct PCP_save_event *)) + neweventid; + + pd->pcp.destroy_new_eventid_func= + (void (*)(struct PCP *, struct PCP_new_eventid *)) + destroyeventid; + + pd->pcp.commit_func= + (int (*)(struct PCP *, struct PCP_new_eventid *, + struct PCP_commit *)) + commitevent; + + pd->pcp.book_func= + (int (*)(struct PCP *, struct PCP_new_eventid *, + struct PCP_commit *)) + bookevent; + + pd->pcp.list_all_func= + (int (*)(struct PCP *, struct PCP_list_all *)) + listallevents; + + pd->pcp.cancel_func= + (int (*)(struct PCP *, const char *, int *)) + cancelevent; + + pd->pcp.uncancel_func= + (int (*)(struct PCP *, const char *, int, + struct PCP_uncancel *)) + uncancelevent; + + pd->pcp.delete_func= + (int (*)(struct PCP *, struct PCP_delete *)) + deleteevent; + + pd->pcp.retr_func= + (int (*)(struct PCP *, struct PCP_retr *)) + retrevent; + + pd->pcp.errmsg_func=errmsg; + + pd->pcp.noop_func=noop; + + pd->pcp.acl_func= + (int (*)(struct PCP *, const char *, int))setacl; + pd->pcp.listacl_func= + (int (*)(struct PCP *, int (*)(const char *, int, void *), + void *))listacl; + + return ((struct PCP *)pd); +} + +static void pcp_close_dir(struct PCPdir *pd) +{ + free(pd->newindexname); + free(pd->indexname); + free(pd->dirname); + free(pd->username); + free(pd); +} + + +static void mkunique(struct PCPdir *pd) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + sprintf(pd->unique_filename_buf, "%lu.%lu.%lu_%u.", + (unsigned long)tv.tv_sec, + (unsigned long)tv.tv_usec, + (unsigned long)getpid(), + ++pd->uniq_cnt); + + strncat(pd->unique_filename_buf, pd->hostname_buf, + sizeof(pd->unique_filename_buf)-1 + -strlen(pd->unique_filename_buf)); +} + +static char *dotlockname(struct PCPdir *pd) +{ + char *p=malloc(strlen(pd->dirname)+sizeof("/.lock")); + + if (!p) + return (NULL); + + return (strcat(strcpy(p, pd->dirname), "/.lock")); +} + +/* +** Mark calendar as updated +*/ + +static char *changedname(struct PCPdir *pd) +{ + char *p=malloc(strlen(pd->dirname)+sizeof("/changed")); + + if (!p) + { + fprintf(stderr, "CRIT: out of memory.\n"); + return (NULL); + } + + return (strcat(strcpy(p, pd->dirname), "/changed")); +} + +static void markchanged(struct PCPdir *pd) +{ + char *p=changedname(pd); + int n; + + if (!p) return; + + n=open(p, O_RDWR|O_CREAT, 0600); + if (n >= 0) + close(n); +} + +/* +** Attempt to get rid of a stale dot-lock file. +** +** Return code: 0 - no dot-lock exists +** 1 - transient race condition, try again later +** 2 - valid dot-lock exists +** <0 - permanent error. +*/ + +#define TRY_SLEEP 3 +#define TRY_MAX 3 + +static int kill_stale_lock(struct PCPdir *pd, const char *stale_lock, + time_t *tm) +{ + int fd; + char buffer[HOSTNAMELEN+256]; + int n; + struct stat stat_buf; + + fd=open(stale_lock, O_RDONLY); + if (fd < 0) + { + if (errno == ENOENT) + return (0); + return (-1); + } + + n=read(fd, buffer, sizeof(buffer)-1); + + if (n < 0 || fstat(fd, &stat_buf) < 0) + { + close(fd); + return (-1); + } + + *tm=stat_buf.st_mtime; + close(fd); + + if (n > 0 && buffer[n-1] == '\n') + { + char *p; + + buffer[n-1]=0; + + p=strchr(buffer, ' '); + if (p) + { + *p++=0; + if (strcmp(p, pd->hostname_buf) == 0) + /* Dot-lock created on this server */ + { + pid_t pn=atol(buffer); + + if (pn > 1) + { + if (kill(pn, 0) == 0 || + errno != ESRCH) + { + return (2); + } + /* Process doesn't exist no more */ + + if (unlink(stale_lock) == 0 + || errno == ENOENT) + { + return (1); + } + return (-1); + } + + fprintf(stderr, "INFO: " + "Corrupted dot-lock - removing %s\n", + stale_lock); + + if (unlink(stale_lock) == 0 + || errno == ENOENT) + { + return (1); + } + return (-1); + } + + return (1); + } + } + + fprintf(stderr, "INFO: Corrupted dot-lock - removing %s\n", stale_lock); + + if (unlink(stale_lock) == 0 + || errno == ENOENT) + { + return (1); + } + + return (-1); +} + +static char *acquire_dotlock(struct PCPdir *pd) +{ + char *n=dotlockname(pd); + char *tmpname; + FILE *fp; + unsigned try_cnt; + time_t last_time; + + if (!n) + return (NULL); + + mkunique(pd); + + tmpname=malloc(strlen(pd->dirname)+strlen(pd->unique_filename_buf) + +20); + + if (!tmpname) + { + free(n); + fprintf(stderr, "ALERT: Failed to create dotlock: %s - %s\n", + n, strerror(errno)); + return (NULL); + } + strcat(strcat(strcat(strcpy(tmpname, pd->dirname), "/."), + pd->unique_filename_buf), ".lock"); + + fp=fopen(tmpname, "w"); + + if (!fp) + { + free(tmpname); + free(n); + fprintf(stderr, "ALERT: Failed to create dotlock: %s - %s\n", + n, strerror(errno)); + return (NULL); + } + + if (fprintf(fp, "%lu %s\n", (unsigned long)getpid(), pd->hostname_buf) + < 0 || fflush(fp)) + { + fclose(fp); + unlink(tmpname); + free(tmpname); + free(n); + fprintf(stderr, "ALERT: Failed to create dotlock: %s - %s\n", + n, strerror(errno)); + return (NULL); + } + + if (fclose(fp) < 0) + { + unlink(tmpname); + free(tmpname); + free(n); + fprintf(stderr, "ALERT: Failed to create dotlock: %s - %s\n", + n, strerror(errno)); + return (NULL); + } + + try_cnt=0; + last_time=0; + for (;;) + { + time_t timestamp; + int rc=kill_stale_lock(pd, n, ×tamp); + + if (rc > 0) + { + ++try_cnt; + if (last_time == 0) + last_time=timestamp; + + if (timestamp != last_time) + try_cnt=0; /* Dot-lock was modified, + ** reset. + */ + + if (try_cnt == TRY_MAX) /* Time to kill dot lock */ + { + if (rc > 1 || (unlink(n) < 0 + && errno != ENOENT)) + { + fprintf(stderr, "ALERT: " + "Failed to obtain dotlock: %s\n", + n); + + unlink(tmpname); + free(tmpname); + free(n); + return (NULL); + } + fprintf(stderr, "ALERT: Removed stale dotlock: %s\n", + n); + + } + else if (try_cnt > TRY_MAX) + { + fprintf(stderr, "ALERT: " + "Failed to obtain dotlock: %s\n", + n); + + unlink(tmpname); + free(tmpname); + free(n); + return (NULL); + } + last_time=timestamp; + sleep(TRY_SLEEP); + continue; + } + + if (rc < 0) + { + fprintf(stderr, "ALERT: Failed to obtain dotlock: %s\n", + n); + unlink(tmpname); + free(tmpname); + free(n); + return (NULL); + } + + if (link(tmpname, n) == 0) + break; + + if (try_cnt > TRY_MAX) + { + fprintf(stderr, "ALERT: Failed to obtain dotlock: %s\n", + n); + + unlink(tmpname); + free(tmpname); + free(n); + return (NULL); + } + ++try_cnt; + sleep(TRY_SLEEP); + } + + unlink(tmpname); + free(tmpname); + return (n); +} + +#if 0 +static void renew_dotlock(struct PCPdir *pd, char *n) +{ + FILE *fp; + char buf[HOSTNAMELEN*2]; + + fp=fopen(n, "r"); + + if (fp && fgets(buf, sizeof(buf), fp) != NULL) + { + char *p=strchr(buf, '\n'); + + if (p) + *p=0; + + p=strchr(buf, ' '); + if (p) + { + *p++=0; + + if (atol(buf) == getpid() && + strcmp(p, pd->hostname_buf) == 0) + { + fclose(fp); + fp=fopen(n, "r+"); + if (fp) + { + if (fseek(fp, -1, SEEK_END) >= 0) + putc('\n', fp); + fflush(fp); + fclose(fp); + } + return; + } + } + } + if (fp) + fclose(fp); +} +#endif + +static void release_dotlock(struct PCPdir *pd, char *n) +{ + FILE *fp; + char buf[HOSTNAMELEN*2]; + + fp=fopen(n, "r"); + + if (fp && fgets(buf, sizeof(buf), fp) != NULL) + { + char *p=strchr(buf, '\n'); + + if (p) + *p=0; + + p=strchr(buf, ' '); + if (p) + { + *p++=0; + + if (atol(buf) == getpid() && + strcmp(p, pd->hostname_buf) == 0) + { + fclose(fp); + unlink(n); + free(n); + return; + } + } + } + if (fp) + fclose(fp); + fprintf(stderr, "ALERT: Dotlock unexpectedly gone: %s\n", n); + free(n); +} + +static struct PCPdir_new_eventid *neweventid(struct PCPdir *pd, + const char *oldeventid_arg, + struct PCP_save_event *se) +{ + unsigned ut; + + struct PCPdir_new_eventid *p=(struct PCPdir_new_eventid *) + malloc(sizeof(struct PCPdir_new_eventid)); + + if (!p) + return (NULL); + + mkunique(pd); + + p->eventid.eventid=malloc(strlen(pd->unique_filename_buf)+ + strlen(pd->username)+2); + if (!p->eventid.eventid) + { + free(p); + return (NULL); + } + + strcat(strcat(strcpy(p->eventid.eventid, pd->unique_filename_buf), + "@"), pd->username); + + p->tmpfile=malloc(strlen(pd->unique_filename_buf) + +strlen(pd->dirname)+10); + if (!p->tmpfile) + { + free(p->eventid.eventid); + free(p); + return (NULL); + } + strcat(strcat(strcat(strcpy(p->tmpfile, pd->dirname), + "/."), pd->unique_filename_buf), ".tmp"); + + p->newfile=malloc(strlen(pd->unique_filename_buf) + +strlen(pd->dirname)+2); + if (!p->newfile) + { + free(p->tmpfile); + free(p->eventid.eventid); + free(p); + return (NULL); + } + strcat(strcat(strcpy(p->newfile, pd->dirname), + "/"), pd->unique_filename_buf); + + if (oldeventid_arg) + { + if ((p->oldeventid=strdup(oldeventid_arg)) == NULL) + { + free(p->newfile); + free(p->tmpfile); + free(p->eventid.eventid); + free(p); + return (NULL); + } + } + else + p->oldeventid=0; + p->booked=0; + + p->event_participants=NULL; + p->n_event_participants=0; + + for (ut=0; ut<se->n_event_participants; ut++) + { + const char *q; + + for (q=se->event_participants[ut].address; *q; q++) + if (isspace((int)(unsigned char)*q)) + { + errno=EINVAL; + destroyeventid(pd, p); + return (NULL); + } + + for (q=se->event_participants[ut].eventid; q && *q; q++) + if (isspace((int)(unsigned char)*q)) + { + errno=EINVAL; + destroyeventid(pd, p); + return (NULL); + } + } + + if (se->n_event_participants > 0) + { + if ((p->event_participants= + malloc( sizeof (*p->event_participants) + * se->n_event_participants)) == 0) + { + destroyeventid(pd, p); + return (NULL); + } + + for ( ; p->n_event_participants < se->n_event_participants; + ++p->n_event_participants) + { + p->event_participants[p->n_event_participants] + .eventid=NULL; + + if (se->event_participants[p->n_event_participants] + .eventid + && (p->event_participants[p->n_event_participants] + .eventid=strdup(se->event_participants + [p->n_event_participants] + .eventid)) + == NULL) + { + destroyeventid(pd, p); + return (NULL); + } + + if ( (p->event_participants[p->n_event_participants] + .address=strdup(se->event_participants + [p->n_event_participants] + .address)) + == NULL) + { + if (p->event_participants + [p->n_event_participants].eventid) + free(p->event_participants + [p->n_event_participants] + .eventid); + destroyeventid(pd, p); + return (NULL); + } + } + } + + if (saveevent(pd, p, se)) + { + destroyeventid(pd, p); + p=NULL; + } + + return (p); +} + +static void destroyeventid(struct PCPdir *pd, struct PCPdir_new_eventid *p) +{ + unsigned i; + + for (i=0; i<p->n_event_participants; i++) + { + if (p->event_participants[i].eventid) + free(p->event_participants[i].eventid); + if (p->event_participants[i].address) + free(p->event_participants[i].address); + } + if (p->event_participants) + free(p->event_participants); + + if (p->booked && p->eventid.eventid) + { + struct PCP_delete del; + + memset(&del, 0, sizeof(del)); + del.id=p->eventid.eventid; + + deleteevent(pd, &del); + } + + if (p->oldeventid) + free(p->oldeventid); + free(p->newfile); + + if (p->tmpfile) + { + unlink(p->tmpfile); + free(p->tmpfile); + p->tmpfile=NULL; + } + + free(p->eventid.eventid); + free(p); +} + +static int saveevent(struct PCPdir *pd, struct PCPdir_new_eventid *ne, + struct PCP_save_event *si) +{ + char buf[BUFSIZ]; + int n; + + FILE *nf=fopen(ne->tmpfile, "w"); + + if (!nf) + return (-1); + + while ((n=pcp_read_saveevent(si, buf, sizeof(buf))) > 0) + { + if (fwrite(buf, n, 1, nf) != 1) + { + fclose(nf); + return (-1); + } + } + + if (fflush(nf) || ferror(nf)) + n= -1; + if (fclose(nf)) + n= -1; + + if (n < 0) + return (-1); + + return (0); +} + +/* +** Structure of the index file: +** +** eventid<TAB>filename<TAB>field<TAB>field<TAB>field... +** +** field is name=value +** +** Fields: +*/ + +#define TIME_FIELD "t" /* start,end */ +#define CANCELLED_FIELD "c" /* This event has been cancelled */ +#define BOOKED_FIELD "b" /* This is a booked event */ +#define PENDEL_FIELD "d" /* This event is pending to be deleted */ +#define PROXY_FIELD "P" /* This event was placed by proxy */ +#define PARTICIPANT_FIELD "p" /* event participant */ + +/* The line_buffer struct is a line's worth of a buffer */ + +struct line_buffer { + int bufsiz; + char *buffer; /* Also the eventid */ + + char *next_field; /* Iterator through the fields */ +} ; + +static int lb_init(struct line_buffer *pi) +{ + pi->bufsiz=BUFSIZ; + pi->buffer=malloc(pi->bufsiz); + if (!pi->buffer) + return (-1); + return (0); +} + +static void lb_destroy(struct line_buffer *pi) +{ + free(pi->buffer); +} + +static int lb_read(FILE *fp, struct line_buffer *pi) +{ + int n=0; + int c; + + for (;;) + { + if (n >= pi->bufsiz) + { + int news=pi->bufsiz + BUFSIZ; + char *newp=realloc(pi->buffer, news); + + if (!newp) + return (-1); + + pi->buffer=newp; + pi->bufsiz=news; + } + + c=getc(fp); + if (c == EOF) + return (-1); + + if (c == '\n') + { + pi->buffer[n]=0; + break; + } + pi->buffer[n++]=c; + } + + return (0); +} + +static int lb_is_eventid(struct line_buffer *pi, const char *ei) +{ + int l=strlen(ei); + const char *p=pi->buffer; + + return (p && strncmp(p, ei, l) == 0 && p[l] == '\t'); +} + +static char *lb_event_id(struct line_buffer *pi) +{ + char *p=strchr(pi->buffer, '\t'); + char *q; + + if (!p) + return (strdup("")); /* Corrupted index file */ + + q=malloc(p-pi->buffer+1); + + if (!q) + return (NULL); + + memcpy(q, pi->buffer, p-pi->buffer); + q[p-pi->buffer]=0; + return (q); +} + +static char *lb_filename(struct PCPdir *pd, struct line_buffer *pi) +{ + char *p=strchr(pi->buffer, '\t'); + char *q, *r; + + if (!p) + return (strdup("")); /* Corrupted index file */ + + ++p; + r=strchr(p, '\t'); + if (!r) + r=p+strlen(p); + + q=malloc(strlen(pd->dirname)+2+ (r-p)); + + if (!q) + return (NULL); + + strncat(strcat(strcpy(q, pd->dirname), "/"), p, r-p); + return (q); +} + +static char *lb_filename_nodir(struct line_buffer *pi) +{ + char *p=strchr(pi->buffer, '\t'); + char *q, *r; + + if (!p) + return (strdup("")); /* Corrupted index file */ + + ++p; + r=strchr(p, '\t'); + if (!r) + r=p+strlen(p); + + q=malloc(1+(r-p)); + + if (!q) + return (NULL); + + *q=0; + strncat(q, p, r-p); + return (q); +} + +static const char *lb_first_field(struct line_buffer *pi) +{ + char *p; + + p=pi->buffer ? strchr(pi->buffer, '\t'):NULL; + + if (p) + { + ++p; /* p now is the filename */ + p=strchr(p, '\t'); + if (p) + ++p; + } + + return (pi->next_field=p); +} + +static const char *lb_next_field(struct line_buffer *pi) +{ + char *p=pi->next_field ? strchr(pi->next_field, '\t'):NULL; + + if (p) + ++p; + return (pi->next_field=p); +} + +static int lb_is_field(const char *p, const char *n) +{ + if (p) + { + int l=strlen(n); + + if (memcmp(p, n, l) == 0 && + (p[l] == '=' || p[l] == '\t' || p[l] == 0)) + return (1); + } + return (0); +} + +static int lb_is_cancelled(struct line_buffer *pi) +{ + const char *p; + + for (p=lb_first_field(pi); p; p=lb_next_field(pi)) + if (lb_is_field(p, CANCELLED_FIELD)) + return (1); + return (0); +} + +static int lb_is_proxy(struct line_buffer *pi) +{ + const char *p; + + for (p=lb_first_field(pi); p; p=lb_next_field(pi)) + if (lb_is_field(p, PROXY_FIELD)) + return (1); + return (0); +} + +static const char *lb_field_value(const char *p) +{ + while (p && *p && *p != '\t') + if ( *p++ == '=') + return (p); + return (NULL); +} + +static int lb_is_booked(struct line_buffer *pi, time_t *t) +{ + const char *p; + + for (p=lb_first_field(pi); p; p=lb_next_field(pi)) + if (lb_is_field(p, BOOKED_FIELD)) + { + const char *q=lb_field_value(p); + unsigned long ul; + + if (q && sscanf(q, "%lu", &ul) == 1) + { + if (t) + *t= (time_t)ul; + return (1); + } + } + return (0); +} + +static char *lb_field_value_buf(const char *p) +{ + const char *q; + char *r; + + while (p && *p && *p != '\t') + if ( *p++ == '=') + break; + + if (!p) + return (strdup("")); + + q=strchr(p, '\t'); + if (!q) + return (strdup(p)); + + r=malloc(q-p+1); + + if (!r) + return (NULL); + + memcpy(r, p, q-p); + r[q-p]=0; + return (r); +} + +static const char *lb_is_pendel(struct line_buffer *pi) +{ + const char *p; + + for (p=lb_first_field(pi); p; p=lb_next_field(pi)) + if (lb_is_field(p, PENDEL_FIELD)) + return (p); + return (0); +} + +static int lb_remove_field(struct line_buffer *pi, + FILE *nfp, + const char *fieldname, + const char *newfield, + const char *newvalue) +{ + char *q; + const char *cp; + + q=lb_event_id(pi); + if (!q) + return (-1); + + fprintf(nfp, "%s\t", q); + free(q); + q=lb_filename_nodir(pi); + if (!q) + return (-1); + fprintf(nfp, "%s", q); + free(q); + + for (cp=lb_first_field(pi); cp; cp=lb_next_field(pi)) + { + const char *cc; + + if (lb_is_field(cp, fieldname)) + continue; + + cc=strchr(cp, '\t'); + if (!cc) cc=cp+strlen(cp); + + putc('\t', nfp); + if (fwrite(cp, cc-cp, 1, nfp) != 1) + break; + } + if (newfield) + { + fprintf(nfp, "\t%s", newfield); + if (newvalue) + fprintf(nfp, "=%s", newvalue); + } + putc('\n', nfp); + return (0); +} + +static int docommitevent(struct PCPdir *, struct PCPdir_new_eventid *, + int, + struct PCP_commit *); + +static int commitevent(struct PCPdir *pd, struct PCPdir_new_eventid *ae, + struct PCP_commit *ci) +{ + return (docommitevent(pd, ae, 0, ci)); +} + +static int bookevent(struct PCPdir *pd, struct PCPdir_new_eventid *ae, + struct PCP_commit *ci) +{ + return (docommitevent(pd, ae, 1, ci)); +} + +static int pcp_is_conflict(struct line_buffer *, + const struct PCP_event_time **, unsigned, + struct PCP_commit *, + struct PCP_uncancel *); + +static int eventexists(struct PCPdir *pd, const char *e, int *flag) +{ + FILE *fp=fopen(pd->indexname, "r"); + struct line_buffer pi; + + if (!fp) + return (0); + + if (fseek(fp, 0L, SEEK_SET) < 0) + return (-1); + + + if (lb_init(&pi)) + { + fclose(fp); + return (-1); + } + + *flag=0; + + while (lb_read(fp, &pi) == 0) + { + if (lb_is_eventid(&pi, e)) + { + *flag=1; + break; + } + } + lb_destroy(&pi); + if (ferror(fp)) + { + fclose(fp); + return (-1); + } + + if (fclose(fp)) + return (-1); + return (0); +} + + +static int docommitevent(struct PCPdir *pd, struct PCPdir_new_eventid *ae, + int bookmode, + struct PCP_commit *ci) +{ + const struct PCP_event_time **times; + char *dotlock; + FILE *fp; + FILE *nfp; + int rc; + unsigned ut; + + char *deleted_event=0; /* Filename of deleted event */ + int is_cancelled=0; + int is_booked=0; + int was_booked=0; + + ci->errcode=0; + + + if (ae->tmpfile == NULL) /* Already commited */ + { + ci->errcode=PCP_ERR_EVENTNOTFOUND; + return (-1); + } + + if (ci->n_event_times <= 0) + { + ci->errcode=PCP_ERR_CONFLICT; + return (-1); + } + + /* Sort event times in chronological order */ + + times=pcp_add_sort_times(ci->event_times, + ci->n_event_times); + if (!times) + return (-1); + + /* Use a dotlock to protect: reading old index + checking for + ** conflicts, writing a new index, updating everything */ + + dotlock=acquire_dotlock(pd); + + if (!dotlock) + { + ci->errcode=PCP_ERR_LOCK; + free(times); + return (-1); + } + + rc= -1; + fp=fopen(pd->indexname, "a+"); + if (fp && fseek(fp, 0L, SEEK_SET) >= 0) + { + nfp=fopen(pd->newindexname, "w"); + if (nfp) + { + struct line_buffer pi; + + if (lb_init(&pi) == 0) + { + rc= 0; + while (lb_read(fp, &pi) == 0) + { + if (ae->booked && + lb_is_eventid(&pi, + ae->eventid.eventid)) + { + was_booked=1; + if (bookmode) + continue; + /* Drop old booking */ + + if (lb_remove_field(&pi, nfp, + BOOKED_FIELD, + NULL, + NULL)) + rc= -1; + is_booked=1; + continue; + } + if (ae->oldeventid + && lb_is_eventid(&pi, + ae->oldeventid)) + { + if (lb_is_cancelled(&pi)) + is_cancelled=1; + if (deleted_event) /* ??? */ + free(deleted_event); + deleted_event=lb_filename + (pd, &pi); + if (!deleted_event) + rc= -1; + if (bookmode) + { + const char *pendel= + lb_is_pendel(&pi); + if (pendel) + { + /* Event is locked by another book/update? */ + + char *delid=lb_field_value_buf(pendel); + + if (!delid) + rc= -1; + else if (strcmp(delid, ae->eventid.eventid)) + { + int flag=0; + /* + ** Possibly, but make sure the locking + ** event actually exists. + */ + if (eventexists(pd, delid, &flag)) + rc= -1; + else if (flag) + { + rc= -1; + ci->errcode=PCP_ERR_EVENTLOCKED; + } + } + fprintf(nfp, "%s\n", + pi.buffer); + free(delid); + } + else + { + lb_remove_field(&pi, + nfp, + PENDEL_FIELD, + PENDEL_FIELD, + ae->eventid.eventid); + } + /* Dont del just yet */ + } + continue; + } + fprintf(nfp, "%s\n", pi.buffer); + + /* Check for conflicts UNLESS */ + + if ((ci->flags & PCP_OK_CONFLICT) + == 0 && + /* Conflics are OK */ + + (!ae->booked || bookmode) && + /* + ** We're not committing a booked + ** event (check for conflicts at + ** time of booking). + */ + + !lb_is_cancelled(&pi) && + /* This event is cancelled */ + + pcp_is_conflict(&pi, + times, + ci->n_event_times, + ci, + NULL) + ) + { + rc= -1; + } + } + + if (rc == 0 && !ferror(fp) && !ferror(nfp) + && !is_booked) + { + /* Add a new index record */ + + const char *p= + strrchr(ae->newfile, '/'); + + if (p) + ++p; + else + p=ae->newfile; + + fprintf(nfp, "%s\t%s", + ae->eventid.eventid, p); + + /* + ** If older event is cancelled, so + ** is this event. + */ + + if (is_cancelled) + { + fprintf(nfp, "\t" + CANCELLED_FIELD); + } + + if (ci->flags & PCP_BYPROXY) + fprintf(nfp, "\t" + PROXY_FIELD); + + if (bookmode) + { + time_t book_time; + + time(&book_time); + fprintf(nfp, "\t" BOOKED_FIELD + "=%lu", + (unsigned long) + book_time); + } + + for (ut=0; ut<ci->n_event_times; ut++) + fprintf(nfp, "\t" + TIME_FIELD + "=%lu,%lu", + (unsigned long) + times[ut]->start, + (unsigned long) + times[ut]->end); + + + for (ut=0; ut<ae->n_event_participants; + ut++) + { + const char *p= + ae->event_participants + [ut].address; + const char *q= + ae->event_participants + [ut].eventid; + + if (!p) + continue; + + fprintf(nfp, + "\t" + PARTICIPANT_FIELD + "=%s%s%s", p, + q ? " ":"", q ? q:""); + + /* Sanity check: */ + + while (*p) + { + if ((int) + (unsigned char)*p + <= ' ') + { + rc= -1; + errno=EINVAL; + } + ++p; + } + } + + fprintf(nfp, "\n"); + } + if (ae->booked && !was_booked) + { + rc= -1; + ci->errcode=PCP_ERR_EVENTNOTFOUND; + ae->booked=0; + /* Booked event disappeared */ + } + + if (rc == 0 && + (fflush(nfp) < 0 || ferror(nfp))) + rc= -1; + + lb_destroy(&pi); + } + if (fclose(nfp)) + rc= -1; + } + if (fclose(fp)) + rc= -1; + + if (rc == 0 && !bookmode) + { + if (rename(ae->tmpfile, ae->newfile)) + rc= -1; + } + + if (rc == 0) + { + if (rename(pd->newindexname, pd->indexname)) + rc=-1; + } + + if (rc == 0 && !bookmode) + { + if (deleted_event) + unlink(deleted_event); + free(ae->tmpfile); + ae->tmpfile=0; + ae->booked=0; + } + if (rc == 0 && bookmode) + ae->booked=1; + } + else if (fp) + fclose(fp); + + if (deleted_event) + free(deleted_event); + markchanged(pd); + release_dotlock(pd, dotlock); + free(times); + return (rc); +} + +/* +** Spring cleaning. +*/ + +struct cleanup_filename_list { + struct cleanup_filename_list *next; + char *filename; +} ; + +static int event_expired(struct line_buffer *, time_t); + +static int cleanup(struct PCPdir *pd) +{ + char *dotlock; + FILE *fp; + FILE *nfp; + int rc; + struct stat stat_buf; + struct cleanup_filename_list *list=NULL; + time_t now; + + dotlock=acquire_dotlock(pd); + + if (!dotlock) + return (-1); + + /* + ** Read the current index. + ** Remove entries for events whose files don't exist. + ** + ** Save list of all files that are indexed. + */ + + time(&now); + rc= -1; + fp=fopen(pd->indexname, "a+"); + if (fp && fseek(fp, 0L, SEEK_SET) >= 0) + { + nfp=fopen(pd->newindexname, "w"); + if (nfp) + { + struct line_buffer pi; + + if (lb_init(&pi) == 0) + { + rc= 0; + while (lb_read(fp, &pi) == 0) + { + char *filename=lb_filename(pd, &pi); + struct cleanup_filename_list *l; + time_t booked_time; + + if (!filename) + { + rc= -1; + break; + } + + if (!*filename) + { + free(filename); + continue; + } + + if (lb_is_booked(&pi, &booked_time)) + { + /* Expire books after 1 hr */ + + if (booked_time < now - 60*60) + { + free(filename); + continue; + } + } + else if (stat(filename, &stat_buf)) + { + if (errno != ENOENT) + { + free(filename); + rc= -1; + break; + } + free(filename); + continue; + } + else if ( event_expired(&pi, + now - + CALENDARPURGE * + 60 * 60 * 24)) + { + free(filename); + continue; + /* + ** cleanup loop below will + ** take care of the event file + */ + } + + l=malloc(sizeof(*list)); + l->next=list; + l->filename=filename; + list=l; + + fprintf(nfp, "%s\n", pi.buffer); + } + lb_destroy(&pi); + } + + if (rc == 0 && (ferror(fp) || ferror(nfp) + || fflush(nfp))) + rc= -1; + + if (fclose(nfp)) + rc= -1; + } + if (fclose(fp)) + rc= -1; + + if (rename(pd->newindexname, pd->indexname)) + rc= -1; + } + else if (fp) + fclose(fp); + + if (rc == 0) /* Time to scan the directory */ + { + DIR *dirp=opendir(pd->dirname); + struct dirent *de; + const char *p; + time_t now; + + time(&now); + + while (dirp && (de=readdir(dirp)) != NULL) + { + char *filename=malloc(strlen(pd->dirname)+2+ + strlen(de->d_name)); + if (!filename) + { + rc= -1; + break; + } + strcat(strcat(strcpy(filename, pd->dirname), + "/"), de->d_name); + if (isdigit((int)(unsigned char)de->d_name[0])) + { + struct cleanup_filename_list *l; + for (l=list; l; l=l->next) + if (strcmp(l->filename, filename) == 0) + break; + + if (!l) + { + unlink(filename); + } + } + else + { + p=strrchr(filename, '.'); + if (p && (strcmp(p, ".tmp") == 0 || + strcmp(p, ".lock") == 0) && + stat(filename, &stat_buf) == 0 && + stat_buf.st_mtime < now - 36 * 60 * 60) + unlink(filename); + } + free(filename); + } + if (dirp) + closedir(dirp); + } + release_dotlock(pd, dotlock); + + while (list) + { + struct cleanup_filename_list *l=list; + + list=l->next; + free(l->filename); + free(l); + } + return (rc); +} + +static int parse_time_field(const char *, time_t *, time_t *); + +static int event_expired(struct line_buffer *pi, time_t when) +{ + const char *p; + + for (p=lb_first_field(pi); p; p=lb_next_field(pi)) + { + time_t start_time; + time_t end_time; + + if (!lb_is_field(p, TIME_FIELD)) + continue; + + if (parse_time_field(lb_field_value(p), + &start_time, &end_time)) + continue; + if (end_time > when) + return (0); + } + return (1); +} + +/* Check if existing event conflicts with new event */ + +static int parse_time_field(const char *p, time_t *start_time, + time_t *end_time) +{ + if (!p) + return (-1); + + *start_time=0; + *end_time=0; + + while (*p && isdigit((int)(unsigned char)*p)) + { + *start_time=*start_time * 10 + (*p-'0'); + ++p; + } + if (*p != ',') + return (-1); + + ++p; + while (*p && isdigit((int)(unsigned char)*p)) + { + *end_time=*end_time * 10 + (*p-'0'); + ++p; + } + return (0); +} + +static int pcp_is_conflict(struct line_buffer *lb, + const struct PCP_event_time **t, unsigned n, + struct PCP_commit *ae, + struct PCP_uncancel *un) +{ + unsigned i; + const char *p; + int rc=0; + + char *event_id=lb_event_id(lb); + + if (!event_id) + return (-1); + + for (p=lb_first_field(lb); p; p=lb_next_field(lb)) + { + time_t start_time; + time_t end_time; + + if (!lb_is_field(p, TIME_FIELD)) + continue; + + if (parse_time_field(lb_field_value(p), + &start_time, &end_time)) + continue; + + for (i=0; i<n; i++) + { + time_t com_start; + time_t com_end; + + if (start_time >= t[i]->end) + continue; + if (t[i]->start >= end_time) + continue; + + com_start=start_time; + com_end=end_time; + if (t[i]->start > com_start) + com_start=t[i]->start; + if (t[i]->end < com_end) + com_end=t[i]->end; + + rc= -1; + if (ae) + { + ae->errcode=PCP_ERR_CONFLICT; + if (ae->add_conflict_callback) + { + rc=(*ae->add_conflict_callback) + (event_id, com_start, + com_end, + "@", + ae->add_conflict_callback_ptr + ); + if (rc) + { + ae->errcode=0; + break; + } + rc= -1; + } + } + + if (un) + { + un->errcode=PCP_ERR_CONFLICT; + if (un->uncancel_conflict_callback) + { + rc=(*un->uncancel_conflict_callback) + (event_id, com_start, + com_end, "@", + un->uncancel_conflict_callback_ptr + ); + if (rc) + { + un->errcode=0; + break; + } + rc= -1; + } + } + } + + } + free(event_id); + return (rc); +} + +static int listallevents(struct PCPdir *pd, struct PCP_list_all *li) +{ + FILE *fp; + struct line_buffer lb; + int rc=0; + const char *p; + + if ((fp=fopen(pd->indexname, "r")) == NULL) + return (0); /* Empty calendar */ + + if (lb_init(&lb) == 0) + { + while (lb_read(fp, &lb) == 0) + { + char *event_id=lb_event_id(&lb); + + if (!event_id) + { + rc= -1; + break; + } + + li->event_id=event_id; + + for (p=lb_first_field(&lb); p; p=lb_next_field(&lb)) + { + if (lb_is_field(p, TIME_FIELD)) + { + time_t start_time; + time_t end_time; + + if (parse_time_field(lb_field_value(p), + &start_time, + &end_time)) + continue; + + if (li->list_from + && li->list_from == li->list_to) + { + if (li->list_from < start_time + || li->list_to >= end_time) + continue; + } + else + { + if (li->list_from && + end_time <= li->list_from) + continue; + + if (li->list_to && + start_time >= li->list_to) + continue; + } + li->event_from=start_time; + li->event_to=end_time; + + if ((rc= (*li->callback_func) + (li, li->callback_arg)) != 0) + break; + } + if (rc) break; + + } + + free(event_id); + if (rc) + break; + } + lb_destroy(&lb); + } + fclose(fp); + return (rc); +} + +static int read_event_times(struct line_buffer *pi, + struct PCP_event_time **time_ret, + unsigned *time_n_ret) +{ + const char *c; + unsigned n; + time_t start_time, end_time; + + *time_n_ret=0; + *time_ret=NULL; + + for (c=lb_first_field(pi); c; c=lb_next_field(pi)) + if (lb_is_field(c, TIME_FIELD) && + parse_time_field(lb_field_value(c), + &start_time, &end_time) == 0) + ++ *time_n_ret; + + if (!*time_n_ret) + return (0); + + if ( (*time_ret=(struct PCP_event_time *)malloc + (sizeof(struct PCP_event_time)* *time_n_ret)) == NULL) + return (-1); + + n=0; + for (c=lb_first_field(pi); c; c=lb_next_field(pi)) + if (lb_is_field(c, TIME_FIELD) && + parse_time_field(lb_field_value(c), + &start_time, &end_time) == 0) + { + (*time_ret)[n].start=start_time; + (*time_ret)[n].end=end_time; + ++n; + } + return (0); +} + +static int cancelevent(struct PCPdir *pd, const char *event, int *errcode) +{ + char *dotlock; + FILE *fp; + FILE *nfp; + int rc; + int found=0; + + if (errcode) *errcode=0; + dotlock=acquire_dotlock(pd); + + if (!dotlock) + return (-1); + + rc= -1; + fp=fopen(pd->indexname, "a+"); + if (fp && fseek(fp, 0L, SEEK_SET) >= 0) + { + nfp=fopen(pd->newindexname, "w"); + if (nfp) + { + struct line_buffer pi; + + if (lb_init(&pi) == 0) + { + rc= 0; + while (lb_read(fp, &pi) == 0) + { + if (!lb_is_eventid(&pi, event)) + { + fprintf(nfp, "%s\n", + pi.buffer); + continue; + } + found=1; + if (lb_is_cancelled(&pi)) + { + /* Already canned */ + fprintf(nfp, "%s\n", + pi.buffer); + continue; + } + + fprintf(nfp, "%s\t" + CANCELLED_FIELD + "\n", pi.buffer); + } + lb_destroy(&pi); + if (!found) + { + if (errcode) + *errcode=PCP_ERR_EVENTNOTFOUND; + errno=ENOENT; + rc= -1; + } + } + if (ferror(nfp) || fflush(nfp)) + rc= -1; + if (fclose(nfp)) + rc= -1; + } + fclose(fp); + + if (rc == 0 && rename(pd->newindexname, pd->indexname)) + rc= -1; + } + else if (fp) + fclose(fp); + release_dotlock(pd, dotlock); + return (rc); +} + +static int uncancelevent(struct PCPdir *pd, const char *event, int flags, + struct PCP_uncancel *info) +{ + char *dotlock; + FILE *fp; + FILE *nfp; + int rc; + int found=0; + struct PCP_event_time *old_times=NULL; + const struct PCP_event_time **old_times_sorted=NULL; + unsigned n_old_times=0; + + if (info) info->errcode=0; + dotlock=acquire_dotlock(pd); + + if (!dotlock) + return (-1); + + rc= -1; + fp=fopen(pd->indexname, "a+"); + if (fp && fseek(fp, 0L, SEEK_SET) >= 0) + { + struct line_buffer pi; + + /* First, find the existing booked times */ + + if (lb_init(&pi) == 0) + { + if (info) + info->errcode=PCP_ERR_EVENTNOTFOUND; + while (lb_read(fp, &pi) == 0) + { + if (!lb_is_eventid(&pi, event)) + continue; + if (read_event_times(&pi, &old_times, + &n_old_times) == 0) + { + if (fseek(fp, 0L, SEEK_SET) >= 0) + rc=0; + break; + } + } + lb_destroy(&pi); + + if (rc == 0 && info) + info->errcode=0; + } + + if (rc == 0 && n_old_times && + (old_times_sorted= + pcp_add_sort_times(old_times, n_old_times)) == NULL) + rc= -1; + + nfp=rc == 0 ? fopen(pd->newindexname, "w"):NULL; + if (nfp) + { + struct line_buffer pi; + + if (lb_init(&pi) == 0) + { + rc= 0; + while (lb_read(fp, &pi) == 0) + { + char *id=lb_event_id(&pi); + + if (!id) + { + rc= -1; + break; + } + + if (!lb_is_eventid(&pi, event)) + { + if ((flags & PCP_OK_CONFLICT) + == 0 && + !lb_is_cancelled(&pi)) + { + rc= pcp_is_conflict + (&pi, + old_times_sorted, + n_old_times, + NULL, + info); + } + fprintf(nfp, "%s\n", + pi.buffer); + continue; + } + found=1; + if (!lb_is_cancelled(&pi)) + { + /* Already uncanned */ + fprintf(nfp, "%s\n", + pi.buffer); + continue; + } + + if (lb_remove_field(&pi, nfp, + CANCELLED_FIELD, + NULL, + NULL)) + rc= -1; + } + lb_destroy(&pi); + if (!found) + { + if (info) + info->errcode=PCP_ERR_EVENTNOTFOUND; + errno=ENOENT; + rc= -1; + } + } + if (ferror(nfp) || fflush(nfp)) + rc= -1; + if (fclose(nfp)) + rc= -1; + } + fclose(fp); + + if (rc == 0 && rename(pd->newindexname, pd->indexname)) + rc= -1; + } + else if (fp) + fclose(fp); + if (old_times_sorted) + free (old_times_sorted); + if (old_times) + free (old_times); + release_dotlock(pd, dotlock); + return (rc); +} + +static int deleteevent(struct PCPdir *pd, struct PCP_delete *del) +{ + char *dotlock; + FILE *fp; + FILE *nfp; + int rc; + char *deleted_event=0; + + del->errcode=0; + dotlock=acquire_dotlock(pd); + + if (!dotlock) + return (-1); + + rc= -1; + fp=fopen(pd->indexname, "a+"); + if (fp && fseek(fp, 0L, SEEK_SET) >= 0) + { + nfp=fopen(pd->newindexname, "w"); + if (nfp) + { + struct line_buffer pi; + + if (lb_init(&pi) == 0) + { + rc= 0; + while (lb_read(fp, &pi) == 0) + { + if (!lb_is_eventid(&pi, del->id)) + { + fprintf(nfp, "%s\n", + pi.buffer); + continue; + } + + if (deleted_event) /* ??? */ + free(deleted_event); + deleted_event=lb_filename(pd, &pi); + if (!deleted_event) + rc= -1; + } + lb_destroy(&pi); + if (!deleted_event) + { + del->errcode=PCP_ERR_EVENTNOTFOUND; + errno=ENOENT; + rc= -1; + } + } + if (ferror(nfp) || fflush(nfp)) + rc= -1; + if (fclose(nfp)) + rc= -1; + } + fclose(fp); + + if (rc == 0 && rename(pd->newindexname, pd->indexname)) + rc= -1; + } + else if (fp) + fclose(fp); + if (deleted_event && rc == 0) + unlink(deleted_event); + if (deleted_event) + free(deleted_event); + markchanged(pd); + release_dotlock(pd, dotlock); + return (rc); +} + +static int retrheaders(struct PCPdir *, struct PCP_retr *, const char *); +static int retrrfc822(struct PCPdir *, struct PCP_retr *, const char *); + +static int retrevent(struct PCPdir *pd, struct PCP_retr *ri) +{ + FILE *fp; + int rc; + + rc= -1; + + fp=fopen(pd->indexname, "r"); + if (fp && fseek(fp, 0L, SEEK_SET) >= 0) + { + struct line_buffer pi; + + if (lb_init(&pi) == 0) + { + rc= 0; + while (lb_read(fp, &pi) == 0) + { + const char *c; + char *filename; + unsigned x; + int status; + + for (x=0; ri->event_id_list[x]; x++) + if (lb_is_eventid(&pi, + ri->event_id_list[x]) + ) + break; + if (!(ri->event_id=ri->event_id_list[x])) + continue; + + filename=lb_filename(pd, &pi); + if (!filename) + { + rc= -1; + break; + } + if (!*filename) + { + free(filename); + continue; + } + + status=0; + if (lb_is_cancelled(&pi)) + status |= LIST_CANCELLED; + if (lb_is_booked(&pi, NULL)) + status |= LIST_BOOKED; + if (lb_is_proxy(&pi)) + status |= LIST_PROXY; + + if (ri->callback_retr_status) + { + rc= (*ri->callback_retr_status) + (ri, status, + ri->callback_arg); + if (rc) + break; + } + + for (c=lb_first_field(&pi); c; + c=lb_next_field(&pi)) + { + if (lb_is_field(c, TIME_FIELD)) + { + time_t from, to; + + if (!ri->callback_retr_date) + continue; + + if (parse_time_field + (lb_field_value(c), + &from, &to)) + continue; + + rc=(*ri-> + callback_retr_date) + (ri, from, to, + ri->callback_arg); + if (rc) + break; + continue; + } + + if (lb_is_field(c, PARTICIPANT_FIELD)) + { + char *pp, *qq;; + if (ri-> + callback_retr_participants + == NULL) + continue; + + pp=lb_field_value_buf(c); + + if (!pp) + { + rc= -1; + break; + } + + if ((qq=strchr(pp, ' ')) != 0) + *qq++=0; + + rc=(*ri-> + callback_retr_participants) + (ri, pp, qq, + ri->callback_arg); + free(pp); + if (rc) + break; + continue; + } + } + + if (rc) + { + free(filename); + break; + } + + if (ri->callback_headers_func == NULL && + ri->callback_rfc822_func == NULL) + { + free(filename); + continue; + } + + if (ri->callback_begin_func) + { + rc= (*ri->callback_begin_func) + (ri, ri->callback_arg); + if (rc) + { + free(filename); + break; + } + } + + if (ri->callback_rfc822_func) + rc=retrrfc822(pd, ri, filename); + else if (ri->callback_headers_func) + rc=retrheaders(pd, ri, filename); + + if (rc == 0 && ri->callback_end_func) + rc= (*ri->callback_end_func) + (ri, ri->callback_arg); + + free(filename); + if (rc) + break; + } + lb_destroy(&pi); + } + fclose(fp); + } + else if (fp) + fclose(fp); + return (rc); +} + +static int retrheaders(struct PCPdir *pd, struct PCP_retr *ri, + const char *filename) +{ + FILE *fp=fopen(filename, "r"); + struct rfc822hdr h; + int rc=0; + + if (!fp) + return (errno == ENOENT ? 0:-1); + + rfc822hdr_init(&h, 8192); + + while (rfc822hdr_read(&h, fp, NULL, 0) == 0) + { + if ((rc= (*ri->callback_headers_func)(ri, h.header, + h.value, + ri->callback_arg)) != 0) + break; + } + if (rc == 0 && ferror(fp)) + rc= -1; + rfc822hdr_free(&h); + fclose(fp); + return (0); +} + +static int retrrfc822(struct PCPdir *pd, struct PCP_retr *ri, + const char *filename) +{ + int fd=open(filename, O_RDONLY); + char buf[BUFSIZ]; + int n; + int rc=0; + + if (fd < 0) + return (errno == ENOENT ? 0:-1); + + while ((n=read(fd, buf, sizeof(buf))) > 0) + if ((rc= (*ri->callback_rfc822_func)(ri, buf, n, + ri->callback_arg)) != 0) + break; + if (rc == 0 && n < 0) + rc= -1; + close(fd); + return (rc); +} + +static int setacl(struct PCPdir *pd, const char *who, int flags) +{ + char *dotlock; + FILE *fp; + FILE *nfp; + int rc; + char *aclname; + char *newaclname; + char buf[1024]; + + if (strchr(who, '\r') || strchr(who, '\n') || strlen(who) > 512) + { + errno=EINVAL; + return (-1); + } + + aclname=malloc(strlen(pd->dirname)+sizeof("/acl")); + + if (!aclname) + return (-1); + + newaclname=malloc(strlen(pd->dirname)+sizeof("/acl.new")); + + if (!newaclname) + { + free(aclname); + return (-1); + } + + dotlock=acquire_dotlock(pd); + + if (!dotlock) + { + free(newaclname); + free(aclname); + return (-1); + } + + strcat(strcpy(aclname, pd->dirname), "/acl"); + strcat(strcpy(newaclname, pd->dirname), "/acl.new"); + + rc= -1; + nfp=fopen(newaclname, "w"); + if (nfp) + { + int l=strlen(who); + + fp=fopen(aclname, "r"); + if (fp) + { + while (fgets(buf, sizeof(buf), fp)) + { + if (strncmp(buf, who, l) == 0 && + isspace((int)(unsigned char)buf[l])) + continue; + fprintf(nfp, "%s", buf); + } + fclose(fp); + } + if (flags) + fprintf(nfp, "%s\t%d\n", who, flags); + rc=0; + if (fflush(nfp) || ferror(nfp)) + rc= -1; + if (fclose(nfp) || rename(newaclname, aclname)) + rc= -1; + } + release_dotlock(pd, dotlock); + free(newaclname); + free(aclname); + return (rc); +} + +static int listacl(struct PCPdir *pd, int (*func)(const char *, int, void *), + void *arg) +{ + char *aclname; + char buf[1024]; + FILE *fp; + + aclname=malloc(strlen(pd->dirname)+sizeof("/acl")); + + if (!aclname) + return (-1); + + strcat(strcpy(aclname, pd->dirname), "/acl"); + + fp=fopen(aclname, "r"); + free(aclname); + + if (fp) + { + while (fgets(buf, sizeof(buf), fp)) + { + char *p; + int rc; + + for (p=buf; *p; p++) + if (isspace((int)(unsigned char)*p)) + { + *p++=0; + break; + } + + rc= (*func)(buf, atoi(p), arg); + if (rc) + { + fclose(fp); + return (rc); + } + } + fclose(fp); + } + return (0); +} |
