/* -*- 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) 1997-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): Dan Christian * Marcel DePaolis * Mike Blakely * * 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. */ /* Protocol test for IMAP4 */ #include "bench.h" #include "pish.h" typedef struct IMAP_t { int seq_num; /* IMAP command seq number */ char resp_buffer[MAX_RESPONSE_LEN]; char selected_folder[MAX_IMAP_FOLDERNAME_LEN]; } IMAP; /* protos */ static int retrImapMsg(ptcx_t ptcx, SOCKET sock, int seqNum, char *buffer, int msgSize, int maxBytes); static int readImapResponse(ptcx_t ptcx, SOCKET sock, int seqNum, char *buffer, int buflen); static int doImapCommandResponse(ptcx_t ptcx, SOCKET sock, int SeqNum, char *cmdand, char *response, int resplen); static int imapLogin(ptcx_t ptcx, IMAP *, SOCKET, mail_command_t *, cmd_stats_t *ptimer); static int imapLogout(ptcx_t ptcx, IMAP *, SOCKET); static int imapCheckINBOX(ptcx_t ptcx, IMAP *, SOCKET, mail_command_t *, cmd_stats_t *); static int imapSelectFolder(ptcx_t ptcx, IMAP *, SOCKET, char *, cmd_stats_t *); static int imapCloseFolder(ptcx_t ptcx, IMAP *, SOCKET, cmd_stats_t *); static int imapParseSelectResponse(ptcx_t ptcx, IMAP *, char *, int *); static int imapRetrRecentMessages(ptcx_t ptcx, IMAP *, SOCKET, mail_command_t *, cmd_stats_t *, int, int); static int imapComputeSearchSchedule(ptcx_t ptcx, mail_command_t *, int **); static int imapSearchFolder(ptcx_t ptcx, IMAP *, SOCKET, mail_command_t *, cmd_stats_t *); #if 0 static int imapCreateFolder(ptcx_t ptcx, IMAP *pIMAP, const char *folder); #endif static int imapDeleteFolder(ptcx_t ptcx, IMAP *pIMAP, const char *folder); typedef struct _doIMAP4_state { IMAP IMAP_state; IMAP *pIMAP; unsigned int timeUntilSearch; int currentSearch; int numSearches; int *searchSchedule; } doIMAP4_state_t; static void doImap4Exit (ptcx_t ptcx, doIMAP4_state_t *me); #define IMAP_LEAVE_UNSEEN 2 static int ImapParseNameValue (pmail_command_t cmd, char *name, char *tok) { pish_command_t *pish = (pish_command_t *)cmd->data; /* find a home for the attr/value */ if (pishParseNameValue(cmd, name, tok) == 0) ; /* done */ else if (strcmp(name, "leavemailonserver") == 0) { int v = atoi(tok); if (v <= 0) { /* turn off */ pish->leaveMailOnServer = 0; } else if (0 == pish->leaveMailOnServer) { /* turn on if < leavemailunseen */ pish->leaveMailOnServer = 1; } D_PRINTF (stderr, "leaveMailOnServer=%d\n", pish->leaveMailOnServer); } else if (strcmp(name, "leavemailunseen") == 0) { pish->leaveMailOnServer = IMAP_LEAVE_UNSEEN * (atoi(tok) > 0); D_PRINTF (stderr, "leaveMailOnServer=%d\n", pish->leaveMailOnServer); } else { return -1; } return 0; } /* Set defaults in command structure */ int Imap4ParseStart (pmail_command_t cmd, char *line, param_list_t *defparm) { param_list_t *pp; pish_command_t *pish = (pish_command_t *)mycalloc (sizeof (pish_command_t)); cmd->data = pish; cmd->loopDelay = 10*60; /* default 10 min */ pish->hostInfo.portNum = IMAP4_PORT; /* get default port */ D_PRINTF(stderr, "Imap4 Assign defaults\n"); /* Fill in defaults first, ignore defaults we dont use */ for (pp = defparm; pp; pp = pp->next) { (void)ImapParseNameValue (cmd, pp->name, pp->value); } return 1; } /* Fill in structure from a list of lines */ int Imap4ParseEnd (pmail_command_t cmd, string_list_t *section, param_list_t *defparm) { string_list_t *sp; pish_command_t *pish = (pish_command_t *)cmd->data; /* Now parse section lines */ D_PRINTF(stderr, "Imap4 Assign section lines\n"); /* skip first and last */ for (sp = section->next; sp->next; sp = sp->next) { char *name = sp->value; char *tok = name + strcspn(name, " \t="); *tok++ = 0; /* split name off */ tok += strspn(tok, " \t="); string_tolower(name); tok = string_unquote(tok); if (ImapParseNameValue (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); } } /* check for some of the required command attrs */ if (!pish->hostInfo.hostName) { D_PRINTF(stderr,"missing server for command"); return returnerr(stderr,"missing server for command\n"); } if (!pish->loginFormat) { D_PRINTF(stderr,"missing loginFormat for command"); return returnerr(stderr,"missing loginFormat for command\n"); } if (!pish->passwdFormat) { D_PRINTF(stderr,"missing passwdFormat for command"); return returnerr(stderr,"missing passwdFormat for command\n"); } /* see if we can resolve the mailserver addr */ 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->hostInfo.hostName); } else { pish->hostInfo.resolved = 1; /* mark the hostInfo resolved */ } rangeSetFirstCount (&pish->loginRange, pish->loginRange.first, pish->loginRange.span, pish->loginRange.sequential); rangeSetFirstCount (&pish->domainRange, pish->domainRange.first, pish->domainRange.span, pish->domainRange.sequential); return 1; } /* * imap4 entry point * */ void * 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; pish_stats_t *stats = (pish_stats_t *)ptimer->data; int rc; if (!me) return NULL; me->pIMAP = &me->IMAP_state; me->timeUntilSearch = 0; me->currentSearch = 0; me->numSearches = 0; me->searchSchedule = NULL; me->pIMAP->seq_num = 1; event_start(ptcx, &stats->connect); 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->hostInfo.hostName, neterrstr()); } myfree (me); return NULL; } if (gf_abortive_close) { if (set_abortive_close(ptcx->sock) != 0) { returnerr (debugfile, "SMTP: WARNING: Could not set abortive close\n"); } } /* READ connect response from server */ event_start(ptcx, &stats->banner); rc = readResponse(ptcx, ptcx->sock, me->pIMAP->resp_buffer, sizeof(me->pIMAP->resp_buffer)); event_stop(ptcx, &stats->banner); if (rc <= 0) { if (gf_timeexpired < EXIT_FAST) { stats->banner.errs++; returnerr(debugfile, "IMAP4 Error reading banner: %s\n", neterrstr()); } doImap4Exit (ptcx, me); return NULL; } rc = imapLogin(ptcx, me->pIMAP, ptcx->sock, cmd, ptimer); if (rc != 0) { doImap4Exit (ptcx, me); return NULL; } me->pIMAP->selected_folder[0] = '\0'; /* compute the search intervals */ me->searchSchedule = 0; me->currentSearch = me->numSearches = 0; if (pish->imapSearchRate) { me->numSearches = imapComputeSearchSchedule(ptcx, cmd, &me->searchSchedule); (void)me->numSearches; /* ??? */ me->timeUntilSearch = me->searchSchedule[me->currentSearch]; } D_PRINTF(debugfile,"computed search schedule\n"); return me; } int doImap4Loop(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, void *mystate) { doIMAP4_state_t *me = (doIMAP4_state_t *)mystate; if (!me) return -1; /* should never happen */ /* Enter command loop, we will be here for numLoops */ /* select INBOX, check for mail, fetching and (optionally) deleting RECENT msgs */ if (imapCheckINBOX(ptcx, me->pIMAP, ptcx->sock, cmd, ptimer)) { return -1; /* signal to logout, clean up */ } #if 0 /* update for new loop model */ rc = selectFolder(ptcx, me->pIMAP, ptcx->sock, "INBOX", ptimer); /* does NOOP */ if (rc != 0) { return -1; /* signal to clean up */ } #endif if (me->searchSchedule) { /* check if it's time to search */ me->timeUntilSearch -= cmd->loopDelay; if (me->timeUntilSearch <= 0) { imapSearchFolder(ptcx, me->pIMAP, ptcx->sock, cmd, ptimer); me->currentSearch++; me->timeUntilSearch = me->searchSchedule[me->currentSearch]; } } return 0; } void doImap4End(ptcx_t ptcx, mail_command_t *cmd, cmd_stats_t *ptimer, void *mystate) { doIMAP4_state_t *me = (doIMAP4_state_t *)mystate; int rc; pish_stats_t *stats = (pish_stats_t *)ptimer->data; if (!me) return; if (BADSOCKET(ptcx->sock)) return; /* closed by previous error */ /* close the folder */ rc = imapCloseFolder(ptcx, me->pIMAP, ptcx->sock, ptimer); if (rc != 0) { doImap4Exit (ptcx, me); return; } /* if we created a mailstone folder, delete it now */ if (gf_imapForceUniqueness) { /* TESTED??? */ char folder[DATESTAMP_LEN]; sprintf(folder, "ms%s", gs_dateStamp); rc = imapDeleteFolder(ptcx, me->pIMAP, folder); if (rc == -1) { doImap4Exit (ptcx, me); return; } } event_start(ptcx, &stats->logout); imapLogout(ptcx, me->pIMAP, ptcx->sock); event_stop(ptcx, &stats->logout); doImap4Exit(ptcx, me); /* clean up */ } /* shutdown the connection hard */ /* free memory */ void doImap4Exit (ptcx_t ptcx, doIMAP4_state_t *me) { if (!BADSOCKET(ptcx->sock)) NETCLOSE(ptcx->sock); ptcx->sock = BADSOCKET_VALUE; if (me->searchSchedule) myfree(me->searchSchedule); myfree (me); } static int readImapResponse(ptcx_t ptcx, SOCKET sock, int seqNum, char *buffer, int buflen) { /* read the server response and do nothing with it */ int totalbytesread = 0; int bytesread; char markerSingleLine[20]; char markerMultiLine[20]; /* marker to tell us when we've recieved the final line from server */ sprintf(markerSingleLine, "%d ", seqNum); sprintf(markerMultiLine, "\n%d ", seqNum); while (totalbytesread < buflen) { if (gf_timeexpired >= EXIT_FAST) { D_PRINTF(debugfile,"Time expired while reading messages - in readIMAPresponse\n"); break; } if ((bytesread = retryRead(ptcx, sock, buffer+totalbytesread, buflen-totalbytesread)) <= 0) { if (gf_timeexpired < EXIT_FAST) { returnerr(stderr, "readImapResponse(%d, %d) %s\n", sock, seqNum, neterrstr()); } return -1; } totalbytesread += bytesread; ptcx->bytesread += bytesread; buffer[totalbytesread] = 0; /* search for end of response */ if (strstr(buffer, markerSingleLine)) { break; } if (strstr(buffer, markerMultiLine)) { break; } } D_PRINTF(debugfile, "Read from server: %s\n", buffer ); return totalbytesread; } /* read from socket until we find . */ static int retrImapMsg(ptcx_t ptcx, SOCKET sock, int seqNum, char *buffer, int msgSize, int maxBytes) { int totalbytesread = 0; int bytesread; char markerSingleLine[20]; char markerMultiLine[20]; /* marker to tell us when we've recieved the final line from server */ sprintf(markerSingleLine, "%d ", seqNum); sprintf(markerMultiLine, "\n%d ", seqNum); memset (buffer, 0, maxBytes); while (totalbytesread < maxBytes) { if (gf_timeexpired >= EXIT_FAST) { D_PRINTF(debugfile,"Time expired while reading messages - in retrimap4Msg\n"); break; } bytesread = retryRead(ptcx, sock, buffer+totalbytesread, maxBytes-totalbytesread); if (bytesread <= 0) { if (gf_timeexpired < EXIT_FAST) { returnerr(debugfile, "retrImapMsg(%d) %s\n", sock, neterrstr()); } return -1; } totalbytesread += bytesread; buffer[totalbytesread] = 0; if (totalbytesread > msgSize) { /* search for end of response */ if (strstr(buffer, markerSingleLine)) { break; } if (strstr(buffer, markerMultiLine)) { break; } } if (gf_timeexpired >= EXIT_FAST) { D_PRINTF(debugfile,"Time expired while reading messages - in retrIMAP\n"); break; } } D_PRINTF(debugfile,"buffer=%s\n", buffer); ptcx->bytesread += totalbytesread; return totalbytesread; } static int doImapCommandResponse(ptcx_t ptcx, SOCKET sock, int seqnum, char *command, char *response, int resplen) { int ret; if (response == NULL) return -1; memset(response, 0, resplen); /* send the IMAP command already formatted */ T_PRINTF(ptcx->logfile, command, strlen (command), "IMAP4 SendCommand"); if ((ret = sendCommand(ptcx, sock, command)) == -1) { if (gf_timeexpired < EXIT_FAST) { trimEndWhite (command); returnerr(debugfile, "Error sending [%s] command to server: %s\n", command, neterrstr()); } return -1; } /* read server response for this sequence number */ if ((ret = readImapResponse(ptcx, sock, seqnum, response, resplen)) <= 0) { if (gf_timeexpired < EXIT_FAST) { trimEndWhite (command); returnerr(debugfile, "Error reading [%s] response: %s\n", command, neterrstr()); } return -1; } T_PRINTF(ptcx->logfile, response, strlen(response), "IMAP4 ReadResponse"); /* telemetry log. should be lower level */ return ret; } static int imapLogin(ptcx_t ptcx, IMAP *pIMAP, SOCKET sock, mail_command_t *cmd, cmd_stats_t *ptimer) { unsigned long next_login; unsigned long next_domain; char mailUser[MAX_MAILADDR_LEN]; char userPasswd[MAX_USERNAME_LEN]; char command[MAX_COMMAND_LEN]; int done = 0; int ret; pish_stats_t *stats = (pish_stats_t *)ptimer->data; pish_command_t *pish = (pish_command_t *)cmd->data; gf_imapForceUniqueness = 0; while (!done) { next_domain = rangeNext(&stats->domainRange, stats->lastDomain); stats->lastDomain = next_domain; next_login = rangeNext(&stats->loginRange, stats->lastLogin); stats->lastLogin = next_login; sprintf(mailUser, pish->loginFormat, next_login, next_domain); sprintf(userPasswd, pish->passwdFormat, next_login); D_PRINTF(debugfile,"mailUser=%s, passwd=%s\n", mailUser, userPasswd); /* LOGIN */ sprintf(command, "%d LOGIN %s %s%s", ++pIMAP->seq_num, mailUser, userPasswd, CRLF); event_start(ptcx, &stats->login); ret = doImapCommandResponse(ptcx, sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)); event_stop(ptcx, &stats->login); if (ret == -1) { if (gf_timeexpired < EXIT_FAST) { stats->login.errs++; } return -1; } done = 1; #if 0 if (gf_imapForceUniqueness) { char *bufPtr = 0; int mailboxInUse = 0; char listResponse[MAX_RESPONSE_LEN]; char folderDate[DATESTAMP_LEN]; char folder[DATESTAMP_LEN]; /* look for a mailstone folder, telling us whether a client has already logged into this mailbox */ sprintf(command, "%d LIST \"\" *%s", ++pIMAP->seq_num, CRLF); if (doImapCommandResponse(ptcx, sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)) == -1) { return -1; } strcpy(listResponse, pIMAP->resp_buffer); bufPtr = listResponse; memset(folderDate, 0, DATESTAMP_LEN*sizeof(char)); mailboxInUse = 0; while (*bufPtr) { if (sscanf(bufPtr, "* LIST () \"/\" ms%s", folderDate)) { D_PRINTF(debugfile,"dateStamp: %s\n", gs_dateStamp); D_PRINTF(debugfile,"folderDate: %s\n", folderDate); /* compare the folderDate with dateStamp, if the same, then another mailstone client is logged in, try another mailbox */ if (strcmp(folderDate, gs_dateStamp) == 0) { D_PRINTF(debugfile,"YES\n"); mailboxInUse = 1; break; } else { D_PRINTF(debugfile,"NO\n"); /* it's an old mailstone folder, remove it */ sprintf(folder, "ms%s", folderDate); if (deleteFolder(ptcx, pIMAP, folder) == -1) { return -1; } } } bufPtr = strchr(bufPtr, '\n'); if (bufPtr) bufPtr++; } if (mailboxInUse) continue; /* mailbox not in use, let's create a ms folder */ sprintf(folder, "ms%s", gs_dateStamp); if (createFolder(ptcx, pIMAP, folder) == -1) { return -1; } done = 1; } /* if (gf_imapForceUniqueness) */ #endif } D_PRINTF(debugfile,"Done with login\n"); return 0; } static int imapLogout(ptcx_t ptcx, IMAP *pIMAP, SOCKET sock) { char command[MAX_COMMAND_LEN]; /* LOGOUT (closes the connection) */ sprintf(command, "%d LOGOUT%s", ++pIMAP->seq_num, CRLF); if (doImapCommandResponse(ptcx, sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)) == -1) { return -1; } return 0; } static int imapCheckINBOX(ptcx_t ptcx, IMAP *pIMAP, SOCKET sock, mail_command_t *cmd, cmd_stats_t *timer) { int numExists = 0; int numRecent = 0; /* SELECT INBOX */ if (imapSelectFolder(ptcx, pIMAP, sock, "INBOX", timer) < 0) { return -1; } /* parse number of existing/recent msgs out of buffer */ if (imapParseSelectResponse(ptcx, pIMAP, "EXISTS", &numExists) == -1) return -1; if (imapParseSelectResponse(ptcx, pIMAP, "RECENT", &numRecent) == -1) return -1; D_PRINTF(debugfile,"SELECT INBOX shows %d recent msgs of %d existing msgs\n", numRecent, numExists); /* FETCH messages */ if (imapRetrRecentMessages(ptcx, pIMAP, sock, cmd, timer, numExists, numRecent)) return -1; return 0; } static int imapSelectFolder(ptcx_t ptcx, IMAP *pIMAP, SOCKET sock, char *folder, cmd_stats_t *ptimer) { char command[MAX_COMMAND_LEN]; int rc; pish_stats_t *stats = (pish_stats_t *)ptimer->data; if (gf_timeexpired >= EXIT_FAST) return -1; /* check if the folder is already selected */ if (strcmp(pIMAP->selected_folder, folder) == 0) { /* it is, so send a NOOP to check for recent msgs */ sprintf(command, "%d NOOP%s", ++pIMAP->seq_num, CRLF); event_start(ptcx, &stats->cmd); rc = doImapCommandResponse(ptcx, sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)); event_stop (ptcx, &stats->cmd); if (rc == -1) { return -1; } } else { sprintf(command, "%d SELECT %s%s", ++pIMAP->seq_num, folder, CRLF); event_start(ptcx, &stats->cmd); rc = doImapCommandResponse(ptcx, sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)); event_stop (ptcx, &stats->cmd); if (rc == -1) { return -1; } } strcpy(pIMAP->selected_folder, folder); return 0; } static int imapCloseFolder(ptcx_t ptcx, IMAP *pIMAP, SOCKET sock, cmd_stats_t *ptimer) { char command[MAX_COMMAND_LEN]; int rc; pish_stats_t *stats = (pish_stats_t *)ptimer->data; pIMAP->selected_folder[0] = '\0'; sprintf(command, "%d CLOSE%s", ++pIMAP->seq_num, CRLF); event_start(ptcx, &stats->cmd); rc = doImapCommandResponse(ptcx, sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)); event_stop (ptcx, &stats->cmd); if (rc == -1) { if (gf_timeexpired < EXIT_FAST) { stats->cmd.errs++; } return -1; } return 0; } static int imapParseSelectResponse(ptcx_t ptcx, IMAP *pIMAP, char *attr, int *value) { char *bufPtr = pIMAP->resp_buffer; char parsedAttr[32]; int parsedValue = 0; *value = 0; /* parse number of existing/recent msgs out of buffer */ while (bufPtr && *bufPtr) { if (sscanf(bufPtr, "* %d %s", &parsedValue, parsedAttr)) { D_PRINTF(debugfile,"found: %d %s\n", parsedValue, parsedAttr); if (strcmp(parsedAttr, attr) == 0) *value = parsedValue; } bufPtr = strchr(bufPtr, '\n'); if (bufPtr) bufPtr++; } return 0; } static int imapRetrRecentMessages(ptcx_t ptcx, IMAP *pIMAP, SOCKET sock, mail_command_t *cmd, cmd_stats_t *ptimer, int numExists, int numRecent) { int i = 0; char command[MAX_COMMAND_LEN]; int numBytes = 0; long msgSize = 0; char *msgBuffer = NULL; int msgBufferSize = 0; int rc; pish_stats_t *stats = (pish_stats_t *)ptimer->data; pish_command_t *pish = (pish_command_t *)cmd->data; if ( numRecent == 0 ) return 0; /* retr the msgs */ for (i = numExists - numRecent + 1; i <= numExists; i++) { /* bail if time is up */ if (gf_timeexpired >= EXIT_SOON) { D_PRINTF(debugfile,"Time expired while reading messages\n"); break; } /* fetch the size of the recent msg */ sprintf(command, "%d FETCH %d (RFC822.SIZE)%s", ++pIMAP->seq_num, i, CRLF); event_start(ptcx, &stats->cmd); rc = doImapCommandResponse(ptcx, sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)); event_stop (ptcx, &stats->cmd); if (rc == -1) { if (gf_timeexpired >= EXIT_FAST) break; /* dont fall into error */ return -1; } /* parse the SIZE out of buffer */ if (!sscanf(pIMAP->resp_buffer, "* %*d FETCH (RFC822.SIZE %ld)", &msgSize)) { returnerr(debugfile, "IMAP4 Error parsing size of msg from response, %s: %s\n", pIMAP->resp_buffer, neterrstr()); return -1; } /* malloc buffer for msg, with room for control (flags) info */ msgBufferSize = msgSize+1024; msgBuffer = (char *) mycalloc(msgBufferSize); /* FETCH the msg */ sprintf(command, "%d FETCH %d (RFC822)%s", ++pIMAP->seq_num, i, CRLF); event_start(ptcx, &stats->msgread); numBytes = sendCommand(ptcx, sock, command); if (numBytes == -1) { event_stop(ptcx, &stats->msgread); myfree(msgBuffer); if (gf_timeexpired >= EXIT_FAST) break; /* dont fall into error */ stats->msgread.errs++; returnerr(debugfile, "IMAP4 Error sending [%s] command: %s\n", command, neterrstr()); return -1; } /* read msg */ numBytes = retrImapMsg(ptcx, sock, pIMAP->seq_num, msgBuffer, msgSize, msgBufferSize); event_stop(ptcx, &stats->msgread); if (numBytes <= 0) { myfree(msgBuffer); if (gf_timeexpired >= EXIT_FAST) break; /* dont fall into error */ stats->msgread.errs++; returnerr(debugfile, "IMAP4 Error retrieving msg %d: %s\n", i, neterrstr()); return -1; } myfree(msgBuffer); /* send a NOOP */ sprintf(command, "%d NOOP%s",++pIMAP->seq_num, CRLF); event_start(ptcx, &stats->cmd); rc = doImapCommandResponse(ptcx, sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)); event_stop (ptcx, &stats->cmd); if (rc == -1) { if (gf_timeexpired >= EXIT_FAST) break; /* dont fall into error */ stats->cmd.errs++; return -1; } /* if we're told to leave mail on server, do not delete the message */ if (pish->leaveMailOnServer < IMAP_LEAVE_UNSEEN) { if (0 == pish->leaveMailOnServer) { /* mark the msg \deleted and \seen */ sprintf(command, "%d STORE %d +FLAGS (\\DELETED \\SEEN)%s", ++pIMAP->seq_num,i, CRLF); } else { /* mark the msg \seen needed??? */ sprintf(command, "%d STORE %d +FLAGS (\\SEEN)%s", ++pIMAP->seq_num,i, CRLF); } event_start(ptcx, &stats->cmd); rc = doImapCommandResponse(ptcx, sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)); event_stop (ptcx, &stats->cmd); if (rc == -1) { if (gf_timeexpired >= EXIT_FAST) break; /* dont fall into error */ stats->cmd.errs++; return -1; } } else { /* We dont mark it seen, but it still isnt new anymore... */ D_PRINTF (stderr, "retrRecentMsgs() Leaving messaged %d unseen\n", i); } } if (0 == pish->leaveMailOnServer) { /* expunge if we are deleting */ /* EXPUNGE \deleted messages */ sprintf(command, "%d EXPUNGE%s", ++pIMAP->seq_num, CRLF); event_start(ptcx, &stats->cmd); rc = doImapCommandResponse(ptcx, sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)); event_stop (ptcx, &stats->cmd); if (rc == -1) { if (gf_timeexpired < EXIT_FAST) /* dont fall into error */ return -1; stats->cmd.errs++; } } return 0; } /* Given the imapSearchRate, the number of searches to perform in a 8 hour period, pick a set of random numbers telling us when to search. Ex. imapSearchRate = 3 and testime = 1*60*60 (1 hour). pick 3 numbers at random between 1 and 8*60*60 Ex. imapSearchRate = 5 and testime = 12*60*60 (12 hours), pick 5*(12/8) numbers a t random between 1 and 12*60*60 The numbers are ordered and stored in the array searchSchedule. */ static int imapComputeSearchSchedule(ptcx_t ptcx, mail_command_t *cmd, int **searchSchedule) { int i,j,k; int numSearches = 0; int ran = 0; pish_command_t *pish = (pish_command_t *)cmd->data; *searchSchedule = NULL; /* make sure we need to be here */ if (!pish->imapSearchRate) return 0; /* determine number of searches we'll perform, based on searchRate and number of hours in testtime (if testime greater than 8 hours) */ if (gt_testtime <= 8*60*60) numSearches = pish->imapSearchRate; else numSearches = pish->imapSearchRate * (gt_testtime / (8*60*60)); D_PRINTF(debugfile,"num searches to perform=%d\n", numSearches); /* malloc the searchSchedule array */ *searchSchedule = (int *) mycalloc(numSearches*sizeof(int)); D_PRINTF(debugfile,"searchSchedule=%ld\n", *searchSchedule); if (!*searchSchedule) { returnerr(debugfile, "IMAP4 Error, could not malloc searchSchedule: %s\n",neterrstr()); return 0; } memset(*searchSchedule, 0, numSearches*sizeof(int)); /* fill in the schedule, ordering the array */ for (i = 0; i < numSearches; i++) { ran = (RANDOM() % (gt_testtime <= 8*60*60 ? 8*60*60 : gt_testtime)); j = 0; while ((j < i) && ((*searchSchedule)[j] <= ran)) j++; /* insert time */ for (k = i; k > j; k--) (*searchSchedule)[k] = (*searchSchedule)[k-1]; (*searchSchedule)[j] = ran; } return numSearches; } static int imapSearchFolder(ptcx_t ptcx, IMAP *pIMAP, SOCKET sock, mail_command_t *cmd, cmd_stats_t *ptimer) { struct timeval beforeSearch; struct timeval afterSearch; char command[MAX_COMMAND_LEN]; int rc; pish_command_t *pish = (pish_command_t *)cmd->data; pish_stats_t *stats = (pish_stats_t *)ptimer->data; timeval_stamp(&beforeSearch); /* search the folder for messages containing subj pattern, * don't do anything with the search results right now. */ sprintf(command, "%d SEARCH subject %s%s", ++pIMAP->seq_num, pish->imapSearchPattern, CRLF); event_start(ptcx, &stats->cmd); rc = doImapCommandResponse(ptcx, sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)); event_stop (ptcx, &stats->cmd); if (rc == -1) { return -1; } timeval_stamp(&afterSearch); /* print out a SEARCH (begin_time, end_time) to graph */ { /* needs to be updated */ char buf[64]; sprintf(buf, "SEARCH: time=(%lu,%lu)\n", beforeSearch.tv_sec, afterSearch.tv_sec); sendOutput(ptcx->ofd, buf); } return 0; } #if 0 /* not currently used */ static int imapCreateFolder(ptcx_t ptcx, IMAP *pIMAP, const char *folder) { char command[MAX_COMMAND_LEN]; sprintf(command, "%d CREATE %s%s", ++pIMAP->seq_num, folder, CRLF); if (doImapCommandResponse(ptcx, ptcx->sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)) == -1) { return -1; } return 0; } #endif static int imapDeleteFolder(ptcx_t ptcx, IMAP *pIMAP, const char *folder) { char command[MAX_COMMAND_LEN]; sprintf(command, "%d DELETE %s%s", ++pIMAP->seq_num, folder, CRLF); if (doImapCommandResponse(ptcx, ptcx->sock, pIMAP->seq_num, command, pIMAP->resp_buffer, sizeof(pIMAP->resp_buffer)) == -1) { return -1; } return 0; }