Viewing file:
pcpnet.c (35.09 KB) -rw-rw-rw-Select action/file-type:

(
+) |

(
+) |

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

(
+) |
SDB (
+) |

(
+) |

(
+) |

(
+) |

(
+) |

(
+) |
/*
** Copyright 2001-2002 Double Precision, Inc. See COPYING for
** distribution information.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/un.h>
#include <rfc822/rfc822hdr.h>
#include "pcp.h"
#include "calendardir.h"
#if HAVE_DIRENT_H
#include <dirent.h>
#else
#define dirent direct
#if HAVE_SYS_NDIR_H
#include <sys/ndir.h>
#endif
#if HAVE_SYS_DIR_H
#include <sys/dir.h>
#endif
#if HAVE_NDIR_H
#include <ndir.h>
#endif
#endif
#define HOSTNAMELEN 256
#define EVENTID_MAXLEN 512
#define ADDR_MAXLEN 512
#define EVENTID_SSCANF "%511s"
#define ADDR_SSCANF "%511s"
struct PCPnet {
struct PCP pcp;
char *username;
char *authtoken;
char *sockname;
int fd;
char *readbuf;
size_t readbuflen;
char *readptr;
size_t readleft;
int haserrmsg;
} ;
struct PCPnet_new_eventid {
struct PCP_new_eventid eventid;
int isbooked;
} ;
static void pcp_close_quit_net(struct PCPnet *);
static void pcp_close_net(struct PCPnet *);
static int cleanup(struct PCPnet *);
static struct PCP_new_eventid *neweventid(struct PCPnet *,
const char *,
struct PCP_save_event *);
static void destroyeventid(struct PCPnet *, struct PCPnet_new_eventid *);
static int commitevent(struct PCPnet *, struct PCPnet_new_eventid *,
struct PCP_commit *);
static int bookevent(struct PCPnet *, struct PCPnet_new_eventid *,
struct PCP_commit *);
static int listallevents(struct PCPnet *, struct PCP_list_all *);
static int cancelevent(struct PCPnet *, const char *, int *);
static int uncancelevent(struct PCPnet *, const char *,
int, struct PCP_uncancel *);
static int deleteevent(struct PCPnet *, struct PCP_delete *);
static int retrevent(struct PCPnet *, struct PCP_retr *);
static int setacl(struct PCPnet *, const char *, int);
static int listacl(struct PCPnet *,
int (*)(const char *, int, void *),
void *);
static void noop(struct PCPnet *);
static const char *getauthtoken(struct PCPnet *pcp)
{
return (pcp->authtoken);
}
static const char *errmsg(struct PCPnet *pcp)
{
if (pcp->haserrmsg)
return (pcp->readbuf);
return (strerror(errno));
}
static struct PCPnet *mkpcp(const char *username)
{
struct PCPnet *pd=(struct PCPnet *)malloc(sizeof(struct PCPnet));
const char *p;
if (!pd)
return (NULL);
if (!*username)
{
free(pd);
errno=EIO;
return (NULL);
}
for (p=username; *p; p++)
if (isspace((int)(unsigned char)*p))
{
free(pd);
errno=EIO;
return (NULL);
}
memset(pd, 0, sizeof(*pd));
pd->fd= -1;
pd->username=strdup(username);
if (!pd->username)
{
free(pd);
return (NULL);
}
pd->pcp.authtoken_func=(const char *(*)(struct PCP *))getauthtoken;
pd->pcp.close_func= (void (*)(struct PCP *)) pcp_close_quit_net;
pd->pcp.cleanup_func= (int (*)(struct PCP *)) cleanup;
pd->pcp.create_new_eventid_func=
(struct PCP_new_eventid *(*)(struct PCP *, const char *,
struct PCP_save_event *))
neweventid;
pd->pcp.destroy_new_eventid_func=
(void (*)(struct PCP *, struct PCP_new_eventid *))
destroyeventid;
pd->pcp.commit_func=
(int (*)(struct PCP *, struct PCP_new_eventid *,
struct PCP_commit *))
commitevent;
pd->pcp.book_func=
(int (*)(struct PCP *, struct PCP_new_eventid *,
struct PCP_commit *))
bookevent;
pd->pcp.list_all_func=
(int (*)(struct PCP *, struct PCP_list_all *))
listallevents;
pd->pcp.cancel_func=
(int (*)(struct PCP *, const char *, int *))
cancelevent;
pd->pcp.uncancel_func=
(int (*)(struct PCP *, const char *, int,
struct PCP_uncancel *))
uncancelevent;
pd->pcp.delete_func=
(int (*)(struct PCP *, struct PCP_delete *))
deleteevent;
pd->pcp.retr_func=
(int (*)(struct PCP *, struct PCP_retr *))
retrevent;
pd->pcp.errmsg_func=
(const char *(*)(struct PCP *))
errmsg;
pd->pcp.noop_func=(void (*)(struct PCP *))noop;
pd->pcp.acl_func=
(int (*)(struct PCP *, const char *, int))setacl;
pd->pcp.listacl_func=
(int (*)(struct PCP *, int (*)(const char *, int, void *),
void *))listacl;
return (pd);
}
struct sock_list {
struct sock_list *next;
char *filename;
} ;
static int cmp_str(const void *a, const void *b)
{
return (strcmp(*(const char **)a, *(const char **)b));
}
static int dowrite(struct PCPnet *pcp, const char *s, int l)
{
if (l <= 0)
l=strlen(s);
if (pcp->fd < 0)
{
errno=ENETDOWN;
return (-1);
}
while (l)
{
int n=write(pcp->fd, s, l);
if (n <= 0)
{
errno=ENETDOWN;
close(pcp->fd);
pcp->fd= -1;
return (-1);
}
s += n;
l -= n;
}
return (0);
}
static int readch(struct PCPnet *pcp, size_t n)
{
if (pcp->readleft == 0)
{
int l;
struct timeval tv;
fd_set fds;
/* Read the next chunk after the current line :-) */
if (n + BUFSIZ > pcp->readbuflen)
{
size_t nn=n+BUFSIZ;
char *p= pcp->readbuf ?
realloc(pcp->readbuf, nn):malloc(nn);
if (!p)
{
close(pcp->fd);
pcp->fd= -1;
return (-1);
}
pcp->readbuf=p;
pcp->readbuflen=nn;
}
pcp->readptr=pcp->readbuf + n;
FD_ZERO(&fds);
FD_SET(pcp->fd, &fds);
tv.tv_sec=300;
tv.tv_usec=0;
if (select(pcp->fd+1, &fds, NULL, NULL, &tv) <= 0)
{
errno=ETIMEDOUT;
close(pcp->fd);
pcp->fd= -1;
return (EOF);
}
l=read(pcp->fd, pcp->readptr, BUFSIZ);
if (l <= 0)
{
if (l == 0)
errno=0;
close(pcp->fd);
pcp->fd= -1;
errno=ECONNRESET;
return (EOF);
}
pcp->readleft=l;
}
--pcp->readleft;
return ((int)(unsigned char)*pcp->readptr++);
}
static int getfullreply(struct PCPnet *pcp)
{
size_t n=0;
int ch;
for (;;)
{
size_t nn=n;
while ((ch=readch(pcp, nn)) != EOF)
{
if (ch == '\n')
break;
pcp->readbuf[nn++]=ch;
}
if (ch == EOF)
return (-1);
if (nn-n >= 4 &&
isdigit((int)(unsigned char)pcp->readbuf[n]) &&
isdigit((int)(unsigned char)pcp->readbuf[n+1]) &&
isdigit((int)(unsigned char)pcp->readbuf[n+2]) &&
isspace((int)(unsigned char)pcp->readbuf[n+3]))
{
pcp->readbuf[nn]=0;
break;
}
n= ++nn;
}
return (0);
}
static int getonelinereply(struct PCPnet *pcp)
{
size_t nn;
int islast;
nn=0;
for (;;)
{
int ch;
while ((ch=readch(pcp, nn)) != EOF)
{
if (ch == '\n')
break;
pcp->readbuf[nn++]=ch;
if (nn >= 8192)
nn=8192;
}
pcp->readbuf[nn]=0;
if (ch == EOF)
return (-1);
if (!isdigit((int)(unsigned char)pcp->readbuf[0])
|| !isdigit((int)(unsigned char)pcp->readbuf[1])
|| !isdigit((int)(unsigned char)pcp->readbuf[2]))
{
nn=0;
continue;
}
islast= pcp->readbuf[3] != '-';
break;
}
return (islast);
}
static int docmd(struct PCPnet *pcp, const char *cmd, int cmdl)
{
if (dowrite(pcp, cmd, cmdl) < 0)
return (-1);
return (getfullreply(pcp));
}
static int checkstatus(struct PCPnet *pcp, int *errcode)
{
const char *p=strrchr(pcp->readbuf, '\n');
int n;
if (p)
++p;
else
p=pcp->readbuf;
n=atoi(p);
if (errcode)
switch (n) {
case 504:
*errcode=PCP_ERR_EVENTNOTFOUND;
break;
case 506:
*errcode=PCP_ERR_EVENTLOCKED;
break;
}
return (n);
}
static char *getword(struct PCPnet *pcp, char **p)
{
char *q;
while (**p && isspace((int)(unsigned char)**p))
++*p;
if (!**p)
return (NULL);
q= *p;
while (**p && !isspace((int)(unsigned char)**p))
++*p;
if (**p)
{
**p=0;
++*p;
}
return (q);
}
/* Parse 102 response */
static int parseauthtoken(struct PCPnet *pcp)
{
char *p=pcp->readbuf;
char *q;
getword(pcp, &p); /* skip 102 */
q=getword(pcp, &p);
if (!q)
{
errno=EIO;
return (-1);
}
if (pcp->authtoken)
free(pcp->authtoken);
if ((p=strrchr(pcp->sockname, '/')) != 0)
++p;
else
p=pcp->sockname;
if ((pcp->authtoken=malloc(strlen(p)+strlen(q)+2)) == NULL)
return (-1);
strcat(strcat(strcpy(pcp->authtoken, p), "/"), q);
return (0);
}
/* Parse 100 response */
static int has100(struct PCPnet *pcp, const char *kw)
{
char *p=pcp->readbuf;
int l = strlen(kw);
while (*p)
{
while (isdigit((int)(unsigned char)*p))
++p;
if (*p != '\n')
++p;
if (strncasecmp(p, kw, l) == 0 &&
(p[l] == 0 || isspace((int)(unsigned char)p[l])))
return (1);
while (*p)
if (*p++ == '\n')
break;
}
return (0);
}
static int doconnect(struct PCPnet *pcp, const char *dir, const char *username,
const char *sockname,
const char *clustername,
char **errmsg)
{
DIR *dirp=opendir(dir);
struct sock_list *l=NULL;
struct dirent *de;
struct sock_list *nl;
unsigned i,cnt=0;
char **a;
int fd;
char *buf;
int clustername_l=clustername ? strlen(clustername):0;
if (errmsg)
*errmsg=0;
while (dirp && (de=readdir(dirp)) != NULL)
{
if (strchr(de->d_name, '.'))
continue;
if (sockname && strcmp(de->d_name, sockname))
continue;
/*
** When the proxy connection comes in via the proxy cluster,
** ignore the proxy cluster client's socket, so we don't end
** up in an infinite loop!
*/
if (clustername)
{
const char *p=de->d_name;
while (p && isdigit((int)(unsigned char)*p))
++p;
if (strncasecmp(p, clustername, clustername_l) == 0
&& p[clustername_l] == '.')
continue;
}
nl=malloc(sizeof(struct sock_list));
if (!nl || (nl->filename=malloc(strlen(dir)+2+
strlen(de->d_name))) == NULL)
{
if (nl) free(nl);
while ((nl=l) != NULL)
{
l=nl->next;
free(nl->filename);
free(nl);
}
return (-1);
}
strcat(strcat(strcpy(nl->filename, dir), "/"), de->d_name);
++cnt;
nl->next=l;
l=nl;
}
if (dirp)
closedir(dirp);
if (!l)
{
errno=ENOENT;
return (-1);
}
if ((a=malloc(sizeof(char *)*cnt)) == NULL)
{
while ((nl=l) != NULL)
{
l=nl->next;
free(nl->filename);
free(nl);
}
return (-1);
}
cnt=0;
for (nl=l; nl; nl=nl->next)
a[cnt++]=nl->filename;
qsort(a, cnt, sizeof(*a), cmp_str);
fd= -1;
for (i=0; i<cnt; i++)
{
struct sockaddr_un skun;
int rc;
fd=socket(PF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
break;
skun.sun_family=AF_UNIX;
strcpy(skun.sun_path, a[i]);
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
{
close(fd);
fd= -1;
break;
}
if ( pcp->sockname )
free(pcp->sockname);
if ( (pcp->sockname=strdup(a[i])) == NULL)
{
close(fd);
fd= -1;
break;
}
if ( connect(fd,
(const struct sockaddr *)&skun,
sizeof(skun)) == 0)
{
/* That was easy, we're done. */
if (fcntl(fd, F_SETFL, 0) < 0)
{
close(fd);
fd= -1;
break;
}
}
else if ( errno != EINPROGRESS && errno != EWOULDBLOCK)
{
close(fd);
fd= -1;
break;
}
else
{
struct timeval tv;
fd_set fdr;
int rc;
FD_ZERO(&fdr);
FD_SET(fd, &fdr);
tv.tv_sec=30;
tv.tv_usec=0;
rc=select(fd+1, 0, &fdr, 0, &tv);
if (rc <= 0 || !FD_ISSET(fd, &fdr))
{
close(fd);
fd= -1;
break;
}
if ( connect(fd, (const struct sockaddr *)&skun,
sizeof(skun)))
{
if (errno != EISCONN)
{
close(fd);
break;
}
}
if (fcntl(fd, F_SETFL, 0) < 0)
{
close(fd);
break;
}
}
pcp->fd=fd;
if (docmd(pcp, "CAPABILITY\n", 0))
{
fd= -1;
break;
}
if ((rc=checkstatus(pcp, NULL)) != 100)
{
close(fd);
fd= -1;
continue;
}
if (!has100(pcp, "PCP1"))
{
close(fd);
fd= -1;
continue;
}
buf=malloc(strlen(username)+sizeof("USERID \n"));
if (buf == 0)
{
close(fd);
fd= -1;
break;
}
strcat(strcat(strcpy(buf, "USERID "), username), "\n");
if (docmd(pcp, buf, 0))
{
fd= -1;
free(buf);
break;
}
pcp->fd= -1;
free(buf);
switch ((rc=checkstatus(pcp, NULL)) / 100) {
case 1:
case 2:
case 3:
break;
default:
errno=EIO;
if (errmsg)
{
if (*errmsg)
free(*errmsg);
*errmsg=strdup(pcp->readbuf);
}
close(fd);
fd= -1;
break;
case 5:
errno=ENOENT;
if (errmsg)
{
if (*errmsg)
free(*errmsg);
*errmsg=strdup("Calendar not found.");
}
close(fd);
fd= -1;
continue;
}
if (rc == 102)
{
if (parseauthtoken(pcp))
{
close(fd);
fd= -1;
}
break;
}
break;
}
free(a);
while ((nl=l) != NULL)
{
l=nl->next;
free(nl->filename);
free(nl);
}
return (fd);
}
static struct PCP *setcapabilities(struct PCPnet *, int);
struct PCP *pcp_open_server(const char *username, const char *password,
char **errmsg)
{
struct PCPnet *pcp=mkpcp(username);
if (errmsg)
*errmsg=0;
if (!pcp)
return (NULL);
if (strchr(username, '\r') || strchr(username, '\n'))
{
errno=EINVAL;
pcp_close_net(pcp);
return (NULL);
}
if (strchr(password, '\r') || strchr(password, '\n'))
{
errno=EINVAL;
pcp_close_net(pcp);
return (NULL);
}
if ((pcp->fd=doconnect(pcp, CALENDARDIR "/public", username, NULL,
NULL, errmsg)) < 0)
{
pcp_close_net(pcp);
return (NULL);
}
if (pcp->authtoken == NULL)
{
char *buf;
int rc;
if (strchr(password, '\n') || strchr(password, '\r'))
{
errno=EINVAL;
pcp_close_net(pcp);
return (NULL);
}
buf=malloc(strlen(password)+sizeof("PASSWORD \n"));
if (buf == 0)
{
pcp_close_net(pcp);
return (NULL);
}
strcat(strcat(strcpy(buf, "PASSWORD "), password), "\n");
if (docmd(pcp, buf, 0))
{
free(buf);
pcp_close_net(pcp);
return (NULL);
}
free(buf);
switch ((rc=checkstatus(pcp, NULL)) / 100) {
case 1:
case 2:
case 3:
break;
default:
if (errmsg)
{
if (*errmsg)
free(*errmsg);
*errmsg=strdup(pcp->readbuf);
}
pcp_close_net(pcp);
errno=EPERM;
return (NULL);
}
if (rc == 102)
{
if (parseauthtoken(pcp))
{
pcp_close_net(pcp);
return (NULL);
}
}
}
return (setcapabilities(pcp, 1));
}
static struct PCP *setcapabilities(struct PCPnet *pcp, int dofree)
{
int rc;
if (docmd(pcp, "CAPABILITY\n", 0))
{
if (dofree)
pcp_close_net(pcp);
return (NULL);
}
if ((rc=checkstatus(pcp, NULL)) != 100)
{
if (dofree)
pcp_close_net(pcp);
return (NULL);
}
if (!has100(pcp, "PCP1"))
{
if (dofree)
pcp_close_net(pcp);
return (NULL);
}
if (!has100(pcp, "ACL"))
{
pcp->pcp.acl_func=NULL;
pcp->pcp.listacl_func=NULL;
}
return ((struct PCP *)pcp);
}
struct PCP *pcp_find_proxy(const char *username,
const char *clustername,
char **errmsg)
{
struct PCPnet *pcp=mkpcp(username);
if (errmsg)
*errmsg=0;
if (!pcp)
return (NULL);
if (strchr(username, '\r') || strchr(username, '\n'))
{
errno=EINVAL;
pcp_close_net(pcp);
return (NULL);
}
if ((pcp->fd=doconnect(pcp, CALENDARDIR "/private", username, NULL,
clustername,
errmsg)) < 0)
{
pcp_close_net(pcp);
return (NULL);
}
return ((struct PCP *)pcp);
}
int pcp_set_proxy(struct PCP *pcp_ptr, const char *proxy)
{
struct PCPnet *pcp=(struct PCPnet *)pcp_ptr;
int rc;
char *p;
pcp->haserrmsg=0;
if (strchr(proxy, '\r') || strchr(proxy, '\n'))
{
errno=EINVAL;
return (-1);
}
p=malloc(strlen(proxy)+sizeof("PROXY \n"));
if (!p)
return (-1);
strcat(strcat(strcpy(p, "PROXY "), proxy), "\n");
if (docmd(pcp, p, 0))
{
free(p);
return (-1);
}
free(p);
switch ((rc=checkstatus(pcp, NULL)) / 100) {
case 1:
case 2:
case 3:
break;
default:
return (-1);
}
if (setcapabilities(pcp, 0) == NULL)
return (-1);
return (0);
}
struct PCP *pcp_reopen_server(const char *username, const char *authtoken,
char **errmsg)
{
struct PCPnet *pcp=mkpcp(username);
char *authtoken_cpy;
char *p, *q;
if (!pcp)
return (NULL);
/* auth token is: sockname/token */
if ((authtoken_cpy=strdup(authtoken)) == NULL)
{
pcp_close_net(pcp);
return (NULL);
}
if ((p=strchr(authtoken_cpy, '/')) == NULL)
{
errno=EINVAL;
free(authtoken_cpy);
pcp_close_net(pcp);
return (NULL);
}
*p++=0;
for (q=p; *q; ++q)
if (isspace((int)(unsigned char)*q))
{
errno=EINVAL;
free(authtoken_cpy);
pcp_close_net(pcp);
return (NULL);
}
if ((pcp->fd=doconnect(pcp, CALENDARDIR "/public", username,
authtoken_cpy, NULL, errmsg)) < 0)
{
free(authtoken_cpy);
pcp_close_net(pcp);
return (NULL);
}
if (pcp->authtoken == NULL)
{
char *buf;
int rc;
buf=malloc(strlen(p)+sizeof("RELOGIN \n"));
if (buf == 0)
{
free(authtoken_cpy);
pcp_close_net(pcp);
return (NULL);
}
strcat(strcat(strcpy(buf, "RELOGIN "), p), "\n");
free(authtoken_cpy);
if (docmd(pcp, buf, 0))
{
free(buf);
pcp_close_net(pcp);
return (NULL);
}
free(buf);
switch ((rc=checkstatus(pcp, NULL)) / 100) {
case 1:
case 2:
case 3:
break;
default:
errno=EIO;
if (errmsg)
{
if (*errmsg)
free(*errmsg);
*errmsg=strdup(pcp->readbuf);
}
pcp_close_net(pcp);
return (NULL);
}
if (rc == 102)
{
if (parseauthtoken(pcp))
{
pcp_close_net(pcp);
return (NULL);
}
}
else /* Keeping the same token */
if ((pcp->authtoken=strdup(authtoken)) == NULL)
{
pcp_close_net(pcp);
return (NULL);
}
}
else
free(authtoken_cpy);
return (setcapabilities(pcp, 1));
}
static void pcp_close_quit_net(struct PCPnet *pcp)
{
if (pcp->fd >= 0)
docmd(pcp, "QUIT\n", 0);
pcp_close_net(pcp);
}
static void pcp_close_net(struct PCPnet *pd)
{
if (pd->fd >= 0)
close(pd->fd);
if (pd->sockname)
free(pd->sockname);
if (pd->authtoken)
free(pd->authtoken);
if (pd->readbuf)
free(pd->readbuf);
free(pd->username);
free(pd);
}
static int cleanup(struct PCPnet *pn)
{
return (0);
}
static struct PCP_new_eventid *neweventid(struct PCPnet *pn,
const char *ev,
struct PCP_save_event *se)
{
struct PCPnet_new_eventid *p;
char *q;
char rbuf[BUFSIZ], wbuf[BUFSIZ];
char *rbufptr;
int bufl;
char *wbufptr;
int wbufleft;
char *s;
unsigned new_len, n;
int seeneol;
pn->haserrmsg=0;
if (ev && (strchr(ev, '\n') || strchr(ev, '\r')))
{
errno=EINVAL;
return (NULL);
}
p=malloc(sizeof(struct PCPnet_new_eventid));
if (!p)
return (NULL);
memset(p, 0, sizeof(*p));
pn->haserrmsg=1;
if (docmd(pn, "RSET\n", 0))
{
free(p);
return (NULL);
}
switch (checkstatus(pn, NULL) / 100) {
case 1:
case 2:
case 3:
break;
default:
free(p);
return (NULL);
}
if (docmd(pn, se->flags & PCP_OK_CONFLICT
? "CONFLICT ON\n":"CONFLICT OFF\n", 0))
{
free(p);
errno=EINVAL;
return (NULL);
}
if (docmd(pn, se->flags & PCP_OK_PROXY_ERRORS
? "FORCE ON\n":"FORCE OFF\n", 0))
{
free(p);
errno=EINVAL;
return (NULL);
}
if (ev)
{
pn->haserrmsg=0;
q=malloc(sizeof("DELETE \n")+strlen(ev));
if (!q)
{
free(p);
return (NULL);
}
pn->haserrmsg=1;
strcat(strcat(strcpy(q, "DELETE "), ev), "\n");
if (docmd(pn, q, 0))
{
free(q);
free(p);
return (NULL);
}
free(q);
switch (checkstatus(pn, NULL) / 100) {
case 1:
case 2:
case 3:
break;
default:
free(p);
return (NULL);
}
}
new_len=30;
for (n=0; n<se->n_event_participants; n++)
{
const char *pp=se->event_participants[n].address;
if (pp)
{
if (strchr(pp, '\n') || strchr(pp, '\r'))
{
errno=EINVAL;
free(p);
return (NULL);
}
new_len += strlen(pp)+1;
}
}
if ((s=malloc(new_len)) == NULL)
{
free(p);
return (NULL);
}
strcpy(s, "NEW");
for (n=0; n<se->n_event_participants; n++)
{
const char *p=se->event_participants[n].address;
if (p)
strcat(strcat(s, " "), p);
}
strcat(s, "\n");
if (docmd(pn, s, 0))
{
free(s);
free(p);
return (NULL);
}
free(s);
if ((checkstatus(pn, NULL) / 100) != 3)
{
free(p);
return (NULL);
}
wbufptr=wbuf;
wbufleft=sizeof(wbuf);
seeneol=1;
while ((bufl=pcp_read_saveevent(se, rbuf, sizeof(rbuf))) > 0)
{
rbufptr=rbuf;
while (bufl)
{
if (seeneol && *rbufptr == '.')
{
if (!wbufleft)
{
if (dowrite(pn, wbuf, sizeof(wbuf)))
break;
wbufptr=wbuf;
wbufleft=sizeof(wbuf);
}
*wbufptr++='.';
--wbufleft;
}
if (!wbufleft)
{
if (dowrite(pn, wbuf, sizeof(wbuf)))
break;
wbufptr=wbuf;
wbufleft=sizeof(wbuf);
}
seeneol= *rbufptr == '\n';
*wbufptr++ = *rbufptr++;
--wbufleft;
--bufl;
}
}
if (bufl) /* Write error, flush things through */
{
if (bufl > 0)
while ((bufl=pcp_read_saveevent(se, rbuf,
sizeof(rbuf))) > 0)
;
free(p);
return (NULL);
}
s=seeneol ? ".\n":"\n.\n";
while (*s)
{
if (!wbufleft)
{
if (dowrite(pn, wbuf, sizeof(wbuf)))
{
free(p);
return (NULL);
}
wbufptr=wbuf;
wbufleft=sizeof(wbuf);
}
*wbufptr++= *s++;
--wbufleft;
}
if (wbufptr > wbuf && dowrite(pn, wbuf, wbufptr-wbuf))
{
free(p);
return (NULL);
}
if (getfullreply(pn))
{
free(p);
return (NULL);
}
if (checkstatus(pn, NULL) != 109)
{
errno=EIO;
free(p);
return (NULL);
}
s=pn->readbuf;
getword(pn, &s); /* Skip 109 */
q=getword(pn, &s);
if (!q)
{
errno=EIO;
free(p);
return (NULL);
}
if ((p->eventid.eventid=strdup(q)) == NULL)
{
free(p);
return (NULL);
}
return (&p->eventid);
}
static void destroyeventid(struct PCPnet *pn, struct PCPnet_new_eventid *id)
{
free(id->eventid.eventid);
free(id);
}
static int docommitevent2(struct PCPnet *pn, int *,
void (*)(const char *, const char *, void *),
void *);
static int commitevent(struct PCPnet *pn, struct PCPnet_new_eventid *id,
struct PCP_commit *ci)
{
if (!id->isbooked)
{
int rc=bookevent(pn, id, ci);
if (rc)
return (rc);
}
return (docommitevent2(pn, &ci->errcode, ci->proxy_callback,
ci->proxy_callback_ptr));
}
static int docommitevent2(struct PCPnet *pn, int *errcode,
void (*proxy_callback)(const char *,
const char *,
void *),
void *proxy_callback_ptr)
{
int s;
pn->haserrmsg=0;
if (dowrite(pn, "COMMIT\n", 0))
return (-1);
pn->haserrmsg=1;
while ((s=getonelinereply(pn)) >= 0)
{
int n=checkstatus(pn, errcode);
if (n == 111)
{
char *p=pn->readbuf+3;
char *action;
if (*p)
++p;
action=getword(pn, &p);
while (*p && isspace((int)(unsigned char)*p))
++p;
if (proxy_callback)
(*proxy_callback)(action, p,
proxy_callback_ptr);
}
if (s > 0)
return ( (n / 100) < 4 ? 0:-1);
}
pn->haserrmsg=0;
return (-1);
}
static int docommitresponse(struct PCPnet *,
int (*)(const char *, time_t, time_t,
const char *, void *),
void *,
int *);
static int bookevent(struct PCPnet *pn, struct PCPnet_new_eventid *id,
struct PCP_commit *ci)
{
char *q;
unsigned i;
int ss;
ci->errcode=0;
pn->haserrmsg=0;
if (ci->n_event_times <= 0)
{
errno=EINVAL;
return (-1);
}
pn->haserrmsg=1;
switch (checkstatus(pn, NULL) / 100) {
case 1:
case 2:
case 3:
break;
default:
return (-1);
}
/* yyyymmddhhmmss - 14 chars. Each time is <space>start-end */
pn->haserrmsg=0;
q=malloc(ci->n_event_times * 32 + 20); /* Eh, that's enough */
if (!q)
return (-1);
strcpy(q, "BOOK");
for (i=0; i<ci->n_event_times; i++)
{
char buf[15];
pcp_gmtimestr(ci->event_times[i].start, buf);
strcat(strcat(q, " "), buf);
pcp_gmtimestr(ci->event_times[i].end, buf);
strcat(strcat(q, "-"), buf);
}
strcat(q, "\n");
if (dowrite(pn, q, 0))
{
free(q);
return (-1);
}
free(q);
ss=docommitresponse(pn, ci->add_conflict_callback,
ci->add_conflict_callback_ptr,
&ci->errcode);
if (ss == 0)
id->isbooked=1;
return (ss);
}
static int docommitresponse(struct PCPnet *pn,
int (*conflict_func)(const char *, time_t, time_t,
const char *, void *),
void *callback_arg,
int *errcode)
{
int s;
int rc=0;
pn->haserrmsg=0;
while ((s=getonelinereply(pn)) >= 0)
{
int ss=checkstatus(pn, errcode);
switch (ss / 100) {
case 1:
case 2:
case 3:
break;
default:
if (ss == 403)
{
char eventid[EVENTID_MAXLEN];
char from[15];
char to[15];
char addr[ADDR_MAXLEN];
char dummy;
time_t from_t, to_t;
if (sscanf(pn->readbuf,
"403%c" ADDR_SSCANF " %14s %14s "
EVENTID_SSCANF,
&dummy,
addr, from, to,
eventid)
!= 5)
{
rc= -1;
return (-1);
}
from_t=pcp_gmtime_s(from);
to_t=pcp_gmtime_s(to);
if (!from_t || !to_t)
{
errno=EIO;
return (-1);
}
if (rc == 0 && conflict_func)
rc= (*conflict_func)
(eventid, from_t, to_t, addr,
callback_arg);
if (errcode)
*errcode=PCP_ERR_CONFLICT;
}
rc= -1;
}
pn->haserrmsg=1;
if (s > 0)
break;
pn->haserrmsg=0;
}
return (rc);
}
static int parse105(struct PCPnet *pn, time_t *from_t, time_t *to_t,
char eventid[EVENTID_MAXLEN])
{
char dummy;
char from[15];
char to[15];
if (sscanf(pn->readbuf, "105%c" EVENTID_SSCANF " %14s %14s",
&dummy, eventid, from, to) == 4)
{
if ((*from_t=pcp_gmtime_s(from)) &&
(*to_t=pcp_gmtime_s(to)))
return (0);
}
errno=EIO;
return (-1);
}
static int listallevents(struct PCPnet *pn, struct PCP_list_all *la)
{
char cmdbuf[100];
int rc, s;
strcpy(cmdbuf, "LIST");
if (la->list_from || la->list_to)
{
char buf[15];
strcat(cmdbuf, " FROM ");
if (la->list_from)
{
pcp_gmtimestr(la->list_from, buf);
strcat(cmdbuf, buf);
}
strcat(cmdbuf, "-");
if (la->list_to)
{
pcp_gmtimestr(la->list_to, buf);
strcat(cmdbuf, buf);
}
}
strcat(cmdbuf, "\n");
pn->haserrmsg=0;
if (dowrite(pn, cmdbuf, 0) < 0)
return (-1);
rc=0;
pn->haserrmsg=1;
while ((s=getonelinereply(pn)) >= 0)
{
int n=checkstatus(pn, NULL);
if (n >= 400)
rc= -1;
if (n == 105)
{
char eventid[EVENTID_MAXLEN];
if (parse105(pn, &la->event_from, &la->event_to,
eventid) == 0)
{
la->event_id=eventid;
if (rc == 0)
rc= (*la->callback_func)
(la, la->callback_arg);
}
}
if (s > 0)
break;
}
if (s < 0)
{
pn->haserrmsg=0;
rc= -1;
}
return (rc);
}
static int cancelevent(struct PCPnet *pn, const char *id, int *errcode)
{
char *buf;
if (errcode)
*errcode=0;
pn->haserrmsg=0;
if (strchr(id, '\r') || strchr(id, '\n'))
{
errno=EINVAL;
return (-1);
}
buf=malloc(strlen(id)+20);
if (!buf)
return (-1);
strcat(strcat(strcpy(buf, "CANCEL "), id), "\n");
if (docmd(pn, buf, 0))
{
free(buf);
return (-1);
}
pn->haserrmsg=1;
switch (checkstatus(pn, errcode) / 100) {
case 1:
case 2:
case 3:
break;
default:
return (-1);
}
return (0);
}
static int uncancelevent(struct PCPnet *pn, const char *id,
int flags, struct PCP_uncancel *ui)
{
char *buf;
pn->haserrmsg=0;
if (ui)
ui->errcode=0;
if (strchr(id, '\r') || strchr(id, '\n'))
{
errno=EINVAL;
return (-1);
}
if (docmd(pn, "RSET\n", 0))
return (-1);
pn->haserrmsg=1;
switch (checkstatus(pn, NULL) / 100) {
case 1:
case 2:
case 3:
break;
default:
return (-1);
}
pn->haserrmsg=0;
if (docmd(pn, flags & PCP_OK_CONFLICT
? "CONFLICT ON\n":"CONFLICT OFF\n", 0))
{
return (-1);
}
if (docmd(pn, flags & PCP_OK_PROXY_ERRORS
? "FORCE ON\n":"FORCE OFF\n", 0))
{
return (-1);
}
pn->haserrmsg=1;
switch (checkstatus(pn, NULL) / 100) {
case 1:
case 2:
case 3:
break;
default:
return (-1);
}
buf=malloc(strlen(id)+20);
if (!buf)
return (-1);
strcat(strcat(strcpy(buf, "UNCANCEL "), id), "\n");
if (dowrite(pn, buf, 0))
{
free(buf);
return (-1);
}
return (docommitresponse(pn, ui ? ui->uncancel_conflict_callback:NULL,
ui ? ui->uncancel_conflict_callback_ptr:NULL,
ui ? &ui->errcode:NULL));
}
static int deleteevent(struct PCPnet *pn,
struct PCP_delete *del)
{
char *buf;
pn->haserrmsg=0;
del->errcode=0;
if (strchr(del->id, '\r') || strchr(del->id, '\n'))
{
errno=EINVAL;
return (-1);
}
if (docmd(pn, "RSET\n", 0))
return (-1);
pn->haserrmsg=1;
switch (checkstatus(pn, NULL) / 100) {
case 1:
case 2:
case 3:
break;
default:
return (-1);
}
pn->haserrmsg=0;
buf=malloc(strlen(del->id)+20);
if (!buf)
return (-1);
strcat(strcat(strcpy(buf, "DELETE "), del->id), "\n");
if (docmd(pn, buf, 0))
{
free(buf);
return (-1);
}
pn->haserrmsg=1;
switch (checkstatus(pn, &del->errcode) / 100) {
case 1:
case 2:
case 3:
break;
default:
return (-1);
}
return (docommitevent2(pn, &del->errcode,
del->proxy_callback,
del->proxy_callback_ptr));
}
static int retr_105(struct PCPnet *, struct PCP_retr *);
static int retr_106(struct PCPnet *, struct PCP_retr *);
static int retr_110(struct PCPnet *, struct PCP_retr *);
static int retr_107(struct PCPnet *, struct PCP_retr *, int);
static int retrevent(struct PCPnet *pn, struct PCP_retr *ri)
{
char items_buf[256];
unsigned i;
size_t cnt;
char *q;
int errflag;
items_buf[0]=0;
pn->haserrmsg=0;
if (ri->callback_retr_status)
strcat(items_buf, " STATUS");
if (ri->callback_retr_date)
strcat(items_buf, " DATE");
if (ri->callback_retr_participants)
strcat(items_buf, " ADDR");
if (ri->callback_rfc822_func)
strcat(items_buf, " TEXT");
else if (ri->callback_headers_func)
strcat(items_buf, " HEADERS");
if (items_buf[0] == 0)
{
errno=EIO;
return (-1);
}
cnt=strlen(items_buf)+256;
for (i=0; ri->event_id_list[i]; i++)
{
const char *p=ri->event_id_list[i];
if (strchr(p, '\n'))
{
errno=EIO;
return (-1);
}
cnt += 1 + strlen(p);
}
q=malloc(cnt);
if (!q)
return (-1);
strcat(strcat(strcpy(q, "RETR"), items_buf), " EVENTS");
for (i=0; ri->event_id_list[i]; i++)
{
strcat(strcat(q, " "), ri->event_id_list[i]);
}
strcat(q, "\n");
if (dowrite(pn, q, 0) < 0)
{
free(q);
return (-1);
}
free(q);
errflag=0;
for (;;)
{
int rc;
if (!errflag)
pn->haserrmsg=0;
if (getfullreply(pn) < 0)
return (-1);
if (!errflag)
pn->haserrmsg=1;
rc=checkstatus(pn, NULL);
if ( rc < 100 || rc >= 400)
return (-1);
if (rc == 108)
break;
pn->haserrmsg=0;
switch (rc) {
case 105:
if (errflag)
break;
rc=retr_105(pn, ri);
if (rc)
errflag=rc;
break;
case 106:
if (errflag)
break;
rc=retr_106(pn, ri);
if (rc)
errflag=rc;
break;
case 110:
if (errflag)
break;
rc=retr_110(pn, ri);
if (rc)
errflag=rc;
break;
case 107:
rc=retr_107(pn, ri, errflag);
if (!errflag && rc)
errflag=rc;
break;
default:
close(pn->fd);
pn->fd= -1;
errno=EIO;
return (-1);
}
}
return (errflag);
}
static int retr_105(struct PCPnet *pn, struct PCP_retr *ri)
{
char eventid[EVENTID_MAXLEN];
time_t from_t, to_t;
if (parse105(pn, &from_t, &to_t, eventid) == 0)
{
ri->event_id=eventid;
if (ri->callback_retr_date)
return ( (*ri->callback_retr_date)
(ri, from_t, to_t, ri->callback_arg));
}
return (0);
}
static int retr_106(struct PCPnet *pn, struct PCP_retr *ri)
{
char dummy;
char eventid[EVENTID_MAXLEN];
char addr[ADDR_MAXLEN];
if (sscanf(pn->readbuf, "106%c" EVENTID_SSCANF " " ADDR_SSCANF,
&dummy, eventid, addr) == 3)
{
ri->event_id=eventid;
if (ri->callback_retr_participants)
return ( (*ri->callback_retr_participants)
(ri, addr, NULL, ri->callback_arg));
}
return (0);
}
static int retr_110(struct PCPnet *pn, struct PCP_retr *ri)
{
char dummy;
char eventid[EVENTID_MAXLEN];
if (sscanf(pn->readbuf, "110%c" EVENTID_SSCANF,
&dummy, eventid) == 2)
{
const char *p, *q;
char *r, *s;
int flags=0;
ri->event_id=eventid;
p=pn->readbuf+4;
while (p)
{
if (isspace((int)(unsigned char)*p))
break;
++p;
}
while (p)
{
if (!isspace((int)(unsigned char)*p))
break;
++p;
}
for (q=p; *q; q++)
{
if (isspace((int)(unsigned char)*q))
break;
}
r=malloc(q-p+1);
if (!r)
{
pn->haserrmsg=0;
return (-1);
}
memcpy(r, p, q-p);
r[q-p]=0;
for (s=r; (s=strtok(s, ",")) != 0; s=0)
{
if (strcasecmp(s, "CANCELLED") == 0)
flags |= LIST_CANCELLED;
else if (strcasecmp(s, "BOOKED") == 0)
flags |= LIST_BOOKED;
else if (strcasecmp(s, "PROXY") == 0)
flags |= LIST_PROXY;
}
if (ri->callback_retr_status)
return ( (*ri->callback_retr_status)
(ri, flags, ri->callback_arg));
}
return (0);
}
static int retr_107(struct PCPnet *pn, struct PCP_retr *ri, int ignore)
{
char dummy;
char eventid[EVENTID_MAXLEN];
int rc=0;
int ch;
int seeneol;
int seendot;
size_t nn;
if (sscanf(pn->readbuf, "107%c" EVENTID_SSCANF,
&dummy, eventid) != 2)
{
errno=EIO;
rc= -1;
}
ri->event_id=eventid;
if (rc == 0 && ri->callback_begin_func)
rc= (*ri->callback_begin_func)(ri, ri->callback_arg);
seeneol=1;
seendot=1;
nn=0;
ch=EOF;
for (;;)
{
if (ch == EOF)
ch=readch(pn, nn);
if (ch == EOF)
{
rc= -1;
break;
}
if (ch == '\r')
continue;
if (seeneol)
seendot= ch == '.';
else
{
if ( ch == '\n' && seendot)
break;
seendot=0;
}
seeneol= ch == '\n';
if (!seendot)
pn->readbuf[nn++]=ch;
ch=EOF;
if (ri->callback_rfc822_func)
{
if (nn >= 8192)
{
if (rc == 0)
rc= (*ri->callback_rfc822_func)
(ri, pn->readbuf, nn,
ri->callback_arg);
nn=0;
}
}
else if (ri->callback_headers_func)
{
if (nn > 8192)
nn=8192; /* Trim excessive hdrs */
if (seeneol)
{
char *h;
char *v;
ch=readch(pn, nn);
if (ch == EOF)
{
rc= -1;
break;
}
if (ch != '\n' && isspace(ch))
{
/* Header wrapped */
while (ch != EOF && ch != '\n'
&& isspace(ch))
ch=readch(pn, nn);
pn->readbuf[nn-1]=' ';
continue;
}
pn->readbuf[nn-1]=0;
h=pn->readbuf;
if ((v=strchr(h, ':')) == NULL)
v="";
else
{
*v++=0;
while (*v &&
isspace((int)(unsigned char)*v))
++v;
}
if (rc == 0)
rc=(*ri->callback_headers_func)
(ri, h, v, ri->callback_arg);
nn=0;
}
}
else nn=0;
}
if (ri->callback_rfc822_func)
{
if (rc == 0)
rc= (*ri->callback_rfc822_func)
(ri, pn->readbuf, nn, ri->callback_arg);
}
if (rc == 0 && ri->callback_end_func)
rc= (*ri->callback_end_func)(ri, ri->callback_arg);
return (rc);
}
static int setacl(struct PCPnet *pn, const char *who, int flags)
{
char buf[1024];
pn->haserrmsg=0;
if (strchr(who, '\r') || strchr(who, '\n') || strlen(who) > 512)
{
errno=EINVAL;
return (-1);
}
sprintf(buf, "ACL SET %s", who);
pcp_acl_name(flags, buf);
strcat(buf, "\n");
if (docmd(pn, buf, 0))
return (-1);
pn->haserrmsg=1;
switch (checkstatus(pn, NULL) / 100) {
case 1:
case 2:
case 3:
break;
default:
errno=EIO;
return (-1);
}
return (0);
}
static int listacl(struct PCPnet *pn, int (*func)(const char *, int, void *),
void *arg)
{
int rc;
int s;
pn->haserrmsg=0;
if (dowrite(pn, "ACL LIST\n", 0) < 0)
return (-1);
rc=0;
pn->haserrmsg=1;
while ((s=getonelinereply(pn)) >= 0)
{
int n=checkstatus(pn, NULL);
if (n >= 400)
rc= -1;
if (n == 103)
{
char addr[ADDR_MAXLEN];
char dummy;
if (sscanf(pn->readbuf, "103%c" ADDR_SSCANF,
&dummy, addr) == 2)
{
const char *p=pn->readbuf+4;
int flags=0;
for ( ; *p; p++)
if (isspace((int)(unsigned char)*p))
break;
while (*p)
{
const char *q;
char buf[256];
if (isspace((int)(unsigned char)*p))
{
++p;
continue;
}
q=p;
for ( ; *p; p++)
if (isspace((int)
(unsigned char)*p))
break;
buf[0]=0;
strncat(buf, q, p-q < 255 ? p-q:255);
flags |= pcp_acl_num(buf);
}
if (rc == 0)
rc= (*func)(addr, flags, arg);
}
else
{
if (rc == 0)
{
rc= -1;
errno=EIO;
}
}
}
if (s > 0)
break;
}
if (s < 0)
{
pn->haserrmsg=0;
rc= -1;
}
return (rc);
}
static void noop(struct PCPnet *pd)
{
docmd(pd, "NOOP\n", 0);
}