diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 |
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 |
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /pcp/pcpnet.c | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2 | |
Initial checkin
Imported from subversion report, converted to git. Updated all paths in
scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'pcp/pcpnet.c')
| -rw-r--r-- | pcp/pcpnet.c | 2223 |
1 files changed, 2223 insertions, 0 deletions
diff --git a/pcp/pcpnet.c b/pcp/pcpnet.c new file mode 100644 index 0000000..fd1d70f --- /dev/null +++ b/pcp/pcpnet.c @@ -0,0 +1,2223 @@ +/* +** 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); +} |
