summaryrefslogtreecommitdiffstats
path: root/sqwebmail/pcp.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 /sqwebmail/pcp.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 'sqwebmail/pcp.c')
-rw-r--r--sqwebmail/pcp.c4296
1 files changed, 4296 insertions, 0 deletions
diff --git a/sqwebmail/pcp.c b/sqwebmail/pcp.c
new file mode 100644
index 0000000..e10dd4d
--- /dev/null
+++ b/sqwebmail/pcp.c
@@ -0,0 +1,4296 @@
+/*
+** Copyright 2001-2010 Double Precision, Inc. See COPYING for
+** distribution information.
+*/
+
+
+/*
+*/
+#include "sqwebmail.h"
+#include "pcp.h"
+#include "pref.h"
+#include "htmllibdir.h"
+#include "sqconfig.h"
+#include "auth.h"
+#include "addressbook.h"
+#include "pcp/pcp.h"
+#include "cgi/cgi.h"
+#include "rfc822/rfc822.h"
+#include "rfc822/rfc822hdr.h"
+#include "rfc822/rfc2047.h"
+#include "maildir/maildircreate.h"
+#include "maildir/maildirmisc.h"
+#include "maildir/maildirquota.h"
+#include "maildir/maildirgetquota.h"
+#include "numlib/numlib.h"
+#include "maildir.h"
+#include "newmsg.h"
+#include "pref.h"
+#include "courierauth.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#define CACHE "calendar.cache"
+#define TOKEN "calendar.authtoken"
+
+#define CHANGEDFILE "calendar/changed" /* Hack */
+
+#include "strftime.h"
+
+extern FILE *open_langform(const char *lang, const char *formname,
+ int print_header);
+
+extern const char *myhostname();
+extern void output_attrencoded_oknl_fp(const char *, FILE *);
+extern void output_scriptptrget();
+extern void output_attrencoded(const char *);
+extern void print_safe(const char *);
+extern const char *sqwebmail_content_charset;
+extern const char *sqwebmail_content_language;
+extern void output_form(const char *);
+extern void newmsg_preview(const char *);
+extern void output_urlencoded(const char *);
+extern void attachments_head(const char *, const char *, const char *);
+extern char *newmsg_createsentmsg(const char *, int *);
+extern const char *sqwebmail_mailboxid;
+extern char *scriptptrget();
+extern void attach_delete(const char *);
+extern int attach_upload(const char *,
+ const char *,
+ const char *);
+
+extern void newmsg_showfp(FILE *, int *);
+
+static struct PCP *calendar=NULL;
+static void refreshcache(struct PCP *);
+extern size_t get_timeoutsoft();
+
+
+/*
+** CGI process startup
+*/
+
+void sqpcp_init()
+{
+ calendar=NULL;
+}
+
+/*
+** CGI process shutdown
+*/
+
+void sqpcp_close()
+{
+ if (calendar)
+ {
+ pcp_close(calendar);
+ calendar=NULL;
+ }
+}
+
+static int checked_calendar_mode=0;
+static char calendar_mode[24];
+
+static const char *sqpcp_mode()
+{
+ if (!checked_calendar_mode)
+ {
+ FILE *f;
+ char *p;
+
+ calendar_mode[0]=0;
+
+ f=fopen(CALENDARMODE, "r");
+
+ if (f)
+ {
+ if (fgets(calendar_mode, sizeof(calendar_mode),
+ f) == NULL)
+ {
+ calendar_mode[0]=0;
+ }
+ fclose(f);
+ }
+
+ if ((p=strchr(calendar_mode, '\n')) != 0)
+ *p=0;
+ checked_calendar_mode=1;
+ }
+
+ return (calendar_mode[0] ? calendar_mode:NULL);
+}
+
+/*
+** Log in to calendar.
+*/
+
+static void do_pcplogin(const char *userid, const char *password, int showerr);
+
+void sqpcp_login(const char *userid, const char *password)
+{
+ do_pcplogin(userid, password, 0);
+}
+
+static void do_pcplogin(const char *userid, const char *password, int showerr)
+{
+ struct PCP *pcp;
+
+ unlink(TOKEN);
+ unlink(CACHE); /* Rebuild it later*/
+
+ if (!sqpcp_mode())
+ return;
+
+ mkdir("calendar", 0700);
+
+ if (sqpcp_has_groupware()) /* Groupware mode, login to server */
+ {
+ char *errflag=0;
+
+ pcp=pcp_open_server(userid, password, &errflag);
+
+ if (!pcp && errflag && showerr)
+ {
+ printf("%s<div class=\"indent\"><pre class=\"small-error\">",
+ getarg("CALENDARLOGINERR"));
+ output_attrencoded_oknl_fp(errflag, stdout);
+ printf("</pre></div>\n");
+ }
+ if (errflag)
+ free(errflag);
+ }
+ else
+ {
+ pcp=sqpcp_calendar();
+ }
+
+ if (pcp)
+ {
+ const char *p=pcp_authtoken(pcp);
+
+ if (p)
+ {
+ FILE *f;
+ int u=umask(077);
+
+ f=fopen(TOKEN, "w");
+ umask(u);
+ if (f)
+ {
+ fprintf(f, "%s\n", p);
+ fclose(f);
+ }
+ }
+ pcp_cleanup(pcp);
+ refreshcache(pcp);
+ }
+}
+
+/*
+** Format of the CACHE file:
+**
+** start<TAB>end<TAB>eventid<TAB>flags<TAB>subject
+*/
+
+struct cacherecord {
+ time_t start, end;
+ char *eventid;
+ char *flags;
+ char *subject;
+} ;
+
+int sqpcp_has_calendar()
+{
+ return (sqpcp_mode() ? 1:0);
+}
+
+int sqpcp_has_groupware()
+{
+ const char *p=sqpcp_mode();
+
+ return (p && strcmp(p, "net") == 0);
+}
+
+static int createcache(struct PCP *,
+ struct cacherecord **, unsigned *, time_t, time_t);
+static void destroycache(struct cacherecord *, unsigned);
+
+/*
+** Are we logged in? If so, refresh the calendar event cache that we
+** display on the main folders screen, if it is stale.
+*/
+
+static int need_refresh();
+
+int sqpcp_loggedin()
+{
+ struct PCP *pcp;
+
+ if (!sqpcp_mode())
+ return (0);
+
+ if (sqpcp_has_groupware())
+ {
+ struct stat stat_buf;
+
+ if (stat(TOKEN, &stat_buf))
+ return (0); /* Login session dropped */
+ }
+
+ if (!need_refresh())
+ return (1);
+ pcp=sqpcp_calendar();
+ if (pcp)
+ refreshcache(pcp);
+ return (1);
+}
+
+/* Check if it's time to rebuild CACHE file */
+
+static const char *parsetimet(const char *buf, time_t *tp)
+{
+ *tp=libmail_strtotime_t(&buf);
+
+ if (*buf == '\t')
+ ++buf;
+ return buf;
+}
+
+static int need_refresh()
+{
+ FILE *fp;
+ struct stat stat_buf;
+
+ /* Check whether it's time to rebuild the cache */
+
+ fp=fopen(CACHE, "r");
+
+ if (stat(CHANGEDFILE, &stat_buf) == 0)
+ {
+ if (fp)
+ fclose(fp);
+ return (1);
+ }
+
+ if (fp)
+ {
+ struct stat stat_buf;
+
+ char buffer[BUFSIZ];
+ const char *p;
+
+ time_t a, b;
+
+ int ch=getc(fp);
+
+ if (ch == EOF)
+ {
+ fclose(fp);
+ return (0); /* No events scheduled */
+ }
+
+ ungetc(ch, fp);
+ if (fgets(buffer, sizeof(buffer), fp) &&
+ (p=parsetimet(buffer, &a)) &&
+ parsetimet(p, &b))
+ {
+ time_t now;
+
+ time(&now);
+
+ if (now < b) /* Event expired */
+ {
+ if (fstat(fileno(fp), &stat_buf) == 0)
+ {
+ /*
+ ** Check in every TIMEOUTSOFT/2 in
+ ** any case.
+ */
+
+ if (stat_buf.st_mtime >
+ now - get_timeoutsoft()/2)
+ {
+ fclose(fp); /* Not yet */
+ return (0);
+ }
+ }
+ }
+ }
+ fclose(fp);
+ unlink(CACHE);
+ return (1);
+ }
+ return (0);
+}
+
+static void refreshcache(struct PCP *pcp)
+{
+ struct cacherecord *recs;
+ unsigned n_recs;
+ unsigned i;
+ char *new_name;
+ int new_fd;
+ char *p;
+ time_t now;
+ FILE *new_fp;
+
+ unlink(CHANGEDFILE);
+ time(&now);
+ if (createcache(pcp, &recs, &n_recs, now, now + 5 * 24 * 60 * 60))
+ return;
+
+ new_fd=maildir_createmsg(INBOX, "cache", &new_name);
+ if (new_fd < 0 || (new_fp=fdopen(new_fd, "w")) == 0)
+ {
+ if (new_fd >= 0) close(new_fd);
+ destroycache(recs, n_recs);
+ return;
+ }
+
+ p=malloc(sizeof("tmp/")+strlen(new_name));
+ if (!p)
+ {
+ fclose(new_fp);
+ free(new_name);
+ destroycache(recs, n_recs);
+ return;
+ }
+ strcat(strcpy(p, "tmp/"), new_name);
+ free(new_name);
+ new_name=p;
+
+ /* Save a max of 5 events, for the main page listing */
+
+ for (i=0; i<n_recs && i < 5; i++)
+ {
+ char buf1[NUMBUFSIZE], buf2[NUMBUFSIZE];
+
+ fprintf(new_fp, "%s\t%s\t%s\t%s\t%s\n",
+ libmail_str_time_t(recs[i].start, buf1),
+ libmail_str_time_t(recs[i].end, buf2),
+ recs[i].eventid,
+ recs[i].flags,
+ recs[i].subject);
+ }
+
+ if (fflush(new_fp) || ferror(new_fp))
+ {
+ fclose(new_fp);
+ unlink(new_name);
+ free(new_name);
+ destroycache(recs, n_recs);
+ return;
+ }
+ fclose(new_fp);
+ rename(new_name, CACHE);
+ free(new_name);
+ destroycache(recs, n_recs);
+ return;
+}
+
+struct cacherecord_list {
+ struct cacherecord_list *next;
+ struct cacherecord rec;
+} ;
+
+static int cmp_reclist(const void *a, const void *b)
+{
+ struct cacherecord_list *aa=*(struct cacherecord_list **)a;
+ struct cacherecord_list *bb=*(struct cacherecord_list **)b;
+
+ return ( aa->rec.end < bb->rec.end ? -1:
+ aa->rec.end > bb->rec.end ? 1:
+ aa->rec.start < bb->rec.start ? -1:
+ aa->rec.start > bb->rec.start ? 1:
+ strcmp(aa->rec.eventid, bb->rec.eventid));
+}
+
+static int callback_createcache(struct PCP_list_all *, void *);
+static int callback_retr_status(struct PCP_retr *, int, void *);
+static int callback_retr_headers(struct PCP_retr *, const char *,
+ const char *, void *);
+
+static void destroycache_rec(struct cacherecord *);
+
+struct retr_xinfo {
+ struct cacherecord *recs;
+ unsigned n_recs;
+} ;
+
+static int createcache(struct PCP *pcp,
+ struct cacherecord **recs, unsigned *n_recs,
+ time_t start, time_t end)
+{
+ struct cacherecord_list *list=NULL, *p, **a;
+ struct PCP_list_all la;
+ struct PCP_retr r;
+ struct retr_xinfo xr;
+
+ unsigned i,n;
+ const char **event_ids;
+
+ *recs=0;
+ *n_recs=0;
+
+ memset(&la, 0, sizeof(la));
+ la.list_from=start;
+ la.list_to=end;
+ la.callback_func=callback_createcache;
+ la.callback_arg= &list;
+
+ if (pcp_list_all(pcp, &la))
+ {
+ while ((p=list) != NULL)
+ {
+ list=p->next;
+ destroycache_rec(&p->rec);
+ free(p);
+ }
+ return (0);
+ }
+
+ /* Sort the returned event list, in chronological order. */
+
+
+ /* First, create an array out of the list */
+
+ for (n=0, p=list; p; p=p->next)
+ ++n;
+
+ if (!n)
+ return (0); /* Nothing */
+
+ a=malloc(sizeof(struct cacherecord_list *)*n);
+ if (!a)
+ {
+ fprintf(stderr, "NOTICE: malloc failed\n");
+
+ while ((p=list) != NULL)
+ {
+ list=p->next;
+ destroycache_rec(&p->rec);
+ free(p);
+ }
+ return (-1);
+ }
+ for (n=0, p=list; p; p=p->next)
+ a[n++]=p;
+
+ /* Sort it, copy the sorted array to the return ptr */
+
+ qsort(a, n, sizeof(*a), cmp_reclist);
+
+ if ((*recs=malloc(sizeof(struct cacherecord)*n)) == NULL)
+ {
+ fprintf(stderr, "NOTICE: malloc failed\n");
+
+ while ((p=list) != NULL)
+ {
+ list=p->next;
+ destroycache_rec(&p->rec);
+ free(p);
+ }
+ free(a);
+ return (-1);
+ }
+
+ for (i=0; i<n; i++)
+ (*recs)[i]= a[i]->rec;
+ *n_recs=n;
+ free(a);
+ while ((p=list) != NULL)
+ {
+ list=p->next;
+ free(p);
+ }
+
+ /* Get the subject of all the events */
+
+ event_ids=malloc(sizeof(const char *)*(n+1));
+
+ if (!event_ids)
+ {
+ fprintf(stderr, "NOTICE: malloc failed\n");
+
+ destroycache(*recs, *n_recs);
+ return (-1);
+ }
+
+ for (i=0; i< *n_recs; i++)
+ event_ids[i]= (*recs)[i].eventid;
+ event_ids[i]=NULL;
+
+ memset(&r, 0, sizeof(r));
+ r.event_id_list=event_ids;
+ r.callback_arg=&xr;
+ xr.recs= *recs;
+ xr.n_recs= *n_recs;
+
+ r.callback_retr_status=callback_retr_status;
+ r.callback_headers_func=callback_retr_headers;
+
+ if (pcp_retr(pcp, &r))
+ {
+ fprintf(stderr, "NOTICE: pcp_retr: error: %s\n", pcp_errmsg(pcp));
+ free(event_ids);
+ destroycache(*recs, *n_recs);
+ return (-1);
+ }
+ free(event_ids);
+ return (0);
+}
+
+/* Callback from PCP LIST - save the event ID in the cacherecord_list */
+
+static int callback_createcache(struct PCP_list_all *a, void *vp)
+{
+ struct cacherecord_list **listp=(struct cacherecord_list **)vp, *p;
+
+ if ((p=malloc(sizeof(struct cacherecord_list))) == NULL)
+ return (-1);
+
+ p->rec.start=a->event_from;
+ p->rec.end=a->event_to;
+
+ if ((p->rec.eventid=strdup(a->event_id)) == NULL)
+ {
+ free(p);
+ return (-1);
+ }
+
+ /* Initialize the other fields, so all ptrs are live */
+
+ if ((p->rec.flags=strdup("")) == NULL)
+ {
+ free(p->rec.eventid);
+ free(p);
+ return (-1);
+ }
+
+ if ((p->rec.subject=strdup("")) == NULL)
+ {
+ free(p->rec.flags);
+ free(p->rec.eventid);
+ free(p);
+ return (-1);
+ }
+
+ p->next= *listp;
+ *listp=p;
+ return (0);
+}
+
+static int callback_retr_status(struct PCP_retr *r, int s, void *vp)
+{
+ struct cacherecord *recs=( (struct retr_xinfo *)vp)->recs;
+ unsigned n_recs=( (struct retr_xinfo *)vp)->n_recs;
+ unsigned i;
+
+ char stat_buf[256];
+ char *p;
+
+ stat_buf[0]=0;
+
+ if (s & LIST_CANCELLED)
+ {
+ strcat(stat_buf, " CANCELLED");
+ }
+
+ if (s & LIST_BOOKED)
+ {
+ strcat(stat_buf, " BOOKED");
+ }
+
+ p=stat_buf;
+ if (*p)
+ ++p;
+
+ for (i=0; i<n_recs; i++)
+ {
+ if (strcmp(recs[i].eventid, r->event_id) == 0)
+ {
+ char *s=strdup(p);
+
+ if (!s)
+ return (-1);
+ free(recs[i].flags);
+ recs[i].flags=s;
+ break;
+ }
+ }
+ return (0);
+}
+
+/* RETR callback for headers - save the Subject: header */
+
+static void collapse_subject(char *s)
+{
+ /* Collapse multiline subjects */
+
+ char *t, *u;
+
+ for (t=u=s; *t; )
+ {
+ if (*t != '\n')
+ {
+ *u++=*t++;
+ continue;
+ }
+
+ while (*t && isspace((int)(unsigned char)*t))
+ ++t;
+ *u++=' ';
+ }
+ *u=0;
+}
+
+static int callback_retr_headers(struct PCP_retr *r, const char *h,
+ const char *v, void *vp)
+{
+ struct cacherecord *recs=( (struct retr_xinfo *)vp)->recs;
+ unsigned n_recs=( (struct retr_xinfo *)vp)->n_recs;
+ unsigned i;
+
+ if (strcasecmp(h, "Subject") || !v)
+ return (0);
+
+ for (i=0; i<n_recs; i++)
+ {
+ if (strcmp(recs[i].eventid, r->event_id) == 0)
+ {
+ char *s;
+
+ s=strdup(v);
+
+ if (!s)
+ return (-1);
+
+ collapse_subject(s);
+ free(recs[i].subject);
+ recs[i].subject=s;
+ }
+ }
+
+ return (0);
+}
+
+static void destroycache(struct cacherecord *c, unsigned n)
+{
+ unsigned i;
+
+ for (i=0; i<n; i++)
+ {
+ destroycache_rec(c+i);
+ }
+ if (c)
+ free(c);
+}
+
+static void destroycache_rec(struct cacherecord *c)
+{
+ free(c->eventid);
+ free(c->flags);
+ free(c->subject);
+}
+
+static void parsecache_rec(char *p, struct cacherecord *cr)
+{
+ unsigned long a;
+
+ memset(cr, 0, sizeof(*cr));
+ cr->eventid="";
+ cr->flags="";
+ cr->subject="";
+
+ if (!p || sscanf(p, "%lu", &a) <= 0)
+ return;
+ p=strchr(p, '\t');
+ cr->start= (time_t)a;
+ if (!p || sscanf(p, "%lu", &a) <= 0)
+ return;
+ cr->end= (time_t)a;
+ p=strchr(p+1, '\t');
+ if (!p) return;
+ ++p;
+
+ cr->eventid=p;
+ p=strchr(p, '\t');
+ if (!p) return;
+ *p++=0;
+
+ cr->flags=p;
+ p=strchr(p, '\t');
+ if (!p) return;
+ *p++=0;
+
+ cr->subject=p;
+}
+
+struct PCP *sqpcp_calendar()
+{
+ const char *p;
+
+ if (!sqpcp_mode())
+ {
+ errno=ENOENT;
+ return (NULL);
+ }
+
+ if (calendar)
+ return (calendar);
+
+ p=getenv("AUTHADDR");
+
+ if (!p)
+ {
+ errno=ENOENT;
+ return (NULL);
+ }
+
+ if (sqpcp_has_groupware())
+ {
+ char token[256];
+ char *pp;
+
+ FILE *f=fopen(TOKEN, "r");
+
+ if (!f)
+ return (NULL);
+
+ if (fgets(token, sizeof(token), f) == NULL)
+ {
+ fclose(f);
+ return (NULL);
+ }
+
+ if ((pp=strchr(token, '\n')) != 0) *pp=0;
+
+ fclose(f);
+ if (token[0] == 0)
+ {
+ unlink(TOKEN);
+ return (NULL);
+ }
+
+ calendar=pcp_reopen_server(p, token, NULL);
+
+ if (calendar)
+ {
+ p=pcp_authtoken(calendar);
+
+ if (p && strcmp(p, token))
+ {
+ int u=umask(077);
+
+ /* Save new authentication token */
+
+ f=fopen(TOKEN, "w");
+ umask(u);
+ if (f)
+ {
+ fprintf(f, "%s\n", token);
+ fclose(f);
+ }
+ }
+ }
+ else
+ {
+ unlink(TOKEN);
+ }
+ return (calendar);
+ }
+
+ return ((calendar=pcp_open_dir("calendar", p)));
+}
+
+
+/* ---------------------------------------------------------------- */
+/* Display event information */
+/* ---------------------------------------------------------------- */
+
+static struct PCP_event_time *event_time_list=0;
+static unsigned n_event_time_list=0;
+static FILE *openoldfp(const char *p, unsigned long *prev_size);
+
+struct my_participant {
+ struct my_participant *next;
+ char *name;
+ char *address;
+} ;
+
+static struct my_participant *my_participant_list=0;
+
+static void add_my_participant(const char *h)
+{
+ struct my_participant *m=
+ malloc(sizeof(struct my_participant));
+
+ if (!m)
+ enomem();
+
+ memset(m, 0, sizeof(*m));
+ m->next=my_participant_list;
+ my_participant_list=m;
+
+ if ((m->address=strdup(h)) == NULL)
+ enomem();
+}
+
+static char *from_buf=0, *subj_buf=0;
+
+void sqpcp_eventstart()
+{
+ const char *p=cgi("draftmessage");
+ FILE *oldfp;
+ struct rfc822hdr h;
+ struct PCP_event_time_list {
+ struct PCP_event_time_list *next;
+ struct PCP_event_time t;
+ } *event_time_listp=NULL, **lastp= &event_time_listp;
+ unsigned event_time_list_cnt=0;
+
+ sqpcp_eventend();
+
+ if (!p || !*p)
+ return;
+
+ oldfp=openoldfp(p, NULL);
+ if (!oldfp)
+ return;
+
+ rfc822hdr_init(&h, 8192);
+ while (rfc822hdr_read(&h, oldfp, NULL, 0) == 0)
+ {
+ if (strcasecmp(h.header, "X-Event-Time") == 0 && h.value)
+ {
+ unsigned long n1, n2;
+
+ if (sscanf(h.value, "%lu %lu", &n1, &n2) == 2)
+ {
+ struct PCP_event_time_list *t=
+ malloc(sizeof(**lastp));
+
+ if (!t)
+ {
+ rfc822hdr_free(&h);
+ sqpcp_eventend();
+ enomem();
+ return;
+ }
+ *lastp=t;
+ t->next=NULL;
+ t->t.start=(time_t)n1;
+ t->t.end=(time_t)n2;
+ ++event_time_list_cnt;
+ lastp=&t->next;
+ }
+ }
+ else if (strcasecmp(h.header, "X-Event-Participant") == 0 && h.value)
+ {
+ add_my_participant(h.value);
+ }
+ else if (strcasecmp(h.header, "from") == 0 && h.value)
+ {
+ rfc822hdr_collapse(&h);
+ if (from_buf)
+ free(from_buf);
+
+ from_buf=NULL;
+
+ if ((from_buf=
+ rfc822_display_hdrvalue_tobuf(h.header,
+ h.value,
+ sqwebmail_content_charset,
+ NULL, NULL)) == NULL)
+ from_buf=strdup(h.value);
+
+ if (from_buf)
+ cgi_put("from", from_buf);
+ }
+ else if (strcasecmp(h.header, "subject") == 0 && h.value)
+ {
+ rfc822hdr_collapse(&h);
+ if (subj_buf)
+ free(subj_buf);
+
+ subj_buf=rfc822_display_hdrvalue_tobuf(h.header,
+ h.value,
+ sqwebmail_content_charset,
+ NULL, NULL);
+ if (!subj_buf)
+ subj_buf=strdup(subj_buf);
+
+ if (subj_buf)
+ cgi_put("headersubject", subj_buf);
+ }
+ }
+ rfc822hdr_free(&h);
+ if (event_time_list_cnt)
+ {
+ struct PCP_event_time *list1;
+ struct PCP_event_time_list *p;
+
+ list1=malloc(sizeof(struct PCP_event_time_list)
+ *event_time_list_cnt);
+ if (!list1)
+ {
+ sqpcp_eventend();
+ enomem();
+ return;
+ }
+ event_time_list_cnt=0;
+ while ((p=event_time_listp) != NULL)
+ {
+ list1[event_time_list_cnt]=p->t;
+ ++event_time_list_cnt;
+ event_time_listp=p->next;
+ free(p);
+ }
+
+ event_time_list=list1;
+ n_event_time_list=event_time_list_cnt;
+ }
+}
+
+void sqpcp_eventend()
+{
+ struct my_participant *m;
+
+ while ((m=my_participant_list) != NULL)
+ {
+ my_participant_list=m->next;
+ if (m->name)
+ free(m->name);
+ if (m->address)
+ free(m->address);
+ free(m);
+ }
+
+ if (event_time_list)
+ free(event_time_list);
+ event_time_list=NULL;
+ n_event_time_list=0;
+
+}
+
+void sqpcp_eventtimes()
+{
+ char buffer[512];
+ unsigned i;
+
+ for (i=0; i<n_event_time_list; i++)
+ {
+ if (i)
+ printf("<br />\n");
+ if (pcp_fmttimerange(buffer, sizeof(buffer),
+ event_time_list[i].start,
+ event_time_list[i].end))
+ continue;
+ printf("<span class=\"tt\">");
+ print_safe(buffer);
+ printf("&nbsp;&nbsp;</span><a href=\"");
+ output_scriptptrget();
+ printf("&amp;form=newevent&amp;draftmessage=");
+ output_urlencoded(cgi("draftmessage"));
+ printf("&amp;do.deleventtime=%s-%s\"><font size=\"-2\">"
+ "(%s)</font></a>",
+ libmail_str_time_t(event_time_list[i].start, buffer),
+ libmail_str_time_t(event_time_list[i].end, buffer+NUMBUFSIZE),
+ getarg("REMOVE")
+ );
+ }
+}
+
+static int save_participant_names(const char *addr, const char *name,
+ void *vp)
+{
+ struct my_participant *p=(struct my_participant *)vp;
+
+ for ( ; name && p; p=p->next)
+ if (strcasecmp(p->address, addr) == 0 && p->name == 0)
+ p->name=strdup(name);
+ return (0);
+}
+
+void sqpcp_eventparticipants()
+{
+ struct my_participant *m;
+
+ if (!my_participant_list)
+ return;
+
+ (void)ab_get_nameaddr(save_participant_names, my_participant_list);
+
+ printf("<table border=\"0\">");
+
+ for (m=my_participant_list; m; m=m->next)
+ {
+ printf("<tr><td><span class=\"tt\">");
+ if (m->address)
+ ab_nameaddr_show(m->name, m->address);
+
+ printf("</span></td><td>&nbsp;&nbsp;<a href=\"");
+
+ output_scriptptrget();
+ printf("&amp;form=newevent&amp;draftmessage=");
+ output_urlencoded(cgi("draftmessage"));
+ printf("&amp;do.delparticipant=");
+ output_urlencoded(m->address);
+ printf("\"><font size=\"-2\">(%s)</font></a></td></tr>\n",
+ getarg("REMOVE"));
+ }
+ printf("</table>");
+}
+
+void sqpcp_eventfrom()
+{
+ if (auth_getoptionenvint("wbnochangingfrom"))
+ {
+ printf("<span class=\"tt\">");
+ print_safe(login_fromhdr());
+ printf("</span>");
+ }
+ else
+ {
+ const char *p=cgi("headerfrom");
+
+ if (!p || !*p)
+ p=pref_from;
+ if (!p || !*p)
+ p=login_fromhdr();
+ if (!p)
+ p="";
+
+ printf("<input type=\"text\" name=\"headerfrom\" size=\"60\" value=\"");
+ output_attrencoded(p);
+ printf("\" />");
+ }
+}
+
+void sqpcp_eventtext()
+{
+ const char *p;
+
+ printf("<textarea name=\"message\" cols=\"%d\" rows=\"15\" wrap=\"soft\">",
+ MYLINESIZE);
+ p=cgi("draftmessage");
+
+ if (p && *p)
+ {
+ FILE *fp=openoldfp(p, NULL);
+ int dummy;
+
+ if (fp)
+ {
+ newmsg_showfp(fp, &dummy);
+ }
+ fclose(fp);
+ }
+ printf("</textarea>\n");
+}
+
+void sqpcp_eventattach()
+{
+ const char *p;
+
+ p=cgi("draftmessage");
+
+ if (p && *p)
+ {
+ attachments_head(NULL, cgi("pos"), p);
+ }
+}
+
+/* ------- Display the cached event summary -------- */
+
+static void print_event_subject(char *, const char *, unsigned);
+static void print_event_link_url(const char *, const char *);
+
+void sqpcp_summary()
+{
+ FILE *fp;
+ int cnt=0;
+ time_t now;
+ struct tm *tmptr;
+
+ if (!sqpcp_mode())
+ return;
+
+ if (*cgi("do.calendarlogin"))
+ {
+ const char *userid=getenv("AUTHADDR");
+ const char *password=cgi("password");
+
+ if (userid && password)
+ {
+ do_pcplogin(userid, password, 1);
+ }
+ }
+
+ if (sqpcp_has_groupware())
+ {
+ if (!sqpcp_loggedin())
+ {
+ insert_include("calendarlogin");
+ return;
+ }
+ }
+
+ time(&now);
+ tmptr=localtime(&now);
+
+ if (tmptr)
+ {
+ char *p=scriptptrget();
+ char *q=malloc(strlen(p)+200);
+
+ if (!q)
+ {
+ free(p);
+ enomem();
+ }
+
+ strcpy(q, p);
+ free(p);
+ strcat(q, "&amp;form=eventdaily&amp;clearcache=1&amp;date=");
+ sprintf(q+strlen(q), "%04d%02d%02d",
+ tmptr->tm_year + 1900,
+ tmptr->tm_mon+1,
+ tmptr->tm_mday);
+
+ printf(getarg("CALENDAR"), q);
+ free(q);
+ }
+
+ printf("<table class=\"eventsummary\" width=\"100%%\">");
+
+ if ((fp=fopen(CACHE, "r")) != NULL)
+ {
+ char buffer[BUFSIZ];
+ int i;
+ char last_date[256];
+ char date[256];
+ char date2[256];
+ struct tm *tm;
+ char yyyymmdd[9];
+
+ char time1[128];
+ char time2[128];
+
+ last_date[0]=0;
+
+ for (;;)
+ {
+ int c;
+ struct cacherecord cr;
+
+ i=0;
+
+ while ((c=getc(fp)) != EOF && c != '\n')
+ {
+ if (i < sizeof(buffer)-1)
+ buffer[i++]=(char)c;
+ }
+ buffer[i]=0;
+ if (i == 0 && c == EOF)
+ break;
+
+ parsecache_rec(buffer, &cr);
+
+ if (pcp_fmttime(date, sizeof(date),
+ cr.start, FMTTIME_DATE))
+ continue;
+ if (pcp_fmttime(date2, sizeof(date2),
+ cr.end, FMTTIME_DATE))
+ continue;
+
+ if (pcp_fmttime(time1, sizeof(time1),
+ cr.start,
+ FMTTIME_TIME))
+ continue;
+
+ if (pcp_fmttime(time2,
+ sizeof(time2),
+ cr.end,
+ FMTTIME_TIME))
+ continue;
+
+ tm=localtime(&cr.start);
+ if (!tm)
+ continue;
+ sprintf(yyyymmdd, "%04d%02d%02d",
+ tm->tm_year + 1900,
+ tm->tm_mon + 1,
+ tm->tm_mday);
+
+ i=0;
+ if (last_date[0] && strcmp(last_date, date) == 0)
+ {
+ if (strcmp(last_date, date2) == 0)
+ {
+ /* Same day as last event */
+
+ printf("<tr><td align=\"left\" width=\"1%%\">&nbsp;</td>"
+ "<td align=\"left\">&nbsp;"
+ "<a href=\"");
+ print_event_link_url(cr.eventid, "");
+ printf("&amp;date=%s\">", yyyymmdd);
+ print_safe(time1);
+ printf("&nbsp;-&nbsp;");
+ print_safe(time2);
+ printf("</a></td>");
+ i=1;
+ }
+ else
+ {
+ last_date[0]=0;
+ }
+ }
+ else
+ {
+ last_date[0]=0;
+ if (strcmp(date, date2) == 0)
+ strcpy(last_date, date);
+ }
+
+ if (!i)
+ {
+ if (cnt)
+ printf("<tr><td style=\"line-height: 0px; font-size: 8px\" colspan=\"3\">"
+ "&nbsp;"
+ "</td></tr>\n");
+ if (strcmp(date, date2))
+ {
+ printf("<tr><td colspan=\"2\">"
+ "<a href=\"");
+ print_event_link_url(cr.eventid, "");
+ printf("&amp;date=%s\">", yyyymmdd);
+ print_safe(date);
+ printf("&nbsp;");
+ print_safe(time1);
+ printf("&nbsp;-&nbsp;");
+ print_safe(date2);
+ printf("&nbsp;");
+ print_safe(time2);
+ printf("</a></td>");
+ }
+ else
+ {
+ printf("<tr><td width=\"1%%\">"
+ "<a href=\"");
+ print_event_link_url(cr.eventid, "");
+ printf("&amp;date=%s\">", yyyymmdd);
+ print_safe(date);
+ printf("</a></td><td>&nbsp;"
+ "<a href=\"");
+ print_event_link_url(cr.eventid, "");
+ printf("&amp;date=%s\">", yyyymmdd);
+ print_safe(time1);
+ printf("&nbsp;-&nbsp;");
+ print_safe(time2);
+ printf("</a></td>");
+ }
+ }
+
+ printf("<td width=\"100%%\">"
+ "<a href=\"");
+ print_event_link_url(cr.eventid, "");
+ printf("&amp;date=%s\">&nbsp;&nbsp;", yyyymmdd);
+ print_event_subject(cr.flags, cr.subject, 60);
+ printf("</a></td></tr>\n");
+ ++cnt;
+ }
+ fclose(fp);
+ }
+
+ if (cnt == 0)
+ {
+ printf("<tr><td align=\"center\">%s</td></tr>\n",
+ getarg("NOEVENTS"));
+ }
+
+ printf("</table>\n");
+ printf("<a href=\"");
+ output_scriptptrget();
+ printf("&amp;form=newevent\">%s</a>", getarg("NEWEVENT"));
+}
+
+
+static void print_event_subject(char *flags, const char *subject, unsigned w)
+{
+ unsigned i;
+ char *p;
+
+ /* Print event flags first: CANCELLED... */
+
+ for (p=flags; p && (p=strtok(p, " \t\r")) != 0; p=0)
+ {
+ printf("%s", getarg(p));
+ }
+
+ p=rfc822_display_hdrvalue_tobuf("subject",
+ subject ? subject:"",
+ sqwebmail_content_charset,
+ NULL,
+ NULL);
+
+ if (!p)
+ p=strdup(subject ? subject:"");
+
+ if (!p)
+ return;
+
+ if (strlen(p) > w)
+ {
+ /* Truncate long subject lines */
+ i=w-5;
+ while (i)
+ {
+ if (isspace((int)(unsigned char) p[i]))
+ {
+ strcpy(p+i, "...");
+ break;
+ }
+ --i;
+ }
+ }
+ print_safe(p);
+ free(p);
+}
+
+/* ------- New event support code -------- */
+
+static int addtime(int);
+static void addparticipant(int, const char *);
+
+static FILE *openoldfp(const char *p, unsigned long *prev_size)
+{
+ struct stat stat_buf;
+ char *filename;
+ int x;
+ FILE *oldfp;
+
+ CHECKFILENAME(p);
+
+ filename=maildir_find(INBOX "." DRAFTS, p);
+ if (!filename)
+ return (NULL);
+
+ x=maildir_safeopen(filename, O_RDONLY, 0);
+ free(filename);
+ if (x < 0)
+ return (NULL);
+
+
+ if (fstat(x, &stat_buf) < 0 || (oldfp=fdopen(x, "r")) == NULL)
+ {
+ close(x);
+ return (NULL);
+ }
+
+ if (prev_size)
+ *prev_size=stat_buf.st_size;
+ return (oldfp);
+}
+
+
+/* ------------- Conflict indicators ---------------- */
+
+struct conflict_list {
+ struct conflict_list *next;
+ char *event_id;
+ time_t start, end;
+ char *address;
+ char *subject;
+} ;
+
+static struct conflict_list *conflict_list=NULL;
+
+static void init_save_conflict()
+{
+ while (conflict_list)
+ {
+ struct conflict_list *p=conflict_list;
+
+ conflict_list=p->next;
+ if (p->event_id)
+ free(p->event_id);
+ if (p->address)
+ free(p->address);
+ if (p->subject)
+ free(p->subject);
+ free(p);
+ }
+}
+
+static int save_conflict(const char *event_id, time_t start, time_t end,
+ const char *address,
+ void *dummy)
+{
+ struct conflict_list *p, **ptr;
+
+ for (ptr= &conflict_list; *ptr; ptr=&(*ptr)->next)
+ {
+ if ( (*ptr)->end > end)
+ break;
+ }
+
+ if ((p=malloc(sizeof(struct conflict_list))) == NULL)
+ return (-1);
+ memset(p, 0, sizeof(*p));
+ p->next= *ptr;
+ *ptr=p;
+
+ p->start=start;
+ p->end=end;
+ if ((p->event_id=strdup(event_id)) == NULL ||
+ (address && (p->address=strdup(address)) == NULL))
+ return (-1);
+
+ return (0);
+}
+
+static int save_conflict_subj(struct PCP_retr *, const char *,
+ const char *, void *);
+
+static void show_conflict_error(struct PCP *pcp)
+{
+ unsigned n;
+ struct conflict_list *p;
+ const char **l;
+ struct PCP_retr r;
+
+ for (n=0, p=conflict_list; p; p=p->next)
+ if (p->event_id && (!p->address ||
+ strcmp(p->address, "@") == 0))
+ ++n;
+
+ if ((l=malloc(sizeof(const char *)*(n+1))) == NULL)
+ return;
+
+ for (n=0, p=conflict_list; p; p=p->next)
+ if (p->event_id && (!p->address ||
+ strcmp(p->address, "@") == 0))
+ {
+ l[n]=p->event_id;
+ ++n;
+ }
+
+ l[n]=0;
+ memset(&r, 0, sizeof(r));
+ r.event_id_list=l;
+ r.callback_headers_func=save_conflict_subj;
+
+ if (n == 0 || pcp_retr(pcp, &r) == 0)
+ {
+ printf("<table border=\"0\" width=\"100%%\" class=\"small-error\">");
+ for (p=conflict_list; p; p=p->next)
+ {
+ char buffer[512];
+
+ printf("<tr><td width=\"30\">&nbsp;</td><td><span class=\"tt\">");
+ if (pcp_fmttimerange(buffer, sizeof(buffer),
+ p->start,
+ p->end) == 0)
+ {
+ print_safe(buffer);
+ }
+ printf("</span></td><td width=\"30\">&nbsp;</td><td width=\"100%%\"><span class=\"tt\">");
+ if (p->address && strcmp(p->address, "@"))
+ {
+ printf(getarg("CONFLICTERR2"));
+ print_safe(p->address);
+ }
+ else
+ print_event_subject("", p->subject
+ ? p->subject:"", 60);
+ printf("</span></td></tr>\n");
+ }
+ printf("<tr><td colspan=\"4\"><hr width=\"90%%\" /></td></tr></table>\n");
+ }
+ free(l);
+ init_save_conflict();
+}
+
+static int save_conflict_subj(struct PCP_retr *r, const char *h,
+ const char *v, void *dummy)
+{
+ struct conflict_list *p;
+
+ if (strcasecmp(h, "subject") || !v)
+ return (0);
+
+ for (p=conflict_list; p; p=p->next)
+ if (p->event_id && strcmp(p->event_id, r->event_id) == 0)
+ {
+ if (p->subject)
+ free(p->subject);
+ if ((p->subject=strdup(v)) != NULL)
+ collapse_subject(p->subject);
+ break;
+ }
+ return (0);
+}
+
+/* -------------------------------------------------- */
+
+static void showerror()
+{
+ const char *p;
+
+ p=cgi("error");
+ if (strcmp(p, "quota") == 0)
+ printf("%s", getarg("QUOTAERR"));
+ if (strcmp(p, "time") == 0)
+ printf("%s", getarg("NOTIMEERR"));
+ if (strcmp(p, "conflict") == 0)
+ {
+ struct PCP *pcp=sqpcp_calendar();
+
+ printf("%s", getarg("CONFLICTERR"));
+ if (pcp)
+ show_conflict_error(pcp);
+ }
+ if (strcmp(p, "calendar") == 0)
+ {
+ printf(getarg("CALENDARERR"), cgi("pcperror"));
+ }
+ if (strcmp(p, "locked") == 0)
+ printf("%s", getarg("LOCKERR"));
+ if (strcmp(p, "notfound") == 0)
+ printf("%s", getarg("NOTFOUNDERR"));
+ if (strcmp(p, "eventlocked") == 0)
+ printf("%s", getarg("EVENTLOCKEDERR"));
+}
+
+#if 0
+/*
+** Create/Estimate the To: header which lists event participants.
+*/
+
+static void mktohdr(char *h, size_t *sizep)
+{
+ int need_toh=1;
+ size_t l=0;
+ struct my_participant *p;
+
+ *sizep=4;
+
+ for (p=my_participant_list; p; p=p->next)
+ {
+ const char *c;
+
+ if (*sizep - l > 500)
+ {
+ if (h)
+ *h++='\n';
+ ++*sizep;
+ need_toh=1;
+ l= *sizep;
+ }
+
+ if (need_toh)
+ {
+ if (h)
+ {
+ strcpy(h, "To: ");
+ h += 4;
+ }
+ *sizep += 4;
+ need_toh=0;
+ }
+ else
+ {
+ if (h)
+ {
+ strcpy(h, ",\n ");
+ h += 4;
+ }
+ *sizep += 4;
+ }
+
+ if (p->name && *p->name)
+ {
+ if (h)
+ *h++='"';
+ ++*sizep;
+
+ for (c=p->name; *c; c++)
+ {
+ if (*c == '"' || *c == '\\')
+ {
+ if (h)
+ *h++ = '\\';
+ ++*sizep;
+ }
+ if (h)
+ *h++ = *c;
+ ++*sizep;
+ }
+
+ if (h)
+ {
+ *h++='"';
+ *h++=' ';
+ }
+ *sizep += 2;
+ }
+
+ if (h)
+ *h++='<';
+ ++*sizep;
+
+ for (c=p->address; *c; c++)
+ {
+ if (h)
+ *h++= *c;
+ ++*sizep;
+ }
+ if (h)
+ *h++='>';
+ ++*sizep;
+ }
+
+ if (my_participant_list)
+ {
+ if (h)
+ *h++='\n';
+ ++*sizep;
+ }
+
+ if (h)
+ *h++=0;
+ ++*sizep;
+}
+#endif
+
+void sqpcp_newevent()
+{
+ char *draftfilename;
+ int newdraftfd;
+ const char *p;
+ unsigned long prev_size=0;
+ FILE *oldfp=NULL;
+ int errflag=0;
+ int do_newevent= *cgi("do.neweventtime") ? 1:0;
+ int do_newparticipant= *cgi("do.addparticipant") ? 1:0;
+ int do_delevent=0;
+ int do_delparticipant= *cgi("do.delparticipant") ? 1:0;
+ time_t delstart=0, delend=0;
+
+ static char *draftmessage_buf=0;
+
+ showerror();
+
+ if (!do_newevent)
+ {
+ if ((p=cgi("do.deleventtime")) && *p)
+ {
+ unsigned long a, b;
+
+ if (sscanf(p, "%lu-%lu", &a, &b) == 2)
+ {
+ delstart=(time_t)a;
+ delend=(time_t)b;
+ do_delevent=1;
+ }
+ }
+ }
+
+ p=cgi("draftmessage");
+
+ if (!do_newevent && !do_delevent && !do_newparticipant
+ && !do_delparticipant)
+ {
+ if (*cgi("do.neweventpreview") && p && *p)
+ {
+ printf("<table width=\"100%%\" border=\"0\" cellspacing=\"0\" cellpadding=\"1\" class=\"box-small-outer\"><tr><td>\n");
+ printf("<table width=\"100%%\" border=\"0\" cellspacing=\"0\" cellpadding=\"4\" class=\"preview\"><tr><td>\n");
+ newmsg_preview(p);
+ printf("</td></tr></table>\n");
+ printf("</td></tr></table>\n");
+ }
+ return;
+ }
+
+ if (p && *p)
+ {
+ oldfp=openoldfp(p, &prev_size);
+ if (!oldfp)
+ p="";
+ }
+
+ if (p && *p)
+ {
+ newdraftfd=maildir_recreatemsg(INBOX "." DRAFTS, p, &draftfilename);
+ }
+ else
+ {
+ newdraftfd=maildir_createmsg(INBOX "." DRAFTS, 0, &draftfilename);
+ maildir_writemsgstr(newdraftfd, "X-Event: 1\n");
+
+ if (draftmessage_buf)
+ free(draftmessage_buf);
+ if ((draftmessage_buf=strdup(draftfilename)) != 0)
+ cgi_put("draftmessage", draftmessage_buf);
+ }
+
+ if (do_newevent || do_delevent || do_newparticipant
+ || do_delparticipant)
+ {
+ if (oldfp)
+ {
+ struct rfc822hdr h;
+
+ rfc822hdr_init(&h, 8192);
+ while (rfc822hdr_read(&h, oldfp, NULL, 0) == 0)
+ {
+ unsigned long a, b;
+ if (do_delevent && strcasecmp(h.header,
+ "X-Event-Time")
+ == 0 && h.value && sscanf(h.value,
+ "%lu %lu",
+ &a, &b) == 2)
+
+ {
+ if ( (time_t)a == delstart &&
+ (time_t)b == delend)
+ continue;
+ }
+
+ if (do_delparticipant
+ && strcasecmp(h.header, "X-Event-Participant")
+ == 0 && h.value)
+ {
+ if (strcmp(h.value,
+ cgi("do.delparticipant"))
+ == 0)
+ continue;
+ }
+ if (strcasecmp(h.header,
+ "X-Event-Participant") == 0 &&
+ h.value)
+ add_my_participant(h.value);
+
+ if (strcasecmp(h.header, "To") == 0)
+ continue;
+ /* To: header rebuilt later */
+
+ maildir_writemsgstr(newdraftfd, h.header);
+ maildir_writemsgstr(newdraftfd, ": ");
+ maildir_writemsgstr(newdraftfd, h.value);
+ maildir_writemsgstr(newdraftfd, "\n");
+ }
+ rfc822hdr_free(&h);
+
+ }
+
+ if (do_newevent && addtime(newdraftfd))
+ {
+ errflag=1;
+ printf("%s", getarg("TIMEERR"));
+ }
+
+ if (do_newparticipant)
+ {
+ const char *p=cgi("addressbookname");
+
+ if (!*p)
+ p=cgi("participant1");
+
+ addparticipant(newdraftfd, p);
+ }
+
+ (void)ab_get_nameaddr(save_participant_names,
+ my_participant_list);
+
+#if 0
+ {
+ struct rfc822t *t;
+ struct rfc822a *a;
+ char *p;
+ char *tohdr;
+ size_t tohdr_size;
+
+ mktohdr(NULL, &tohdr_size);
+
+ tohdr=malloc(tohdr_size);
+ if (!tohdr)
+ enomem();
+
+ mktohdr(tohdr, &tohdr_size);
+
+ if ((t=rfc822t_alloc_new(tohdr, NULL, NULL)) == NULL)
+ {
+ free(tohdr);
+ enomem();
+ }
+
+ if ((a=rfc822a_alloc(t)) == NULL)
+ {
+ rfc822t_free(t);
+ free(tohdr);
+ enomem();
+ }
+
+ p=rfc2047_encode_header(a,sqwebmail_content_charset);
+
+ if (!p)
+ {
+ rfc822a_free(a);
+ rfc822t_free(t);
+ free(tohdr);
+ enomem();
+ }
+ free(tohdr);
+ tohdr=p;
+ rfc822a_free(a);
+ rfc822t_free(t);
+
+ maildir_writemsgstr(newdraftfd, tohdr);
+ maildir_writemsgstr(newdraftfd, "\n");
+ free(tohdr);
+ }
+#endif
+
+ sqpcp_eventend(); /* Deallocate participant list */
+
+ maildir_writemsgstr(newdraftfd, "\n");
+ }
+
+ if (oldfp)
+ {
+ char buf[BUFSIZ];
+ int n;
+
+ while ((n=fread(buf, 1, sizeof(buf), oldfp)) > 0)
+ maildir_writemsg(newdraftfd, buf, n);
+ }
+
+ if (oldfp)
+ fclose(oldfp);
+
+ if (errflag)
+ {
+ maildir_deletenewmsg(newdraftfd, INBOX "." DRAFTS, draftfilename);
+ }
+ else if (maildir_closemsg(newdraftfd, INBOX "." DRAFTS, draftfilename, 1,
+ prev_size))
+ {
+ printf("%s", getarg("QUOTAERR"));
+ }
+
+ free(draftfilename);
+}
+
+/* Split apart date/time string into space-separated words */
+
+static char **mkargv(char *p, int *argc)
+{
+ int pass;
+ char **argv=0;
+ char *q;
+
+ /* Two passes - count words, then make them */
+
+ for (pass=0; pass<2; pass++)
+ {
+ if (pass)
+ {
+ if ((argv=malloc(sizeof(char *)* (*argc+1))) == NULL)
+ return (NULL);
+ }
+ *argc=0;
+
+ for (q=p; *q; )
+ {
+ if (isspace((int)(unsigned char)*q))
+ {
+ ++q;
+ continue; /* Skip leading space */
+ }
+
+ if (pass)
+ argv[ *argc ] = q;
+ ++*argc;
+
+ while (*q) /* Look for next space */
+ {
+ if (isspace((int)(unsigned char)*q))
+ {
+ if (pass)
+ *q=0;
+ ++q;
+ break;
+ }
+ ++q;
+ }
+ }
+ }
+ argv[ *argc ] = 0;
+ return (argv);
+}
+
+static int savetime(time_t, time_t, void *);
+
+static int addtime(int newdraftfd)
+{
+ struct pcp_parse_datetime_info pdi;
+ char *t_buf;
+ char **t_argv;
+ time_t starttime, endtime;
+ int argn, argc;
+ int h, m;
+
+ pdi.today_name=cgi("today"); /* Locale string */
+ pdi.tomorrow_name=cgi("tomorrow"); /* Locale string */
+
+ t_buf=strdup(cgi("starttime"));
+ if (!t_buf)
+ return (-1);
+ t_argv=mkargv(t_buf, &argc);
+ if (!t_argv)
+ {
+ free(t_buf);
+ return (-1);
+ }
+
+ argn=0;
+ starttime=pcp_parse_datetime(&argn, argc, t_argv, &pdi);
+ free(t_argv);
+ free(t_buf);
+ if (!starttime)
+ return (-1);
+ h=atoi(cgi("hours"));
+ m=atoi(cgi("mins"));
+ if (h < 0 || m < 0 || h > 999 || m > 59)
+ return (-1);
+
+ endtime=starttime + h * 60 * 60 + m * 60;
+
+ argn=0;
+ t_buf=strdup(cgi("endtime"));
+ if (!t_buf)
+ return (-1);
+ t_argv=mkargv(t_buf, &argc);
+ if (!t_argv)
+ {
+ free(t_buf);
+ return (-1);
+ }
+
+ if (argc == 0) /* Not a weekly event */
+ {
+ savetime(starttime, endtime, &newdraftfd);
+ }
+ else
+ {
+ if (pcp_parse_datetime_until(starttime, endtime, &argn, argc,
+ t_argv,
+ atoi(cgi("recurring")),
+ savetime, &newdraftfd))
+ {
+ free(t_argv);
+ free(t_buf);
+ return (-1);
+ }
+ }
+ free(t_argv);
+ free(t_buf);
+ return (0);
+}
+
+static int savetime(time_t from, time_t to, void *dummy)
+{
+ char buf[NUMBUFSIZE];
+ int fd= *(int *)dummy;
+
+ maildir_writemsgstr(fd, "X-Event-Time: ");
+ maildir_writemsgstr(fd, libmail_str_time_t(from, buf));
+ maildir_writemsgstr(fd, " ");
+ maildir_writemsgstr(fd, libmail_str_time_t(to, buf));
+ maildir_writemsgstr(fd, "\n");
+ return (0);
+}
+
+static void addparticipant(int fd, const char *n)
+{
+ char *nn, *p, *q;
+
+ const char *domain;
+
+ domain=getenv("AUTHADDR");
+
+ if (domain)
+ domain=strrchr(domain, '@');
+
+ if (domain)
+ ++domain;
+ else
+ domain=myhostname();
+
+ while (n && isspace((int)(unsigned char)*n))
+ ++n;
+
+ if (!n || !*n)
+ return;
+
+ if (strchr(n, '\n') || strchr(n, '\r'))
+ return;
+
+ if ((nn=malloc(strlen(n)+strlen(domain)+2)) == NULL)
+ enomem();
+
+ strcpy(nn, n);
+
+ for (p=q=nn; *p; p++)
+ if (!isspace((int)(unsigned char)*p))
+ q=p+1;
+ *q=0;
+
+ if (strchr(nn, '@') == 0)
+ strcat(strcat(nn, "@"), domain);
+
+
+ maildir_writemsgstr(fd, "X-Event-Participant: ");
+ maildir_writemsgstr(fd, nn);
+ maildir_writemsgstr(fd, "\n");
+ add_my_participant(nn);
+ free(nn);
+}
+
+/* ------------- Save text ------------- */
+
+static char *savedraft()
+{
+ const char *p=cgi("draftmessage");
+ char *msg, *filename;
+
+ if (p && *p)
+ {
+ CHECKFILENAME(p);
+ }
+
+ filename=p && *p ? maildir_find(INBOX "." DRAFTS, p):NULL;
+
+ msg=newmsg_createdraft_do(filename, cgi("message"), NEWMSG_PCP);
+ if (filename)
+ free(filename);
+
+ if (!msg)
+ enomem();
+ return (msg);
+}
+
+static void previewdraft(char *msg, void (*func)(const char *))
+{
+ char *msg2, *msg2p;
+
+ msg2=maildir_find(INBOX "." DRAFTS, msg);
+ free(msg);
+ if (!msg2)
+ enomem();
+ if ((msg2p=strrchr(msg2, '/')) != 0)
+ ++msg2p;
+ else
+ msg2p=msg2;
+
+ cgi_put("draftmessage", msg2p);
+ if (func)
+ (*func)(msg2p);
+ output_form("newevent.html");
+ free(msg2);
+}
+
+void sqpcp_preview()
+{
+ char *msg;
+
+ msg=savedraft();
+ previewdraft(msg, NULL);
+}
+
+void sqpcp_postpone()
+{
+ char *msg;
+
+ msg=savedraft();
+ free(msg);
+ output_form("folders.html");
+}
+
+static void deleteattach(const char *);
+
+void sqpcp_deleteattach()
+{
+ char *msg;
+
+ msg=savedraft();
+ previewdraft(msg, deleteattach);
+}
+
+static void deleteattach(const char *msg)
+{
+ attach_delete(msg);
+}
+
+static void doupload(const char *);
+
+void sqpcp_uploadattach()
+{
+ char *msg;
+
+ msg=savedraft();
+ previewdraft(msg, doupload);
+}
+
+static void doupload(const char *msg)
+{
+ int flag;
+
+ flag=attach_upload(msg, NULL, NULL);
+
+ if (flag)
+ cgi_put("error", "quota");
+}
+
+
+void sqpcp_attachpubkey()
+{
+ char *msg;
+
+ msg=savedraft();
+ previewdraft(msg, NULL);
+}
+
+void sqpcp_attachprivkey()
+{
+ char *msg;
+
+ msg=savedraft();
+ previewdraft(msg, NULL);
+}
+
+/* ---------------- Save event ------------------ */
+
+struct participant_list {
+ struct participant_list *next;
+ char *address;
+} ;
+
+struct saveinfo {
+ struct PCP_event_time *times;
+ unsigned n_times;
+ struct PCP_event_participant *participants;
+ unsigned n_participants;
+ struct participant_list *participant_list;
+
+ char *old_eventid;
+} ;
+
+static int init_saveinfo(struct saveinfo *si, FILE *fp)
+{
+ struct rfc822hdr h;
+
+ struct savetimelist {
+ struct savetimelist *next;
+ struct PCP_event_time event_time;
+ } *tlist=NULL, *p;
+ unsigned tcnt=0;
+ struct participant_list *l;
+
+
+ si->times=NULL;
+ si->n_times=0;
+ si->old_eventid=0;
+ si->participants=NULL;
+ si->n_participants=0;
+ si->participant_list=NULL;
+
+ rfc822hdr_init(&h, BUFSIZ);
+ while (rfc822hdr_read(&h, fp, NULL, 0) == 0)
+ {
+ unsigned long a, b;
+
+ if (strcasecmp(h.header, "X-Event-Participant") == 0 && h.value)
+ {
+ l=malloc(sizeof(struct participant_list));
+ if (!l || (l->address=strdup(h.value)) == NULL)
+ {
+ if (l)
+ free(l);
+
+ while ((l=si->participant_list) != NULL)
+ {
+ si->participant_list=l->next;
+ free(l->address);
+ free(l);
+ }
+ while (tlist)
+ {
+ p=tlist;
+ tlist=p->next;
+ free(p);
+ }
+ rfc822hdr_free(&h);
+ return (-1);
+ }
+ l->next=si->participant_list;
+ si->participant_list=l;
+ ++si->n_participants;
+ }
+ else if (strcasecmp(h.header, "X-Event-Time") == 0 &&
+ h.value && sscanf(h.value, "%lu %lu", &a, &b) == 2)
+ {
+ if ((p=malloc(sizeof(struct savetimelist))) == NULL)
+ {
+ while ((l=si->participant_list) != NULL)
+ {
+ si->participant_list=l->next;
+ free(l->address);
+ free(l);
+ }
+ while (tlist)
+ {
+ p=tlist;
+ tlist=p->next;
+ free(p);
+ }
+ rfc822hdr_free(&h);
+ return (-1);
+ }
+ p->next=tlist;
+ tlist=p;
+ p->event_time.start=a;
+ p->event_time.end=b;
+ ++tcnt;
+ }
+
+ if (strcasecmp(h.header, "X-Old-EventId") == 0 && h.value)
+ {
+ if (si->old_eventid)
+ free(si->old_eventid);
+ si->old_eventid=strdup(h.value);
+ if (!si->old_eventid)
+ {
+ rfc822hdr_free(&h);
+ return (-1);
+ }
+ }
+ }
+ rfc822hdr_free(&h);
+
+ if (si->n_participants)
+ {
+ unsigned n=0;
+
+ if ((si->participants
+ =calloc(sizeof(struct PCP_event_participant),
+ si->n_participants)) == NULL)
+ {
+ while ((l=si->participant_list) != NULL)
+ {
+ si->participant_list=l->next;
+ free(l->address);
+ free(l);
+ }
+ while (tlist)
+ {
+ p=tlist;
+ tlist=p->next;
+ free(p);
+ }
+ return (-1);
+ }
+
+ for (l=si->participant_list; l; l=l->next)
+ {
+ si->participants[n].address=l->address;
+ ++n;
+ }
+ }
+
+ if (tcnt)
+ {
+ si->n_times=tcnt;
+ if ((si->times=malloc(sizeof(struct PCP_event_time)
+ *tcnt)) == NULL)
+ {
+ while ((l=si->participant_list) != NULL)
+ {
+ si->participant_list=l->next;
+ free(l->address);
+ free(l);
+ }
+ while (tlist)
+ {
+ p=tlist;
+ tlist=p->next;
+ free(p);
+ }
+ if (si->old_eventid)
+ free(si->old_eventid);
+ return (-1);
+ }
+ tcnt=0;
+ while (tlist)
+ {
+ p=tlist;
+ tlist=p->next;
+ si->times[tcnt]=p->event_time;
+ free(p);
+ ++tcnt;
+ }
+ }
+ return (0);
+}
+
+static void free_saveinfo(struct saveinfo *si)
+{
+ struct participant_list *l;
+
+ if (si->participants)
+ free(si->participants);
+
+ while ((l=si->participant_list) != NULL)
+ {
+ si->participant_list=l->next;
+ free(l->address);
+ free(l);
+ }
+
+ if (si->times)
+ free(si->times);
+ if (si->old_eventid)
+ free(si->old_eventid);
+}
+
+static void dropquota(const char *filename, int fd)
+{
+ unsigned long filesize=0;
+
+ if (maildir_parsequota(filename, &filesize))
+ {
+ struct stat stat_buf;
+
+ if (fstat(fd, &stat_buf))
+ stat_buf.st_size=0;
+ filesize=stat_buf.st_size;
+ }
+
+ maildir_quota_deleted(".", (int64_t)-filesize, -1);
+}
+
+/* ------------------------------------------------------------------------
+**
+** Save a calendar event
+** ----------------------------------------------------------------------*/
+
+static int dosave(FILE *, struct saveinfo *);
+
+void sqpcp_save()
+{
+ char *msg, *sentmsg, *p;
+ FILE *fp;
+ struct saveinfo si;
+ int isgpgerr;
+
+ msg=savedraft();
+ if (*cgi("error")) /* Error, go back to the screen */
+ {
+ previewdraft(msg, NULL);
+ return;
+ }
+
+ fp=openoldfp(msg, NULL);
+ if (!fp)
+ {
+ free(msg);
+ enomem();
+ }
+
+ if (init_saveinfo(&si, fp))
+ {
+ fclose(fp);
+ free(msg);
+ enomem();
+ }
+
+ if (si.times == 0)
+ {
+ fclose(fp);
+ cgi_put("error", "time");
+ previewdraft(msg, NULL);
+ return;
+ }
+ fclose(fp);
+
+ sentmsg=newmsg_createsentmsg(msg, &isgpgerr);
+
+ /* Immediately remove the formatted event text from the sent folder */
+
+ if (sentmsg)
+ {
+ p=maildir_find(INBOX "." SENT, sentmsg);
+ free(sentmsg);
+ sentmsg=p;
+ }
+
+ if (!sentmsg)
+ {
+ cgi_put("error", "quota"); /* TODO: gpgerr */
+ free_saveinfo(&si);
+ previewdraft(msg, NULL);
+ return;
+ }
+
+
+ fp=fopen(sentmsg, "r");
+ if (!fp)
+ {
+ free(sentmsg);
+ free(msg);
+ free_saveinfo(&si);
+ enomem();
+ return;
+ }
+
+ fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+
+ unlink(sentmsg);
+ dropquota(sentmsg, fileno(fp));
+ free(sentmsg);
+
+ if (dosave(fp, &si))
+ {
+ fclose(fp);
+ free_saveinfo(&si);
+ previewdraft(msg, NULL);
+ return;
+ }
+ fclose(fp);
+
+ p=maildir_find(INBOX "." DRAFTS, msg);
+ free(msg);
+
+ fp=p ? fopen(p, "r"):NULL;
+ unlink(p);
+ if (fp)
+ {
+ dropquota(p, fileno(fp));
+ fclose(fp);
+ }
+ free(p);
+ free_saveinfo(&si);
+ output_form("folders.html");
+}
+
+/* With all the preliminaries out of the way, put it on the calendar */
+
+static void saveerror(struct PCP *pcp, const int *xerror)
+{
+ static char *errmsgbuf=0;
+ const char *p;
+
+ if (xerror)
+ switch (*xerror) {
+ case PCP_ERR_SYSERR: /* Can be deliberate, see addacl() */
+ cgi_put("error", "calendar");
+ cgi_put("pcperror", strerror(errno));
+ return;
+ case PCP_ERR_LOCK:
+ cgi_put("error", "locked");
+ return;
+ case PCP_ERR_CONFLICT:
+ cgi_put("error", "conflict");
+ return;
+ case PCP_ERR_EVENTNOTFOUND:
+ cgi_put("error", "notfound");
+ return;
+ case PCP_ERR_EVENTLOCKED:
+ cgi_put("error", "eventlocked");
+ return;
+ }
+
+ cgi_put("error", "calendar");
+
+ if (errmsgbuf)
+ free(errmsgbuf);
+
+ /*
+ ** Save err msg into a static buffer, because err msg text memory
+ ** may go away after the handle is closed.
+ */
+
+ p=pcp_errmsg(pcp);
+ errmsgbuf=strdup(p ? p:"");
+ cgi_put("pcperror", errmsgbuf);
+}
+
+struct proxy_list_entry {
+ struct proxy_list_entry *next;
+ char *userid;
+} ;
+
+struct proxy_update_list {
+ struct proxy_list_entry *new_list;
+ struct proxy_list_entry *delete_list;
+} ;
+
+static void proxy_update_list_save(const char *action,
+ const char *userid,
+ void *voidarg)
+{
+ struct proxy_update_list *pul=(struct proxy_update_list *)voidarg;
+
+ struct proxy_list_entry **eptr, *e;
+
+ if (strcmp(action, "NEW") == 0)
+ eptr= &pul->new_list;
+ else if (strcmp(action, "DELETE") == 0)
+ eptr= &pul->delete_list;
+ else
+ return;
+
+ while (*eptr && strcmp( (*eptr)->userid, userid) == 0)
+ eptr= &(*eptr)->next;
+
+ if ((e=malloc(sizeof(struct proxy_list_entry))) == NULL ||
+ (e->userid=strdup(userid)) == NULL)
+ {
+ if (e)
+ free(e);
+ fprintf(stderr, "CRIT: out of memory.\n");
+ return;
+ }
+
+ e->next= *eptr;
+ *eptr=e;
+}
+
+static void proxy_update_list_free(struct proxy_update_list *p)
+{
+ struct proxy_list_entry *e;
+
+ while ((e=p->new_list) != NULL)
+ {
+ p->new_list=e->next;
+ free(e->userid);
+ free(e);
+ }
+
+ while ((e=p->delete_list) != NULL)
+ {
+ p->delete_list=e->next;
+ free(e->userid);
+ free(e);
+ }
+}
+
+static void proxy_notify_email_msg(FILE *, struct proxy_list_entry *,
+ const char *,
+ const struct PCP_event_time *,
+ unsigned);
+
+static void proxy_notify_email(FILE *f, struct proxy_update_list *pul,
+ const struct PCP_event_time *t,
+ unsigned tn)
+{
+ proxy_notify_email_msg(f, pul->new_list, "eventnotifynew.txt",
+ t, tn);
+ proxy_notify_email_msg(f, pul->delete_list, "eventnotifydelete.txt",
+ NULL, 0);
+}
+
+static void dosendnotice(FILE *, FILE *, FILE *, struct proxy_list_entry *,
+ const char *, const struct PCP_event_time *,
+ unsigned);
+
+static void proxy_notify_email_msg(FILE *f, struct proxy_list_entry *l,
+ const char *template,
+ const struct PCP_event_time *t,
+ unsigned tn)
+{
+ FILE *tmpfp;
+ pid_t p, p2;
+ int waitstat;
+ int pipefd[2];
+ FILE *tofp;
+ const char *returnaddr=login_returnaddr();
+ char subjectlabel[100];
+
+ if (!l)
+ return;
+
+ if (fseek(f, 0L, SEEK_SET) < 0
+ || lseek(fileno(f), 0L, SEEK_SET) < 0)
+ {
+ fprintf(stderr, "CRIT: seek failed: %s\n", strerror(errno));
+ return;
+ }
+
+ subjectlabel[0]=0;
+
+ if ((tmpfp=open_langform(sqwebmail_content_language,
+ "eventnotifysubject.txt", 0)) != NULL)
+ {
+ if (fgets(subjectlabel, sizeof(subjectlabel), tmpfp) == NULL)
+ subjectlabel[0]=0;
+ else
+ {
+ char *p=strchr(subjectlabel, '\n');
+
+ if (p) *p=0;
+ }
+ fclose(tmpfp);
+ }
+ if (subjectlabel[0] == 0)
+ strcpy(subjectlabel, "[calendar]");
+
+ if ((tmpfp=open_langform(sqwebmail_content_language, template, 0))
+ == NULL)
+ {
+ fprintf(stderr, "CRIT: %s: %s\n", template, strerror(errno));
+ return;
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ if (pipe(pipefd) < 0)
+ {
+ fclose(tmpfp);
+ fprintf(stderr, "CRIT: pipe: %s\n", strerror(errno));
+ return;
+ }
+
+ p=fork();
+
+ if (p < 0)
+ {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ fclose(tmpfp);
+ fprintf(stderr, "CRIT: fork: %s\n", strerror(errno));
+ return;
+ }
+
+ if (p == 0)
+ {
+ dup2(pipefd[0], 0);
+ close(pipefd[0]);
+ close(pipefd[1]);
+ execl(SENDITSH, "sendit.sh", returnaddr,
+ sqwebmail_mailboxid, NULL);
+ fprintf(stderr, "CRIT: exec " SENDITSH ": %s\n", strerror(errno));
+ exit(1);
+ }
+ close(pipefd[0]);
+ if ((tofp=fdopen(pipefd[1], "w")) == NULL)
+ {
+ fprintf(stderr, "CRIT: exec " SENDITSH ": %s\n", strerror(errno));
+ }
+ else
+ {
+ dosendnotice(tofp, tmpfp, f, l, subjectlabel, t, tn);
+ }
+ fclose(tofp);
+ close(pipefd[1]);
+ fclose(tmpfp);
+
+ waitstat=256;
+ while ((p2=wait(&waitstat)) != p && p2 >= 0)
+ ;
+
+ if (!WIFEXITED(waitstat) || WEXITSTATUS(waitstat))
+ fprintf(stderr, "CRIT: event notify mail failed\n");
+}
+
+/*
+** Ok, the preliminaries are out of the way, now spit it out.
+*/
+
+static void dosendnotice(FILE *tofp, /* Pipe to sendit.sh */
+ FILE *tmpfp, /*
+ ** Template file with MIME headers and
+ ** canned verbiage
+ */
+ FILE *eventfp, /* Original event */
+ struct proxy_list_entry *idlist,
+ const char *subjectlabel,
+ const struct PCP_event_time *time_list,
+ unsigned n_time_list)
+{
+ struct rfc822hdr h;
+ const char *p;
+ int c;
+ unsigned u;
+
+ rfc822hdr_init(&h, 8192);
+
+ while (rfc822hdr_read(&h, eventfp, NULL, 0) == 0)
+ {
+ if (strcasecmp(h.header, "From") == 0 ||
+ strcasecmp(h.header, "Date") == 0)
+ {
+ fprintf(tofp, "%s: %s\n", h.header,
+ h.value ? h.value:"");
+ }
+ else if (strcasecmp(h.header, "Subject") == 0)
+ {
+ fprintf(tofp, "%s: %s %s\n", h.header,
+ subjectlabel,
+ h.value ? h.value:"");
+ }
+ }
+ rfc822hdr_free(&h);
+
+ p="To: ";
+
+ while (idlist)
+ {
+ fprintf(tofp, "%s%s", p, idlist->userid);
+ p=",\n ";
+ idlist=idlist->next;
+ }
+ fprintf(tofp, "\n");
+
+ while ((c=getc(tmpfp)) != EOF)
+ putc(c, tofp);
+
+ for (u=0; u<n_time_list; u++)
+ {
+ char buffer[200];
+
+ if (pcp_fmttimerange(buffer, sizeof(buffer),
+ time_list[u].start,
+ time_list[u].end) == 0)
+ {
+ fprintf(tofp, " %s\n", buffer);
+ }
+ }
+}
+
+static int dosave(FILE *fp, struct saveinfo *si)
+{
+ struct PCP *pcp=sqpcp_calendar();
+ struct PCP_save_event se;
+ struct PCP_new_eventid *nei;
+ struct PCP_commit c;
+
+ struct proxy_update_list pul;
+
+ if (!pcp)
+ {
+ cgi_put("error", "calendar"); /* TODO: actual error */
+ cgi_put("pcperror", "");
+ return (-1);
+ }
+
+ memset(&se, 0, sizeof(se));
+ se.write_event_fd=fileno(fp);
+ se.event_participants=si->participants;
+ se.n_event_participants=si->n_participants;
+
+ if (*cgi("okconflict"))
+ se.flags |= PCP_OK_CONFLICT;
+ if (*cgi("okerrors"))
+ se.flags |= PCP_OK_PROXY_ERRORS;
+
+ nei=pcp_new_eventid(pcp, si->old_eventid, &se);
+ if (!nei)
+ {
+ saveerror(pcp, NULL);
+ return (-1);
+ }
+
+ memset(&c, 0, sizeof(c));
+ c.event_times=si->times;
+ c.n_event_times=si->n_times;
+ c.flags=se.flags;
+
+ init_save_conflict();
+ c.add_conflict_callback=save_conflict;
+
+ memset(&pul, 0, sizeof(pul));
+
+ c.proxy_callback= &proxy_update_list_save;
+ c.proxy_callback_ptr= &pul;
+
+ if (pcp_commit(pcp, nei, &c))
+ {
+ proxy_update_list_free(&pul);
+ saveerror(pcp, &c.errcode);
+ pcp_destroy_eventid(pcp, nei);
+ return (-1);
+ }
+ pcp_destroy_eventid(pcp, nei);
+ unlink(CACHE); /* Have it rebuilt */
+ proxy_notify_email(fp, &pul, c.event_times, c.n_event_times);
+ proxy_update_list_free(&pul);
+ refreshcache(pcp);
+ return (0);
+}
+
+/* -------------- Daily stuff --------------- */
+
+void sqpcp_todays_date()
+{
+ unsigned y, m, d;
+ time_t start;
+ time_t end;
+ char buf[100];
+
+ if (sscanf(cgi("date"), "%4u%2u%2u", &y, &m, &d) != 3
+ || pcp_parse_ymd(y, m, d, &start, &end)
+ || pcp_fmttime(buf, sizeof(buf), start, FMTTIME_DATE))
+ return;
+
+ print_safe(buf);
+}
+
+void sqpcp_todays_date_verbose()
+{
+ unsigned y, m, d;
+ time_t start;
+ time_t end;
+ char buf[500];
+ struct tm *tmptr;
+
+ if (sscanf(cgi("date"), "%4u%2u%2u", &y, &m, &d) != 3
+ || pcp_parse_ymd(y, m, d, &start, &end)
+ || (tmptr=localtime(&start)) == NULL
+ || strftime(buf, sizeof(buf), getarg("DATEFORMAT"), tmptr) == 0)
+ return;
+
+ print_safe(buf);
+}
+
+void sqpcp_weeklylink()
+{
+ output_scriptptrget();
+ printf("&amp;form=eventweekly&amp;weekof=%s", cgi("date"));
+}
+
+void sqpcp_monthlylink()
+{
+ const char *p=cgi("date");
+
+ if (!*p)
+ p=cgi("weekof");
+ output_scriptptrget();
+ printf("&amp;form=eventmonthly&amp;monthof=%s", p);
+}
+
+#define VIEW_DAILY 0
+#define VIEW_WEEKLY 1
+#define VIEW_MONTHLY 2
+
+static void do_daily_view(struct cacherecord *, unsigned, int,
+ time_t *, time_t *);
+
+static void show_pcp_errmsg(const char *p)
+{
+ printf("<pre class=\"error\">");
+ output_attrencoded_oknl_fp(p, stdout);
+ printf("</pre>");
+}
+
+void sqpcp_daily_view()
+{
+ unsigned y, m, d;
+ time_t start;
+ time_t end;
+
+ struct PCP *pcp;
+ struct cacherecord *recs;
+ unsigned n_recs;
+
+ if (*cgi("clearcache"))
+ unlink(CACHE);
+
+ if (sscanf(cgi("date"), "%4u%2u%2u", &y, &m, &d) != 3
+ || pcp_parse_ymd(y, m, d, &start, &end))
+ return;
+
+ if ((pcp=sqpcp_calendar()) == NULL)
+ {
+ printf("<span class=\"error\">%s</span>", strerror(errno));
+ return;
+ }
+
+ if (createcache(pcp, &recs, &n_recs, start, end))
+ {
+ show_pcp_errmsg(pcp_errmsg(pcp));
+ return;
+ }
+
+ (void)do_daily_view(recs, n_recs, VIEW_DAILY, NULL, NULL);
+ destroycache(recs, n_recs);
+}
+
+static void print_event_link_url(const char *id, const char *extra)
+{
+ output_scriptptrget();
+ printf("%s&amp;form=eventshow&amp;eventid=", extra);
+ output_urlencoded(id);
+ if (*cgi("date"))
+ printf("&amp;date=%s", cgi("date"));
+ if (*cgi("weekof"))
+ printf("&amp;weekof=%s", cgi("weekof"));
+ if (*cgi("monthof"))
+ printf("&amp;monthof=%s", cgi("monthof"));
+}
+
+static void print_event_link(const char *id, const char *extra,
+ const char *extra2)
+{
+ printf("<a href=\"");
+ print_event_link_url(id, extra);
+ printf("\" %s >", extra2);
+}
+
+static void do_daily_view(struct cacherecord *recs, unsigned n_recs,
+ int viewtype, time_t *start_ptr, time_t *end_ptr)
+{
+ unsigned i;
+ int printed=0;
+
+ printf("<table width=\"100%%\">");
+
+ for (i=0; i<n_recs; i++)
+ {
+ char date1[256];
+ char date2[256];
+
+ char time1[128];
+ char time2[128];
+
+ time_t start=recs[i].start;
+ time_t end=recs[i].end;
+
+ if (start_ptr && *start_ptr >= end)
+ continue;
+
+ if (end_ptr && *end_ptr <= start)
+ continue;
+
+ if ( start_ptr && *start_ptr > start)
+ start= *start_ptr;
+
+ if ( end_ptr && *end_ptr < end)
+ end= *end_ptr;
+
+ if (pcp_fmttime(date1, sizeof(date1),
+ start, FMTTIME_DATE))
+ continue;
+ if (pcp_fmttime(date2, sizeof(date2),
+ end, FMTTIME_DATE))
+ continue;
+
+ printed=1;
+
+ if (strcmp(date1, date2) && viewtype == VIEW_DAILY)
+ {
+ char timerange[512];
+
+ if (pcp_fmttimerange(timerange, sizeof(timerange),
+ start, end))
+ continue;
+
+ printf("<tr><td align=\"left\">");
+ print_event_link(recs[i].eventid, "", "class=\"dailyeventtimes\"");
+ print_safe(timerange);
+ }
+ else
+ {
+ if (pcp_fmttime(time1, sizeof(time1),
+ start,
+ FMTTIME_TIME))
+ continue;
+
+ if (pcp_fmttime(time2,
+ sizeof(time2),
+ end,
+ FMTTIME_TIME))
+ continue;
+
+ printf("<tr><td align=\"left\">");
+ print_event_link(recs[i].eventid, "", "class=\"dailyeventtimes\"");
+ print_safe(time1);
+ printf("&nbsp;-&nbsp;");
+ print_safe(time2);
+ }
+ printf("</a>");
+
+ if (viewtype == VIEW_DAILY)
+ {
+ printf("</td><td width=\"100%%\">");
+ }
+ else
+ printf("<br />");
+
+ printf("&nbsp;&nbsp;");
+ print_event_link(recs[i].eventid, "", "class=\"dailyeventsubject\"");
+ print_event_subject(recs[i].flags, recs[i].subject,
+ viewtype == VIEW_DAILY ? 80:15);
+ printf("</a>");
+ if (viewtype != VIEW_DAILY)
+ printf("<br />&nbsp;");
+
+ printf("</td></tr>\n");
+ }
+
+ if (!printed)
+ {
+ printf("<tr><td align=\"center\">%s</td></tr>",
+ getarg("NOEVENTS"));
+ }
+
+ printf("</table>\n");
+}
+
+void sqpcp_prevday()
+{
+ unsigned y, m, d;
+ time_t start;
+ time_t end;
+ struct tm *tm;
+ char buf[256];
+
+ if (sscanf(cgi("date"), "%4u%2u%2u", &y, &m, &d) != 3
+ || pcp_parse_ymd(y, m, d, &start, &end))
+ return;
+
+ start -= 12 * 60 * 60;
+
+ if ((tm=localtime(&start)) == NULL)
+ return;
+
+ y=tm->tm_year + 1900;
+ m=tm->tm_mon + 1;
+ d=tm->tm_mday;
+
+ if (pcp_parse_ymd(y, m, d, &start, &end)
+ || (tm=localtime(&start)) == NULL
+ || strftime(buf, sizeof(buf), getarg("PREVDAY"), tm) == 0)
+ return;
+
+ printf("<a class=\"dailynextprev\" href=\"");
+ output_scriptptrget();
+ printf("&amp;form=eventdaily&amp;date=%04d%02d%02d\">%s</a>", y, m, d, buf);
+}
+
+void sqpcp_nextday()
+{
+ unsigned y, m, d;
+ time_t start;
+ time_t end;
+ struct tm *tm;
+ char buf[256];
+
+ if (sscanf(cgi("date"), "%4u%2u%2u", &y, &m, &d) != 3
+ || pcp_parse_ymd(y, m, d, &start, &end))
+ return;
+
+ start=end;
+
+ if ((tm=localtime(&start)) == NULL)
+ return;
+
+ y=tm->tm_year + 1900;
+ m=tm->tm_mon + 1;
+ d=tm->tm_mday;
+
+ if (pcp_parse_ymd(y, m, d, &start, &end)
+ || (tm=localtime(&start)) == NULL
+ || strftime(buf, sizeof(buf), getarg("NEXTDAY"), tm) == 0)
+ return;
+
+ printf("<a class=\"dailynextprev\" href=\"");
+ output_scriptptrget();
+ printf("&amp;form=eventdaily&amp;date=%04d%02d%02d\">%s</a>", y, m, d, buf);
+}
+
+/* -------------- Display event stuff --------------- */
+
+struct display_retr_time_list {
+ struct display_retr_time_list *next;
+ time_t start;
+ time_t end;
+} ;
+
+struct display_retr_participant_list {
+ struct display_retr_participant_list *next;
+ char *participant;
+} ;
+
+struct display_retr {
+ FILE *f;
+
+ struct display_retr_time_list *time_list;
+ struct display_retr_participant_list *participant_list;
+
+} ;
+
+static void free_display_retr(struct display_retr *r)
+{
+}
+
+
+static int save_displayed_event(struct PCP_retr *, const char *, int, void *);
+
+static int set_status(struct PCP_retr *, int, void *);
+
+void sqpcp_displayeventinit()
+{
+ struct PCP *pcp;
+ const char *event_id_list[2];
+ struct PCP_retr r;
+
+ showerror();
+
+ pcp=sqpcp_calendar();
+
+ if (!pcp)
+ return;
+
+ event_id_list[0]=cgi("eventid");
+ event_id_list[1]=0;
+
+ init_save_conflict();
+ if (*cgi("docancel"))
+ {
+ if (pcp_cancel(pcp, event_id_list[0], NULL))
+ show_pcp_errmsg(pcp_errmsg(pcp));
+ unlink(CACHE);
+ }
+
+ if (*cgi("douncancel"))
+ {
+ struct PCP_uncancel u;
+ int flags=0;
+
+ memset(&u, 0, sizeof(u));
+
+ if (*cgi("okconflict"))
+ flags |= PCP_OK_CONFLICT;
+
+ u.uncancel_conflict_callback=save_conflict;
+ if (pcp_uncancel(pcp, event_id_list[0], flags, &u))
+ {
+ saveerror(pcp, &u.errcode);
+ showerror();
+ if (u.errcode == PCP_ERR_CONFLICT)
+ cgi_put("okconflict", "1");
+ }
+ unlink(CACHE);
+ }
+
+ memset(&r, 0, sizeof(r));
+ r.event_id_list=event_id_list;
+ r.callback_retr_status=set_status;
+
+ if (pcp_retr(pcp, &r))
+ {
+ show_pcp_errmsg(pcp_errmsg(pcp));
+ return;
+ }
+
+}
+
+static int set_status(struct PCP_retr *pcp, int status, void *dummy)
+{
+ cgi_put("event_cancelled", status & LIST_CANCELLED ? "1":"");
+ cgi_put("event_booked", status & LIST_BOOKED ? "1":"");
+ cgi_put("event_proxy", status & LIST_PROXY ? "1":"");
+ return (0);
+}
+
+static int save_displayed_date(struct PCP_retr *r, time_t start, time_t end,
+ void *vp)
+{
+ struct display_retr *dr=(struct display_retr *)vp;
+
+ struct display_retr_time_list **ptr, *p;
+
+ for (ptr= &dr->time_list; *ptr; ptr=&(*ptr)->next)
+ if ((*ptr)->start > start)
+ break;
+
+ if ((p=malloc(sizeof(struct display_retr_time_list))) == NULL)
+ return (-1);
+
+ p->next= *ptr;
+ *ptr=p;
+ p->start=start;
+ p->end=end;
+
+ return (0);
+}
+
+static int save_displayed_participants(struct PCP_retr *r, const char *address,
+ const char *dummy, void *vp)
+{
+ struct display_retr *dr=(struct display_retr *)vp;
+
+ struct display_retr_participant_list **ptr, *p;
+
+ for (ptr= &dr->participant_list; *ptr; ptr=&(*ptr)->next)
+ if (strcasecmp((*ptr)->participant, address) > 0)
+ break;
+
+ if ((p=malloc(sizeof(struct display_retr_participant_list))) == NULL)
+ return (-1);
+
+ if ((p->participant=strdup(address)) == NULL)
+ {
+ free(p);
+ return (-1);
+ }
+ p->next= *ptr;
+ *ptr=p;
+ return (0);
+}
+
+void sqpcp_displayevent()
+{
+ struct PCP *pcp=sqpcp_calendar();
+ const char *event_id_list[2];
+ struct PCP_retr r;
+ struct display_retr dr;
+ struct maildir_tmpcreate_info createInfo;
+ struct display_retr_time_list *tl;
+ struct display_retr_participant_list *pl;
+
+ if (!pcp)
+ return;
+
+ event_id_list[0]=cgi("eventid");
+ event_id_list[1]=0;
+
+ memset(&r, 0, sizeof(r));
+ memset(&dr, 0, sizeof(dr));
+ r.event_id_list=event_id_list;
+ r.callback_arg=&dr;
+ r.callback_rfc822_func=save_displayed_event;
+ r.callback_retr_date=save_displayed_date;
+ r.callback_retr_participants=save_displayed_participants;
+
+ maildir_purgemimegpg(); /* Delete previous :calendar: file */
+
+ maildir_tmpcreate_init(&createInfo);
+ createInfo.uniq=":calendar:";
+ createInfo.doordie=1;
+
+ if ((dr.f=maildir_tmpcreate_fp(&createInfo)) == NULL)
+ {
+ error(strerror(errno));
+ }
+
+ cgi_put(MIMEGPGFILENAME, strrchr(createInfo.tmpname, '/')+1);
+
+ if (pcp_retr(pcp, &r))
+ {
+ free_display_retr(&dr);
+ fclose(dr.f);
+ cgi_put(MIMEGPGFILENAME, "");
+ unlink(createInfo.tmpname);
+ maildir_tmpcreate_free(&createInfo);
+ show_pcp_errmsg(pcp_errmsg(pcp));
+ return;
+ }
+ fclose(dr.f);
+
+ printf("<table class=\"calendarevent\" align=\"center\" border=\"0\"><tr valign=\"top\"><th align=\"left\">%s</th><td>",
+ getarg("EVENT"));
+
+ for (tl=dr.time_list; tl; tl=tl->next)
+ {
+ char buffer[512];
+
+ if (pcp_fmttimerange(buffer, sizeof(buffer),
+ tl->start, tl->end))
+ continue;
+
+ printf("<span class=\"tt\">");
+ print_safe(buffer);
+ printf("</span><br />\n");
+ }
+ printf("</td></tr>\n");
+
+ if (dr.participant_list)
+ {
+ printf("<tr valign=\"top\"><th align=\"left\">%s</th><td>",
+ getarg("PARTICIPANTS"));
+ for (pl=dr.participant_list; pl; pl=pl->next)
+ {
+ printf("<span class=\"tt\">&lt;");
+ print_safe(pl->participant);
+ printf("&gt;</span><br />\n");
+ }
+ printf("</td></tr>");
+ }
+ printf("</table>\n");
+ folder_showmsg(INBOX "." DRAFTS, 0);
+ free_display_retr(&dr);
+ cgi_put(MIMEGPGFILENAME, "");
+ maildir_tmpcreate_free(&createInfo);
+}
+
+static int save_displayed_event(struct PCP_retr *r,
+ const char *buf, int cnt,
+ void *vp)
+{
+ if (fwrite( buf, cnt, 1, ((struct display_retr *)vp)->f) != 1)
+ return (-1);
+
+ return (0);
+}
+
+
+static void back_to_summary()
+{
+ const char *p;
+
+ if (*(p=cgi("monthof")) != 0)
+ {
+ printf("&amp;form=eventmonthly&amp;monthof=%s", p);
+ }
+ else if (*(p=cgi("weekof")) != 0)
+ {
+ printf("&amp;form=eventweekly&amp;weekof=%s", p);
+ }
+ else
+ {
+ p=cgi("date");
+ printf("&amp;form=eventdaily&amp;date=%s", p);
+ }
+}
+
+void sqpcp_eventbacklink()
+{
+ output_scriptptrget();
+ back_to_summary();
+}
+
+void sqpcp_eventeditlink()
+{
+ const char *p=cgi("date");
+
+ output_scriptptrget();
+ printf("&amp;form=event-edit&amp;date=%s&amp;eventid=", p);
+ output_urlencoded(cgi("eventid"));
+}
+
+void sqpcp_eventdeletelink()
+{
+ const char *p=cgi("date");
+
+ output_scriptptrget();
+ printf("&amp;eventid=");
+
+ output_urlencoded(cgi("eventid"));
+ printf("&amp;form=eventdelete&amp;date=%s", p);
+}
+
+void sqpcp_eventcanceluncancellink()
+{
+ print_event_link_url(cgi("eventid"), *cgi("event_cancelled")
+ ? (*cgi("okconflict") ?
+ "&amp;okconflict=1&amp;douncancel=1"
+ :"&amp;douncancel=1")
+ :"&amp;docancel=1");
+}
+
+void sqpcp_eventcanceluncancelimage()
+{
+ printf("%s", getarg(*cgi("event_cancelled")
+ ? "UNCANCELIMAGE":"CANCELIMAGE"));
+}
+
+void sqpcp_eventcanceluncanceltext()
+{
+ printf("%s", getarg(*cgi("event_cancelled")
+ ? "UNCANCELTEXT":"CANCELTEXT"));
+}
+
+void sqpcp_deleteeventinit()
+{
+ sqpcp_displayeventinit();
+ if (*cgi("event_proxy"))
+ printf("%s", getarg("PROXYWARN"));
+}
+
+static int save_orig_headers(struct PCP_retr *pcp,
+ const char *h, const char *v, void *vp)
+{
+ FILE *fp=(FILE *)vp;
+
+ if (strcasecmp(h, "Date"))
+ fprintf(fp, "%s: %s\n", h, v);
+ return (0);
+}
+
+
+void sqpcp_dodelete()
+{
+ struct PCP *pcp=sqpcp_calendar();
+ struct PCP_retr r;
+ const char *event_list_ary[2];
+ struct PCP_delete del;
+ struct proxy_update_list pul;
+ FILE *tmpfp;
+
+ if (!pcp)
+ return;
+
+ memset(&del, 0, sizeof(del));
+ del.id=cgi("eventid");
+
+ memset(&r, 0, sizeof(r));
+ event_list_ary[0]=del.id;
+ event_list_ary[1]=NULL;
+ r.event_id_list=event_list_ary;
+ r.callback_headers_func=save_orig_headers;
+
+ tmpfp=tmpfile();
+ if (!tmpfp)
+ enomem();
+ r.callback_arg=tmpfp;
+ pcp_retr(pcp, &r);
+
+ {
+ time_t t;
+
+ time(&t);
+
+ fprintf(tmpfp, "Date: %s\n\n", rfc822_mkdate(t));
+ }
+
+ memset(&pul, 0, sizeof(pul));
+ del.proxy_callback=&proxy_update_list_save;
+ del.proxy_callback_ptr=&pul;
+
+ if (pcp_delete(pcp, &del))
+ {
+ saveerror(pcp, &del.errcode);
+ output_form("eventdelete.html");
+ }
+ else
+ {
+ proxy_notify_email(tmpfp, &pul, NULL, 0);
+ }
+ fclose(tmpfp);
+ proxy_update_list_free(&pul);
+ unlink(CACHE);
+ output_form("eventdaily.html");
+}
+
+/* ------------- Bring in an event to edit -------------------------- */
+
+static int doeventedit(struct PCP *, int);
+
+int sqpcp_eventedit()
+{
+ struct PCP *pcp=sqpcp_calendar();
+ int newdraftfd;
+ char *draftfilename;
+
+ if (!pcp)
+ return (-1);
+
+ newdraftfd=maildir_createmsg(INBOX "." DRAFTS, 0, &draftfilename);
+ if (doeventedit(pcp, newdraftfd))
+ {
+ maildir_deletenewmsg(newdraftfd, INBOX "." DRAFTS, draftfilename);
+ }
+ else if (maildir_closemsg(newdraftfd, INBOX "." DRAFTS, draftfilename, 1, 0))
+ {
+ cgi_put("error", "quota");
+ }
+ else
+ {
+ static char *filenamebuf=0;
+
+ if (filenamebuf)
+ free(filenamebuf);
+
+ filenamebuf=draftfilename;
+ cgi_put("draftmessage", filenamebuf);
+ return (0);
+ }
+ free(draftfilename);
+ return (-1);
+}
+
+struct getedit_info {
+ int fd;
+ int flag;
+
+ int in_headers;
+ int sol;
+ int skiph;
+
+} ;
+
+static int get_date(struct PCP_retr *r, time_t start, time_t end, void *vp)
+{
+ char buf[NUMBUFSIZE];
+ struct getedit_info *ge=(struct getedit_info *)vp;
+
+ maildir_writemsgstr(ge->fd, "X-Event-Time: ");
+ maildir_writemsgstr(ge->fd, libmail_str_time_t(start, buf));
+ maildir_writemsgstr(ge->fd, " ");
+ maildir_writemsgstr(ge->fd, libmail_str_time_t(end, buf));
+ maildir_writemsgstr(ge->fd, "\n");
+
+ return (0);
+}
+
+static int get_participants(struct PCP_retr *r, const char *address,
+ const char *dummy, void *vp)
+{
+ struct getedit_info *ge=(struct getedit_info *)vp;
+
+ maildir_writemsgstr(ge->fd, "X-Event-Participant: ");
+ maildir_writemsgstr(ge->fd, address);
+ maildir_writemsgstr(ge->fd, "\n");
+
+ return (0);
+}
+
+static int get_msgtext(struct PCP_retr *r, const char *ptr, int n, void *vp)
+{
+ struct getedit_info *ge=(struct getedit_info *)vp;
+
+ /* We want to drop all X headers when we read in this event */
+ /* Also, drop CRs */
+
+ ge->flag=1;
+
+ while (n)
+ {
+ int i;
+
+ if (!ge->in_headers) /* Write out msg body */
+ {
+ while (n)
+ {
+ if (*ptr == '\r')
+ {
+ ++ptr;
+ --n;
+ continue;
+ }
+
+ for (i=0; i<n; i++)
+ if (ptr[i] == '\r')
+ break;
+
+ maildir_writemsg(ge->fd, ptr, i);
+ ptr += i;
+ n -= i;
+ }
+ break;
+ }
+
+ if (*ptr == '\r')
+ {
+ ++ptr;
+ --n;
+ continue;
+ }
+
+ if (*ptr == '\n')
+ {
+ if (!ge->skiph)
+ maildir_writemsgstr(ge->fd, "\n");
+
+ ge->skiph=0;
+ if (ge->sol)
+ ge->in_headers=0; /* End of headers */
+ ge->sol=1;
+ ++ptr;
+ --n;
+ continue;
+ }
+
+ if (ge->sol && (*ptr == 'x' || *ptr == 'X'))
+ ge->skiph=1;
+
+ for (i=0; i<n; i++)
+ if (ptr[i] == '\r' || ptr[i] == '\n')
+ break;
+
+ if (!ge->skiph) /* Skip X- header */
+ maildir_writemsg(ge->fd, ptr, i);
+ ptr += i;
+ n -= i;
+ }
+ return (0);
+}
+
+static int doeventedit(struct PCP *pcp, int fd)
+{
+ const char *p=cgi("eventid");
+ struct PCP_retr r;
+ struct getedit_info ge;
+
+ const char *eventid[2];
+
+ if (!p || !*p)
+ return (-1);
+
+ maildir_writemsgstr(fd, "X-Event: 1\nX-Old-EventId: ");
+ maildir_writemsgstr(fd, p);
+ maildir_writemsgstr(fd, "\n");
+
+ memset(&r, 0, sizeof(r));
+ eventid[0]=p;
+ eventid[1]=NULL;
+
+ r.event_id_list=eventid;
+
+ ge.fd=fd;
+ ge.flag=0;
+ r.callback_arg=&ge;
+
+ r.callback_retr_date=get_date;
+ r.callback_retr_participants=get_participants;
+
+ if (pcp_retr(pcp, &r))
+ {
+ saveerror(pcp, &r.errcode);
+ return (-1);
+ }
+
+ memset(&r, 0, sizeof(r));
+ eventid[0]=p;
+ eventid[1]=NULL;
+
+ r.event_id_list=eventid;
+
+ ge.fd=fd;
+ ge.flag=0;
+ r.callback_arg=&ge;
+
+ ge.in_headers=1;
+ ge.sol=1;
+ ge.skiph=0;
+
+ r.callback_rfc822_func=get_msgtext;
+ if (pcp_retr(pcp, &r))
+ {
+ saveerror(pcp, &r.errcode);
+ return (-1);
+ }
+
+ if (!ge.flag)
+ {
+ cgi_put("error", "notfound");
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* ------ Weekly stuff ------- */
+
+static void prevday(time_t *tm)
+{
+ struct tm *tmptr;
+ time_t t= *tm - 12 * 60 * 60;
+
+ if ((tmptr=localtime(&t)) == NULL)
+ {
+ enomem();
+ return;
+ }
+
+ if (pcp_parse_ymd(tmptr->tm_year + 1900, tmptr->tm_mon + 1,
+ tmptr->tm_mday, tm, &t))
+ {
+ enomem();
+ }
+}
+
+static void nextday(time_t *tm)
+{
+ struct tm *tmptr;
+ time_t t= *tm + 36 * 60 * 60;
+
+ if ((tmptr=localtime(&t)) == NULL)
+ {
+ enomem();
+ return;
+ }
+
+ if (pcp_parse_ymd(tmptr->tm_year + 1900, tmptr->tm_mon + 1,
+ tmptr->tm_mday, tm, &t))
+ {
+ enomem();
+ }
+}
+
+static time_t get_start_of_week()
+{
+ unsigned y, m, d;
+ time_t start;
+ time_t end;
+ struct tm *tmptr;
+ int i;
+
+ if (sscanf(cgi("weekof"), "%4u%2u%2u", &y, &m, &d) != 3
+ || pcp_parse_ymd(y, m, d, &start, &end))
+ {
+ time(&start);
+ if ((tmptr=localtime(&start)) == NULL)
+ {
+ enomem();
+ return (0);
+ }
+ y=tmptr->tm_year + 1900;
+ m=tmptr->tm_mon + 1;
+ d=tmptr->tm_mday;
+
+ if (pcp_parse_ymd(y, m, d, &start, &end))
+ {
+ enomem();
+ return (0);
+ }
+ }
+
+ for (i=0; i<7; i++)
+ {
+ tmptr=localtime(&start);
+ if (!tmptr)
+ enomem();
+
+ if (tmptr->tm_wday == pref_startofweek)
+ break;
+
+ prevday(&start);
+ }
+
+ return (start);
+}
+
+void sqpcp_show_cal_week()
+{
+ time_t start=get_start_of_week();
+ struct tm *tmptr;
+ char buf[512];
+
+ if ((tmptr=localtime(&start)) == NULL)
+ return;
+
+ if (strftime(buf, sizeof(buf), getarg("DATEFORMAT"), tmptr) == 0)
+ return;
+
+ print_safe(buf);
+}
+
+void sqpcp_show_cal_nextweek()
+{
+ time_t start=get_start_of_week();
+ int i;
+ struct tm *tmptr;
+
+ for (i=0; i<7; i++)
+ nextday(&start);
+
+ if ((tmptr=localtime(&start)) == NULL)
+ return;
+
+ output_scriptptrget();
+ printf("&amp;form=eventweekly&amp;weekof=%04d%02d%02d",
+ tmptr->tm_year + 1900, tmptr->tm_mon+1, tmptr->tm_mday);
+
+}
+
+void sqpcp_show_cal_prevweek()
+{
+ time_t start=get_start_of_week();
+ int i;
+ struct tm *tmptr;
+
+ for (i=0; i<7; i++)
+ prevday(&start);
+
+ if ((tmptr=localtime(&start)) == NULL)
+ return;
+
+ output_scriptptrget();
+ printf("&amp;form=eventweekly&amp;weekof=%04d%02d%02d",
+ tmptr->tm_year + 1900, tmptr->tm_mon+1, tmptr->tm_mday);
+
+}
+
+void sqpcp_displayweek()
+{
+ int i;
+ time_t start=get_start_of_week(), save_start;
+ time_t end;
+ struct PCP *pcp=sqpcp_calendar();
+ struct cacherecord *recs;
+ unsigned n_recs;
+
+ if (!pcp)
+ return;
+
+ save_start=start;
+
+ for (i=0; i<7; i++)
+ {
+ nextday(&start);
+ }
+
+ if (createcache(pcp, &recs, &n_recs, save_start, start))
+ {
+ show_pcp_errmsg(pcp_errmsg(pcp));
+ return;
+ }
+
+ printf("<table align=\"center\" border=\"0\" class=\"weekly-border\""
+ " cellpadding=\"0\" cellspacing=\"0\" width=\"100%%\">"
+ "<tr><td>\n");
+ printf("<table border=\"1\" class=\"weekly-bg\" cellspacing=\"0\" cellpadding=\"10\" width=\"100%%\">"
+ "<tr valign=\"top\">");
+
+ start=save_start;
+ for (i=0; i<7; i++)
+ {
+ const char *p;
+ struct tm *tmptr;
+
+ printf("<td width=\"14%%\">");
+ p=pcp_wdayname((i + pref_startofweek) % 7);
+ printf("<div align=\"center\" class=\"weekly-day\">");
+ printf("<a href=\"");
+ output_scriptptrget();
+ printf("&amp;form=eventdaily&amp;date=");
+
+ tmptr=localtime(&start);
+
+ if (tmptr)
+ {
+ printf("%04d%02d%02d", tmptr->tm_year + 1900,
+ tmptr->tm_mon+1,
+ tmptr->tm_mday);
+ }
+ printf("\">");
+ print_safe(p);
+ printf("</a><hr width=\"70%%\" />");
+ printf("</div>\n");
+
+ end=start;
+ nextday(&end);
+
+
+ do_daily_view(recs, n_recs, VIEW_WEEKLY, &start, &end);
+
+ start=end;
+ printf("</td>");
+ }
+ destroycache(recs, n_recs);
+ printf("</tr></table>\n");
+ printf("</td></tr></table>\n");
+}
+
+/* ---------------- Monthly view ---------------- */
+
+static time_t get_start_of_month()
+{
+ unsigned y, m;
+ time_t start;
+ time_t end;
+ struct tm *tmptr;
+
+ if (sscanf(cgi("monthof"), "%4u%2u", &y, &m) != 2
+ || pcp_parse_ymd(y, m, 1, &start, &end))
+ {
+ time(&start);
+ if ((tmptr=localtime(&start)) == NULL)
+ {
+ enomem();
+ return (0);
+ }
+ y=tmptr->tm_year + 1900;
+ m=tmptr->tm_mon + 1;
+
+ if (pcp_parse_ymd(y, m, 1, &start, &end))
+ {
+ enomem();
+ return (0);
+ }
+ }
+
+ return (start);
+}
+
+void sqpcp_show_cal_month()
+{
+ time_t start=get_start_of_month();
+ struct tm *tmptr;
+ char buf[512];
+
+ if ((tmptr=localtime(&start)) == NULL)
+ return;
+
+ if (strftime(buf, sizeof(buf), getarg("DATEFORMAT"), tmptr) == 0)
+ return;
+
+ print_safe(buf);
+
+}
+
+void sqpcp_show_cal_nextmonth()
+{
+ time_t start=get_start_of_month();
+ struct tm *tmptr;
+ int m, y;
+
+ if ((tmptr=localtime(&start)) == NULL)
+ return;
+
+ y=tmptr->tm_year + 1900;
+ m=tmptr->tm_mon + 1;
+
+ ++m;
+ if (m > 12)
+ {
+ m=1;
+ ++y;
+ }
+
+ output_scriptptrget();
+ printf("&amp;form=eventmonthly&amp;monthof=%04d%02d01", y, m);
+}
+
+void sqpcp_show_cal_prevmonth()
+{
+ time_t start=get_start_of_month();
+ struct tm *tmptr;
+ int m, y;
+
+ if ((tmptr=localtime(&start)) == NULL)
+ return;
+
+ y=tmptr->tm_year + 1900;
+ m=tmptr->tm_mon + 1;
+
+ --m;
+ if (m <= 0)
+ {
+ m=12;
+ --y;
+ }
+
+ output_scriptptrget();
+ printf("&amp;form=eventmonthly&amp;monthof=%04d%02d01", y, m);
+}
+
+void sqpcp_displaymonth()
+{
+ int i, y, m;
+ time_t start=get_start_of_month(), save_start;
+ time_t end;
+ struct PCP *pcp=sqpcp_calendar();
+ struct cacherecord *recs;
+ unsigned n_recs;
+ struct tm *tmptr;
+
+ if (!pcp)
+ return;
+
+ if ((tmptr=localtime(&start)) == NULL)
+ return;
+
+ y=tmptr->tm_year + 1900;
+ m=tmptr->tm_mon + 1;
+
+ if (++m > 12)
+ {
+ m=1;
+ ++y;
+ }
+
+ if (pcp_parse_ymd(y, m, 1, &end, &save_start))
+ return;
+
+ if (createcache(pcp, &recs, &n_recs, start, end))
+ {
+ show_pcp_errmsg(pcp_errmsg(pcp));
+ return;
+ }
+
+ printf("<table align=\"center\" border=\"0\" class=\"monthly-border\""
+ " cellpadding=\"0\" cellspacing=\"0\" width=\"100%%\">"
+ "<tr><td>\n");
+ printf("<table border=\"1\" class=\"monthly-bg\" cellspacing=\"0\" cellpadding=\"10\" width=\"100%%\">"
+ "<tr valign=\"top\">");
+
+ for (i=0; i<7; i++)
+ {
+ printf("<td width=\"14%%\" align=\"center\" class=\"monthly-day\">");
+ print_safe(pcp_wdayname((i + pref_startofweek) % 7));
+ printf("</td>");
+ }
+ printf("</tr>\n");
+
+ while (start < end)
+ {
+ printf("<tr valign=\"top\">\n");
+
+ for (i=0; i<7; i++)
+ {
+ struct tm *tmptr;
+ time_t next_day;
+
+ tmptr=localtime(&start);
+
+ if (tmptr->tm_wday != (i + pref_startofweek) % 7
+ || start >= end)
+ {
+ printf("<td width=\"14%%\" class=\"monthly-bg-othermonth\">&nbsp;</td>");
+ continue;
+ }
+
+ printf("<td width=\"14%%\">");
+
+
+ printf("<a href=\"");
+ output_scriptptrget();
+ printf("&amp;form=eventdaily&amp;date=");
+
+ if (tmptr)
+ {
+ printf("%04d%02d%02d", tmptr->tm_year + 1900,
+ tmptr->tm_mon+1,
+ tmptr->tm_mday);
+ }
+ printf("\" class=\"monthly-day\">");
+
+ printf("%2d", tmptr->tm_mday);
+ printf("</a><div align=\"center\"><hr width=\"70%%\" /></div>\n");
+
+ next_day=start;
+ nextday(&next_day);
+
+ do_daily_view(recs, n_recs, VIEW_MONTHLY, &start,
+ &next_day);
+
+ start=next_day;
+ printf("</td>\n");
+ }
+ printf("</tr>\n");
+ }
+ destroycache(recs, n_recs);
+ printf("</table>\n");
+ printf("</td></tr></table>\n");
+}
+
+/* -------------------------------------------------------------------- */
+/* Access control lists */
+
+static void addacl(const char *);
+
+struct acl_list {
+ struct acl_list *next;
+ char *addr;
+ char *name;
+ int flags;
+} ;
+
+static int listacl(const char *a, int f, void *vp)
+{
+ struct acl_list **p=(struct acl_list **)vp, *q;
+
+ if ((q=malloc(sizeof(struct acl_list))) == NULL)
+ return (-1);
+ memset(q, 0, sizeof(*q));
+ if ((q->addr=strdup(a)) == NULL)
+ {
+ free(q);
+ return (-1);
+ }
+
+ q->flags=f;
+
+ while (*p)
+ {
+ if (strcasecmp( (*p)->addr, a) > 0)
+ break;
+ p= &(*p)->next;
+ }
+
+ q->next= *p;
+ *p=q;
+ return (0);
+}
+
+static int save_listacl_names(const char *addr, const char *name,
+ void *vp)
+{
+ struct acl_list *p=(struct acl_list *)vp;
+
+ for ( ; name && p; p=p->next)
+ if (strcasecmp(p->addr, addr) == 0 && p->name == 0)
+ p->name=strdup(name);
+ return (0);
+}
+
+void sqpcp_eventacl()
+{
+ const char *p;
+ struct acl_list *acl_list=NULL;
+ struct PCP *pcp;
+ struct acl_list *pp;
+
+ if (!sqpcp_has_groupware())
+ return;
+
+ p=cgi("addemail");
+
+ while (*p && isspace((int)(unsigned char)*p))
+ ++p;
+
+ if (!*p)
+ p=cgi("addressbookname");
+
+ if (*p)
+ addacl(p);
+
+ pcp=sqpcp_calendar();
+
+ if (!pcp)
+ return;
+
+ if (*(p=cgi("remove")))
+ {
+ if (pcp_acl(pcp, p, 0))
+ {
+ saveerror(pcp, NULL);
+ showerror();
+ }
+ }
+
+ if (pcp_list_acl(pcp, listacl, &acl_list))
+ {
+ saveerror(pcp, NULL);
+ showerror();
+ }
+ else if (ab_get_nameaddr(save_listacl_names, acl_list))
+ {
+ int dummy=PCP_ERR_SYSERR;
+
+ saveerror(NULL, &dummy);
+ showerror();
+ }
+ else if (acl_list)
+ {
+ printf("<table align=\"center\">\n");
+
+ for (pp=acl_list; pp; pp=pp->next)
+ {
+ printf("<tr><td align=\"right\"><span class=\"tt\">");
+ if (pp->addr)
+ ab_nameaddr_show(pp->name, pp->addr);
+
+ printf("</span></td><td>-");
+ if (pp->flags & PCP_ACL_MODIFY)
+ printf("&nbsp;%s", getarg("MODIFY"));
+ if (pp->flags & PCP_ACL_CONFLICT)
+ printf("&nbsp;%s", getarg("CONFLICT"));
+ printf("</td><td><a href=\"");
+ output_scriptptrget();
+ printf("&amp;form=eventacl&amp;remove=");
+ output_urlencoded(pp->addr);
+ printf("\">%s</a></td></tr>\n", getarg("REMOVE"));
+ }
+ printf("</table>\n");
+ }
+
+ while ((pp=acl_list) != NULL)
+ {
+ acl_list=pp->next;
+ if (pp->addr)
+ free(pp->addr);
+ if (pp->name)
+ free(pp->name);
+ free(pp);
+ }
+}
+
+static void addacl(const char *p)
+{
+ int flags=0;
+ struct PCP *pcp;
+
+ if (strchr(p, '@') == NULL)
+ {
+ const char *mhn=myhostname();
+ char *q=malloc(strlen(p)+strlen(mhn)+2);
+
+ if (!q)
+ enomem();
+
+ strcat(strcat(strcpy(q, p), "@"), mhn);
+ addacl(q);
+ free(q);
+ return;
+ }
+
+ if (*cgi("aclMODIFY"))
+ flags |= PCP_ACL_MODIFY;
+
+ if (*cgi("aclCONFLICT"))
+ flags |= PCP_ACL_CONFLICT;
+
+ if (!flags)
+ return; /* Noop */
+
+ pcp=sqpcp_calendar();
+
+ if (!pcp)
+ {
+ int xerror=PCP_ERR_SYSERR;
+
+ saveerror(NULL, &xerror);
+ showerror();
+ return;
+ }
+
+ if (!pcp_has_acl(pcp))
+ {
+ printf("%s\n", getarg("NOACL"));
+ return;
+ }
+
+ if (pcp_acl(pcp, p, flags))
+ {
+ saveerror(pcp, NULL);
+ showerror();
+ return;
+ }
+}