Isolate common http functionality

This commit is contained in:
dac%x.cx 2000-05-02 01:23:29 +00:00
parent 8aeb6464b8
commit 822c47528d
11 changed files with 640 additions and 656 deletions

View File

@ -47,7 +47,7 @@ STONE = $(OBJDIR)/mailclient$(EXE_SUFFIX)
STONESRCS = bench.c client.c errexit.c main.c \
parse.c sysdep.c timefunc.c \
http.c imap4.c pop3.c smtp.c wmap.c
http-util.c http.c imap4.c pop3.c smtp.c wmap.c
STONEOBJS = $(addprefix $(OBJDIR)/, $(STONESRCS:.c=.$(OBJ_SUFFIX)) )
@ -67,7 +67,7 @@ $(OBJDIR)/client.$(OBJ_SUFFIX): client.c bench.h sysdep.h Makefile
$(OBJDIR)/errexit.$(OBJ_SUFFIX): errexit.c bench.h sysdep.h Makefile
$(OBJDIR)/http.$(OBJ_SUFFIX): http.c bench.h pish.h sysdep.h Makefile
$(OBJDIR)/http.$(OBJ_SUFFIX): http.c bench.h sysdep.h http-util.h Makefile
$(OBJDIR)/imap4.$(OBJ_SUFFIX): imap4.c bench.h pish.h sysdep.h Makefile
@ -85,7 +85,9 @@ $(OBJDIR)/sysdep.$(OBJ_SUFFIX): sysdep.c bench.h sysdep.h Makefile
$(OBJDIR)/timefunc.$(OBJ_SUFFIX): timefunc.c bench.h sysdep.h Makefile
$(OBJDIR)/wmap.$(OBJ_SUFFIX): wmap.c bench.h sysdep.h Makefile
$(OBJDIR)/wmap.$(OBJ_SUFFIX): wmap.c bench.h sysdep.h Makefile http-util.h
$(OBJDIR)/http-util.$(OBJ_SUFFIX): http-util.c bench.h sysdep.h Makefile http-util.h
# currently broken. See ../Makefile for packaging
install: all

View File

@ -192,6 +192,8 @@ typedef struct stats { /* used for throttling ??? */
} stats_t;
typedef struct resolved_addr {
char * hostName; /* name of server */
NETPORT portNum; /* port ot use */
int resolved;
struct hostent host_phe;
struct protoent host_ppe;
@ -465,8 +467,7 @@ extern double compdifftime_double(struct timeval *End, struct timeval *Strt);
/* routines in main.c */
extern char *safe_inet_ntoa(struct in_addr ina, char *psz);
extern SOCKET connectsock(ptcx_t ptcx, char *host, resolved_addr_t *, NETPORT portnum,
char *protocol);
extern SOCKET connectSocket(ptcx_t ptcx, resolved_addr_t *, char *protocol);
extern int set_abortive_close(SOCKET sock);
extern void throttle(ptcx_t ptcx, mail_command_t *comm, cmd_stats_t *timer);

419
mstone/src/http-util.c Normal file
View File

@ -0,0 +1,419 @@
/* -*- Mode: C; c-file-style: "bsd"; comment-column: 40 -*- */
/*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Mailstone utility,
* released March 17, 2000.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Dan Christian <robodan@netscape.com>
* Marcel DePaolis <marcel@netcape.com>
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License Version 2 or later (the "GPL"), in
* which case the provisions of the GPL are applicable instead of
* those above. If you wish to allow use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the NPL or the GPL.
*/
/* http-util.c -- Shared HTTP functions */
#include "bench.h"
#include "http-util.h"
/*
Establish (or re-establish) connection to server.
No data is exchanged.
*/
int
HttpConnect (
ptcx_t ptcx,
resolved_addr_t *hostInfo,
event_timer_t *timer)
{
int rc=0;
D_PRINTF(stderr, "HttpConnect()\n");
event_start(ptcx, timer);
ptcx->sock = connectSocket(ptcx, hostInfo, "tcp");
event_stop(ptcx, timer);
if (BADSOCKET(ptcx->sock)) {
if (gf_timeexpired < EXIT_FAST) {
timer->errs++;
returnerr(debugfile,
"%s<HttpConnect: HTTP Couldn't connect to %s: %s\n",
ptcx->errMsg, hostInfo->hostName, neterrstr());
}
return -1;
}
if (gf_abortive_close) {
if (set_abortive_close(ptcx->sock) != 0) {
returnerr(debugfile,
"%s<HttpConnect: HTTP: WARNING: Could not set abortive close\n",
ptcx->errMsg, neterrstr());
}
}
return rc;
}
/*
HttpReadResponse handles the details of reading a HTTP response It
takes and interactive parsing function and a response buffer. The
response buffer will hold at least the last 1/2 buffer of the data
stream. This allows errors to be handled by the calling routine.
Long responses should be parsed on the fly by the parsing callback.
HttpReadResponse handles find the content-length and header break.
Once the break is called, it will call the parsing callback so that it can
set findStr (which is NULL).
The findStr search is case insensitive if the first character of findStr
has distinct upper and lower cases (see toupper and tolower).
The callback is called with the buffer, the bytes in the buffer,
a pointer just past the findStr match, and a pointer to findStr.
The callback should then update findStr and searchStr; and return -2
for an error (abort message), 0 for success, and 1 for need more data.
findStr is expected to be a constant or static storage.
Before exit, the parsing callback is called one last time (with NULL
findStr and searchStr), so that it can do any cleanup.
Management of the buffer is done automatically by the movement of searchStr.
HttpReadResponse returns bytesInBuffer on success, -1 for IO error,
-2 for error returned by the parser.
*/
int
HttpReadResponse(
ptcx_t ptcx,
mail_command_t *cmd, /* command info for msgParse */
void *me, /* connection state for msgParse */
char *buffer,
int buflen,
parseMsgPtr_t msgParse
)
{
/* read the server response and do nothing with it */
/* Will need to look for HTTP headers and Content-Length/Keep-Alive */
int gotheader = 0;
int totalbytesread = 0;
int bytesread;
int bytesInBuffer = 0;
const char *findStr; /* string we are watching for */
const char *oldFindStr = NULL;
char findStrSpn[3]; /* case insensitive search start */
int findLen=0; /* length of string */
char *foundStr; /* what we found */
char *searchStart; /* where next to look for findStr */
int contentlength = 0; /* HTTP content length */
int bytestoread = buflen-1; /* start by reading lots */
int getBytes = 0; /* need more input */
D_PRINTF(stderr, "HttpReadResponse()\n");
findStr = HTTP_CONTENT_LENGTH;
searchStart = buffer;
memset(buffer, 0, buflen); /* DEBUG */
while (1)
{
if (gf_timeexpired >= EXIT_FAST) {
D_PRINTF(stderr,"HttpReadResponse() Time expired.\n");
break;
}
if (findStr != oldFindStr) { /* findStr changed */
if (NULL == findStr) {
findLen = 0;
findStrSpn[0] = 0;
} else {
char upperC, lowerC; /* upper and lower of findStr[0] */
findLen = strlen (findStr);
upperC = toupper (findStr[0]);
lowerC = tolower (findStr[0]);
if (lowerC != upperC) {
/*D_PRINTF (stderr, "New findStr [%s] upper %c lower %c\n",
findStr, upperC, lowerC);*/
findStrSpn[0] = upperC;
findStrSpn[1] = lowerC;
findStrSpn[2] = 0;
} else {
/*D_PRINTF (stderr, "New findStr [%s] NOT case sensitive\n",
findStr);*/
findStrSpn[0] = 0;
}
}
oldFindStr = findStr;
}
/* See if we should read more */
if ((getBytes > 0)
|| (0 == findLen)
|| ((bytesInBuffer - (searchStart - buffer)) < findLen)) {
int count;
if (totalbytesread >= bytestoread) { /* done reading */
break;
}
/* Make room for more data */
/* need to be careful to save data for response. shift by half */
if ((searchStart - buffer) > buflen/2) {
/* This is the only place we reduce searchStart or bytesInBuffer */
int toSkip = buflen/2;
int toSave = bytesInBuffer - toSkip;
/*D_PRINTF (stderr, "Shifting %d of %d bytes\n",
toSave, bytesInBuffer);*/
memcpy (buffer, buffer+toSkip, toSave); /* shift data down */
bytesInBuffer = toSave;
buffer[bytesInBuffer] = 0; /* re-terminate??? */
searchStart -= toSkip;
assert (searchStart >= buffer);
}
count = MIN (bytestoread-totalbytesread,
buflen-bytesInBuffer-1);
/*D_PRINTF(stderr, "retryRead() size=%d %d/%d inBuffer=%d\n",
count, totalbytesread, bytestoread, bytesInBuffer);*/
bytesread = retryRead(ptcx, ptcx->sock, buffer+bytesInBuffer, count);
if (0 == bytesread) { /* most likely an error */
strcpy (ptcx->errMsg, "HttpReadResponse:got 0 bytes");
break;
}
if (bytesread < 0) { /* ERROR */
strcat (ptcx->errMsg, "<HttpReadResponse");
return -1;
}
T_PRINTF(ptcx->logfile, buffer+bytesInBuffer, bytesread,
"%s ReadResponse", cmd->proto->name); /* telemetry log */
totalbytesread += bytesread;
bytesInBuffer += bytesread;
buffer[bytesInBuffer] = 0; /* terminate for string searches */
ptcx->bytesread += bytesread; /* log statistics */
getBytes = 0;
} else {
/*D_PRINTF (stderr,
"Not reading, have %d only need %d. %d/%d\n",
bytesInBuffer- (searchStart - buffer), findLen,
bytestoread, totalbytesread);*/
}
if (NULL == findStr) { /* nothing to search for */
searchStart = buffer+bytesInBuffer; /* discard buffer */
continue;
}
/* otherwise search for findStr */
if (findStrSpn[0]) { /* do case insensitive version */
char *cp;
foundStr = NULL;
for (cp = searchStart; (cp - buffer) < bytesInBuffer; ++cp) {
cp = strpbrk (cp, findStrSpn); /* look for first char match */
if (NULL == cp) {
break;
}
if (!strnicmp(cp, findStr, findLen)) { /* check whole match */
foundStr = cp;
break;
}
}
} else { /* non case sensitive version */
foundStr = strstr(searchStart, findStr);
}
if (NULL == foundStr) { /* found nothing, shift and continue */
/* jump to findLen-1 from end */
if (bytesInBuffer < findLen)
continue; /* nothing to shift */
searchStart = buffer + bytesInBuffer - findLen + 1;
continue; /* get more input */
}
/* Found search string */
/*D_PRINTF (stderr, "Found [%s] after %d bytes\n",
findStr, totalbytesread);*/
searchStart = foundStr + findLen; /* found this much */
if (0 == gotheader) { /* found length */
contentlength = atoi(searchStart); /* save contentlength */
searchStart = strchr (searchStart, '\n'); /* jump to EOL */
if (NULL == searchStart) { /* missing line end, get more */
searchStart = buffer;
} else { /* got complete contentlength line */
gotheader++;
findStr = HTTP_HEADER_BREAK; /* now look for break */
}
continue;
}
else if (1 == gotheader) { /* found header break */
gotheader++;
/* now compute bytetoread */
bytestoread = contentlength
+ ((searchStart - buffer) /* position in buffer */
+ (totalbytesread - bytesInBuffer)); /* buffer offset */
D_PRINTF(stderr, "contentlength=%d, bytestoread=%d\n",
contentlength, bytestoread);
findStr = NULL; /* should chain into extra searches */
if (msgParse) { /* initialize callback */
int rc;
/* having findStr == NULL signal initial callback */
rc = (*(msgParse)) (ptcx, cmd, me,
buffer, bytesInBuffer, &searchStart, &findStr);
if (rc < 0) { /* parser signaled error */
return rc;
}
if (NULL == searchStart) {
searchStart = buffer+bytesInBuffer; /* discard buffer */
}
}
}
else {
if (msgParse) { /* call extra searches */
int rc;
rc = (*(msgParse)) (ptcx, cmd, me,
buffer, bytesInBuffer, &searchStart, &findStr);
if (rc < 0) { /* protocol error */
return rc;
}
if (rc > 0) { /* need more data */
getBytes = rc;
searchStart = foundStr; /* back up search string */
continue;
}
if (searchStart < buffer) { /* or NULL */
searchStart = buffer+bytesInBuffer; /* discard buffer */
}
} else {
searchStart = buffer+bytesInBuffer; /* discard buffer */
}
}
}
D_PRINTF(stderr, "HttpReadResponse: Read %d/%d saved %d\n",
totalbytesread, bytestoread, bytesInBuffer);
if (msgParse) { /* call extra searches */
findStr = NULL; /* signal final callback */
searchStart = NULL;
(void)(*(msgParse)) (ptcx, cmd, me,
buffer, bytesInBuffer, &searchStart, &findStr);
}
return bytesInBuffer;
}
/*
Handle a command-response transaction. Connects/retries as needed.
Expects pointers to buffers of these sizes:
char command[MAX_COMMAND_LEN]
char response[resplen]
*/
int
HttpCommandResponse (
ptcx_t ptcx, /* thread state */
mail_command_t *cmd, /* command info [blind] */
void *me, /* connection state for msgParse */
resolved_addr_t *hostInfo, /* reconnect info */
event_timer_t *reconnect, /* timer for reconnects */
event_timer_t *timer, /* timer for this command */
char *command, /* command to send */
char *response, /* response buffer */
int resplen, /* size of response buffer */
parseMsgPtr_t msgParse) /* response handler */
{
int retries = 0;
int ret;
if (response == NULL)
return -1;
response[0] = 0; /* make sure it makes sense on error */
D_PRINTF(stderr, "HttpCommandResponse() command=[%.99s]\n", command);
while (retries++ < HTTP_MAX_RECONNECTS) {
if (BADSOCKET(ptcx->sock)) {
ret = HttpConnect(ptcx, hostInfo, reconnect);
if (ret == -1) {
return -1;
}
}
/* send the command already formatted */
T_PRINTF(ptcx->logfile, command, strlen (command),
"%s SendCommand", cmd->proto->name);
event_start(ptcx, timer);
ret = sendCommand(ptcx, ptcx->sock, command);
if (ret == -1) {
event_stop(ptcx, timer);
reconnect->errs++;
/* this can only mean an IO error. Probably EPIPE */
NETCLOSE(ptcx->sock);
ptcx->sock = BADSOCKET_VALUE;
strcat (ptcx->errMsg, "<HttpCommandResponse");
D_PRINTF (stderr, "%s Aborting connection %s\n",
ptcx->errMsg, neterrstr());
continue;
}
/* read server response */
ret = HttpReadResponse(ptcx, cmd, me, response, resplen, msgParse);
event_stop(ptcx, timer);
if (ret == 0) { /* got nothing */
/* Probably an IO error. It will be definate if re-tried. */
MS_usleep (5000); /* time for bytes to show up */
continue; /* just re-try it */
} else if (ret == -1) { /* IO error */
reconnect->errs++;
NETCLOSE(ptcx->sock);
ptcx->sock = BADSOCKET_VALUE;
strcat (ptcx->errMsg, "<HttpCommandResponse");
D_PRINTF (stderr, "%s Aborting connection %s\n",
ptcx->errMsg, neterrstr());
continue;
} else if (ret < -1) { /* some other error */
timer->errs++;
/* this is overkill */
NETCLOSE(ptcx->sock);
ptcx->sock = BADSOCKET_VALUE;
strcat (ptcx->errMsg, "<HttpCommandResponse: protocol ERROR");
return -1;
}
/*D_PRINTF (stderr, "HttpCommandResponse saved %d bytes response=[%s]\n",
ret, response);*/
/* You get parent.timeoutCB on any kind of error (like bad sid) */
if (strstr (response, "parent.timeoutCB()")) {
timer->errs++;
NETCLOSE(ptcx->sock);
ptcx->sock = BADSOCKET_VALUE;
strcpy (ptcx->errMsg, "HttpCommandResponse: got HTTP error msg");
return -1;
}
return ret;
}
reconnect->errs++;
NETCLOSE(ptcx->sock);
ptcx->sock = BADSOCKET_VALUE;
strcat (ptcx->errMsg, "<HttpCommandResponse: Too many connection retries");
return -1;
}

60
mstone/src/http-util.h Normal file
View File

@ -0,0 +1,60 @@
/* -*- Mode: C; c-file-style: "bsd"; comment-column: 40 -*- */
/*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Netscape Mailstone utility,
* released March 17, 2000.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s): Dan Christian <robodan@netscape.com>
* Marcel DePaolis <marcel@netcape.com>
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License Version 2 or later (the "GPL"), in
* which case the provisions of the GPL are applicable instead of
* those above. If you wish to allow use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the NPL or the GPL.
*/
/* http-util.h -- Shared HTTP functions */
#define HTTP_HEADER_BREAK "\r\n\r\n"
#define HTTP_CONTENT_TYPE_POST "Content-type: application/x-www-form-urlencoded"
#define HTTP_CONTENT_LENGTH "Content-length:"
#define HTTP_LOCATION "Location:"
#define HTTP_PROTOCOL "http://"
#define HTTP_MAX_RECONNECTS 5 /* max to reconnect/retry a command */
/* Callback type for parsing messages on the fly */
typedef int (*parseMsgPtr_t)(
ptcx_t, pmail_command_t, void *me,
char *buffer, int bytesInBuffer, char **searchStr, const char **findStr);
extern int HttpConnect(ptcx_t ptcx, resolved_addr_t *hostInfo,
event_timer_t *timer);
extern int HttpReadResponse(ptcx_t ptcx, mail_command_t *cmd, void *me,
char *responce, int resplen,
parseMsgPtr_t msgParse);
extern int HttpCommandResponse (ptcx_t ptcx, mail_command_t *cmd, void *me,
resolved_addr_t *hostInfo, event_timer_t *reconnect,
event_timer_t *timer, char *command, char *response, int resplen,
parseMsgPtr_t msgParse);

View File

@ -35,7 +35,7 @@
/* http.c: HTTP protocol test */
#include "bench.h"
#include "pish.h"
#include "http-util.h"
#define MAX_HTTP_RESPONSE_LEN (2*1024) /* size of sliding buffer */
@ -44,24 +44,36 @@
There is one of these for every command in every thread.
*/
typedef struct http_stats {
event_timer_t connect;
event_timer_t connect; /* initial connections */
event_timer_t reconnect; /* re-connections */
event_timer_t msgread; /* AKA retrieve */
event_timer_t logout; /* AKA dis-connect */
/* no local storage */
} http_stats_t;
/* These are common to POP, IMAP, SMTP, HTTP */
typedef struct http_command {
resolved_addr_t hostInfo; /* Host, port, and IP cache */
char * httpCommand; /* Old: single HTTP command */
#if 0
string_list_t getFirstCmds; /* GET commands after connect */
string_list_t getLoopCmds; /* GET commands, each loop */
string_list_t getLastCmds; /* GET commands before disconnect */
#endif
} http_command_t;
/*
State during command execution.
*/
typedef struct _doHTTP_state {
int numMsgs; /* messages in folder */
int totalMsgLength; /* total msg length */
int msgCounter; /* count in download */
int nothingHere;
} doHTTP_state_t;
static void doHttpExit (ptcx_t ptcx, doHTTP_state_t *me);
static int readHttpResponse(ptcx_t ptcx, SOCKET sock, char *buffer, int buflen);
static int doHttpCommandResponse(ptcx_t ptcx, SOCKET sock, char *command, char *response, int resplen);
static int HttpParseNameValue (pmail_command_t cmd, char *name, char *tok);
/*
Set defaults in command structure
@ -72,17 +84,17 @@ HttpParseStart (pmail_command_t cmd,
param_list_t *defparm)
{
param_list_t *pp;
pish_command_t *pish = (pish_command_t *)mycalloc
(sizeof (pish_command_t));
cmd->data = pish;
http_command_t *mycmd = (http_command_t *)mycalloc
(sizeof (http_command_t));
cmd->data = mycmd;
cmd->numLoops = 1; /* default 1 downloads */
pish->portNum = HTTP_PORT; /* get default port */
mycmd->hostInfo.portNum = HTTP_PORT; /* get default port */
D_PRINTF(stderr, "Http Assign defaults\n");
/* Fill in defaults first, ignore defaults we dont use */
for (pp = defparm; pp; pp = pp->next) {
(void)pishParseNameValue (cmd, pp->name, pp->value);
(void)HttpParseNameValue (cmd, pp->name, pp->value);
}
return 1;
@ -96,7 +108,7 @@ HttpParseEnd (pmail_command_t cmd,
string_list_t *section,
param_list_t *defparm)
{
pish_command_t *pish = (pish_command_t *)cmd->data;
http_command_t *mycmd = (http_command_t *)cmd->data;
string_list_t *sp;
/* Now parse section lines */
@ -111,7 +123,7 @@ HttpParseEnd (pmail_command_t cmd,
string_tolower(name);
tok = string_unquote(tok);
if (pishParseNameValue (cmd, name, tok) < 0) {
if (HttpParseNameValue (cmd, name, tok) < 0) {
/* not a known attr */
D_PRINTF(stderr,"unknown attribute '%s' '%s'\n", name, tok);
returnerr(stderr,"unknown attribute '%s' '%s'\n", name, tok);
@ -119,31 +131,58 @@ HttpParseEnd (pmail_command_t cmd,
}
/* check for some of the required command attrs */
if (!pish->mailServer) {
if (!mycmd->hostInfo.hostName) {
D_PRINTF(stderr,"missing server for command");
return returnerr(stderr,"missing server for command\n");
}
if (!pish->httpCommand) {
if (!mycmd->httpCommand) {
D_PRINTF(stderr,"missing httpcommand for HTTP");
return returnerr(stderr,"missing httpcommand for HTTP\n");
}
/* see if we can resolve the mailserver addr */
if (resolve_addrs(pish->mailServer, "tcp",
&(pish->hostInfo.host_phe),
&(pish->hostInfo.host_ppe),
&(pish->hostInfo.host_addr),
&(pish->hostInfo.host_type))) {
if (resolve_addrs(mycmd->hostInfo.hostName, "tcp",
&(mycmd->hostInfo.host_phe),
&(mycmd->hostInfo.host_ppe),
&(mycmd->hostInfo.host_addr),
&(mycmd->hostInfo.host_type))) {
return returnerr (stderr, "Error resolving hostname '%s'\n",
pish->mailServer);
mycmd->hostInfo.hostName);
} else {
pish->hostInfo.resolved = 1; /* mark the hostInfo resolved */
mycmd->hostInfo.resolved = 1; /* mark the hostInfo resolved */
}
return 1;
}
/*
Parse arguments for http command
*/
static int
HttpParseNameValue (pmail_command_t cmd,
char *name,
char *tok)
{
http_command_t *mycmd = (http_command_t *)cmd->data;
D_PRINTF (stderr, "HttpParseNameValue(name='%s' value='%s')\n", name, tok);
/* find a home for the attr/value */
if (cmdParseNameValue(cmd, name, tok))
; /* done */
else if (strcmp(name, "server") == 0)
mycmd->hostInfo.hostName = mystrdup (tok);
else if (strcmp(name, "portnum") == 0)
mycmd->hostInfo.portNum = atoi(tok);
else if (strcmp(name, "httpcommand") == 0)
mycmd->httpCommand = mystrdup (tok);
/*stringListAdd(&mycmd->getFirstCmds, tok);*/
else {
return -1;
}
return 0;
}
/* PROTOCOL specific */
void
HttpStatsInit(mail_command_t *cmd, cmd_stats_t *p, int procNum, int threadNum)
@ -157,7 +196,7 @@ HttpStatsInit(mail_command_t *cmd, cmd_stats_t *p, int procNum, int threadNum)
}
if (cmd) { /* do sub-range calulations */
/*pish_command_t *pish = (pish_command_t *)cmd->data;
/*http_command_t *mycmd = (http_command_t *)cmd->data;
http_stats_t *stats = (http_stats_t *)p->data;*/
}
}
@ -258,182 +297,56 @@ HttpStatsFormat (protocol_t *pp,
strcat (cp, "</FORMAT>\n");
}
static int
readHttpResponse(ptcx_t ptcx, SOCKET sock, char *buffer, int buflen)
{
/* read the server response and do nothing with it */
int totalbytesread = 0;
int bytesread;
memset (buffer, 0, buflen);
while (totalbytesread < buflen)
{
if (gf_timeexpired >= EXIT_FAST) {
D_PRINTF(debugfile,"readHttpResponse() Time expired.\n");
break;
}
bytesread = retryRead(ptcx, sock, buffer, buflen-1);
if (bytesread == 0) /* just read until we hit emtpy */
break;
if (bytesread < 0) { /* IO error */
strcat (ptcx->errMsg, "<ReadHttpResponse");
return -1;
}
totalbytesread += bytesread;
buffer[bytesread] = 0; /* terminate for later searches */
ptcx->bytesread += bytesread;
T_PRINTF(ptcx->logfile, buffer, bytesread, "HTTP ReadResponse");
}
D_PRINTF(debugfile, "Read %d from server [%.99s]\n", totalbytesread, buffer);
if (0 == totalbytesread) {
strcpy (ptcx->errMsg, "ReadHttpResponse: Read 0 bytes");
return -1;
}
return totalbytesread;
}
/* expects pointers to buffers of these sizes */
/* char command[MAX_COMMAND_LEN] */
/* char response[resplen] */
static int
doHttpCommandResponse(ptcx_t ptcx, SOCKET sock, char *command, char *response, int resplen)
{
int ret;
if (response == NULL)
return -1;
memset(response, 0, resplen);
T_PRINTF(ptcx->logfile, command, strlen (command), "HTTP SendCommand");
/* send the command already formatted */
if ((ret = sendCommand(ptcx, sock, command)) == -1) {
if (gf_timeexpired < EXIT_FAST) {
returnerr(debugfile, "Error sending [%s] command to server: %s\n",
command, neterrstr());
}
return -1;
}
/* read server response */
if ((ret = readHttpResponse(ptcx, sock, response, resplen)) <= 0) {
if (gf_timeexpired < EXIT_FAST) {
trimEndWhite (command);
returnerr(debugfile, "Error reading [%s] response: %s\n",
command, neterrstr());
}
return -1;
}
return ret;
}
#if 0
int
httpLogin(ptcx_t ptcx, mail_command_t *cmd, SOCKET sock)
{
char command[MAX_COMMAND_LEN];
char respBuffer[MAX_RESPONSE_LEN];
char mailUser[MAX_MAILADDR_LEN];
char userPasswd[MAX_MAILADDR_LEN];
unsigned long loginNum;
/* generate a random username (with a mailbox on the server) */
loginNum = (RANDOM() % pish->numLogins);
/* add the the base user */
loginNum += pish->firstLogin;
sprintf(mailUser, pish->loginFormat, loginNum);
D_PRINTF(debugfile,"mailUser=%s\n", mailUser);
sprintf(command, "USER %s%s", mailUser, CRLF);
if (doHttpCommandResponse(ptcx, sock, command, respBuffer) == -1) {
return -1;
}
/* send Password */
sprintf(userPasswd, pish->passwdFormat, loginNum);
sprintf(command, "PASS %s%s", userPasswd, CRLF);
if (doHttpCommandResponse(ptcx, sock, command, respBuffer) == -1) {
if (gf_timeexpired < EXIT_FAST) {
returnerr(debugfile,"HTTP cannot login user=%s pass=%s\n",
mailUser, userPasswd);
}
return -1;
}
return 0;
}
#endif
void *
doHttpStart(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer)
{
doHTTP_state_t *me = (doHTTP_state_t *)mycalloc (sizeof (doHTTP_state_t));
http_stats_t *stats = (http_stats_t *)ptimer->data;
pish_command_t *pish = (pish_command_t *)cmd->data;
NETPORT port;
http_command_t *mycmd = (http_command_t *)cmd->data;
if (!me) return NULL;
me->numMsgs = 0;
me->totalMsgLength = 0;
me->msgCounter = 0;
port = pish->portNum;
event_start(ptcx, &stats->connect);
ptcx->sock = connectsock(ptcx, pish->mailServer, &pish->hostInfo, port, "tcp");
event_stop(ptcx, &stats->connect);
if (BADSOCKET(ptcx->sock)) {
if (gf_timeexpired < EXIT_FAST) {
stats->connect.errs++;
returnerr(debugfile,
"%s<wmapConnect: HTTP Couldn't connect to %s: %s\n",
ptcx->errMsg, pish->mailServer, neterrstr());
}
myfree (me);
if (HttpConnect(ptcx, &mycmd->hostInfo, &stats->connect) == -1) {
return NULL;
}
if (gf_abortive_close) {
if (set_abortive_close(ptcx->sock) != 0) {
returnerr (debugfile,
"HTTP: WARNING: Could not set abortive close\n");
}
}
/* there is no banner or login state for HTTP */
return me;
}
int
doHttpLoop(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, void *mystate)
doHttpLoop (
ptcx_t ptcx,
mail_command_t *cmd,
cmd_stats_t *ptimer,
void *mystate)
{
doHTTP_state_t *me = (doHTTP_state_t *)mystate;
char command[MAX_COMMAND_LEN];
char respBuffer[MAX_HTTP_RESPONSE_LEN];
int rc;
http_stats_t *stats = (http_stats_t *)ptimer->data;
pish_command_t *pish = (pish_command_t *)cmd->data;
http_command_t *mycmd = (http_command_t *)cmd->data;
#if 0
if (me->msgCounter >= me->numMsgs)
return -1; /* done, close */
#endif
if (BADSOCKET(ptcx->sock)) { /* re-connect if needed */
rc = HttpConnect(ptcx, &mycmd->hostInfo, &stats->connect);
if (rc == -1) {
return -1;
}
}
/* send the HTTP command */
/* will have to deal with encoding URLs before too long... */
sprintf(command, pish->httpCommand, me->msgCounter);
strcat(command, " HTTP/1.0\r\n");
strcpy (command, mycmd->httpCommand);
strcat (command, " HTTP/1.0\r\n");
/* other headers go here... */
strcat(command, "\r\n");
event_start(ptcx, &stats->msgread);
rc = doHttpCommandResponse(ptcx, ptcx->sock,
command, respBuffer, sizeof(respBuffer));
event_stop(ptcx, &stats->msgread);
rc = HttpCommandResponse(ptcx, cmd, mystate,
&mycmd->hostInfo, &stats->connect,
&stats->msgread,
command, respBuffer,
sizeof(respBuffer), NULL);
if (rc == 0) {
doHttpExit (ptcx, me);
return -1;

View File

@ -119,7 +119,7 @@ Imap4ParseStart (pmail_command_t cmd,
cmd->data = pish;
cmd->loopDelay = 10*60; /* default 10 min */
pish->portNum = IMAP4_PORT; /* get default port */
pish->hostInfo.portNum = IMAP4_PORT; /* get default port */
D_PRINTF(stderr, "Imap4 Assign defaults\n");
/* Fill in defaults first, ignore defaults we dont use */
@ -161,7 +161,7 @@ Imap4ParseEnd (pmail_command_t cmd,
}
/* check for some of the required command attrs */
if (!pish->mailServer) {
if (!pish->hostInfo.hostName) {
D_PRINTF(stderr,"missing server for command");
return returnerr(stderr,"missing server for command\n");
}
@ -177,13 +177,13 @@ Imap4ParseEnd (pmail_command_t cmd,
}
/* see if we can resolve the mailserver addr */
if (resolve_addrs(pish->mailServer, "tcp",
if (resolve_addrs(pish->hostInfo.hostName, "tcp",
&(pish->hostInfo.host_phe),
&(pish->hostInfo.host_ppe),
&(pish->hostInfo.host_addr),
&(pish->hostInfo.host_type))) {
return returnerr (stderr, "Error resolving hostname '%s'\n",
pish->mailServer);
pish->hostInfo.hostName);
} else {
pish->hostInfo.resolved = 1; /* mark the hostInfo resolved */
}
@ -204,7 +204,6 @@ doImap4Start(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer)
{
doIMAP4_state_t *me = (doIMAP4_state_t *)mycalloc (sizeof (doIMAP4_state_t));
pish_command_t *pish = (pish_command_t *)cmd->data;
NETPORT port = pish->portNum;
pish_stats_t *stats = (pish_stats_t *)ptimer->data;
int rc;
@ -219,13 +218,13 @@ doImap4Start(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer)
me->pIMAP->seq_num = 1;
event_start(ptcx, &stats->connect);
ptcx->sock = connectsock(ptcx, pish->mailServer, &pish->hostInfo, port, "tcp");
ptcx->sock = connectSocket(ptcx, &pish->hostInfo, "tcp");
event_stop(ptcx, &stats->connect);
if (BADSOCKET(ptcx->sock)) {
if (gf_timeexpired < EXIT_FAST) {
stats->connect.errs++;
returnerr(debugfile, "IMAP4 Couldn't connect to %s: %s\n",
pish->mailServer, neterrstr());
pish->hostInfo.hostName, neterrstr());
}
myfree (me);
return NULL;

View File

@ -265,7 +265,7 @@ safe_inet_ntoa(struct in_addr ina, char *psz)
}
/* look up the host name and protocol
* called from each protocol (via connectsock)
* called from each protocol (via connectSocket)
*/
int
@ -344,10 +344,8 @@ resolve_addrs(char *host,
/* connect to a socket given the hostname and protocol */
SOCKET
connectsock(ptcx_t ptcx,
char *host,
connectSocket(ptcx_t ptcx,
resolved_addr_t *hostInfo,
NETPORT portnum,
char *protocol)
{
struct sockaddr_in sin; /* an Internet endpoint address */
@ -357,15 +355,15 @@ connectsock(ptcx_t ptcx,
int returnval; /* temporary return value */
char ntoa_buf[SIZEOF_NTOABUF];
D_PRINTF(debugfile, "Beginning connectsock; host=%s port=%d proto=%s\n",
host, portnum, protocol );
D_PRINTF(debugfile, "Beginning connectSocket; host=%s port=%d proto=%s\n",
hostInfo->hostName, hostInfo->portNum, protocol );
sin.sin_family = AF_INET;
memset((char *)&sin, 0, sizeof(sin));
D_PRINTF(debugfile, "Zeroed address structure\n" );
sin.sin_port = htons(portnum);
D_PRINTF(debugfile, "Set port number %d\n", portnum );
sin.sin_port = htons(hostInfo->portNum);
D_PRINTF(debugfile, "Set port number %d\n", hostInfo->portNum);
/* check if we've resolved this already */
if ((hostInfo) && (hostInfo->resolved)) {
@ -379,10 +377,10 @@ connectsock(ptcx_t ptcx,
unsigned long host_addr;
short host_type; /* socket type */
if (resolve_addrs(host, "tcp",
if (resolve_addrs(hostInfo->hostName, "tcp",
&host_phe, &host_ppe, &host_addr, &host_type)) {
return returnerr(debugfile,"Can't resolve hostname %s in get()\n",
host);
hostInfo->hostName);
}
sin.sin_addr.S_ADDR = host_addr;
sin.sin_family = PF_INET;
@ -419,10 +417,10 @@ connectsock(ptcx_t ptcx,
}
/* all done, returning socket descriptor */
D_PRINTF(debugfile, "Returning %d from connectsock call\n", s );
D_PRINTF(debugfile, "Returning %d from connectSocket call\n", s );
return(s);
} /* END connectsock() */
} /* END connectSocket() */
int
set_abortive_close(SOCKET sock)

View File

@ -55,9 +55,7 @@ typedef struct pish_stats {
/* These are common to POP, IMAP, SMTP, HTTP */
typedef struct pish_command {
char * mailServer;
resolved_addr_t hostInfo; /* should be a read only cache */
NETPORT portNum;
/* These are common to SMTP, POP, IMAP */
char * loginFormat;
@ -87,12 +85,6 @@ typedef struct pish_command {
char * imapSearchFolder;
char * imapSearchPattern;
int imapSearchRate;
/* HTTP command */
char * httpCommand;
/* WMAP command */
char * wmapCommand;
} pish_command_t;
/* TRANSITION functions */

View File

@ -80,7 +80,7 @@ Pop3ParseStart (pmail_command_t cmd,
cmd->data = pish;
cmd->numLoops = 9999; /* default 9999 downloads */
pish->portNum = POP3_PORT; /* default port */
pish->hostInfo.portNum = POP3_PORT; /* default port */
D_PRINTF(stderr, "Pop3 Assign defaults\n");
/* Fill in defaults first, ignore defaults we dont use */
@ -122,7 +122,7 @@ Pop3ParseEnd (pmail_command_t cmd,
}
/* check for some of the required command attrs */
if (!pish->mailServer) {
if (!pish->hostInfo.hostName) {
D_PRINTF(stderr,"missing server for command");
return returnerr(stderr,"missing server for command\n");
}
@ -138,13 +138,13 @@ Pop3ParseEnd (pmail_command_t cmd,
}
/* see if we can resolve the mailserver addr */
if (resolve_addrs(pish->mailServer, "tcp",
if (resolve_addrs(pish->hostInfo.hostName, "tcp",
&(pish->hostInfo.host_phe),
&(pish->hostInfo.host_ppe),
&(pish->hostInfo.host_addr),
&(pish->hostInfo.host_type))) {
return returnerr (stderr, "Error resolving hostname '%s'\n",
pish->mailServer);
pish->hostInfo.hostName);
} else {
pish->hostInfo.resolved = 1; /* mark the hostInfo resolved */
}
@ -241,7 +241,6 @@ doPop3Start(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer)
int rc;
int numBytes;
char command[MAX_COMMAND_LEN];
NETPORT port;
pish_stats_t *stats = (pish_stats_t *)ptimer->data;
pish_command_t *pish = (pish_command_t *)cmd->data;
@ -250,16 +249,15 @@ doPop3Start(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer)
me->numMsgs = 0;
me->totalMsgLength = 0;
me->msgCounter = 0;
port = pish->portNum;
event_start(ptcx, &stats->connect);
ptcx->sock = connectsock(ptcx, pish->mailServer, &pish->hostInfo, port, "tcp");
ptcx->sock = connectSocket(ptcx, &pish->hostInfo, "tcp");
event_stop(ptcx, &stats->connect);
if (BADSOCKET(ptcx->sock)) {
if (gf_timeexpired < EXIT_FAST) {
stats->connect.errs++;
returnerr(debugfile, "POP3 Couldn't connect to %s: %s\n",
pish->mailServer, neterrstr());
pish->hostInfo.hostName, neterrstr());
}
myfree (me);
return NULL;

View File

@ -60,7 +60,7 @@ SmtpParseStart (pmail_command_t cmd,
cmd->data = pish;
cmd->numLoops = 1; /* default 1 message */
pish->portNum = SMTP_PORT; /* get default port */
pish->hostInfo.portNum = SMTP_PORT; /* get default port */
D_PRINTF(stderr, "Smtp Assign defaults\n");
/* Fill in defaults first, ignore defaults we dont use */
@ -105,7 +105,7 @@ SmtpParseEnd (pmail_command_t cmd,
}
/* check for some of the required command attrs */
if (!pish->mailServer) {
if (!pish->hostInfo.hostName) {
D_PRINTF(stderr,"missing server for command");
return returnerr(stderr,"missing server for command\n");
}
@ -170,13 +170,13 @@ SmtpParseEnd (pmail_command_t cmd,
close(fd);
/* see if we can resolve the mailserver addr */
if (resolve_addrs(pish->mailServer, "tcp",
if (resolve_addrs(pish->hostInfo.hostName, "tcp",
&(pish->hostInfo.host_phe),
&(pish->hostInfo.host_ppe),
&(pish->hostInfo.host_addr),
&(pish->hostInfo.host_type))) {
return returnerr (stderr, "Error resolving hostname '%s'\n",
pish->mailServer);
pish->hostInfo.hostName);
} else {
pish->hostInfo.resolved = 1; /* mark the hostInfo resolved */
}
@ -211,7 +211,7 @@ pishParseNameValue (pmail_command_t cmd,
if (cmdParseNameValue(cmd, name, tok))
; /* done */
else if (strcmp(name, "server") == 0)
pish->mailServer = mystrdup (tok);
pish->hostInfo.hostName = mystrdup (tok);
else if (strcmp(name, "smtpmailfrom") == 0)
pish->smtpMailFrom = mystrdup (tok);
else if (strcmp(name, "loginformat") == 0)
@ -237,17 +237,13 @@ pishParseNameValue (pmail_command_t cmd,
else if (strcmp(name, "sequentialaddresses") == 0)
pish->addressRange.sequential = atoi(tok);
else if (strcmp(name, "portnum") == 0)
pish->portNum = atoi(tok);
pish->hostInfo.portNum = atoi(tok);
else if (strcmp(name, "numrecips") == 0)
pish->numRecipients = atoi(tok);
else if (strcmp(name, "numrecipients") == 0)
pish->numRecipients = atoi(tok);
else if (strcmp(name, "file") == 0)
pish->filename= mystrdup (tok);
else if (strcmp(name, "httpcommand") == 0)
pish->httpCommand = mystrdup (tok);
else if (strcmp(name, "wmapcommand") == 0)
pish->wmapCommand = mystrdup (tok);
else if (strcmp(name, "passwdformat") == 0)
pish->passwdFormat = mystrdup (tok);
else if (strcmp(name, "searchfolder") == 0)
@ -689,20 +685,19 @@ sendSMTPStart(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer)
int numBytes;
int rc;
pish_command_t *pish = (pish_command_t *)cmd->data;
NETPORT port = pish->portNum;
pish_stats_t *stats = (pish_stats_t *)ptimer->data;
if (!me) return NULL;
event_start(ptcx, &stats->connect);
ptcx->sock = connectsock(ptcx, pish->mailServer, &pish->hostInfo, port, "tcp");
ptcx->sock = connectSocket(ptcx, &pish->hostInfo, "tcp");
event_stop(ptcx, &stats->connect);
if (BADSOCKET(ptcx->sock)) {
if (gf_timeexpired < EXIT_FAST) {
stats->connect.errs++;
returnerr(debugfile, "%s SMTP Couldn't connect to %s: %s\n",
ptcx->errMsg, pish->mailServer, neterrstr());
ptcx->errMsg, pish->hostInfo.hostName, neterrstr());
}
myfree (me);
return NULL;

View File

@ -35,14 +35,7 @@
/* wmap.c -- Test Netscape Messaging WebMail 4.x (know as Web-IMAP or WMAP) */
#include "bench.h"
#define HTTP_HEADER_BREAK "\r\n\r\n"
#define HTTP_CONTENT_TYPE_POST "Content-type: application/x-www-form-urlencoded"
#define HTTP_CONTENT_LENGTH "Content-length:"
#define HTTP_LOCATION "Location:"
#define HTTP_PROTOCOL "http://"
#define HTTP_MAX_RECONNECTS 5 /* max to reconnect/retry a command */
#include "http-util.h"
/*
these are protocol dependent timers
@ -70,9 +63,7 @@ typedef struct wmap_stats {
/* Command information */
typedef struct wmap_command {
char * mailServer;
resolved_addr_t hostInfo; /* should be a read only cache */
NETPORT portNum;
/* These are common to SMTP, POP, IMAP, WMAP */
char * loginFormat;
@ -135,22 +126,8 @@ typedef struct _doWMAP_state {
int * msgList; /* array[numMsgs] of message IDs */
char response_buffer[RESPONSE_BUFFER_SIZE];
} doWMAP_state_t;
/* Callback type for parsing messages on the fly */
typedef int (*parseMsgPtr_t)(
ptcx_t, pmail_command_t, void *me,
char *buffer, int bytesInBuffer, char **searchStr, const char **findStr);
static int readWmapResponse(
ptcx_t ptcx, mail_command_t *, void *, char *, int, parseMsgPtr_t );
static int doWmapCommandResponse(
ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, doWMAP_state_t *me,
event_timer_t *timer, char *command, char *response, int buflen, parseMsgPtr_t);
static int WmapParseNameValue(pmail_command_t cmd, char *name, char *tok);
static int wmapConnect(ptcx_t ptcx, mail_command_t *cmd, doWMAP_state_t *me, event_timer_t *timer);
static int wmapBanner(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, doWMAP_state_t *me);
static int wmapLogin(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, doWMAP_state_t *me);
static int wmapLogout(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, doWMAP_state_t *me);
@ -169,7 +146,7 @@ WmapParseStart(pmail_command_t cmd, char *line, param_list_t *defparm)
cmd->data = wmap;
cmd->numLoops = 1; /* default 1 downloads */
wmap->portNum = WMAP_PORT; /* get default port */
wmap->hostInfo.portNum = WMAP_PORT; /* get default port */
D_PRINTF(stderr, "Wmap Assign defaults\n");
/* Fill in defaults first, ignore defaults we dont use */
@ -209,7 +186,7 @@ WmapParseEnd(pmail_command_t cmd, string_list_t *section, param_list_t *defparm)
}
/* check for some of the required command attrs */
if (!wmap->mailServer) {
if (!wmap->hostInfo.hostName) {
D_PRINTF(stderr,"missing server for command");
return returnerr(stderr,"missing server for command\n");
}
@ -250,13 +227,13 @@ WmapParseEnd(pmail_command_t cmd, string_list_t *section, param_list_t *defparm)
}
/* see if we can resolve the mailserver addr */
if (resolve_addrs(wmap->mailServer, "tcp",
if (resolve_addrs(wmap->hostInfo.hostName, "tcp",
&(wmap->hostInfo.host_phe),
&(wmap->hostInfo.host_ppe),
&(wmap->hostInfo.host_addr),
&(wmap->hostInfo.host_type))) {
return returnerr (stderr, "Error resolving hostname '%s'\n",
wmap->mailServer);
wmap->hostInfo.hostName);
} else {
wmap->hostInfo.resolved = 1; /* mark the hostInfo resolved */
}
@ -283,7 +260,7 @@ WmapParseNameValue(pmail_command_t cmd, char *name, char *tok)
if (cmdParseNameValue(cmd, name, tok))/* generic stuff */
; /* done */
else if (strcmp(name, "server") == 0)
wmap->mailServer = mystrdup(tok);
wmap->hostInfo.hostName = mystrdup(tok);
else if (strcmp(name, "loginformat") == 0)
wmap->loginFormat = mystrdup(tok);
else if (strcmp(name, "firstlogin") == 0)
@ -315,7 +292,7 @@ WmapParseNameValue(pmail_command_t cmd, char *name, char *tok)
else if (strcmp(name, "numrecipients") == 0)
wmap->numRecipients = atoi(tok);
else if (strcmp(name, "portnum") == 0)
wmap->portNum = atoi(tok);
wmap->hostInfo.portNum = atoi(tok);
else if (strcmp(name, "passwdformat") == 0)
wmap->passwdFormat = mystrdup(tok);
else if (strcmp(name, "leavemailonserver") == 0)
@ -498,390 +475,6 @@ WmapStatsFormat(protocol_t *pp,
strcat(cp, "</FORMAT>\n");
}
/*
Establish (or re-establish) connection to server.
No data is exchanged.
*/
static int
wmapConnect (
ptcx_t ptcx,
mail_command_t *cmd,
doWMAP_state_t *me,
event_timer_t *timer)
{
wmap_command_t *wmap = (wmap_command_t *)cmd->data;
int rc=0;
NETPORT port;
port = wmap->portNum;
D_PRINTF(stderr, "wmapConnect()\n");
event_start(ptcx, timer);
ptcx->sock = connectsock(ptcx, wmap->mailServer, &wmap->hostInfo,
port, "tcp");
event_stop(ptcx, timer);
if (BADSOCKET(ptcx->sock)) {
if (gf_timeexpired < EXIT_FAST) {
timer->errs++;
returnerr(debugfile,
"%s<wmapConnect: WMAP Couldn't connect to %s: %s\n",
ptcx->errMsg, wmap->mailServer, neterrstr());
}
return -1;
}
if (gf_abortive_close) {
if (set_abortive_close(ptcx->sock) != 0) {
returnerr(debugfile,
"%s<wmapConnect: WMAP: WARNING: Could not set abortive close\n",
ptcx->errMsg, neterrstr());
}
}
return rc;
}
/*
readWmapResponse handles the details of reading a HTTP response It
takes and interactive parsing function and a response buffer. The
response buffer will hold at least the last 1/2 buffer of the data
stream. This allows errors to be handled by the calling routine.
Long responses should be parsed on the fly by the parsing callback.
readWmapResponse handles find the content-length and header break.
Once the break is called, it will call the parsing callback so that it can
set findStr (which is NULL).
The findStr search is case insensitive if the first character of findStr
has distinct upper and lower cases (see toupper and tolower).
The callback is called with the buffer, the bytes in the buffer,
a pointer just past the findStr match, and a pointer to findStr.
The callback should then update findStr and searchStr; and return -2
for an error (abort message), 0 for success, and 1 for need more data.
findStr is expected to be a constant or static storage.
Before exit, the parsing callback is called one last time (with NULL
findStr and searchStr), so that it can do any cleanup.
Management of the buffer is done automatically by the movement of searchStr.
readWmapResponse returns bytesInBuffer on success, -1 for IO error,
-2 for error returned by the parser.
*/
static int
readWmapResponse(
ptcx_t ptcx,
mail_command_t *cmd, /* command info for msgParse */
void *me, /* connection state for msgParse */
char *buffer,
int buflen,
parseMsgPtr_t msgParse
)
{
/* read the server response and do nothing with it */
/* Will need to look for HTTP headers and Content-Length/Keep-Alive */
int gotheader = 0;
int totalbytesread = 0;
int bytesread;
int bytesInBuffer = 0;
const char *findStr; /* string we are watching for */
const char *oldFindStr = NULL;
char findStrSpn[3]; /* case insensitive search start */
int findLen=0; /* length of string */
char *foundStr; /* what we found */
char *searchStart; /* where next to look for findStr */
int contentlength = 0; /* HTTP content length */
int bytestoread = buflen-1; /* start by reading lots */
int getBytes = 0; /* need more input */
D_PRINTF(stderr, "readWmapResponse()\n");
findStr = HTTP_CONTENT_LENGTH;
searchStart = buffer;
memset(buffer, 0, buflen); /* DEBUG */
while (1)
{
if (gf_timeexpired >= EXIT_FAST) {
D_PRINTF(stderr,"readWmapResponse() Time expired.\n");
break;
}
if (findStr != oldFindStr) { /* findStr changed */
if (NULL == findStr) {
findLen = 0;
findStrSpn[0] = 0;
} else {
char upperC, lowerC; /* upper and lower of findStr[0] */
findLen = strlen (findStr);
upperC = toupper (findStr[0]);
lowerC = tolower (findStr[0]);
if (lowerC != upperC) {
/*D_PRINTF (stderr, "New findStr [%s] upper %c lower %c\n",
findStr, upperC, lowerC);*/
findStrSpn[0] = upperC;
findStrSpn[1] = lowerC;
findStrSpn[2] = 0;
} else {
/*D_PRINTF (stderr, "New findStr [%s] NOT case sensitive\n",
findStr);*/
findStrSpn[0] = 0;
}
}
oldFindStr = findStr;
}
/* See if we should read more */
if ((getBytes > 0)
|| (0 == findLen)
|| ((bytesInBuffer - (searchStart - buffer)) < findLen)) {
int count;
if (totalbytesread >= bytestoread) { /* done reading */
break;
}
/* Make room for more data */
/* need to be careful to save data for response. shift by half */
if ((searchStart - buffer) > buflen/2) {
/* This is the only place we reduce searchStart or bytesInBuffer */
int toSkip = buflen/2;
int toSave = bytesInBuffer - toSkip;
/*D_PRINTF (stderr, "Shifting %d of %d bytes\n",
toSave, bytesInBuffer);*/
memcpy (buffer, buffer+toSkip, toSave); /* shift data down */
bytesInBuffer = toSave;
buffer[bytesInBuffer] = 0; /* re-terminate??? */
searchStart -= toSkip;
assert (searchStart >= buffer);
}
count = MIN (bytestoread-totalbytesread,
buflen-bytesInBuffer-1);
/*D_PRINTF(stderr, "retryRead() size=%d %d/%d inBuffer=%d\n",
count, totalbytesread, bytestoread, bytesInBuffer);*/
bytesread = retryRead(ptcx, ptcx->sock, buffer+bytesInBuffer, count);
if (0 == bytesread) { /* most likely an error */
strcpy (ptcx->errMsg, "readWmapResponse:got 0 bytes");
break;
}
if (bytesread < 0) { /* ERROR */
strcat (ptcx->errMsg, "<readWmapResponse");
return -1;
}
T_PRINTF(ptcx->logfile, buffer+bytesInBuffer, bytesread,
"WMAP ReadResponse"); /* telemetry log */
totalbytesread += bytesread;
bytesInBuffer += bytesread;
buffer[bytesInBuffer] = 0; /* terminate for string searches */
ptcx->bytesread += bytesread; /* log statistics */
getBytes = 0;
} else {
/*D_PRINTF (stderr,
"Not reading, have %d only need %d. %d/%d\n",
bytesInBuffer- (searchStart - buffer), findLen,
bytestoread, totalbytesread);*/
}
if (NULL == findStr) { /* nothing to search for */
searchStart = buffer+bytesInBuffer; /* discard buffer */
continue;
}
/* otherwise search for findStr */
if (findStrSpn[0]) { /* do case insensitive version */
char *cp;
foundStr = NULL;
for (cp = searchStart; (cp - buffer) < bytesInBuffer; ++cp) {
cp = strpbrk (cp, findStrSpn); /* look for first char match */
if (NULL == cp) {
break;
}
if (!strnicmp(cp, findStr, findLen)) { /* check whole match */
foundStr = cp;
break;
}
}
} else { /* non case sensitive version */
foundStr = strstr(searchStart, findStr);
}
if (NULL == foundStr) { /* found nothing, shift and continue */
/* jump to findLen-1 from end */
if (bytesInBuffer < findLen)
continue; /* nothing to shift */
searchStart = buffer + bytesInBuffer - findLen + 1;
continue; /* get more input */
}
/* Found search string */
/*D_PRINTF (stderr, "Found [%s] after %d bytes\n",
findStr, totalbytesread);*/
searchStart = foundStr + findLen; /* found this much */
if (0 == gotheader) { /* found length */
contentlength = atoi(searchStart); /* save contentlength */
searchStart = strchr (searchStart, '\n'); /* jump to EOL */
if (NULL == searchStart) { /* missing line end, get more */
searchStart = buffer;
} else { /* got complete contentlength line */
gotheader++;
findStr = HTTP_HEADER_BREAK; /* now look for break */
}
continue;
}
else if (1 == gotheader) { /* found header break */
gotheader++;
/* now compute bytetoread */
bytestoread = contentlength
+ ((searchStart - buffer) /* position in buffer */
+ (totalbytesread - bytesInBuffer)); /* buffer offset */
D_PRINTF(stderr, "contentlength=%d, bytestoread=%d\n",
contentlength, bytestoread);
findStr = NULL; /* should chain into extra searches */
if (msgParse) { /* initialize callback */
int rc;
/* having findStr == NULL signal initial callback */
rc = (*(msgParse)) (ptcx, cmd, me,
buffer, bytesInBuffer, &searchStart, &findStr);
if (rc < 0) { /* parser signaled error */
return rc;
}
if (NULL == searchStart) {
searchStart = buffer+bytesInBuffer; /* discard buffer */
}
}
}
else {
if (msgParse) { /* call extra searches */
int rc;
rc = (*(msgParse)) (ptcx, cmd, me,
buffer, bytesInBuffer, &searchStart, &findStr);
if (rc < 0) { /* protocol error */
return rc;
}
if (rc > 0) { /* need more data */
getBytes = rc;
searchStart = foundStr; /* back up search string */
continue;
}
if (searchStart < buffer) { /* or NULL */
searchStart = buffer+bytesInBuffer; /* discard buffer */
}
} else {
searchStart = buffer+bytesInBuffer; /* discard buffer */
}
}
}
D_PRINTF(stderr, "readWmapResponse: Read %d/%d saved %d\n",
totalbytesread, bytestoread, bytesInBuffer);
if (msgParse) { /* call extra searches */
findStr = NULL; /* signal final callback */
searchStart = NULL;
(void)(*(msgParse)) (ptcx, cmd, me,
buffer, bytesInBuffer, &searchStart, &findStr);
}
return bytesInBuffer;
}
/*
Handle a command-response transaction. Connects/retries as needed.
Expects pointers to buffers of these sizes:
char command[MAX_COMMAND_LEN]
char response[resplen]
*/
static int
doWmapCommandResponse (
ptcx_t ptcx, /* thread state */
mail_command_t *cmd, /* command info for exit,connect */
cmd_stats_t *ptimer, /* all timers for connect */
doWMAP_state_t *me, /* connection state */
event_timer_t *timer, /* timer for this command */
char *command, /* command to send */
char *response,
int resplen,
parseMsgPtr_t msgParse) /* response handler */
{
wmap_stats_t *stats = (wmap_stats_t *)ptimer->data;
int retries = 0;
int ret;
if (response == NULL)
return -1;
response[0] = 0; /* make sure it makes sense on error */
D_PRINTF(stderr, "WmapCommandResponse() command=[%.99s]\n", command);
while (retries++ < HTTP_MAX_RECONNECTS) {
if (BADSOCKET(ptcx->sock)) {
ret = wmapConnect(ptcx, cmd, me, &stats->reconnect);
if (ret == -1) {
return -1;
}
}
/* send the command already formatted */
T_PRINTF(ptcx->logfile, command, strlen (command), "WMAP SendCommand");
event_start(ptcx, timer);
ret = sendCommand(ptcx, ptcx->sock, command);
if (ret == -1) {
event_stop(ptcx, timer);
stats->reconnect.errs++;
/* this can only mean an IO error. Probably EPIPE */
NETCLOSE(ptcx->sock);
ptcx->sock = BADSOCKET_VALUE;
strcat (ptcx->errMsg, "<WmapCommandResponse");
D_PRINTF (stderr, "%s Aborting connection %s\n",
ptcx->errMsg, neterrstr());
continue;
}
/* read server response */
ret = readWmapResponse(ptcx, cmd, me, response, resplen, msgParse);
event_stop(ptcx, timer);
if (ret == 0) { /* got nothing */
/* Probably an IO error. It will be definate if re-tried. */
MS_usleep (5000); /* time for bytes to show up */
continue; /* just re-try it */
} else if (ret == -1) { /* IO error */
stats->reconnect.errs++;
NETCLOSE(ptcx->sock);
ptcx->sock = BADSOCKET_VALUE;
strcat (ptcx->errMsg, "<WmapCommandResponse");
D_PRINTF (stderr, "%s Aborting connection %s\n",
ptcx->errMsg, neterrstr());
continue;
} else if (ret < -1) { /* some other error */
timer->errs++;
/* this is overkill */
NETCLOSE(ptcx->sock);
ptcx->sock = BADSOCKET_VALUE;
strcat (ptcx->errMsg, "<WmapCommandResponse: protocol ERROR");
return -1;
}
/*D_PRINTF (stderr, "WmapCommandResponse saved %d bytes response=[%s]\n",
ret, response);*/
/* You get parent.timeoutCB on any kind of error (like bad sid) */
if (strstr (response, "parent.timeoutCB()")) {
timer->errs++;
NETCLOSE(ptcx->sock);
ptcx->sock = BADSOCKET_VALUE;
strcpy (ptcx->errMsg, "WmapCommandResponse: got WMAP error msg");
return -1;
}
return ret;
}
stats->reconnect.errs++;
NETCLOSE(ptcx->sock);
ptcx->sock = BADSOCKET_VALUE;
strcat (ptcx->errMsg, "<WmapCommandResponse: Too many connection retries");
return -1;
}
/*
Get the initial screen
*/
@ -896,7 +489,7 @@ wmapBanner(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, doWMAP_state_t
int rc = 0;
if (BADSOCKET(ptcx->sock)) { /* should always be true */
rc = wmapConnect(ptcx, cmd, me, &stats->connect);
rc = HttpConnect(ptcx, &wmap->hostInfo, &stats->connect);
if (rc == -1) {
return -1;
}
@ -908,11 +501,13 @@ wmapBanner(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, doWMAP_state_t
D_PRINTF(stderr, "wmapBanner() cmd=[%s]\n", pl->value);
sprintf(header, wmap->wmapClientHeader, wmap->mailServer, wmap->mailServer);
sprintf(header, wmap->wmapClientHeader, wmap->hostInfo.hostName, wmap->hostInfo.hostName);
sprintf(command, "%s\r\n%s\r\n", pl->value, header);
assert (strlen(command) < MAX_COMMAND_LEN);
rc = doWmapCommandResponse(ptcx, cmd, ptimer, me, &stats->banner,
rc = HttpCommandResponse(ptcx, cmd, me,
&wmap->hostInfo, &stats->reconnect,
&stats->banner,
command, me->response_buffer,
sizeof(me->response_buffer), NULL);
if (rc == -1) {
@ -953,7 +548,7 @@ wmapLogin(
char *cp;
if (BADSOCKET(ptcx->sock)) {
rc = wmapConnect(ptcx, cmd, me, &stats->reconnect);
rc = HttpConnect(ptcx, &wmap->hostInfo, &stats->reconnect);
if (rc == -1) {
return -1;
}
@ -974,16 +569,18 @@ wmapLogin(
/* send wmapLoginCmd, read response into wmapRedirectURL */
sprintf(content, wmap->wmapLoginData, mailUser, userPasswd);
sprintf(header, wmap->wmapClientHeader, wmap->mailServer, wmap->mailServer);
sprintf(header, wmap->wmapClientHeader, wmap->hostInfo.hostName, wmap->hostInfo.hostName);
sprintf(command, "%s\r\n%s"
HTTP_CONTENT_TYPE_POST "\r\n"
HTTP_CONTENT_LENGTH " %d\r\n\r\n%s\r\n",
wmap->wmapLoginCmd, header, strlen(content), content);
assert (strlen(command) < MAX_COMMAND_LEN);
rc = doWmapCommandResponse(ptcx, cmd, ptimer, me, &stats->login,
rc = HttpCommandResponse(ptcx, cmd, me,
&wmap->hostInfo, &stats->reconnect,
&stats->banner,
command, me->response_buffer,
sizeof(me->response_buffer), NULL);
sizeof(me->response_buffer), NULL);
if (rc == -1) {
if (gf_timeexpired < EXIT_FAST) {
NETCLOSE(ptcx->sock); ptcx->sock = BADSOCKET_VALUE;
@ -1067,9 +664,11 @@ wmapInbox(
/* First grab the redirect URL */
sprintf(command, "GET %s HTTP/1.0\r\n%s" HTTP_CONTENT_LENGTH " 0\r\n\r\n",
me->redirectURL, header);
rc = doWmapCommandResponse(ptcx, cmd, ptimer, me, &stats->cmd,
rc = HttpCommandResponse(ptcx, cmd, me,
&wmap->hostInfo, &stats->reconnect,
&stats->banner,
command, me->response_buffer,
sizeof(me->response_buffer), NULL);
sizeof(me->response_buffer), NULL);
if (rc == -1) { /* most common disconnect point */
if (gf_timeexpired < EXIT_FAST) {
returnerr(debugfile,"%s WMAP cannot get inbox %s\n",
@ -1085,14 +684,16 @@ wmapInbox(
/* send wmapInboxCmd, read response into wmapRedirectURL */
sprintf(header, wmap->wmapClientHeader,
wmap->mailServer, wmap->mailServer);
wmap->hostInfo.hostName, wmap->hostInfo.hostName);
sprintf(getcmd, pl->value,
me->sessionID);
sprintf(command, "%s\r\n%s" HTTP_CONTENT_LENGTH " 0\r\n\r\n",
getcmd, header);
assert (strlen(command) < MAX_COMMAND_LEN);
rc = doWmapCommandResponse(ptcx, cmd, ptimer, me, &stats->cmd,
rc = HttpCommandResponse(ptcx, cmd, me,
&wmap->hostInfo, &stats->reconnect,
&stats->banner,
command, me->response_buffer,
sizeof(me->response_buffer), NULL);
if (rc == -1) { /* most common disconnect point */
@ -1119,7 +720,7 @@ wmapLogout(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, doWMAP_state_t
int rc = 0;
if (BADSOCKET(ptcx->sock)) {
rc = wmapConnect(ptcx, cmd, me, &stats->reconnect);
rc = HttpConnect(ptcx, &wmap->hostInfo, &stats->reconnect);
if (rc == -1) {
return -1;
}
@ -1131,12 +732,14 @@ wmapLogout(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, doWMAP_state_t
D_PRINTF(stderr, "wmapLogout() cmd=[%s]\n", pl->value);
sprintf(header, wmap->wmapClientHeader, wmap->mailServer, wmap->mailServer);
sprintf(header, wmap->wmapClientHeader, wmap->hostInfo.hostName, wmap->hostInfo.hostName);
sprintf(getcmd, pl->value, me->sessionID);
sprintf(command, "%s\r\n%s" HTTP_CONTENT_LENGTH " 0\r\n\r\n", getcmd, header);
assert (strlen(command) < MAX_COMMAND_LEN);
rc = doWmapCommandResponse(ptcx, cmd, ptimer, me, &stats->logout,
rc = HttpCommandResponse(ptcx, cmd, me,
&wmap->hostInfo, &stats->reconnect,
&stats->banner,
command, me->response_buffer,
sizeof(me->response_buffer), NULL);
if (rc == -1) {
@ -1284,12 +887,14 @@ doWmapLoop(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, void *mystate)
sprintf(getcmd, pl->value,
me->sessionID);
sprintf(header, wmap->wmapClientHeader,
wmap->mailServer, wmap->mailServer);
wmap->hostInfo.hostName, wmap->hostInfo.hostName);
sprintf(command, "%s\r\n%s" HTTP_CONTENT_LENGTH " 0\r\n\r\n",
getcmd, header);
assert (strlen(command) < MAX_COMMAND_LEN);
rc = doWmapCommandResponse(ptcx, cmd, ptimer, me, &stats->headers,
rc = HttpCommandResponse(ptcx, cmd, me,
&wmap->hostInfo, &stats->reconnect,
&stats->banner,
command, me->response_buffer,
sizeof(me->response_buffer), &GetMessageNumbers);
if (rc == -1) { /* un-recoverable error */
@ -1317,12 +922,14 @@ doWmapLoop(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, void *mystate)
sprintf(getcmd, pl->value, /* fill out command string */
me->sessionID, me->msgList[seq]);
sprintf(header, wmap->wmapClientHeader, /* fill out header */
wmap->mailServer, wmap->mailServer);
wmap->hostInfo.hostName, wmap->hostInfo.hostName);
sprintf(command, "%s\r\n%s" HTTP_CONTENT_LENGTH " 0\r\n\r\n",
getcmd, header);
assert (strlen(command) < MAX_COMMAND_LEN);
rc = doWmapCommandResponse(ptcx, cmd, ptimer, me, &stats->msgread,
rc = HttpCommandResponse(ptcx, cmd, me,
&wmap->hostInfo, &stats->reconnect,
&stats->banner,
command, me->response_buffer,
sizeof(me->response_buffer), NULL);
if (rc == -1) { /* un-recoverable error */