summaryrefslogtreecommitdiffstats
path: root/pcp/pcpd.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /pcp/pcpd.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2
Initial checkin
Imported from subversion report, converted to git. Updated all paths in scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'pcp/pcpd.c')
-rw-r--r--pcp/pcpd.c2676
1 files changed, 2676 insertions, 0 deletions
diff --git a/pcp/pcpd.c b/pcp/pcpd.c
new file mode 100644
index 0000000..8f505fc
--- /dev/null
+++ b/pcp/pcpd.c
@@ -0,0 +1,2676 @@
+/*
+** Copyright 2001-2006 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+#include "config.h"
+#include "pcp.h"
+#include "pcpdtimer.h"
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include "liblock/config.h"
+#include "liblock/liblock.h"
+#include "numlib/numlib.h"
+#include "maildir/maildircache.h"
+#include "pcpdauth.h"
+#include "pcpdauthtoken.h"
+#include "calendardir.h"
+
+PCP_STRERROR
+
+#define exit(_a_) _exit(_a_)
+
+static char *userid;
+static char *proxy_userid;
+static char *deleted_eventid;
+static struct PCP_new_eventid *new_eventid;
+static int conflict_flag;
+static int force_flag;
+static int notbooked;
+
+static struct PCP_commit new_commit;
+static struct PCP_event_time *new_commit_times;
+static struct PCP_event_participant *new_commit_participants;
+static char *new_commit_participants_buf;
+
+static struct pcpdtimer rebook_timeout;
+
+static int termsig;
+
+static int need_rset;
+static char *input_buffer=NULL;
+static size_t input_buffer_len=0;
+static size_t input_line_len;
+static time_t prev_time;
+static char *inp_ptr;
+static int inp_left;
+
+static void inactive(struct PCP *p, void *dummy)
+{
+ termsig=1;
+}
+
+static int inputchar(struct PCP *pcp)
+{
+ int c;
+
+ while (inp_left == 0)
+ {
+ time_t new_time;
+ struct timeval tv, *tvptr;
+ fd_set fds;
+
+ time(&new_time);
+
+ /* Trigger any needed functions */
+
+ while (first_timer &&
+ (new_time < prev_time - 30 ||
+ new_time >= first_timer->alarm))
+ {
+ void (*func)(struct PCP *, void *)
+ =first_timer->handler;
+ void *arg=first_timer->voidarg;
+
+ pcpdtimer_triggered(first_timer);
+ (*func)(pcp, arg);
+ }
+
+ if (termsig)
+ return (EOF);
+
+ if (first_timer)
+ {
+ tvptr= &tv;
+ tv.tv_sec=first_timer->alarm-new_time;
+ tv.tv_usec=0;
+ }
+ else
+ tvptr=NULL;
+
+ /*
+ ** Read more input. piggy-back after the
+ ** input buffer :-)
+ */
+
+ if (input_line_len + BUFSIZ >= input_buffer_len)
+ {
+ size_t n=input_line_len + BUFSIZ;
+ char *p=realloc(input_buffer, n);
+
+ if (!p)
+ {
+ perror("realloc");
+ exit(1);
+ }
+ input_buffer=p;
+ input_buffer_len=n;
+ }
+
+ inp_ptr=input_buffer + input_line_len;
+
+ FD_ZERO(&fds);
+ FD_SET(0, &fds);
+
+ if (fflush(stdout) || ferror(stdout))
+ {
+ perror("write");
+ termsig=1;
+ return (EOF);
+ }
+
+ if (select(1, &fds, NULL, NULL, tvptr) < 0)
+ {
+ if (termsig)
+ return (EOF);
+
+ if (errno != EINTR)
+ {
+ perror("select");
+ return (EOF);
+ }
+ inp_left=0;
+ continue;
+ }
+
+ if (!FD_ISSET(0, &fds))
+ continue;
+
+ inp_left=read(0, inp_ptr, BUFSIZ);
+
+ if (termsig || inp_left == 0)
+ {
+ termsig=1;
+ return (EOF);
+ }
+
+ if (inp_left < 0)
+ {
+ perror("read");
+ return (EOF);
+ }
+ }
+
+ c= *inp_ptr++;
+ --inp_left;
+ return ((int)(unsigned char)c);
+}
+
+/* --------------------------------------------------------------- */
+
+struct PCP *open_calendar(const char *p)
+{
+ struct PCP *pcp;
+ struct passwd *pw=getpwuid(getuid());
+ const char *cp;
+
+ if (!pw)
+ {
+ perror("getpwuid");
+ exit(1);
+ }
+
+ userid=strdup(pw->pw_name);
+
+ if (p && *p)
+ {
+ if (chdir(p))
+ {
+ perror(p);
+ exit(1);
+ }
+ }
+ else if ((cp=getenv("PCPDIR")) != NULL && *cp)
+ {
+ if (chdir(cp))
+ {
+ perror(cp);
+ exit(1);
+ }
+ }
+
+ pcp=pcp_open_dir(".", userid);
+
+ if (pcp && pcp_cleanup(pcp))
+ {
+ pcp_close(pcp);
+ pcp=NULL;
+ }
+
+ if (!pcp)
+ {
+ perror("pcp_open_dir");
+ exit(1);
+ }
+ return (pcp);
+}
+
+/* --------------------------------------------------------------- */
+
+static void error(int n)
+{
+ const char *p;
+
+ switch (n) {
+ case PCP_ERR_EVENTNOTFOUND:
+ printf("504 Event not found\n");
+ return;
+ case PCP_ERR_EVENTLOCKED:
+ printf("506 This event is temporarily locked\n");
+ return;
+ }
+
+ p=pcp_strerror(n);
+
+ printf("500 %s\n", p ? p:strerror(errno));
+}
+
+/* --------------------------------------------------------------- */
+
+struct proxy_list {
+ struct proxy_list *next;
+ char *userid;
+ char *old_event_id;
+ struct PCP *proxy;
+ struct PCP_new_eventid *newevent;
+ int flags;
+
+#define PROXY_NEW 1
+#define PROXY_IGNORE 2
+
+} ;
+
+struct proxy_list *proxy_list=NULL;
+
+static void proxy_list_rset()
+{
+ struct proxy_list *p;
+
+ while ((p=proxy_list) != NULL)
+ {
+ proxy_list=p->next;
+ if (p->newevent)
+ pcp_destroy_eventid(p->proxy, p->newevent);
+ pcp_close(p->proxy);
+ free(p->userid);
+ if (p->old_event_id)
+ free(p->old_event_id);
+ proxy_list=p->next;
+ free(p);
+ }
+}
+
+/* Compare two e-mail addresses */
+
+static int addrcmp(const char *a, const char *b)
+{
+ char *aa=NULL;
+ const char *h=auth_myhostname();
+ int rc;
+
+ if (!h)
+ return (1);
+
+ if (strchr(a, '@') == NULL)
+ {
+ aa=malloc(strlen(a)+strlen(h)+2);
+
+ if (!aa)
+ {
+ fprintf(stderr, "NOTICE: malloc: out of memory.\n");
+ return (1);
+ }
+ strcat(strcat(strcpy(aa, a), "@"), h);
+ rc=addrcmp(aa, b);
+ free(aa);
+ return (rc);
+ }
+
+ if (strchr(b, '@') == NULL)
+ {
+ aa=malloc(strlen(b)+strlen(h)+2);
+
+ if (!aa)
+ {
+ fprintf(stderr, "NOTICE: malloc: out of memory.\n");
+ return (1);
+ }
+ strcat(strcat(strcpy(aa, b), "@"), h);
+ rc=addrcmp(a, aa);
+ free(aa);
+ return (rc);
+ }
+
+ rc=strcasecmp(a, b);
+ return (rc);
+}
+
+static struct proxy_list *proxy(const char *proxy_userid, char **errmsg)
+{
+ struct proxy_list *p;
+
+ if (errmsg)
+ *errmsg=0;
+
+ for (p=proxy_list; p; p=p->next)
+ {
+ if (addrcmp(proxy_userid, p->userid) == 0)
+ return (p);
+ }
+
+ p=malloc(sizeof(struct proxy_list));
+ if (!p)
+ return (NULL);
+ memset(p, 0, sizeof(*p));
+
+ if ((p->userid=strdup(proxy_userid)) == NULL)
+ {
+ free(p);
+ return (NULL);
+ }
+
+ if ((p->proxy=pcp_find_proxy(proxy_userid, NULL, errmsg)) == NULL ||
+ pcp_set_proxy(p->proxy, userid))
+ {
+ if (p->proxy)
+ pcp_close(p->proxy);
+ free(p->userid);
+ free(p);
+ return (NULL);
+ }
+
+ p->next=proxy_list;
+ proxy_list=p;
+ return (p);
+}
+
+/* --------------------------------------------------------------- */
+
+static void rset(struct PCP *pcp)
+{
+ pcpdtimer_triggered(&rebook_timeout);
+ if (new_eventid)
+ pcp_destroy_eventid(pcp, new_eventid);
+ if (new_commit_times)
+ free(new_commit_times);
+ new_commit_times=NULL;
+ if (new_commit_participants)
+ free(new_commit_participants);
+ if (new_commit_participants_buf)
+ free(new_commit_participants_buf);
+ new_commit_participants=NULL;
+ new_commit_participants_buf=NULL;
+ new_commit.event_times=NULL;
+ new_commit.n_event_times=0;
+ new_eventid=NULL;
+ if (deleted_eventid)
+ free(deleted_eventid);
+ deleted_eventid=NULL;
+ notbooked=0;
+ proxy_list_rset();
+}
+
+struct readnewevent_s {
+ FILE *tmpfile;
+ int seeneol;
+ int seendot;
+ int seeneof;
+ int sentprompt;
+ time_t last_noop_time;
+
+ int cnt;
+ struct PCP *pcp;
+ struct pcpdtimer inactivity_timeout;
+} ;
+
+static int readnewevent_callback(char *p, int n, void *vp)
+{
+ struct readnewevent_s *rne=(struct readnewevent_s *)vp;
+ int cnt=0;
+
+ if (!rne->sentprompt)
+ {
+ rne->sentprompt=1;
+ printf("300 Send event text, terminate by a line with a single dot.\n");
+ }
+
+ while (!rne->seeneof && n)
+ {
+ int c=inputchar(rne->pcp);
+
+ if (c == EOF)
+ {
+ rne->seeneof=1;
+ errno=ETIMEDOUT;
+ return (-1);
+ }
+
+ if (c == '\r')
+ continue;
+
+ if (c == '\n')
+ {
+ if (rne->seendot)
+ rne->seeneof=1;
+ rne->seendot=0;
+ rne->seeneol=1;
+ if (rne->seeneof)
+ continue;
+ }
+ else
+ {
+ rne->seendot= c == '.' && rne->seeneol;
+ rne->seeneol=0;
+ if (rne->seendot)
+ continue;
+ }
+ putc(c, rne->tmpfile);
+ ++cnt;
+ *p++ = c;
+ --n;
+
+ if (++rne->cnt >= 8192)
+ {
+ time_t t;
+
+ time(&t);
+ if (t >= rne->last_noop_time+300) /* Don't timeout */
+ {
+ struct proxy_list *p;
+ rne->last_noop_time=t;
+ for (p=proxy_list; p; p=p->next)
+ pcp_noop(p->proxy);
+ }
+
+ pcpdtimer_install(&rne->inactivity_timeout, 300);
+ rne->cnt=0;
+ }
+ }
+ return (cnt);
+}
+
+static void proxy_error(const char *n, const char *msg)
+{
+ while (*msg)
+ {
+ printf("500-%s - ", n);
+ while (*msg)
+ {
+ if (*msg == '\n')
+ {
+ ++msg;
+ break;
+ }
+ if (*msg != '\r')
+ putchar( *msg);
+ ++msg;
+ }
+ printf("\n");
+ }
+
+}
+
+static int mkparticipants(struct PCP_save_event *se)
+{
+ struct proxy_list *p;
+ unsigned cnt;
+ size_t l=0;
+
+ if (new_commit_participants)
+ free(new_commit_participants);
+ if (new_commit_participants_buf)
+ free(new_commit_participants_buf);
+ new_commit_participants=NULL;
+ new_commit_participants_buf=NULL;
+
+ se->event_participants=NULL;
+ se->n_event_participants=0;
+
+ for (cnt=0, p=proxy_list; p; p=p->next)
+ {
+ if (!p->newevent)
+ continue;
+
+ if (p->flags & PROXY_IGNORE)
+ continue;
+
+ ++cnt;
+ l += strlen(p->userid)+1;
+
+ if (p->newevent->eventid)
+ l += strlen(p->newevent->eventid)+1;
+ }
+ if (cnt == 0)
+ return (0);
+
+ if ((new_commit_participants_buf=malloc(l)) == NULL)
+ return (-1);
+ if ((new_commit_participants
+ =calloc(cnt, sizeof(struct PCP_event_participant))) == NULL)
+ return (-1);
+
+ l=0;
+
+ for (cnt=0, p=proxy_list; p; p=p->next)
+ {
+ if (!p->newevent)
+ continue;
+
+ if (p->flags & PROXY_IGNORE)
+ continue;
+
+ new_commit_participants[cnt].address=
+ strcpy(new_commit_participants_buf+l, p->userid);
+ l += strlen(p->userid)+1;
+
+ if (p->newevent->eventid)
+ {
+ new_commit_participants[cnt].eventid=
+ strcpy(new_commit_participants_buf+l,
+ p->newevent->eventid);
+ l += strlen(p->newevent->eventid)+1;
+ }
+ ++cnt;
+ }
+ se->event_participants=new_commit_participants;
+ se->n_event_participants=cnt;
+ return (0);
+}
+
+static struct PCP_new_eventid *readnewevent(struct PCP *pcp)
+{
+ struct PCP_save_event se;
+ struct readnewevent_s rne;
+ struct PCP_new_eventid *ne;
+ const char *cp;
+ struct proxy_list *p;
+ int first_save=1;
+
+ memset(&rne, 0, sizeof(rne));
+ if (!deleted_eventid)
+ proxy_list_rset();
+
+ /* Open new proxy connections */
+
+ while ((cp=strtok(NULL, " ")) != NULL)
+ {
+ char *errmsg, *q;
+ char *n=strdup(cp);
+ struct proxy_list *pcp;
+
+ if (!n)
+ {
+ fprintf(stderr, "ALERT: Out of memory.\n");
+ exit(1);
+ }
+
+ if (proxy_userid)
+ {
+ printf("500-Cannot create proxy in proxy mode.\n");
+ free(n);
+ errno=EIO;
+ return (NULL);
+ }
+
+ strcpy(n, cp);
+ pcp=proxy(n, &errmsg);
+
+ if (pcp)
+ {
+ pcp->flags |= PROXY_NEW;
+ free(n);
+ continue;
+ }
+
+ if (force_flag)
+ {
+ pcp->flags |= PROXY_IGNORE;
+ free(n);
+ continue;
+ }
+
+ while (errmsg && (q=strchr(errmsg, '\n')) != 0)
+ *q='/';
+ printf("500-%s: %s\n", n, errmsg ? errmsg:"Failed to create a proxy connection.");
+ free(n);
+ proxy_list_rset();
+ return (NULL);
+ }
+
+ memset(&se, 0, sizeof(se));
+ if ((rne.tmpfile=tmpfile()) == NULL)
+ return (NULL);
+ time(&rne.last_noop_time);
+
+ rne.seeneol=1;
+ rne.seendot=0;
+ rne.seeneof=0;
+ rne.cnt=0;
+ rne.pcp=pcp;
+ pcpdtimer_init(&rne.inactivity_timeout);
+ rne.inactivity_timeout.handler=&inactive;
+ pcpdtimer_install(&rne.inactivity_timeout, 300);
+
+ for (p=proxy_list; p; p=p->next)
+ {
+ struct PCP_save_event se;
+
+ if ( !(p->flags & PROXY_NEW))
+ continue;
+
+ if (fseek(rne.tmpfile, 0L, SEEK_SET) < 0
+ || lseek(fileno(rne.tmpfile), 0L, SEEK_SET) < 0)
+ {
+ int save_errno=errno;
+ proxy_list_rset();
+ pcpdtimer_triggered(&rne.inactivity_timeout);
+ fclose(rne.tmpfile);
+ errno=save_errno;
+ return (NULL);
+ }
+
+ memset(&se, 0, sizeof(se));
+ if (first_save)
+ {
+ se.write_event_func_misc_ptr= &rne;
+ se.write_event_func=readnewevent_callback;
+ }
+ else
+ se.write_event_fd=fileno(rne.tmpfile);
+
+ if ((p->newevent=pcp_new_eventid(p->proxy,
+ p->old_event_id,
+ &se)) == NULL)
+ {
+ pcpdtimer_triggered(&rne.inactivity_timeout);
+
+ if (force_flag)
+ {
+ /* Force it through */
+
+ p->flags &= ~PROXY_NEW;
+ p->flags |= PROXY_IGNORE;
+ continue;
+ }
+
+ proxy_error(p->userid,
+ pcp_errmsg(p->proxy));
+ proxy_list_rset();
+ fclose(rne.tmpfile);
+ errno=EIO;
+ return (NULL);
+ }
+ if (first_save)
+ pcpdtimer_triggered(&rne.inactivity_timeout);
+ first_save=0;
+ }
+
+
+ if (first_save)
+ {
+ se.write_event_func_misc_ptr= &rne;
+ se.write_event_func=readnewevent_callback;
+ }
+ else
+ se.write_event_fd=fileno(rne.tmpfile);
+
+ if (mkparticipants(&se) || fseek(rne.tmpfile, 0L, SEEK_SET) < 0
+ || lseek(fileno(rne.tmpfile), 0L, SEEK_SET) < 0)
+ {
+ int save_errno=errno;
+
+ proxy_list_rset();
+ fclose(rne.tmpfile);
+ errno=save_errno;
+ return (NULL);
+ }
+
+ if ((ne=pcp_new_eventid(pcp, deleted_eventid, &se)) == NULL)
+ {
+ while (!rne.seeneof)
+ {
+ char buf[512];
+
+ readnewevent_callback(buf, sizeof(buf), &se);
+ }
+ }
+ pcpdtimer_triggered(&rne.inactivity_timeout);
+ if (first_save)
+ {
+ if (fflush(rne.tmpfile) || ferror(rne.tmpfile))
+ {
+ int save_errno=errno;
+
+ proxy_list_rset();
+ fclose(rne.tmpfile);
+ errno=save_errno;
+ return (NULL);
+ }
+ }
+
+ notbooked=1;
+ fclose(rne.tmpfile);
+ return (ne);
+}
+
+struct book_time_list {
+ struct book_time_list *next;
+ struct PCP_event_time times;
+} ;
+
+struct report_conflict_info {
+ struct report_conflict_info *next;
+ char *conflict_eventid;
+ char *conflict_addr;
+ time_t conflict_start;
+ time_t conflict_end;
+} ;
+
+struct extra_conflict_info {
+ struct report_conflict_info **conflict_list;
+ const char *proxy_addr;
+} ;
+
+static int do_report_conflict(const char *e, time_t start, time_t end,
+ const char *addr, void *vp)
+{
+ struct extra_conflict_info *eci=(struct extra_conflict_info *)vp;
+ struct report_conflict_info **p=eci->conflict_list;
+ struct report_conflict_info *q=malloc(sizeof(**p));
+
+ if (!q)
+ return (-1);
+
+ if (eci->proxy_addr)
+ addr=eci->proxy_addr;
+
+ if ((q->conflict_eventid=strdup(e)) == NULL)
+ {
+ free(q);
+ return (-1);
+ }
+
+ if ((q->conflict_addr=strdup(addr)) == NULL)
+ {
+ free(q->conflict_eventid);
+ free(q);
+ return (-1);
+ }
+
+ while (*p)
+ {
+ p= &(*p)->next;
+ }
+
+ *p=q;
+ q->next=0;
+ q->conflict_start=start;
+ q->conflict_end=end;
+ return (0);
+}
+
+static void report_conflict_destroy(struct report_conflict_info *p)
+{
+ struct report_conflict_info *q;
+
+ while ((q=p) != 0)
+ {
+ p=q->next;
+ free(q->conflict_addr);
+ free(q->conflict_eventid);
+ free(q);
+ }
+}
+
+static void rebook(struct PCP *, void *);
+
+static void rebook_installtimeout(struct PCP *pcp)
+{
+ rebook_timeout.handler=rebook;
+ pcpdtimer_install(&rebook_timeout, 15 * 60);
+}
+
+static void rebook(struct PCP *pcp, void *vp)
+{
+ struct proxy_list *p;
+
+ pcp_noop(pcp);
+
+ for (p=proxy_list; p; p=p->next)
+ pcp_noop(p->proxy);
+ rebook_installtimeout(pcp);
+}
+
+static void dobook(struct PCP *pcp)
+{
+ struct book_time_list *list, **last;
+ unsigned n=0;
+ const char *p;
+ int rc=0;
+ struct PCP_event_time *new_times=NULL;
+ struct report_conflict_info *conflict_list;
+
+ list=NULL;
+ last= &list;
+
+ while ((p=strtok(NULL, " ")) != NULL)
+ {
+ char from_s[14+1];
+ char to_s[14+1];
+
+ if (strlen(p) != 14 + 14 + 1 || p[14] != '-')
+ {
+ printf("500 Syntax error.\n");
+ rc= -1;
+ break;
+ }
+
+ memcpy(from_s, p, 14);
+ memcpy(to_s, p+15, 14);
+ from_s[14]=0;
+ to_s[14]=0;
+
+ if ( (*last=malloc(sizeof(**last))) == NULL)
+ {
+ printf("500 %s\n", strerror(errno));
+ rc= -1;
+ break;
+ }
+
+ (*last)->next=NULL;
+ if (((*last)->times.start=pcp_gmtime_s(from_s)) == 0 ||
+ ((*last)->times.end=pcp_gmtime_s(to_s)) == 0)
+ {
+ printf("500 Invalid date/time\n");
+ rc= -1;
+ break;
+ }
+
+ last=&(*last)->next;
+ ++n;
+ }
+
+ if (rc == 0 && n == 0)
+ {
+ printf("500 Syntax error\n");
+ rc= -1;
+ }
+
+ if (rc == 0 && (new_times=calloc(n, sizeof(struct PCP_event_time)))
+ == NULL)
+ {
+ printf("500 %s\n", strerror(errno));
+ rc= -1;
+ }
+
+ if (rc == 0)
+ {
+ struct book_time_list *l;
+ const struct PCP_event_time *save_times;
+ unsigned n_save_times;
+ struct proxy_list *pr;
+ int is_conflict;
+ struct extra_conflict_info eci;
+
+ n=0;
+ for (l=list; l; l=l->next)
+ new_times[n++]= l->times;
+
+ save_times=new_commit.event_times;
+ n_save_times=new_commit.n_event_times;
+
+ new_commit.event_times=new_times;
+ new_commit.n_event_times=n;
+
+ eci.conflict_list= &conflict_list;
+
+ conflict_list=NULL;
+ new_commit.add_conflict_callback=do_report_conflict;
+ new_commit.add_conflict_callback_ptr= &eci;
+ new_commit.flags=
+ (conflict_flag ? PCP_OK_CONFLICT:0) |
+ (force_flag ? PCP_OK_PROXY_ERRORS:0);
+
+ notbooked=1;
+ is_conflict=0;
+
+ for (pr=proxy_list; pr; pr=pr->next)
+ {
+ eci.proxy_addr=pr->userid;
+
+ if (pr->flags & PROXY_NEW)
+ if (pcp_book(pr->proxy,
+ pr->newevent, &new_commit))
+ is_conflict=1;
+ }
+
+ if (proxy_userid)
+ new_commit.flags |= PCP_BYPROXY;
+
+ eci.proxy_addr=NULL;
+ if (pcp_book(pcp, new_eventid, &new_commit))
+ is_conflict=1;
+
+ if (is_conflict)
+ {
+ new_commit.event_times=save_times;
+ new_commit.n_event_times=n_save_times;
+ free(new_times);
+
+ if (conflict_list)
+ {
+ struct report_conflict_info *p;
+
+ for (p=conflict_list; p; p=p->next)
+ {
+ char from_buf[15];
+ char to_buf[15];
+
+ pcp_gmtimestr(p->conflict_start,
+ from_buf);
+ pcp_gmtimestr(p->conflict_end, to_buf);
+
+ printf("403%c%s %s %s %s conflicts.\n",
+ p->next ? '-':' ',
+ p->conflict_addr,
+ from_buf,
+ to_buf,
+ p->conflict_eventid);
+ }
+ }
+ else
+ {
+ error(new_commit.errcode);
+ }
+ report_conflict_destroy(conflict_list);
+ }
+ else
+ {
+ if (new_commit_times)
+ free(new_commit_times);
+ new_commit_times=new_times;
+ printf("200 Ok\n");
+ notbooked=0;
+ }
+ rebook_installtimeout(pcp);
+ }
+
+ while (list)
+ {
+ struct book_time_list *l=list;
+
+ list=l->next;
+ free(l);
+ }
+
+}
+
+/* ------- LIST ------- */
+
+struct list_struct {
+ struct PCP_list_all list_info;
+ struct list_item *event_list, **last_event;
+} ;
+
+struct list_item {
+ struct list_item *next;
+ char *event_id;
+ time_t start;
+ time_t end;
+} ;
+
+static int list_callback(struct PCP_list_all *p, void *vp)
+{
+ struct list_struct *ls=(struct list_struct *)vp;
+ char *s=strdup(p->event_id);
+
+ if (!s)
+ return (-1);
+
+ if ( ((*ls->last_event)=(struct list_item *)
+ malloc(sizeof(struct list_item))) == NULL)
+ {
+ free(s);
+ return (-1);
+ }
+
+ (*ls->last_event)->event_id=s;
+ (*ls->last_event)->start=p->event_from;
+ (*ls->last_event)->end=p->event_to;
+ (*ls->last_event)->next=NULL;
+
+ ls->last_event= & (*ls->last_event)->next;
+ return (0);
+}
+
+static int list(struct PCP *pcp)
+{
+ struct list_struct ls;
+ const char *q;
+ struct list_item *e;
+
+ memset(&ls, 0, sizeof(ls));
+ ls.list_info.callback_arg= &ls;
+ ls.list_info.callback_func= &list_callback;
+ ls.last_event= &ls.event_list;
+
+ while ((q=strtok(NULL, " ")) != NULL)
+ {
+ if (strcasecmp(q, "FROM") == 0)
+ {
+ char buf[15];
+
+ q=strtok(NULL, " ");
+ if (!q)
+ return (-1);
+ if (ls.list_info.list_from ||
+ ls.list_info.list_to)
+ return (-1);
+
+ if (*q != '-')
+ {
+ if (strlen(q) < 14)
+ return (-1);
+ memcpy(buf, q, 14);
+ buf[14]=0;
+ q += 14;
+ if ((ls.list_info.list_from
+ =pcp_gmtime_s(buf)) == 0)
+ return (-1);
+ }
+ if (*q)
+ {
+ if (*q++ != '-' || strlen(q) != 14)
+ return (-1);
+ memcpy(buf, q, 14);
+ buf[14]=0;
+ q += 14;
+ if ((ls.list_info.list_to
+ =pcp_gmtime_s(buf)) == 0)
+ return (-1);
+ }
+ }
+ }
+
+ if (pcp_list_all(pcp, &ls.list_info))
+ {
+ error(0);
+ }
+ else
+ {
+ for (e=ls.event_list; e; e=e->next)
+ {
+ char from_buf[15], to_buf[15];
+
+ pcp_gmtimestr(e->start, from_buf);
+ pcp_gmtimestr(e->end, to_buf);
+
+ printf("105%c%s %s %s event found.\n",
+ e->next ? '-':' ',
+ e->event_id,
+ from_buf,
+ to_buf);
+ }
+
+ if (ls.event_list == NULL)
+ printf("504 event-id not found.\n");
+ }
+
+ while ((e=ls.event_list) != NULL)
+ {
+ ls.event_list=e->next;
+ free(e->event_id);
+ free(e);
+ }
+ return (0);
+}
+
+/* ------ RETR ------------ */
+
+struct retrinfo {
+ int status;
+ struct retrinfo_event_list *event_list;
+ const char **event_list_array;
+ int text_flag;
+ int text_seen_eol;
+} ;
+
+struct retrinfo_event_list {
+ struct retrinfo_event_list *next;
+ char *event_id;
+} ;
+
+static int callback_retr_date(struct PCP_retr *r,
+ time_t from, time_t to, void *vp)
+{
+ char from_buf[15], to_buf[15];
+
+ pcp_gmtimestr(from, from_buf);
+ pcp_gmtimestr(to, to_buf);
+
+ printf("105 %s %s %s event found\n",
+ r->event_id, from_buf, to_buf);
+ return (0);
+}
+
+static int callback_retr_participants(struct PCP_retr *r,
+ const char *n, const char *id,
+ void *vp)
+{
+ printf("106 %s %s is a participant\n", r->event_id, n);
+ return (0);
+}
+
+static int callback_retr_status(struct PCP_retr *r,
+ int status, void *vp)
+{
+ char status_buf[256];
+ const char *comma="";
+
+ status_buf[0]=0;
+
+ if (status & LIST_CANCELLED)
+ {
+ strcat(strcat(status_buf, comma), "CANCELLED");
+ comma=",";
+ }
+
+ if (status & LIST_BOOKED)
+ {
+ strcat(strcat(status_buf, comma), "BOOKED");
+ comma=",";
+ }
+
+ if (status & LIST_PROXY)
+ {
+ strcat(strcat(status_buf, comma), "PROXY");
+ comma=",";
+ }
+ if (status_buf[0])
+ printf("110 %s %s\n", r->event_id, status_buf);
+ return (0);
+}
+
+static int callback_retr_begin(struct PCP_retr *r, void *vp)
+{
+ struct retrinfo *ri=(struct retrinfo *)vp;
+
+ ri->text_flag=0;
+ ri->text_seen_eol=1;
+
+ return (0);
+}
+
+static int callback_retr_headers(struct PCP_retr *r,
+ const char *h,
+ const char *v,
+ void *vp)
+{
+ struct retrinfo *ri=(struct retrinfo *)vp;
+ int lastchar;
+
+ if (!ri->text_flag)
+ {
+ ri->text_flag=1;
+ printf("107 %s follows\n", r->event_id);
+ }
+
+ if (*h == '.')
+ putchar('.');
+ printf("%s: ", h);
+
+ while (*v && isspace((int)(unsigned char)*v))
+ ++v;
+
+ lastchar=' ';
+
+ while (*v)
+ {
+ if ((int)(unsigned char)*v >= ' ' ||
+ *v == '\n' || *v == '\t')
+ {
+ putchar(*v);
+ lastchar=*v;
+ }
+ ++v;
+ }
+ if (lastchar != '\n')
+ putchar('\n');
+ return (0);
+}
+
+static int callback_retr_message(struct PCP_retr *r,
+ const char *ptr,
+ int l,
+ void *vp)
+{
+ struct retrinfo *ri=(struct retrinfo *)vp;
+
+ if (!ri->text_flag)
+ {
+ ri->text_flag=1;
+ printf("107 %s follows\n", r->event_id);
+ }
+
+ while (l)
+ {
+ if (ri->text_seen_eol && *ptr == '.')
+ putchar('.');
+ ri->text_seen_eol= *ptr == '\n';
+ putchar(*ptr);
+ ++ptr;
+ --l;
+ }
+ return (0);
+}
+
+static int callback_retr_end(struct PCP_retr *r, void *vp)
+{
+ struct retrinfo *ri=(struct retrinfo *)vp;
+
+ if (ri->text_flag)
+ {
+ if (!ri->text_seen_eol)
+ putchar('\n');
+ printf(".\n");
+ }
+ return (0);
+}
+
+static int retr(struct PCP *pcp)
+{
+ struct PCP_retr r;
+ struct retrinfo ri;
+ const char *q;
+ int n;
+ struct retrinfo_event_list *p;
+
+ memset(&r, 0, sizeof(r));
+ memset(&ri, 0, sizeof(ri));
+
+ r.callback_arg= &ri;
+
+ for (;;)
+ {
+ if ((q=strtok(NULL, " ")) == NULL)
+ return (-1);
+
+ if (strcasecmp(q, "EVENTS") == 0)
+ break;
+
+ if (strcasecmp(q, "TEXT") == 0)
+ {
+ r.callback_begin_func=callback_retr_begin;
+ r.callback_rfc822_func=callback_retr_message;
+ r.callback_end_func=callback_retr_end;
+ continue;
+ }
+
+ if (strcasecmp(q, "HEADERS") == 0)
+ {
+ r.callback_begin_func=callback_retr_begin;
+ r.callback_headers_func=callback_retr_headers;
+ r.callback_end_func=callback_retr_end;
+ continue;
+ }
+
+ if (strcasecmp(q, "DATE") == 0)
+ {
+ r.callback_retr_date=callback_retr_date;
+ continue;
+ }
+
+ if (strcasecmp(q, "ADDR") == 0)
+ {
+ r.callback_retr_participants=
+ callback_retr_participants;
+ continue;
+ }
+
+ if (strcasecmp(q, "STATUS") == 0)
+ {
+ r.callback_retr_status=callback_retr_status;
+ continue;
+ }
+ return (-1);
+ }
+
+ if (r.callback_headers_func && r.callback_rfc822_func)
+ return (-1);
+
+ n=0;
+ while ((q=strtok(NULL, " ")) != NULL)
+ {
+ char *s=strdup(q);
+
+ if (!s || (p=malloc(sizeof(struct retrinfo_event_list)))
+ == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ p->event_id=s;
+ p->next=ri.event_list;
+ ri.event_list=p;
+ ++n;
+ }
+
+ if (ri.event_list == NULL)
+ return (-1);
+
+ if ((ri.event_list_array=malloc((n+1) * sizeof(const char *)))
+ == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+
+ n=0;
+ for (p=ri.event_list; p; p=p->next)
+ ri.event_list_array[n++]=p->event_id;
+ ri.event_list_array[n]=0;
+
+ r.event_id_list=ri.event_list_array;
+
+ if (pcp_retr(pcp, &r))
+ error(r.errcode);
+ else
+ printf("108 RETR complete\n");
+
+ while ((p=ri.event_list) != 0)
+ {
+ ri.event_list=p->next;
+ free(p->event_id);
+ free(p);
+ }
+ free(ri.event_list_array);
+ return (0);
+}
+
+/* ---- UNCANCEL ---- */
+
+struct uncancel_list {
+ struct uncancel_list *next;
+ char *id;
+ time_t from, to;
+ char *addr;
+} ;
+
+static int uncancel_callback(const char *event, time_t from, time_t to,
+ const char *addr, void *vp)
+{
+ struct uncancel_list ***tail_ptr=(struct uncancel_list ***)vp;
+ struct uncancel_list **tail= *tail_ptr;
+ char *s=strdup(event);
+ char *a=strdup(addr);
+ struct uncancel_list *newptr;
+
+ if (!s || !a || (newptr=(struct uncancel_list *)
+ malloc(sizeof(struct uncancel_list))) == NULL)
+ {
+ if (a) free(a);
+ if (s) free(s);
+ return (-1);
+ }
+
+ newptr->addr=a;
+ newptr->id=s;
+ newptr->from=from;
+ newptr->to=to;
+
+ *tail=newptr;
+ newptr->next=0;
+
+ *tail_ptr= &(*tail)->next;
+ return (0);
+}
+
+/* --------- ACL LIST ---------- */
+
+struct acl_list {
+ struct acl_list *next;
+ char *who;
+ int flags;
+} ;
+
+static int list_acl_callback(const char *who, int flags, void *dummy)
+{
+ struct acl_list **p=(struct acl_list **)dummy;
+ struct acl_list *q=malloc(sizeof(struct acl_list));
+
+ if (!q)
+ return (-1);
+ if ((q->who=strdup(who)) == NULL)
+ {
+ free(q);
+ return (-1);
+ }
+ q->flags=flags;
+ q->next= *p;
+ *p=q;
+ return (0);
+}
+
+static void listacls(struct PCP *pcp)
+{
+ char buf[1024];
+ struct acl_list *list=NULL;
+ struct acl_list *p;
+
+ if (pcp_list_acl(pcp, list_acl_callback, &list) == 0)
+ {
+ if (list == NULL)
+ printf("203 Empty ACL\n");
+ else
+ {
+ for (p=list; p; p=p->next)
+ {
+ buf[0]=0;
+ pcp_acl_name(p->flags, buf);
+ printf("103%c%s %s\n",
+ p->next ? '-':' ',
+ p->who, buf);
+ }
+ }
+ }
+ else error(0);
+
+ while ((p=list) != NULL)
+ {
+ list=p->next;
+ free(p->who);
+ free(p);
+ }
+}
+
+static int open_event_participant(struct PCP_retr *r,
+ const char *n, const char *id,
+ void *vp)
+{
+ struct proxy_list *p;
+ char *errmsg;
+
+ if (proxy_userid)
+ {
+ printf("500-Cannot create proxy in proxy mode.\n");
+ errno=EIO;
+ return (-1);
+ }
+
+ p=proxy(n, &errmsg);
+
+ if (p)
+ {
+ if ((p->old_event_id=strdup(id)) == NULL)
+ return (-1);
+ return (0);
+ }
+
+ if (!force_flag)
+ {
+ if (errmsg)
+ {
+ proxy_error(n, errmsg);
+ free(errmsg);
+ }
+ errno=EIO;
+ return (-1);
+ }
+ if (errmsg)
+ free(errmsg);
+
+ p->flags |= PROXY_IGNORE;
+ return (0);
+}
+
+/* ------------------------ */
+
+static int check_acl(int, int);
+
+static int doline(struct PCP *pcp, char *p, int acl_flags)
+{
+ char *q=strtok(p, " ");
+
+ if (!q)
+ {
+ printf("500 Syntax error\n");
+ return (0);
+ }
+
+ if (strcasecmp(q, "QUIT") == 0)
+ {
+ printf("200 Bye.\n");
+ return (-1);
+ }
+
+ if (strcasecmp(q, "NOOP") == 0)
+ {
+ printf("200 Ok.\n");
+ return (0);
+ }
+
+ if (strcasecmp(q, "CAPABILITY") == 0)
+ {
+ printf("100-ACL\n");
+ printf("100 PCP1\n");
+ return (0);
+ }
+
+ if (strcasecmp(q, "LIST") == 0)
+ {
+ if (check_acl(acl_flags, PCP_ACL_LIST))
+ return (0);
+
+ if (list(pcp))
+ printf("500 Syntax error\n");
+ return (0);
+ }
+
+ if (strcasecmp(q, "RETR") == 0)
+ {
+ if (check_acl(acl_flags, PCP_ACL_RETR))
+ return (0);
+
+ if (retr(pcp))
+ printf("500 Syntax error\n");
+ return (0);
+ }
+
+
+ if (strcasecmp(q, "ACL") == 0 && pcp_has_acl(pcp) && !proxy_userid)
+ {
+ q=strtok(NULL, " ");
+ if (q && strcasecmp(q, "SET") == 0)
+ {
+ const char *who=strtok(NULL, " ");
+
+ if (who)
+ {
+ int flags=0;
+
+ while ((q=strtok(NULL, " ")) != 0)
+ flags |= pcp_acl_num(q);
+
+ if (pcp_acl(pcp, who, flags))
+ {
+ error(0);
+ return (0);
+ }
+ printf("200 Ok\n");
+ return (0);
+ }
+ }
+ else if (q && strcasecmp(q, "LIST") == 0)
+ {
+ listacls(pcp);
+ return (0);
+ }
+ }
+
+ if (strcasecmp(q, "RSET") == 0)
+ {
+ conflict_flag=0;
+ force_flag=0;
+ need_rset=0;
+ rset(pcp);
+ printf("200 Ok.\n");
+ return (0);
+ }
+
+ if (need_rset)
+ {
+ printf("500 RSET required - calendar in an unknown state.\n");
+ return (0);
+ }
+
+ if (strcasecmp(q, "DELETE") == 0)
+ {
+ struct PCP_retr r;
+ const char *event_id_list[2];
+
+ char *e=strtok(NULL, " ");
+
+ if (check_acl(acl_flags, PCP_ACL_MODIFY))
+ return (0);
+
+ if (e && deleted_eventid == NULL && new_eventid == NULL)
+ {
+ if ((deleted_eventid=strdup(e)) == NULL)
+ {
+ perror("strdup");
+ exit(1);
+ }
+ proxy_list_rset();
+ memset(&r, 0, sizeof(r));
+ r.callback_retr_participants=open_event_participant;
+ event_id_list[0]=deleted_eventid;
+ event_id_list[1]=NULL;
+ r.event_id_list=event_id_list;
+ if (pcp_retr(pcp, &r))
+ {
+ error(r.errcode);
+ proxy_list_rset();
+ }
+ else
+ printf("200 Ok.\n");
+ return (0);
+ }
+ }
+
+ if (strcasecmp(q, "NEW") == 0 && new_eventid == NULL)
+ {
+ if (check_acl(acl_flags, PCP_ACL_MODIFY))
+ return (0);
+
+ new_eventid=readnewevent(pcp);
+
+ if (new_eventid == NULL)
+ {
+ printf("500 %s\n", strerror(errno));
+ }
+ else
+ printf("109 %s ready to be commited.\n",
+ new_eventid->eventid);
+ return (0);
+ }
+
+ if (strcasecmp(q, "BOOK") == 0 && new_eventid)
+ {
+ dobook(pcp);
+ return (0);
+ }
+
+ if (strcasecmp(q, "CONFLICT") == 0)
+ {
+ q=strtok(NULL, " ");
+ if (q && strcasecmp(q, "ON") == 0)
+ {
+ if (check_acl(acl_flags, PCP_ACL_CONFLICT))
+ return (0);
+
+ conflict_flag=1;
+ }
+ else
+ conflict_flag=0;
+ printf("200 Ok.\n");
+ return (0);
+ }
+
+ if (strcasecmp(q, "FORCE") == 0)
+ {
+ q=strtok(NULL, " ");
+ if (q && strcasecmp(q, "ON") == 0)
+ {
+ force_flag=1;
+ }
+ else
+ force_flag=0;
+ printf("200 Ok.\n");
+ return (0);
+ }
+
+ if (strcasecmp(q, "COMMIT") == 0)
+ {
+ if (notbooked)
+ {
+ printf("500 BOOK required.\n");
+ }
+ else if (new_eventid && new_commit_times)
+ {
+ struct proxy_list *pl;
+
+ new_commit.add_conflict_callback=NULL;
+ new_commit.add_conflict_callback_ptr=NULL;
+
+ new_commit.flags=
+ (conflict_flag ? PCP_OK_CONFLICT:0) |
+ (force_flag ? PCP_OK_PROXY_ERRORS:0);
+
+ for (pl=proxy_list; pl; pl=pl->next)
+ {
+ if (pl->flags & PROXY_IGNORE)
+ continue;
+
+ if (pl->flags & PROXY_NEW)
+ {
+ if (pcp_commit(pl->proxy,
+ pl->newevent,
+ &new_commit))
+ {
+ fprintf(stderr, "CRIT: "
+ "COMMIT failed for PROXY %s\n",
+ pl->userid);
+
+ if (!force_flag)
+ {
+ pl->flags &=
+ ~PROXY_NEW;
+ error(new_commit
+ .errcode);
+ return (0);
+ }
+ }
+ }
+ else if (pl->old_event_id)
+ {
+ struct PCP_delete del;
+
+ memset(&del, 0, sizeof(del));
+
+ del.id=pl->old_event_id;
+
+ if (pcp_delete(pl->proxy, &del))
+ {
+ fprintf(stderr, "CRIT: "
+ "DELETE failed for PROXY %s\n",
+ pl->userid);
+ if (!force_flag)
+ {
+ error(del.errcode);
+ return (0);
+ }
+ pl->flags |= PROXY_IGNORE;
+ }
+ }
+ }
+
+ if (proxy_userid)
+ new_commit.flags |= PCP_BYPROXY;
+
+ if (pcp_commit(pcp, new_eventid, &new_commit))
+ error(new_commit.errcode);
+ else
+ {
+ const char *proxy_name=NULL;
+ const char *proxy_action=NULL;
+
+ for (pl=proxy_list; pl; pl=pl->next)
+ {
+ if (proxy_name)
+ printf("111-%s %s\n",
+ proxy_action,
+ proxy_name);
+
+ proxy_action=
+ !(pl->flags & PROXY_IGNORE)
+ && (pl->flags & PROXY_NEW)
+ ? "NEW":"DELETE";
+ proxy_name=pl->userid;
+ }
+
+ if (proxy_name)
+ printf("111 %s %s\n",
+ proxy_action,
+ proxy_name);
+ else
+ printf("200 Ok.\n");
+ }
+ rset(pcp);
+ return (0);
+ }
+ else if (!new_eventid && deleted_eventid)
+ {
+ struct proxy_list *pl;
+ struct PCP_delete del;
+ const char *proxy_userid;
+
+ for (pl=proxy_list; pl; pl=pl->next)
+ {
+ if (pl->flags & PROXY_IGNORE)
+ continue;
+
+ if (pl->old_event_id)
+ {
+ memset(&del, 0, sizeof(del));
+ del.id=pl->old_event_id;
+
+ if (pcp_delete(pl->proxy, &del))
+ {
+ fprintf(stderr, "CRIT: "
+ "DELETE failed for PROXY %s\n",
+ pl->userid);
+ }
+ }
+ }
+
+ memset(&del, 0, sizeof(del));
+ del.id=deleted_eventid;
+
+ if (pcp_delete(pcp, &del))
+ {
+ if (del.errcode != PCP_ERR_EVENTNOTFOUND)
+ {
+ error(del.errcode);
+ return (0);
+ }
+ }
+
+ proxy_userid=NULL;
+ for (pl=proxy_list; pl; pl=pl->next)
+ {
+ if (proxy_userid)
+ printf("111-DELETE %s\n",
+ proxy_userid);
+
+ proxy_userid=pl->userid;
+ }
+
+ if (proxy_userid)
+ printf("111 DELETE %s\n", proxy_userid);
+ else
+ printf("200 Ok\n");
+
+ rset(pcp);
+ return (0);
+ }
+
+ printf("500 There's nothing to commit.\n");
+ return (0);
+ }
+
+ if (strcasecmp(q, "CANCEL") == 0)
+ {
+ int errcode;
+
+ if (check_acl(acl_flags, PCP_ACL_MODIFY))
+ return (0);
+
+ q=strtok(NULL, " ");
+ if (!q)
+ printf("500 Syntax error\n");
+ else if (pcp_cancel(pcp, q, &errcode))
+ error(errcode);
+ else
+ printf("200 Cancelled\n");
+ return (0);
+ }
+
+ if (strcasecmp(q, "UNCANCEL") == 0)
+ {
+ struct PCP_uncancel unc;
+ struct uncancel_list *list=NULL, **tail= &list;
+ struct uncancel_list *p;
+
+ if (check_acl(acl_flags, PCP_ACL_MODIFY))
+ return (0);
+
+ memset(&unc, 0, sizeof(unc));
+ unc.uncancel_conflict_callback=uncancel_callback;
+ unc.uncancel_conflict_callback_ptr=&tail;
+
+ q=strtok(NULL, " ");
+ if (!q)
+ printf("500 Syntax error\n");
+ else if (pcp_uncancel(pcp, q,
+ (conflict_flag ? PCP_OK_CONFLICT:0)|
+ (force_flag ? PCP_OK_PROXY_ERRORS:0),
+ &unc))
+ {
+ if (unc.errcode == PCP_ERR_CONFLICT && list)
+ {
+ for (p=list; p; p=p->next)
+ {
+ char from_buf[15];
+ char to_buf[15];
+
+ pcp_gmtimestr(p->from, from_buf);
+ pcp_gmtimestr(p->to, to_buf);
+
+ printf("403%c%s %s %s %s conflicts.\n",
+ p->next ? '-':' ',
+ p->addr,
+ from_buf,
+ to_buf,
+ p->id);
+ }
+ }
+ else
+ error(unc.errcode);
+ }
+ else
+ printf("200 Uncancelled\n");
+
+ while((p=list) != NULL)
+ {
+ list=p->next;
+ free(p->addr);
+ free(p->id);
+ free(p);
+ }
+ return (0);
+ }
+
+ printf("500 Syntax error\n");
+ return (0);
+}
+
+static int check_acl(int flags, int bit)
+{
+ if (flags & bit)
+ return (0);
+
+ printf("500 Permission denied.\n");
+ return (1);
+}
+
+static RETSIGTYPE catch_sig(int sig_num)
+{
+ termsig=1;
+ signal(SIGALRM, catch_sig);
+ alarm(2);
+
+#if RETSIGTYPE != void
+ return (0);
+#endif
+}
+
+static void setsigs()
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sa_handler=catch_sig;
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+}
+
+struct get_acl {
+ const char *who;
+ int flags;
+};
+
+static int get_acl_callback(const char *w, int f, void *dummy)
+{
+ struct get_acl *ga=(struct get_acl *)dummy;
+
+ if (addrcmp(w, ga->who) == 0)
+ ga->flags=f;
+ return (0);
+}
+
+static void mainloop(struct PCP *pcp)
+{
+ int c;
+ struct pcpdtimer inactivity_timeout;
+ int my_acl_flags=PCP_ACL_MODIFY|PCP_ACL_CONFLICT|
+ PCP_ACL_LIST|PCP_ACL_RETR;
+
+ deleted_eventid=NULL;
+ memset(&new_commit, 0, sizeof(new_commit));
+ new_commit_times=NULL;
+ new_commit_participants=NULL;
+ new_commit_participants_buf=NULL;
+
+ termsig=0;
+
+ setsigs();
+
+ inp_ptr=0;
+ inp_left=0;
+ need_rset=0;
+
+ time(&prev_time);
+
+ pcpdtimer_init(&inactivity_timeout);
+ inactivity_timeout.handler=inactive;
+
+ if (proxy_userid)
+ {
+ struct get_acl ga;
+
+ ga.who=proxy_userid;
+ ga.flags=0;
+ if (pcp_has_acl(pcp))
+ {
+ if (pcp_list_acl(pcp, get_acl_callback, &ga) == 0)
+ {
+ if (ga.flags == 0)
+ {
+ ga.who="public";
+ if (pcp_list_acl(pcp, get_acl_callback,
+ &ga))
+ ga.flags=0;
+ }
+ }
+ else ga.flags=0;
+ }
+ my_acl_flags=ga.flags;
+ }
+
+ do
+ {
+ char *p;
+
+ input_line_len=0;
+ pcpdtimer_install(&inactivity_timeout, 30 * 60);
+
+ for (;;)
+ {
+
+
+ c=inputchar(pcp);
+
+ if (termsig || c == '\n')
+ break;
+ input_buffer[input_line_len++]=c;
+ }
+ if (termsig)
+ break;
+
+ input_buffer[input_line_len]=0;
+
+ for (p=input_buffer; *p; p++)
+ if (isspace((int)(unsigned char)*p))
+ *p=' ';
+ pcpdtimer_triggered(&inactivity_timeout);
+ /* Cancel inactivity_timeout for the duration of the command */
+
+ } while (doline(pcp, input_buffer, my_acl_flags) == 0 && !termsig);
+ alarm(0);
+ free(input_buffer);
+ rset(pcp);
+}
+
+/*
+** Start listening on a socket for connections
+*/
+
+static void accept_pcpd(int, int, int, int);
+
+static void start()
+{
+ int pubsock;
+ int privsock;
+
+ if ((pubsock=pcp_mksocket(PUBDIR, "PCPDLOCAL")) < 0)
+ {
+ exit(1);
+ }
+
+ if ((privsock=pcp_mksocket(PRIVDIR, "PCPDLOCAL")) < 0)
+ {
+ exit (1);
+ }
+
+#if USE_NOCLDWAIT
+ {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler=SIG_IGN;
+ sa.sa_flags=SA_NOCLDWAIT;
+ sigaction(SIGCHLD, &sa, NULL);
+ }
+#else
+ signal(SIGCHLD, SIG_IGN);
+#endif
+
+ for (;;)
+ {
+ fd_set fds;
+ struct timeval tv;
+ int rc;
+
+ FD_ZERO(&fds);
+ FD_SET(pubsock, &fds);
+ FD_SET(privsock, &fds);
+
+ tv.tv_sec=authtoken_check();
+ tv.tv_usec=0;
+
+ if ((rc=select ( (pubsock > privsock ? pubsock:privsock)+1,
+ &fds, NULL, NULL, &tv)) < 0)
+ {
+ perror("pcpd: select");
+ continue;
+ }
+
+ if (rc == 0)
+ continue;
+
+ if (FD_ISSET(pubsock, &fds))
+ accept_pcpd(pubsock, pubsock, privsock, 0);
+
+ if (FD_ISSET(privsock, &fds))
+ accept_pcpd(privsock, pubsock, privsock, 1);
+ }
+}
+
+struct userid_info {
+ char *userid;
+ int isproxy;
+} ;
+
+static int callback_userid(struct userid_callback *a, void *vp)
+{
+ struct userid_info *uinfo=(struct userid_info *)vp;
+ char *u=strdup(a->userid);
+ struct stat stat_buf;
+
+ if (!u)
+ return (-1);
+
+ if (stat(a->homedir, &stat_buf) < 0)
+ {
+ free(u);
+ return (-1);
+ }
+
+ if (stat_buf.st_mode & S_ISVTX)
+ {
+ free(u);
+ errno=EAGAIN;
+ return (1);
+ }
+
+ uinfo->userid=u;
+ return (0);
+}
+
+static int callback_login(struct userid_callback *a, void *vp)
+{
+ struct userid_info *uinfo=(struct userid_info *)vp;
+ struct stat stat_buf;
+ time_t t;
+ char curdir[1024];
+ char *token=NULL;
+
+ if (stat(a->homedir, &stat_buf) < 0)
+ return (-1);
+
+ if (stat_buf.st_mode & S_ISVTX)
+ {
+ errno=EAGAIN;
+ return (1);
+ }
+
+ time(&t);
+ if (!uinfo->isproxy)
+ {
+ token=authtoken_create(uinfo->userid, t);
+ if (!token)
+ {
+ fprintf(stderr, "NOTICE: authtoken_create() failed.\n");
+ maildir_cache_cancel();
+ return (1);
+ }
+ }
+
+ maildir_cache_start();
+
+ libmail_changeuidgid(a->uid, getgid());
+
+ if (chdir(a->homedir) < 0)
+ {
+ free(token);
+ fprintf(stderr, "NOTICE: chdir(%s) failed: %s\n", a->homedir,
+ strerror(errno));
+ maildir_cache_cancel();
+ exit(1);
+ }
+
+ if (chdir(a->maildir && *a->maildir ? a->maildir:"Maildir") < 0)
+ {
+ free(token);
+ fprintf(stderr, "NOTICE: chdir(Maildir) failed: %s\n",
+ strerror(errno));
+ maildir_cache_cancel();
+ exit(1);
+ }
+
+ mkdir("calendar", 0700);
+ if (chdir("calendar") < 0)
+ {
+ free(token);
+ fprintf(stderr, "NOTICE: chdir(calendar) failed: %s\n",
+ strerror(errno));
+ maildir_cache_cancel();
+ exit(1);
+ }
+
+ curdir[sizeof(curdir)-1]=0;
+ if (getcwd(curdir, sizeof(curdir)-1) == 0)
+ {
+ fprintf(stderr, "NOTICE: getcwd() failed: %s\n",
+ strerror(errno));
+ maildir_cache_cancel();
+ exit(1);
+ }
+
+ maildir_cache_save(uinfo->userid, t, curdir, a->uid, getgid());
+
+ alarm(0);
+ if (!uinfo->isproxy)
+ {
+ printf("102 %s logged in.\n", token);
+ free(token);
+ }
+ return (0);
+}
+
+static char *login(int, int *);
+
+static int accept_sock(int sock)
+{
+ struct sockaddr_un saddr;
+ socklen_t saddr_len;
+
+ saddr_len=sizeof(saddr);
+
+ return (accept(sock, (struct sockaddr *)&saddr, &saddr_len));
+}
+
+static void accept_pcpd(int sock, int pubsock, int privsock, int flag)
+{
+ int fd;
+ pid_t pid;
+ struct PCP *pcp;
+
+ if ((fd=accept_sock(sock)) < 0)
+ return;
+
+ if (fcntl(fd, F_SETFL, 0) < 0)
+ {
+ fprintf(stderr, "CRIT: fcntl() failed: %s\n", strerror(errno));
+ close(fd);
+ return;
+ }
+
+ maildir_cache_purge();
+ pid=fork();
+
+ if (pid < 0)
+ {
+ fprintf(stderr, "CRIT: fork() failed: %s\n", strerror(errno));
+ close(fd);
+ return;
+ }
+
+ if (pid)
+ {
+ close(fd);
+ return; /* Parent resumes listening */
+ }
+
+ /* child */
+ signal(SIGCHLD, SIG_DFL);
+
+ close(pubsock);
+ close(privsock);
+
+ close(0);
+ if (dup(fd) != 0)
+ exit(0);
+ close(1);
+ if (dup(fd) != 1)
+ exit(0);
+ close(fd);
+ userid=login(flag, &flag);
+
+ pcp=pcp_open_dir(".", userid);
+
+ if (pcp && flag && pcp_cleanup(pcp))
+ {
+ pcp_close(pcp);
+ fprintf(stderr, "CRIT: pcp_cleanup failed\n");
+ pcp=NULL;
+ }
+
+ if (!pcp)
+ {
+ fprintf(stderr, "CRIT: pcp_open_dir failed\n");
+ perror("pcp_open_dir");
+ exit(1);
+ }
+
+ mainloop(pcp);
+ exit(0);
+}
+
+struct relogin_struct {
+ time_t when;
+ int needauthtoken;
+ const char *userid;
+} ;
+
+static int callback_cache_search(uid_t u, gid_t g, const char *dir, void *vp)
+{
+ struct relogin_struct *rs=(struct relogin_struct *)vp;
+ time_t login_time, now;
+ int reset_flag;
+ char *token=NULL;
+
+ login_time=rs->when;
+ time(&now);
+
+ reset_flag= login_time <= now - TIMEOUT;
+
+ if (reset_flag)
+ {
+ if (rs->needauthtoken)
+ {
+ token=authtoken_create(rs->userid, now);
+ if (!token)
+ {
+ fprintf(stderr,
+ "ALERT: authtoken_create() failed.\n");
+ return (1);
+ }
+ }
+ maildir_cache_start();
+ }
+
+ libmail_changeuidgid(u, g);
+
+ if (chdir(dir) < 0)
+ {
+ maildir_cache_cancel();
+ fprintf(stderr, "NOTICE: chdir(%s) failed: %s\n", dir, strerror(errno));
+ return (-1);
+ }
+
+ alarm(0);
+ if (reset_flag)
+ {
+ maildir_cache_save(rs->userid, now, dir, u, g);
+ if (rs->needauthtoken)
+ {
+ printf("102 %s logged in.\n", token);
+ free(token);
+ }
+ }
+ else if (rs->needauthtoken) /* Not a proxy connection */
+ printf("200 Ok\n");
+ return (0);
+}
+
+static char *login(int isprivate,
+ int *flag /* Cleanup requested */
+ )
+{
+ struct userid_info uinfo;
+
+ proxy_userid=NULL;
+ *flag=0;
+ memset(&uinfo, 0, sizeof(uinfo));
+ alarm(300); /* Better log in in five minutes */
+ for (;;)
+ {
+ int c;
+ char *p;
+
+ input_line_len=0;
+ for (;;)
+ {
+ c=inputchar(NULL);
+ if (c == EOF)
+ exit(0);
+
+ if (c == '\n')
+ break;
+ input_buffer[input_line_len]=c;
+ if (input_line_len < 1024)
+ ++input_line_len;
+ }
+ input_buffer[input_line_len]=0;
+
+ for (p=input_buffer; *p &&
+ isspace((int)(unsigned char)*p); p++)
+ ;
+
+ if (strncasecmp(p, "PASSWORD", 8) == 0 && !isprivate &&
+ isspace((int)(unsigned char)p[8]) && uinfo.userid)
+ {
+ for (p += 9; isspace((int)(unsigned char)*p); p++)
+ ;
+
+ if (*p)
+ {
+ int rc;
+ char *q, *r;
+
+ for (q=r=p; *q; q++)
+ if (!isspace((int)(unsigned char)*q))
+ r=q+1;
+ *r=0;
+
+ rc=authpcp_login(uinfo.userid, p,
+ callback_login, &uinfo);
+
+ if (rc)
+ {
+ printf("%s %s\n",
+ rc < 0 ? "501":"401",
+ strerror(errno));
+ continue;
+ }
+ *flag=1;
+ break;
+ }
+ }
+
+ for (p=input_buffer; *p; p++)
+ if (isspace((int)(unsigned char)*p))
+ *p=' ';
+
+ p=strtok(input_buffer, " ");
+
+ if (p && strcasecmp(p, "CAPABILITY") == 0)
+ {
+ printf("100 PCP1\n");
+ continue;
+ }
+ else if (p && strcasecmp(p, "USERID") == 0 &&
+ uinfo.userid == NULL)
+ {
+ if ((p=strtok(NULL, " ")) != NULL)
+ {
+ int rc= authpcp_userid(p, callback_userid,
+ &uinfo);
+
+ if (rc)
+ {
+ printf("%s %s\n",
+ rc < 0 ? "501":"401",
+ strerror(errno));
+ continue;
+ }
+
+ printf("301 Ok, waiting for password.\n");
+ continue;
+ }
+ }
+ else if (p && strcasecmp(p, "PROXY") == 0 && uinfo.userid &&
+ isprivate)
+ {
+ if ((p=strtok(NULL, " ")) != 0)
+ {
+ struct relogin_struct rs;
+ time_t now;
+ int rc;
+
+ if (proxy_userid)
+ free(proxy_userid);
+ if ((proxy_userid=auth_choplocalhost(p))
+ == NULL)
+ {
+ printf("400 %s\n", strerror(errno));
+ continue;
+ }
+
+ rs.needauthtoken=0;
+ rs.userid=uinfo.userid;
+
+ time(&now);
+
+ rc=maildir_cache_search(uinfo.userid, now,
+ callback_cache_search,
+ &rs);
+ if (rc == 0)
+ {
+ alarm(0);
+ printf("200 PROXY ok\n");
+ break;
+ }
+ now -= TIMEOUT;
+
+ rc=maildir_cache_search(uinfo.userid, now,
+ callback_cache_search,
+ &rs);
+ if (rc == 0)
+ {
+ alarm(0);
+ printf("200 PROXY ok\n");
+ break;
+ }
+
+ uinfo.isproxy=1;
+ rc=authpcp_userid(uinfo.userid, callback_login,
+ &uinfo);
+ if (rc)
+ {
+ fprintf(stderr,
+ "CRIT: auth_userid() failed\n");
+ exit(1);
+ }
+ alarm(0);
+ printf("200 PROXY ok\n");
+ break;
+ }
+
+ }
+ else if (p && strcasecmp(p, "RELOGIN") == 0 && uinfo.userid &&
+ !isprivate)
+ {
+ if ((p=strtok(NULL, " ")) != 0)
+ {
+ struct relogin_struct rs;
+ int rc;
+
+ rs.needauthtoken=1;
+ rs.userid=uinfo.userid;
+ if (authtoken_verify(uinfo.userid, p,
+ &rs.when))
+ {
+ printf("500 Invalid authentication token.\n");
+ continue;
+ }
+
+ rc=maildir_cache_search(uinfo.userid, rs.when,
+ callback_cache_search,
+ &rs);
+ if (rc == 0)
+ break;
+
+ /*
+ ** Couldn't find anything in the login cache.
+ ** call the userid function with the login
+ ** callback.
+ ** This'll initialize lotsa other stuff, but
+ ** we don't care.
+ */
+
+ rc=authpcp_userid(uinfo.userid,
+ callback_login,
+ &uinfo);
+
+ if (rc)
+ {
+ fprintf(stderr,
+ "NOTICE: auth_userid() failed.\n");
+ printf("400 Internal failure - try again later.\n");
+ continue;
+ }
+ break;
+ }
+ }
+ else if (p && strcasecmp(p, "QUIT") == 0)
+ {
+ printf("200 Ok\n");
+ exit (0);
+ }
+ printf("500 Syntax error\n");
+ }
+ return (uinfo.userid);
+}
+
+int main(int argc, char **argv)
+{
+ int argn=1;
+ static const char * const authvars[]={NULL};
+
+ signal(SIGPIPE, SIG_IGN);
+ umask(022);
+
+ if (argn >= argc)
+ {
+ struct PCP *pcp;
+
+ pcp=open_calendar(NULL);
+
+ mainloop(pcp);
+ exit(0);
+ }
+
+ maildir_cache_init(TIMEOUT * 2, CACHEDIR, LOCALCACHEOWNER, authvars);
+
+ if (strcmp(argv[argn], "server") == 0)
+ {
+ struct group *gr;
+
+ if (chdir(CALENDARDIR) < 0)
+ {
+ perror(CALENDARDIR);
+ exit(1);
+ }
+ gr=getgrnam(MAILGROUP);
+
+ if (!gr)
+ {
+ fprintf(stderr, "Unknown group: %s\n", MAILGROUP);
+ exit(1);
+ }
+
+ authtoken_init();
+ libmail_changeuidgid(getuid(), gr->gr_gid);
+ start();
+ }
+ else if (strcmp(argv[argn], "login") == 0 ||
+ strcmp(argv[argn], "slogin") == 0)
+ {
+ struct PCP *pcp;
+ int flag;
+ struct group *gr;
+
+ gr=getgrnam(MAILGROUP);
+
+ if (!gr)
+ {
+ fprintf(stderr, "Unknown group: %s\n", MAILGROUP);
+ exit(1);
+ }
+ libmail_changeuidgid(getuid(), gr->gr_gid);
+
+ if (chdir(CALENDARDIR) < 0)
+ {
+ perror(CALENDARDIR);
+ exit(1);
+ }
+
+ authtoken_init();
+ userid=login(strcmp(argv[argn], "login"), &flag);
+
+ pcp=pcp_open_dir(".", userid);
+
+ if (pcp && flag && pcp_cleanup(pcp))
+ {
+ pcp_close(pcp);
+ fprintf(stderr, "CRIT: pcp_cleanup failed\n");
+ pcp=NULL;
+ }
+
+ if (!pcp)
+ {
+ fprintf(stderr, "CRIT: pcp_open_dir failed\n");
+ perror("pcp_open_dir");
+ exit(1);
+ }
+
+ mainloop(pcp);
+ exit(0);
+ }
+ else if (strcmp(argv[argn], "open") == 0)
+ {
+ ++argn;
+ if (argn < argc)
+ {
+ struct PCP *pcp;
+
+ pcp=open_calendar(argv[argn]);
+
+ mainloop(pcp);
+ exit(0);
+ }
+ }
+ fprintf(stderr, "Usage: %s (server|open [path])\n", argv[0]);
+ exit(0); /* exit(1) breaks Courier rpm %preun script */
+ return (0);
+}