1 /* Copyright 1996 by Eric S. Raymond
3 * For license terms, see the file COPYING in this directory.
6 /***********************************************************************
9 programmer: Eric S. Raymond
10 description: IMAP client code
12 ***********************************************************************/
18 #if defined(STDC_HEADERS)
21 #if defined(HAVE_UNISTD_H)
30 #include "popclient.h"
34 #ifdef HAVE_PROTOTYPES
35 /* prototypes for internal functions */
36 int IMAP_OK (char *buf, int socket);
39 int IMAP_readmsg (int socket, int mboxfd, int len,
40 char *host, int topipe, int rewrite);
44 static char tag[TAGLEN];
46 #define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag)
52 /*********************************************************************
54 description: retrieve messages from the specified mail server
55 using IMAP Version 2bis or Version 4.
58 queryctl fully-specified options (i.e. parsed, defaults invoked,
61 return value: exit code from the set of PS_.* constants defined in
64 globals: reads outlevel.
65 *********************************************************************/
68 struct hostrec *queryctl;
72 char buf [POPBUFSIZE];
74 int first,number,count;
78 /* open stdout or the mailbox, locking it if it is a folder */
79 if (queryctl->output == TO_FOLDER || queryctl->output == TO_STDOUT)
80 if ((mboxfd = openuserfolder(queryctl)) < 0)
84 if ((socket = Socket(queryctl->servername,IMAP_PORT)) < 0) {
85 perror("doIMAP: socket");
90 /* accept greeting message from IMAP server */
91 ok = IMAP_OK(buf,socket);
94 IMAP_cmd(socket, "LOGOUT");
99 /* print the greeting */
100 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
101 fprintf(stderr,"IMAP greeting: %s\n",buf);
103 /* try to get authorized */
104 ok = IMAP_cmd(socket,
106 queryctl->remotename, queryctl->password);
112 /* find out how many messages are waiting */
113 exists = unseen = recent = -1;
114 ok = IMAP_cmd(socket,
116 queryctl->remotefolder[0] ? queryctl->remotefolder : "INBOX");
120 /* compute size of message run */
122 if (queryctl->fetchall)
125 if (exists > 0 && unseen == -1) {
127 "no UNSEEN response; assuming all %d RECENT messages are unseen\n",
129 first = exists - recent + 1;
135 /* show them how many messages we'll be downloading */
136 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
138 fprintf(stderr,"%d messages in folder, %d new messages.\n",
139 count, count - first + 1);
141 fprintf(stderr,"%d %smessages in folder.\n", count, ok ? "" : "new ");
144 for (number = queryctl->flush ? 1 : first; number<=count; number++) {
148 /* open the mail pipe if we're using an MDA */
149 if (queryctl->output == TO_MDA
150 && (queryctl->fetchall || number >= first)) {
151 ok = (mboxfd = openmailpipe(queryctl)) < 0 ? -1 : 0;
156 if (queryctl->flush && number < first && !queryctl->fetchall)
157 ok = 0; /* no command to send here, will delete message below */
160 "PARTIAL %d RFC822 0 %d",
167 if (number >= first || queryctl->fetchall) {
168 /* looking for FETCH response */
170 if (SockGets(socket, buf,sizeof(buf)) < 0)
173 (sscanf(buf+2, "%d FETCH (RFC822 {%d}", &num, &len)!=2);
174 if (outlevel == O_VERBOSE)
175 fprintf(stderr,"fetching message %d (%d bytes)\n",num,len);
176 ok = IMAP_readmsg(socket,mboxfd, len,
177 queryctl->servername,
178 queryctl->output == TO_MDA,
180 /* discard tail of FETCH response */
181 if (SockGets(socket, buf,sizeof(buf)) < 0)
190 /* maybe we delete this message now? */
191 if ((number < first && queryctl->flush) || !queryctl->keep) {
192 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
193 fprintf(stderr,"flushing message %d\n", number);
196 ok = IMAP_cmd(socket,
197 "STORE %d +FLAGS (\\Deleted)",
203 /* close the mail pipe, we'll reopen before next message */
204 if (queryctl->output == TO_MDA
205 && (queryctl->fetchall || number >= first)) {
206 ok = closemailpipe(mboxfd);
212 /* remove all messages flagged for deletion */
215 ok = IMAP_cmd(socket, "EXPUNGE");
220 ok = IMAP_cmd(socket, "LOGOUT");
227 ok = IMAP_cmd(socket, "LOGOUT");
235 if (ok != 0 && ok != PS_SOCKET)
236 IMAP_cmd(socket, "LOGOUT");
239 if (queryctl->output == TO_FOLDER)
240 if (closeuserfolder(mboxfd) < 0 && ok == 0)
243 if (ok == PS_IOERR || ok == PS_SOCKET)
244 perror("doIMAP: cleanUp");
249 /*********************************************************************
251 description: get the server's response to a command, and return
252 the extra arguments sent with the response.
254 argbuf buffer to receive the argument string.
255 socket socket to which the server is connected.
257 return value: zero if okay, else return code.
259 globals: reads outlevel.
260 *********************************************************************/
262 int IMAP_OK (argbuf,socket)
267 char buf [POPBUFSIZE];
272 if (SockGets(socket, buf, sizeof(buf)) < 0)
275 if (outlevel == O_VERBOSE)
276 fprintf(stderr,"%s\n",buf);
278 /* interpret untagged status responses */
279 if (strstr(buf, "EXISTS"))
280 exists = atoi(buf+2);
281 if (strstr(buf, "RECENT"))
282 recent = atoi(buf+2);
283 if (sscanf(buf + 2, "OK [UNSEEN %d]", &n) == 1)
287 (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
292 if (strncmp(buf + TAGLEN + 1, "OK", 2) == 0) {
293 strcpy(argbuf, buf + TAGLEN);
296 else if (strncmp(buf + TAGLEN + 1, "BAD", 2) == 0)
303 /*********************************************************************
305 description: Assemble command in print style and send to the server
308 socket socket to which the server is connected.
309 fmt printf-style format
313 globals: reads outlevel.
314 *********************************************************************/
316 void IMAP_send(socket, fmt, va_alist)
321 char buf [POPBUFSIZE];
324 (void) sprintf(buf, "%s ", GENSYM);
327 vsprintf(buf + strlen(buf), fmt, ap);
330 SockPuts(socket, buf);
332 if (outlevel == O_VERBOSE)
333 fprintf(stderr,"> %s\n", buf);
336 /*********************************************************************
338 description: Assemble command in print style and send to the server
341 socket socket to which the server is connected.
342 fmt printf-style format
345 calls: SockPuts, IMAP_OK.
346 globals: reads outlevel.
347 *********************************************************************/
349 int IMAP_cmd(socket, fmt, va_alist)
355 char buf [POPBUFSIZE];
358 (void) sprintf(buf, "%s ", GENSYM);
361 vsprintf(buf + strlen(buf), fmt, ap);
364 SockPuts(socket, buf);
366 if (outlevel == O_VERBOSE)
367 fprintf(stderr,"> %s\n", buf);
369 ok = IMAP_OK(buf,socket);
370 if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
371 fprintf(stderr,"%s\n",buf);
376 /*********************************************************************
377 function: IMAP_readmsg
378 description: Read the message content
380 as described in RFC 1225.
382 socket ... to which the server is connected.
383 mboxfd open file descriptor to which the retrieved message will
386 pophost name of the POP host
387 topipe true if we're writing to the system mailbox pipe.
389 return value: zero if success else PS_* return code.
391 globals: reads outlevel.
392 *********************************************************************/
394 int IMAP_readmsg (socket,mboxfd,len,pophost,topipe,rewrite)
402 char buf [MSGBUFSIZE];
405 char fromBuf[MSGBUFSIZE];
409 int lines,sizeticker;
411 /* This keeps the retrieved message count for display purposes */
412 static int msgnum = 0;
414 /* set up for status message if outlevel allows it */
415 if (outlevel > O_SILENT && outlevel < O_VERBOSE) {
416 fprintf(stderr,"reading message %d",++msgnum);
417 /* won't do the '...' if retrieved messages are being sent to stdout */
426 /* read the message content from the server */
429 sizeticker = MSGBUFSIZE;
431 if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
435 if (buf[0] == '\r' || buf[0] == '\n')
442 /* Check for Unix 'From' header, and add a bogus one if it's not
443 present -- only if not using an MDA.
444 XXX -- should probably parse real From: header and use its
445 address field instead of bogus 'POPmail' string.
447 if (!topipe && lines == 0) {
448 if (strlen(bufp) >= strlen("From ")) {
451 needFrom = strcmp(bufp,"From ") != 0;
458 sprintf(fromBuf,"From POPmail %s",ctime(&now));
459 if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) {
460 perror("IMAP_readmsg: write");
467 * Edit some headers so that replies will work properly.
469 if (inheaders && rewrite)
470 reply_hack(bufp, pophost);
472 /* write this line to the file */
473 if (write(mboxfd,bufp,strlen(bufp)) < 0) {
474 perror("IMAP_readmsg: write");
478 sizeticker -= strlen(bufp);
479 if (sizeticker <= 0) {
480 if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
482 sizeticker = MSGBUFSIZE;
488 /* The server may not write the extra newline required by the Unix
489 mail folder format, so we write one here just in case */
490 if (write(mboxfd,"\n",1) < 0) {
491 perror("IMAP_readmsg: write");
496 /* The mail delivery agent may require a terminator. Write it if
497 it has been defined */
499 if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
500 perror("IMAP_readmsg: write");
506 /* finish up display output */
507 if (outlevel == O_VERBOSE)
508 fprintf(stderr,"(%d lines of message content)\n",lines);
509 else if (outlevel > O_SILENT && mboxfd != 1)