diff options
-rw-r--r-- | imap.c | 374 |
1 files changed, 231 insertions, 143 deletions
@@ -29,14 +29,12 @@ #include "socket.h" #include "popclient.h" -#define IMAP_PORT 143 - #ifdef HAVE_PROTOTYPES /* prototypes for internal functions */ -int IMAP_OK (char *buf, int socket); -void IMAP_send (); -int IMAP_cmd (); -int IMAP_readmsg (int socket, int mboxfd, int len, +int generic_ok (char *buf, int socket); +void generic_send (); +int transact (); +int generic_readmsg (int socket, int mboxfd, int len, char *host, int topipe, int rewrite); #endif @@ -45,9 +43,178 @@ static char tag[TAGLEN]; static int tagnum; #define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag) -static int exists; -static int unseen; -static int recent; +static int count, first; + +typedef struct +{ + char *name; /* protocol name */ + int port; /* service port */ + int tagged; /* if true, generate & expect command tags */ + int (*parse_response)(); /* response_parsing function */ + int (*getauth)(); /* authorization fetcher */ + int (*getrange)(); /* get message range to fetch */ + int (*fetch)(); /* fetch a given message */ + int (*trail)(); /* eat trailer of a message */ + char *delete_cmd; /* delete command */ + char *expunge_cmd; /* expunge command */ + char *exit_cmd; /* exit command */ +} +method; + +/********************************************************************* + + Method declarations for IMAP + + *********************************************************************/ + +static int exists, unseen, recent; + +int imap_ok (argbuf,socket) +/* parse command response */ +char *argbuf; +int socket; +{ + int ok; + char buf [POPBUFSIZE]; + char *bufp; + int n; + + do { + if (SockGets(socket, buf, sizeof(buf)) < 0) + return(PS_SOCKET); + + if (outlevel == O_VERBOSE) + fprintf(stderr,"%s\n",buf); + + /* interpret untagged status responses */ + if (strstr(buf, "EXISTS")) + exists = atoi(buf+2); + if (strstr(buf, "RECENT")) + recent = atoi(buf+2); + if (sscanf(buf + 2, "OK [UNSEEN %d]", &n) == 1) + unseen = n; + + } while + (tag[0] != '\0' && strncmp(buf, tag, strlen(tag))); + + if (tag[0] == '\0') + return(0); + else { + if (strncmp(buf + TAGLEN + 1, "OK", 2) == 0) { + strcpy(argbuf, buf + TAGLEN); + return(0); + } + else if (strncmp(buf + TAGLEN + 1, "BAD", 2) == 0) + return(PS_ERROR); + else + return(PS_PROTOCOL); + } +} + +int imap_getauth(socket, queryctl) +/* apply for connection authorization */ +int socket; +struct hostrec *queryctl; +{ + /* try to get authorized */ + return(transact(socket, + "LOGIN %s %s", + queryctl->remotename, queryctl->password)); +} + +static imap_getrange(socket, queryctl, countp, firstp) +/* get range of messages to be fetched */ +int socket; +struct hostrec *queryctl; +int *countp; +int *firstp; +{ + int ok; + + /* find out how many messages are waiting */ + exists = unseen = recent = -1; + ok = transact(socket, + "SELECT %s", + queryctl->remotefolder[0] ? queryctl->remotefolder : "INBOX"); + if (ok != 0) + return(ok); + + /* compute size of message run */ + *countp = exists; + if (queryctl->fetchall) + *firstp = 1; + else { + if (exists > 0 && unseen == -1) { + fprintf(stderr, + "no UNSEEN response; assuming all %d RECENT messages are unseen\n", + recent); + *firstp = exists - recent + 1; + } else { + *firstp = unseen; + } + } + + return(0); +} + +static int imap_fetch(socket, number, limit, lenp) +/* request nth message */ +int socket; +int number; +int limit; +int *lenp; +{ + char buf [POPBUFSIZE]; + int num; + + if (limit) + generic_send(socket, + "PARTIAL %d RFC822 0 %d", + number, limit); + else + generic_send(socket, + "FETCH %d RFC822", + number); + + /* looking for FETCH response */ + do { + if (SockGets(socket, buf,sizeof(buf)) < 0) + return(PS_SOCKET); + } while + (sscanf(buf+2, "%d FETCH (RFC822 {%d}", &num, lenp) != 2); + + if (num != number) + return(PS_ERROR); + else + return(0); +} + +static imap_trail(socket) +/* discard tail of FETCH response */ +int socket; +{ + char buf [POPBUFSIZE]; + + if (SockGets(socket, buf,sizeof(buf)) < 0) + return(PS_SOCKET); +} + +static method imap = +{ + "IMAP", /* Internet Message Access Protocol */ + 143, /* standard IMAP3bis/IMAP4 port */ + 1, /* this is a tagged protocol */ + imap_ok, /* parse command response */ + imap_getauth, /* get authorization */ + imap_getrange, /* query range of messages */ + imap_fetch, /* request given message */ + imap_trail, /* eat message trailer */ + "STORE %d +FLAGS (\\Deleted)", /* set IMAP delete flag */ + "EXPUNGE", /* the IMAP expunge command */ + "LOGOUT", /* the IMAP exit command */ +}; + +static method *protocol; /********************************************************************* function: doIMAP @@ -67,7 +234,20 @@ static int recent; int doIMAP (queryctl) struct hostrec *queryctl; { - int ok, num, len; + protocol = &imap; + return(do_protocol(queryctl)); +} + +/********************************************************************* + + Everything below here is generic to all protocols. + + *********************************************************************/ + +int do_protocol(queryctl) +struct hostrec *queryctl; +{ + int ok, len; int mboxfd; char buf [POPBUFSIZE]; int socket; @@ -81,57 +261,36 @@ struct hostrec *queryctl; return(PS_IOERR); /* open the socket */ - if ((socket = Socket(queryctl->servername,IMAP_PORT)) < 0) { - perror("doIMAP: socket"); + if ((socket = Socket(queryctl->servername,protocol->port)) < 0) { + perror("do_protocol: socket"); ok = PS_SOCKET; goto closeUp; } /* accept greeting message from IMAP server */ - ok = IMAP_OK(buf,socket); + ok = imap_ok(buf,socket); if (ok != 0) { if (ok != PS_SOCKET) - IMAP_cmd(socket, "LOGOUT"); + transact(socket, protocol->exit_cmd); close(socket); goto closeUp; } /* print the greeting */ if (outlevel > O_SILENT && outlevel < O_VERBOSE) - fprintf(stderr,"IMAP greeting: %s\n",buf); + fprintf(stderr,"%s greeting: %s\n", protocol->name, buf); - /* try to get authorized */ - ok = IMAP_cmd(socket, - "LOGIN %s %s", - queryctl->remotename, queryctl->password); + /* try to get authorized to fetch mail */ + ok = (protocol->getauth)(socket, queryctl); if (ok == PS_ERROR) ok = PS_AUTHFAIL; if (ok != 0) goto cleanUp; - /* find out how many messages are waiting */ - exists = unseen = recent = -1; - ok = IMAP_cmd(socket, - "SELECT %s", - queryctl->remotefolder[0] ? queryctl->remotefolder : "INBOX"); - if (ok != 0) + /* compute count and first */ + if ((*protocol->getrange)(socket, queryctl, &count, &first) != 0) goto cleanUp; - /* compute size of message run */ - count = exists; - if (queryctl->fetchall) - first = 1; - else { - if (exists > 0 && unseen == -1) { - fprintf(stderr, - "no UNSEEN response; assuming all %d RECENT messages are unseen\n", - recent); - first = exists - recent + 1; - } else { - first = unseen; - } - } - /* show them how many messages we'll be downloading */ if (outlevel > O_SILENT && outlevel < O_VERBOSE) if (first > 1) @@ -155,37 +314,20 @@ struct hostrec *queryctl; if (queryctl->flush && number < first && !queryctl->fetchall) ok = 0; /* no command to send here, will delete message below */ - else if (linelimit) - IMAP_send(socket, - "PARTIAL %d RFC822 0 %d", - number, linelimit); - else - IMAP_send(socket, - "FETCH %d RFC822", - number); - - if (number >= first || queryctl->fetchall) { - /* looking for FETCH response */ - do { - if (SockGets(socket, buf,sizeof(buf)) < 0) - return(PS_SOCKET); - } while - (sscanf(buf+2, "%d FETCH (RFC822 {%d}", &num, &len)!=2); + else + { + (*protocol->fetch)(socket, number, linelimit, &len); if (outlevel == O_VERBOSE) - fprintf(stderr,"fetching message %d (%d bytes)\n",num,len); - ok = IMAP_readmsg(socket,mboxfd, len, + fprintf(stderr,"fetching message %d (%d bytes)\n",number,len); + ok = generic_readmsg(socket,mboxfd,len, queryctl->servername, queryctl->output == TO_MDA, queryctl->rewrite); - /* discard tail of FETCH response */ - if (SockGets(socket, buf,sizeof(buf)) < 0) - return(PS_SOCKET); + if (protocol->trail) + (*protocol->trail)(socket); + if (ok != 0) + goto cleanUp; } - else - ok = 0; - - if (ok != 0) - goto cleanUp; /* maybe we delete this message now? */ if ((number < first && queryctl->flush) || !queryctl->keep) { @@ -193,9 +335,7 @@ struct hostrec *queryctl; fprintf(stderr,"flushing message %d\n", number); else ; - ok = IMAP_cmd(socket, - "STORE %d +FLAGS (\\Deleted)", - number); + ok = transact(socket, protocol->delete_cmd, number); if (ok != 0) goto cleanUp; } @@ -210,21 +350,21 @@ struct hostrec *queryctl; } /* remove all messages flagged for deletion */ - if (!queryctl->keep) + if (!queryctl->keep && protocol->expunge_cmd) { - ok = IMAP_cmd(socket, "EXPUNGE"); + ok = transact(socket, protocol->expunge_cmd); if (ok != 0) goto cleanUp; } - ok = IMAP_cmd(socket, "LOGOUT"); + ok = transact(socket, protocol->exit_cmd); if (ok == 0) ok = PS_SUCCESS; close(socket); goto closeUp; } else { - ok = IMAP_cmd(socket, "LOGOUT"); + ok = transact(socket, protocol->exit_cmd); if (ok == 0) ok = PS_NOMAIL; close(socket); @@ -233,7 +373,7 @@ struct hostrec *queryctl; cleanUp: if (ok != 0 && ok != PS_SOCKET) - IMAP_cmd(socket, "LOGOUT"); + transact(socket, protocol->exit_cmd); closeUp: if (queryctl->output == TO_FOLDER) @@ -241,67 +381,13 @@ closeUp: ok = PS_IOERR; if (ok == PS_IOERR || ok == PS_SOCKET) - perror("doIMAP: cleanUp"); + perror("do_protocol: cleanUp"); return(ok); } /********************************************************************* - function: IMAP_OK - description: get the server's response to a command, and return - the extra arguments sent with the response. - arguments: - argbuf buffer to receive the argument string. - socket socket to which the server is connected. - - return value: zero if okay, else return code. - calls: SockGets - globals: reads outlevel. - *********************************************************************/ - -int IMAP_OK (argbuf,socket) -char *argbuf; -int socket; -{ - int ok; - char buf [POPBUFSIZE]; - char *bufp; - int n; - - do { - if (SockGets(socket, buf, sizeof(buf)) < 0) - return(PS_SOCKET); - - if (outlevel == O_VERBOSE) - fprintf(stderr,"%s\n",buf); - - /* interpret untagged status responses */ - if (strstr(buf, "EXISTS")) - exists = atoi(buf+2); - if (strstr(buf, "RECENT")) - recent = atoi(buf+2); - if (sscanf(buf + 2, "OK [UNSEEN %d]", &n) == 1) - unseen = n; - - } while - (tag[0] != '\0' && strncmp(buf, tag, strlen(tag))); - - if (tag[0] == '\0') - return(0); - else { - if (strncmp(buf + TAGLEN + 1, "OK", 2) == 0) { - strcpy(argbuf, buf + TAGLEN); - return(0); - } - else if (strncmp(buf + TAGLEN + 1, "BAD", 2) == 0) - return(PS_ERROR); - else - return(PS_PROTOCOL); - } -} - -/********************************************************************* - function: IMAP_send + function: generic_send description: Assemble command in print style and send to the server arguments: @@ -313,7 +399,7 @@ int socket; globals: reads outlevel. *********************************************************************/ -void IMAP_send(socket, fmt, va_alist) +void generic_send(socket, fmt, va_alist) int socket; const char *fmt; va_dcl { @@ -321,7 +407,8 @@ va_dcl { char buf [POPBUFSIZE]; va_list ap; - (void) sprintf(buf, "%s ", GENSYM); + if (protocol->tagged) + (void) sprintf(buf, "%s ", GENSYM); va_start(ap); vsprintf(buf + strlen(buf), fmt, ap); @@ -334,19 +421,20 @@ va_dcl { } /********************************************************************* - function: IMAP_cmd - description: Assemble command in print style and send to the server + function: transact + description: Assemble command in print style and send to the server. + then accept a protocol-dependent response. arguments: socket socket to which the server is connected. fmt printf-style format return value: none. - calls: SockPuts, IMAP_OK. + calls: SockPuts, imap_ok. globals: reads outlevel. *********************************************************************/ -int IMAP_cmd(socket, fmt, va_alist) +int transact(socket, fmt, va_alist) int socket; const char *fmt; va_dcl { @@ -366,7 +454,7 @@ va_dcl { if (outlevel == O_VERBOSE) fprintf(stderr,"> %s\n", buf); - ok = IMAP_OK(buf,socket); + ok = (protocol->parse_response)(buf,socket); if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE) fprintf(stderr,"%s\n",buf); @@ -374,7 +462,7 @@ va_dcl { } /********************************************************************* - function: IMAP_readmsg + function: generic_readmsg description: Read the message content as described in RFC 1225. @@ -391,7 +479,7 @@ va_dcl { globals: reads outlevel. *********************************************************************/ -int IMAP_readmsg (socket,mboxfd,len,pophost,topipe,rewrite) +int generic_readmsg (socket,mboxfd,len,pophost,topipe,rewrite) int socket; int mboxfd; int len; @@ -457,7 +545,7 @@ int rewrite; now = time(NULL); sprintf(fromBuf,"From POPmail %s",ctime(&now)); if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) { - perror("IMAP_readmsg: write"); + perror("generic_readmsg: write"); return(PS_IOERR); } } @@ -471,7 +559,7 @@ int rewrite; /* write this line to the file */ if (write(mboxfd,bufp,strlen(bufp)) < 0) { - perror("IMAP_readmsg: write"); + perror("generic_readmsg: write"); return(PS_IOERR); } @@ -488,7 +576,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("IMAP_readmsg: write"); + perror("generic_readmsg: write"); return(PS_IOERR); } } @@ -497,7 +585,7 @@ int rewrite; it has been defined */ #ifdef BINMAIL_TERM if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) { - perror("IMAP_readmsg: write"); + perror("generic_readmsg: write"); return(PS_IOERR); } #endif |