diff options
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); +} | 
