From 46170e9ee247b92505c9e3270449b5f05705d176 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Tue, 27 Aug 1996 14:54:36 +0000 Subject: Genericized version of POP3 works. svn path=/trunk/; revision=70 --- imap.c | 359 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 303 insertions(+), 56 deletions(-) diff --git a/imap.c b/imap.c index eeb115f0..5e4688f3 100644 --- a/imap.c +++ b/imap.c @@ -31,13 +31,15 @@ #ifdef HAVE_PROTOTYPES /* prototypes for internal functions */ -int generic_ok (char *buf, int socket); -void generic_send (); -int transact (); -int generic_readmsg (int socket, int mboxfd, int len, +int gen_ok (char *buf, int socket); +void gen_send (); +int gen_transact (); +int gen_readmsg (int socket, int mboxfd, long len, int delimited, char *host, int topipe, int rewrite); #endif +#define DELIMITED 99999L + #define TAGLEN 5 static char tag[TAGLEN]; static int tagnum; @@ -45,11 +47,12 @@ static int tagnum; static int count, first; -typedef struct +struct method { char *name; /* protocol name */ int port; /* service port */ int tagged; /* if true, generate & expect command tags */ + int delimited; /* if true, accept "." message delimiter */ int (*parse_response)(); /* response_parsing function */ int (*getauth)(); /* authorization fetcher */ int (*getrange)(); /* get message range to fetch */ @@ -58,8 +61,233 @@ typedef struct char *delete_cmd; /* delete command */ char *expunge_cmd; /* expunge command */ char *exit_cmd; /* exit command */ +}; + +/********************************************************************* + + Method declarations for POP3 + + *********************************************************************/ + +int pop3_ok (argbuf,socket) +/* parse command response */ +char *argbuf; +int socket; +{ + int ok; + char buf [POPBUFSIZE]; + char *bufp; + + if (SockGets(socket, buf, sizeof(buf)) >= 0) { + if (outlevel == O_VERBOSE) + fprintf(stderr,"%s\n",buf); + + bufp = buf; + if (*bufp == '+' || *bufp == '-') + bufp++; + else + return(PS_PROTOCOL); + + while (isalpha(*bufp)) + bufp++; + *(bufp++) = '\0'; + + if (strcmp(buf,"+OK") == 0) + ok = 0; + else if (strcmp(buf,"-ERR") == 0) + ok = PS_ERROR; + else + ok = PS_PROTOCOL; + + if (argbuf != NULL) + strcpy(argbuf,bufp); + } + else + ok = PS_SOCKET; + + return(ok); +} + +int pop3_getauth(socket, queryctl, greeting) +/* apply for connection authorization */ +int socket; +struct hostrec *queryctl; +char *greeting; +{ + char buf [POPBUFSIZE]; + +#if defined(HAVE_APOP_SUPPORT) + /* build MD5 digest from greeting timestamp + password */ + if (queryctl->whichpop == P_APOP) + if (POP3_BuildDigest(greeting,queryctl) != 0) { + return(PS_AUTHFAIL); + } +#endif + + switch (queryctl->protocol) { + case P_POP3: + SockPrintf(socket,"USER %s\r\n",queryctl->remotename); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> USER %s\n",queryctl->remotename); + if (POP3_OK(buf,socket) != 0) + goto badAuth; + + SockPrintf(socket,"PASS %s\r\n",queryctl->password); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> PASS password\n"); + if (POP3_OK(buf,socket) != 0) + goto badAuth; + + break; + +#if defined(HAVE_APOP_SUPPORT) + case P_APOP: + SockPrintf(socket,"APOP %s %s\r\n", + queryctl->remotename, queryctl->digest); + if (outlevel == O_VERBOSE) + fprintf(stderr,"> APOP %s %s\n",queryctl->remotename, queryctl->digest); + if (POP3_OK(buf,socket) != 0) + goto badAuth; + break; +#endif /* HAVE_APOP_SUPPORT */ + +#if defined(HAVE_RPOP_SUPPORT) + case P_RPOP: + SockPrintf(socket, "RPOP %s\r\n", queryctl->remotename); + if (POP3_OK(buf,socket) != 0) + goto badAuth; + if (outlevel == O_VERBOSE) + fprintf(stderr,"> RPOP %s %s\n",queryctl->remotename); + break; +#endif /* HAVE_RPOP_SUPPORT */ + + default: + fprintf(stderr,"Undefined protocol request in POP3_auth\n"); + } + + /* we're approved */ + return(0); + + /*NOTREACHED*/ + +badAuth: + if (outlevel > O_SILENT && outlevel < O_VERBOSE) + fprintf(stderr,"%s\n",buf); + else + ; /* say nothing */ + + return(PS_ERROR); +} + +static int use_uidl; + +static pop3_getrange(socket, queryctl, countp, firstp) +/* get range of messages to be fetched */ +int socket; +struct hostrec *queryctl; +int *countp; +int *firstp; +{ + int ok; + + ok = POP3_sendSTAT(countp,socket); + if (ok != 0) { + return(ok); + } + + /* + * Ask for number of last message retrieved. + * Newer, RFC-1760-conformant POP servers may not have the LAST command. + * Therefore we don't croak if we get a nonzero return. Instead, send + * UIDL and try to find the last received ID stored for this host in + * the list we get back. + */ + *firstp = 1; + use_uidl = 0; + if (!queryctl->fetchall) { + char buf [POPBUFSIZE]; + char id [IDLEN]; + int num; + + /* try LAST first */ + ok = POP3_sendLAST(firstp, socket); + use_uidl = (ok != 0); + + /* otherwise, if we have a stored last ID for this host, + * send UIDL and search the returned list for it + */ + if (use_uidl && queryctl->lastid[0]) { + if ((ok = POP3_sendUIDL(-1, socket, 0)) == 0) { + while (SockGets(socket, buf, sizeof(buf)) >= 0) { + if (outlevel == O_VERBOSE) + fprintf(stderr,"%s\n",buf); + if (strcmp(buf, ".\n") == 0) { + break; + } + if (sscanf(buf, "%d %s\n", &num, id) == 2) + if (strcmp(id, queryctl->lastid) == 0) + *firstp = num; + } + } + } + + if (ok == 0) + (*firstp)++; + } + + return(0); +} + +static int pop3_fetch(socket, number, limit, lenp) +/* request nth message */ +int socket; +int number; +int limit; +int *lenp; +{ + *lenp = DELIMITED; + if (limit) + return(POP3_sendTOP(number, limit, socket)); + else + return(POP3_sendRETR(number, socket)); +} + +static pop3_trail(socket, queryctl, number) +/* update the last-seen field for this host */ +int socket; +struct hostrec *queryctl; +int number; +{ + char *cp; + int ok = 0; + + if (use_uidl && (ok = POP3_sendUIDL(number, socket, &cp)) == 0) + (void) strcpy(queryctl->lastid, cp); + return(ok); +} + +static struct method pop3 = +{ + "POP3", /* Post Office Protocol v3 */ + 110, /* standard POP3 port */ + 0, /* this is not a tagged protocol */ + 1, /* this uses a message delimiter */ + pop3_ok, /* parse command response */ + pop3_getauth, /* get authorization */ + pop3_getrange, /* query range of messages */ + pop3_fetch, /* request given message */ + pop3_trail, /* eat message trailer */ + "DELE %d", /* set POP3 delete flag */ + NULL, /* the POP3 expunge command */ + "QUIT", /* the POP3 exit command */ +}; + +int doPOP3bis (queryctl) +/* retrieve messages using POP3 */ +struct hostrec *queryctl; +{ + return(do_protocol(queryctl, &pop3)); } -method; /********************************************************************* @@ -111,13 +339,14 @@ int socket; } } -int imap_getauth(socket, queryctl) +int imap_getauth(socket, queryctl, buf) /* apply for connection authorization */ int socket; struct hostrec *queryctl; +char *buf; { /* try to get authorized */ - return(transact(socket, + return(gen_transact(socket, "LOGIN %s %s", queryctl->remotename, queryctl->password)); } @@ -133,7 +362,7 @@ int *firstp; /* find out how many messages are waiting */ exists = unseen = recent = -1; - ok = transact(socket, + ok = gen_transact(socket, "SELECT %s", queryctl->remotefolder[0] ? queryctl->remotefolder : "INBOX"); if (ok != 0) @@ -168,11 +397,11 @@ int *lenp; int num; if (limit) - generic_send(socket, + gen_send(socket, "PARTIAL %d RFC822 0 %d", number, limit); else - generic_send(socket, + gen_send(socket, "FETCH %d RFC822", number); @@ -189,21 +418,26 @@ int *lenp; return(0); } -static imap_trail(socket) +static imap_trail(socket, queryctl, number) /* discard tail of FETCH response */ int socket; +struct hostrec *queryctl; +int number; { char buf [POPBUFSIZE]; if (SockGets(socket, buf,sizeof(buf)) < 0) return(PS_SOCKET); + else + return(0); } -static method imap = +static struct method imap = { "IMAP", /* Internet Message Access Protocol */ 143, /* standard IMAP3bis/IMAP4 port */ 1, /* this is a tagged protocol */ + 0, /* no message delimiter */ imap_ok, /* parse command response */ imap_getauth, /* get authorization */ imap_getrange, /* query range of messages */ @@ -214,16 +448,30 @@ static method imap = "LOGOUT", /* the IMAP exit command */ }; -static method *protocol; +int doIMAP (queryctl) +/* retrieve messages using IMAP Version 2bis or Version 4 */ +struct hostrec *queryctl; +{ + return(do_protocol(queryctl, &imap)); +} + +/********************************************************************* + + Everything below here is generic to all protocols. + + *********************************************************************/ + +static struct method *protocol; /********************************************************************* - function: doIMAP + function: do_protocol description: retrieve messages from the specified mail server - using IMAP Version 2bis or Version 4. + using a given set of mrthods arguments: queryctl fully-specified options (i.e. parsed, defaults invoked, etc). + proto protocol method pointer return value: exit code from the set of PS_.* constants defined in popclient.h @@ -231,21 +479,9 @@ static method *protocol; globals: reads outlevel. *********************************************************************/ -int doIMAP (queryctl) -struct hostrec *queryctl; -{ - protocol = &imap; - return(do_protocol(queryctl)); -} - -/********************************************************************* - - Everything below here is generic to all protocols. - - *********************************************************************/ - -int do_protocol(queryctl) +int do_protocol(queryctl, proto) struct hostrec *queryctl; +struct method *proto; { int ok, len; int mboxfd; @@ -254,6 +490,7 @@ struct hostrec *queryctl; int first,number,count; tagnum = 0; + protocol = proto; /* open stdout or the mailbox, locking it if it is a folder */ if (queryctl->output == TO_FOLDER || queryctl->output == TO_STDOUT) @@ -271,7 +508,7 @@ struct hostrec *queryctl; ok = imap_ok(buf,socket); if (ok != 0) { if (ok != PS_SOCKET) - transact(socket, protocol->exit_cmd); + gen_transact(socket, protocol->exit_cmd); close(socket); goto closeUp; } @@ -281,7 +518,7 @@ struct hostrec *queryctl; fprintf(stderr,"%s greeting: %s\n", protocol->name, buf); /* try to get authorized to fetch mail */ - ok = (protocol->getauth)(socket, queryctl); + ok = (protocol->getauth)(socket, queryctl, buf); if (ok == PS_ERROR) ok = PS_AUTHFAIL; if (ok != 0) @@ -318,13 +555,16 @@ struct hostrec *queryctl; { (*protocol->fetch)(socket, number, linelimit, &len); if (outlevel == O_VERBOSE) + if (protocol->delimited) + fprintf(stderr,"fetching message %d (delimited)\n",number); + else fprintf(stderr,"fetching message %d (%d bytes)\n",number,len); - ok = generic_readmsg(socket,mboxfd,len, + ok = gen_readmsg(socket,mboxfd,len,protocol->delimited, queryctl->servername, queryctl->output == TO_MDA, queryctl->rewrite); if (protocol->trail) - (*protocol->trail)(socket); + (*protocol->trail)(socket, queryctl, number); if (ok != 0) goto cleanUp; } @@ -335,7 +575,7 @@ struct hostrec *queryctl; fprintf(stderr,"flushing message %d\n", number); else ; - ok = transact(socket, protocol->delete_cmd, number); + ok = gen_transact(socket, protocol->delete_cmd, number); if (ok != 0) goto cleanUp; } @@ -352,19 +592,19 @@ struct hostrec *queryctl; /* remove all messages flagged for deletion */ if (!queryctl->keep && protocol->expunge_cmd) { - ok = transact(socket, protocol->expunge_cmd); + ok = gen_transact(socket, protocol->expunge_cmd); if (ok != 0) goto cleanUp; } - ok = transact(socket, protocol->exit_cmd); + ok = gen_transact(socket, protocol->exit_cmd); if (ok == 0) ok = PS_SUCCESS; close(socket); goto closeUp; } else { - ok = transact(socket, protocol->exit_cmd); + ok = gen_transact(socket, protocol->exit_cmd); if (ok == 0) ok = PS_NOMAIL; close(socket); @@ -373,7 +613,7 @@ struct hostrec *queryctl; cleanUp: if (ok != 0 && ok != PS_SOCKET) - transact(socket, protocol->exit_cmd); + gen_transact(socket, protocol->exit_cmd); closeUp: if (queryctl->output == TO_FOLDER) @@ -387,7 +627,7 @@ closeUp: } /********************************************************************* - function: generic_send + function: gen_send description: Assemble command in print style and send to the server arguments: @@ -399,7 +639,7 @@ closeUp: globals: reads outlevel. *********************************************************************/ -void generic_send(socket, fmt, va_alist) +void gen_send(socket, fmt, va_alist) int socket; const char *fmt; va_dcl { @@ -409,6 +649,8 @@ va_dcl { if (protocol->tagged) (void) sprintf(buf, "%s ", GENSYM); + else + buf[0] = '\0'; va_start(ap); vsprintf(buf + strlen(buf), fmt, ap); @@ -421,7 +663,7 @@ va_dcl { } /********************************************************************* - function: transact + function: gen_transact description: Assemble command in print style and send to the server. then accept a protocol-dependent response. @@ -434,7 +676,7 @@ va_dcl { globals: reads outlevel. *********************************************************************/ -int transact(socket, fmt, va_alist) +int gen_transact(socket, fmt, va_alist) int socket; const char *fmt; va_dcl { @@ -443,7 +685,10 @@ va_dcl { char buf [POPBUFSIZE]; va_list ap; - (void) sprintf(buf, "%s ", GENSYM); + if (protocol->tagged) + (void) sprintf(buf, "%s ", GENSYM); + else + buf[0] = '\0'; va_start(ap); vsprintf(buf + strlen(buf), fmt, ap); @@ -462,7 +707,7 @@ va_dcl { } /********************************************************************* - function: generic_readmsg + function: gen_readmsg description: Read the message content as described in RFC 1225. @@ -470,7 +715,7 @@ va_dcl { socket ... to which the server is connected. mboxfd open file descriptor to which the retrieved message will be written. - len lrength of text + len length of text pophost name of the POP host topipe true if we're writing to the system mailbox pipe. @@ -479,10 +724,11 @@ va_dcl { globals: reads outlevel. *********************************************************************/ -int generic_readmsg (socket,mboxfd,len,pophost,topipe,rewrite) +int gen_readmsg (socket,mboxfd,len,delimited,pophost,topipe,rewrite) int socket; int mboxfd; -int len; +long len; +int delimited; char *pophost; int topipe; int rewrite; @@ -515,15 +761,19 @@ int rewrite; inheaders = 1; lines = 0; sizeticker = MSGBUFSIZE; - while (len > 0) { + while (delimited || len > 0) { if ((n = SockGets(socket,buf,sizeof(buf))) < 0) return(PS_SOCKET); len -= n; + if (outlevel == O_VERBOSE) + (void) fprintf(stderr, "%s\n", buf); bufp = buf; if (buf[0] == '\r' || buf[0] == '\n') inheaders = 0; if (*bufp == '.') { bufp++; + if (delimited && *bufp == 0) + break; /* end of message */ } strcat(bufp,"\n"); @@ -545,7 +795,7 @@ int rewrite; now = time(NULL); sprintf(fromBuf,"From POPmail %s",ctime(&now)); if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) { - perror("generic_readmsg: write"); + perror("gen_readmsg: write"); return(PS_IOERR); } } @@ -559,7 +809,7 @@ int rewrite; /* write this line to the file */ if (write(mboxfd,bufp,strlen(bufp)) < 0) { - perror("generic_readmsg: write"); + perror("gen_readmsg: write"); return(PS_IOERR); } @@ -576,7 +826,7 @@ int rewrite; /* The server may not write the extra newline required by the Unix mail folder format, so we write one here just in case */ if (write(mboxfd,"\n",1) < 0) { - perror("generic_readmsg: write"); + perror("gen_readmsg: write"); return(PS_IOERR); } } @@ -585,7 +835,7 @@ int rewrite; it has been defined */ #ifdef BINMAIL_TERM if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) { - perror("generic_readmsg: write"); + perror("gen_readmsg: write"); return(PS_IOERR); } #endif @@ -600,6 +850,3 @@ int rewrite; ; return(0); } - - - -- cgit v1.2.3