/*
** Copyright 2000-2004 University of Illinois Board of Trustees
** Copyright 2000-2004 Mark D. Roth
** All rights reserved.
**
** protocol.c - libphclient protocol code
**
** Mark D. Roth <roth@feep.net>
*/
#include <internal.h>
#include <errno.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <string.h>
# include <stdarg.h>
#else
# ifdef HAVE_VARARGS_H
# include <varargs.h>
# endif
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_POLL_H
# include <sys/poll.h>
#else
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
#endif
/*
** ph_set_sendhook() - set the send hook function.
*/
void
ph_set_sendhook(PH *ph, ph_debug_func_t sendfunc)
{
ph->ph_sendhook = sendfunc;
}
/*
** ph_set_recvhook() - set the receive hook function.
*/
void
ph_set_recvhook(PH *ph, ph_debug_func_t recvfunc)
{
ph->ph_recvhook = recvfunc;
}
/*
** ph_set_hookdata() - set the hook data to be passed to the debug hooks
*/
void
ph_set_hookdata(PH *ph, void *hook_data)
{
ph->ph_hook_data = hook_data;
}
/* buffer plugin to read from server */
ssize_t
ph_buffer_read(void *app_data, char *buf, size_t buflen)
{
PH *ph = (PH *)app_data;
return read(ph->ph_rfd[0], buf, buflen);
}
/* ph_get_response() - read and parse a response from the server. */
int
ph_get_response(PH *ph, int *code, char *buf, size_t bufsize)
{
char linebuf[PH_BUF_SIZE];
char *linep, *fieldp;
ssize_t sz;
if (ph_buffer_read_line(ph->ph_buf, linebuf, sizeof(linebuf)) == -1)
return -1;
/* call receive hook, if set */
if (ph->ph_recvhook != NULL)
(*(ph->ph_recvhook))(ph->ph_hook_data, linebuf);
/* blank line */
if (linebuf[0] == '\0')
{
errno = EINVAL;
return -1;
}
/* extract response code */
linep = linebuf;
fieldp = strsep(&linep, ":");
if (sscanf(fieldp, "%d", code) != 1)
{
errno = EINVAL;
return -1;
}
/* response code with no text */
if (linep == NULL)
{
if (buf != NULL)
buf[0] = '\0';
errno = EINVAL;
return -1;
}
/* valid line, copy it to caller-supplied buffer */
if (buf != NULL)
strlcpy(buf, linep, bufsize);
return 0;
}
/*
** ph_parse_response() - utility function to split up ':'-delimited
** sections of the text returned by ph_get_response()
*/
int
ph_parse_response(char *buf, int *subcode, char **field1, char **field2)
{
/* extract entry number */
if (subcode != NULL)
{
if (sscanf(buf, "%d:", subcode) != 1)
{
errno = EINVAL;
return -1;
}
strsep(&buf, ":");
}
/* extract fields */
if (field1 != NULL)
{
*field1 = strsep(&buf, ":");
if (*field1 == NULL)
{
errno = EINVAL;
return -1;
}
}
if (field2 != NULL)
{
*field2 = buf;
if (*field2 == NULL)
{
errno = EINVAL;
return -1;
}
}
return 0;
}
/* ph_send_command() - send a command to the server */
ssize_t
#ifdef STDC_HEADERS
ph_send_command(PH *ph, char *fmt, ...)
#else
ph_send_command(PH *ph, char *fmt, int va_alist)
#endif
{
va_list args;
char buf[PH_BUF_SIZE];
int fd;
#ifdef HAVE_POLL
struct pollfd pfd[1];
#else /* ! HAVE_POLL */
fd_set wfds, efds;
int max;
struct timeval tm;
#endif /* HAVE_POLL */
fd = ph_wfd(ph);
#ifdef HAVE_POLL
memset(pfd, 0, sizeof(pfd));
pfd[0].events = POLLOUT;
pfd[0].fd = fd;
if (poll(pfd, 1, 0) == -1)
return -1;
if (! BIT_ISSET(pfd[0].revents, POLLOUT))
{
errno = EPIPE;
return -1;
}
#else /* ! HAVE_POLL */
FD_ZERO(&wfds);
FD_ZERO(&efds);
FD_SET(fd, &wfds);
FD_SET(fd, &efds);
memset(&tm, 0, sizeof(tm));
if (select(fd + 1, NULL, &wfds, &efds, &tm) == -1)
return -1;
if (! FD_ISSET(fd, &wfds) || FD_ISSET(fd, &efds))
{
errno = EPIPE;
return -1;
}
#endif /* HAVE_POLL */
#ifdef STDC_HEADERS
va_start(args, fmt);
#else
va_start(args);
#endif
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
#ifdef DEBUG
printf("ph_send_command(): sending \"%s\"\n", buf);
#endif
/* execute the send hook, if set */
if (ph->ph_sendhook != NULL)
(*(ph->ph_sendhook))(ph->ph_hook_data, buf);
strlcat(buf, "\n", sizeof(buf));
return write(fd, buf, strlen(buf) + 1);
}
/*
** ph_quote_value() - convert a literal field value to a server-parsable
** quoted string. returns number of characters written.
*/
static size_t
ph_quote_value(char *value, char *buf, size_t bufsize)
{
char tmpbuf[PH_BUF_SIZE];
char *sp, *p, *q;
char c;
/*
** FIXME: need to check return value from strlcpy() and strlcat()
*/
strlcpy(tmpbuf, value, sizeof(tmpbuf));
strlcat(buf, "\"", bufsize);
for (sp = tmpbuf, p = strpbrk(sp, "\n\t\"\\");
p != NULL;
sp = p + 1, p = strpbrk(sp, "\n\t\"\\"))
{
c = *p;
*p = '\0';
strlcat(buf, sp, bufsize);
switch (c)
{
case '\n':
q = "\\n";
break;
case '\t':
q = "\\t";
break;
case '\\':
q = "\\\\";
break;
case '"':
if (p == value || *(p - 1) != '\\')
q = "\\\"";
else
q = "\"";
break;
default:
q = "";
break;
}
if (strlen(q) != 0)
strlcat(buf, q, bufsize);
}
strlcat(buf, sp, bufsize);
return strlcat(buf, "\"", bufsize);
}
/*
** ph_dequote_value() - convert a server-parsable quoted value to a literal
** field value. returns number of characters written.
*/
size_t
ph_dequote_value(char *value, char *buf, size_t bufsize)
{
size_t sz;
char *sp, *p;
#ifdef DEBUG
printf("==> ph_dequote_value(\"%s\")\n", value);
#endif
sz = strlcpy(buf, value, bufsize);
/* first, eliminate the non-escaped quotes */
for (sp = buf; (p = strchr(sp, '"')) != NULL; sp = p)
if (p == buf || *(p - 1) != '\\')
{
memmove(p, p + 1, strlen(p + 1) + 1);
sz--;
}
else
p++;
/* change ``\\"'' to "\\\"" and ``\"'' to "\"" */
for (sp = buf; (p = strstr(sp, "\\\"")) != NULL; sp = p)
{
if (p == buf || *(p - 1) == '\\')
memmove(p - 1, p, strlen(p) + 1);
else
{
*p = '"';
memmove(p + 1, p + 2, strlen(p + 2) + 1);
}
sz--;
}
/* change ``\\n'' to "\\n" and ``\n'' to "\n" */
for (sp = buf; (p = strstr(sp, "\\n")) != NULL; sp = p)
{
if (p == buf || *(p - 1) == '\\')
memmove(p - 1, p, strlen(p) + 1);
else
{
*p = '\n';
memmove(p + 1, p + 2, strlen(p + 2) + 1);
}
sz--;
}
/* change ``\\t'' to "\\t" and ``\t'' to "\t" */
for (sp = buf; (p = strstr(sp, "\\t")) != NULL; sp = p)
{
if (p == buf || *(p - 1) == '\\')
memmove(p - 1, p, strlen(p) + 1);
else
{
*p = '\t';
memmove(p + 1, p + 2, strlen(p + 2) + 1);
}
sz--;
}
/* change ``\\'' to "\\" and ``\'' to "" */
for (sp = buf; (p = strstr(sp, "\\\\")) != NULL; sp = p)
{
if (p == buf || *(p - 1) == '\\')
memmove(p - 1, p, strlen(p) + 1);
else
{
*p = '"';
memmove(p + 1, p + 2, strlen(p + 2) + 1);
}
sz--;
}
return sz;
}
/*
** ph_decode_selectors() - decode a ph_fieldselector array into a string
** to send to the server. returns number of characters written.
*/
size_t
ph_decode_selectors(struct ph_fieldselector selectors[],
char *buf, size_t bufsize)
{
int i;
size_t buflen = 0;
for (i = 0; selectors[i].pfs_value != NULL; i++)
{
buflen = strlcat(buf, " ", bufsize);
if (selectors[i].pfs_field != NULL
&& selectors[i].pfs_field[0] != '\0')
buflen += snprintf(buf + buflen, bufsize - buflen,
"%s%c",
selectors[i].pfs_field,
selectors[i].pfs_operation);
buflen += ph_quote_value(selectors[i].pfs_value, buf + buflen,
bufsize - buflen);
}
return buflen;
}
/*
** ph_encode_selector() - parse the selector string and add an entry to
** the selectors array.
** returns:
** 0 success
** -1 parse error
*/
int
ph_encode_selector(char *string, int requirefield, int *lastindex,
struct ph_fieldselector **selectors)
{
char *p;
char buf[PH_BUF_SIZE], valbuf[PH_BUF_SIZE];
char op;
void *ptr;
#ifdef DEBUG
printf("==> ph_encode_selector(\"%s\")\n", string);
#endif
strlcpy(buf, string, sizeof(buf));
/* check for an operator */
p = strpbrk(buf, "=><~");
if (p == NULL
&& requirefield)
{
errno = EINVAL;
return -1;
}
/* allocate memory */
ptr = realloc(*selectors,
(*lastindex + 2) * sizeof(struct ph_fieldselector));
if (ptr == NULL)
return -1;
*selectors = (struct ph_fieldselector *)ptr;
memset(&((*selectors)[*lastindex]), 0,
2 * sizeof(struct ph_fieldselector));
if (p != NULL)
{
/* encode field and operator */
op = *p;
*p++ = '\0';
(*selectors)[*lastindex].pfs_field = strdup(buf);
if ((*selectors)[*lastindex].pfs_field == NULL)
return -1;
(*selectors)[*lastindex].pfs_operation = op;
}
else
/* set p to point to value */
p = buf;
/* encode value */
ph_dequote_value(p, valbuf, sizeof(valbuf));
(*selectors)[*lastindex].pfs_value = strdup(valbuf);
if ((*selectors)[*lastindex].pfs_value == NULL)
return -1;
#ifdef DEBUG
printf("field=\"%s\", value=\"%s\"\n",
(*selectors)[*lastindex].pfs_field
? (*selectors)[*lastindex].pfs_field
: "NULL",
(*selectors)[*lastindex].pfs_value
? (*selectors)[*lastindex].pfs_value
: "NULL");
#endif
/* increment last index */
(*lastindex)++;
return 0;
}
/*
** ph_free_selectors() - free memory associated with an array of selectors
*/
void
ph_free_selectors(struct ph_fieldselector *selectors)
{
int i;
if (selectors == NULL)
return;
for (i = 0; selectors[i].pfs_value != NULL; i++)
{
if (selectors[i].pfs_field != NULL)
free(selectors[i].pfs_field);
if (selectors[i].pfs_value != NULL)
free(selectors[i].pfs_value);
}
free(selectors);
}
syntax highlighted by Code2HTML, v. 0.9.1