Viewing file:
pcpd.c (45.85 KB) -rw-rw-rw-Select action/file-type:

(
+) |

(
+) |

(
+) |
Code (
+) |
Session (
+) |

(
+) |
SDB (
+) |

(
+) |

(
+) |

(
+) |

(
+) |

(
+) |
/*
** 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);
}