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: Generic driver for mail fetch method protocols
12 ***********************************************************************/
18 #if defined(STDC_HEADERS)
21 #if defined(HAVE_UNISTD_H)
31 #include "popclient.h"
34 static struct method *protocol;
36 #define SMTP_PORT 25 /* standard SMTP service port */
40 #define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag)
42 static int gen_readmsg (int socket, int mboxfd, long len, int delimited,
43 char *host, int topipe, int rewrite);
45 /*********************************************************************
47 description: retrieve messages from the specified mail server
48 using a given set of methods
51 queryctl fully-specified options (i.e. parsed, defaults invoked,
53 proto protocol method pointer
55 return value: exit code from the set of PS_.* constants defined in
58 globals: reads outlevel.
59 *********************************************************************/
61 int do_protocol(queryctl, proto)
62 struct hostrec *queryctl;
67 char buf [POPBUFSIZE], host[HOSTLEN];
69 int first,number,count;
74 /* open the output sink, locking it if it is a folder */
75 if (queryctl->output == TO_FOLDER || queryctl->output == TO_STDOUT) {
76 if ((mboxfd = openuserfolder(queryctl)) < 0)
78 } else if (queryctl->output == TO_SMTP) {
79 if ((mboxfd = Socket(queryctl->smtphost,SMTP_PORT)) < 0)
82 /* make it look like mail is coming from the server */
83 if (SMTP_helo(mboxfd,queryctl->servername) != SM_OK) {
90 /* open a socket to the mail server */
91 if ((socket = Socket(queryctl->servername,protocol->port)) < 0) {
92 perror("do_protocol: socket");
97 /* accept greeting message from mail server */
98 ok = (protocol->parse_response)(buf, socket);
101 gen_transact(socket, protocol->exit_cmd);
106 /* print the greeting */
107 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
108 fprintf(stderr,"%s greeting: %s\n", protocol->name, buf);
110 /* try to get authorized to fetch mail */
111 ok = (protocol->getauth)(socket, queryctl, buf);
117 /* compute count and first */
118 if ((*protocol->getrange)(socket, queryctl, &count, &first) != 0)
121 /* show them how many messages we'll be downloading */
122 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
124 fprintf(stderr,"%d messages in folder, %d new messages.\n",
125 count, count - first + 1);
127 fprintf(stderr,"%d %smessages in folder.\n", count, ok ? "" : "new ");
130 for (number = queryctl->flush ? 1 : first; number<=count; number++) {
134 /* open the mail pipe if we're using an MDA */
135 if (queryctl->output == TO_MDA
136 && (queryctl->fetchall || number >= first)) {
137 ok = (mboxfd = openmailpipe(queryctl)) < 0 ? -1 : 0;
142 if (queryctl->flush && number < first && !queryctl->fetchall)
143 ok = 0; /* no command to send here, will delete message below */
146 (*protocol->fetch)(socket, number, linelimit, &len);
147 if (outlevel == O_VERBOSE)
148 if (protocol->delimited)
149 fprintf(stderr,"fetching message %d (delimited)\n",number);
151 fprintf(stderr,"fetching message %d (%d bytes)\n",number,len);
152 ok = gen_readmsg(socket,mboxfd,len,protocol->delimited,
153 queryctl->servername,
155 !queryctl->norewrite);
157 (*protocol->trail)(socket, queryctl, number);
162 /* maybe we delete this message now? */
163 if ((number < first && queryctl->flush) || !queryctl->keep) {
164 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
165 fprintf(stderr,"flushing message %d\n", number);
168 ok = gen_transact(socket, protocol->delete_cmd, number);
173 /* close the mail pipe, we'll reopen before next message */
174 if (queryctl->output == TO_MDA
175 && (queryctl->fetchall || number >= first)) {
176 ok = closemailpipe(mboxfd);
182 /* remove all messages flagged for deletion */
183 if (!queryctl->keep && protocol->expunge_cmd)
185 ok = gen_transact(socket, protocol->expunge_cmd);
190 ok = gen_transact(socket, protocol->exit_cmd);
197 ok = gen_transact(socket, protocol->exit_cmd);
205 if (ok != 0 && ok != PS_SOCKET)
206 gen_transact(socket, protocol->exit_cmd);
209 if (queryctl->output == TO_FOLDER)
211 if (closeuserfolder(mboxfd) < 0 && ok == 0)
214 else if (queryctl->output == TO_SMTP && mboxfd > 0)
217 if (ok == PS_IOERR || ok == PS_SOCKET)
218 perror("do_protocol: cleanUp");
223 /*********************************************************************
225 description: Assemble command in print style and send to the server
228 socket socket to which the server is connected.
229 fmt printf-style format
233 globals: reads outlevel.
234 *********************************************************************/
236 void gen_send(socket, fmt, va_alist)
241 char buf [POPBUFSIZE];
244 if (protocol->tagged)
245 (void) sprintf(buf, "%s ", GENSYM);
250 vsprintf(buf + strlen(buf), fmt, ap);
253 SockPuts(socket, buf);
255 if (outlevel == O_VERBOSE)
256 fprintf(stderr,"> %s\n", buf);
259 /*********************************************************************
260 function: gen_transact
261 description: Assemble command in print style and send to the server.
262 then accept a protocol-dependent response.
265 socket socket to which the server is connected.
266 fmt printf-style format
269 calls: SockPuts, imap_ok.
270 globals: reads outlevel.
271 *********************************************************************/
273 int gen_transact(socket, fmt, va_alist)
279 char buf [POPBUFSIZE];
282 if (protocol->tagged)
283 (void) sprintf(buf, "%s ", GENSYM);
288 vsprintf(buf + strlen(buf), fmt, ap);
291 SockPuts(socket, buf);
293 if (outlevel == O_VERBOSE)
294 fprintf(stderr,"> %s\n", buf);
296 ok = (protocol->parse_response)(buf,socket);
297 if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
298 fprintf(stderr,"%s\n",buf);
303 /*********************************************************************
304 function: gen_readmsg
305 description: Read the message content
307 as described in RFC 1225.
309 socket ... to which the server is connected.
310 mboxfd open file descriptor to which the retrieved message will
313 pophost name of the POP host
316 return value: zero if success else PS_* return code.
318 globals: reads outlevel.
319 *********************************************************************/
321 int gen_readmsg (socket,mboxfd,len,delimited,pophost,output,rewrite)
330 char buf [MSGBUFSIZE];
331 char fromBuf[MSGBUFSIZE];
332 char *bufp, *headers, *unixfrom, *fromhdr, *tohdr, *cchdr, *bcchdr;
335 int lines,sizeticker;
337 /* This keeps the retrieved message count for display purposes */
338 static int msgnum = 0;
340 /* set up for status message if outlevel allows it */
341 if (outlevel > O_SILENT && outlevel < O_VERBOSE) {
342 fprintf(stderr,"reading message %d",++msgnum);
343 /* won't do the '...' if retrieved messages are being sent to stdout */
348 /* read the message content from the server */
350 headers = unixfrom = fromhdr = tohdr = cchdr = bcchdr = NULL;
352 sizeticker = MSGBUFSIZE;
353 while (delimited || len > 0) {
354 if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
358 if (buf[0] == '\0' || buf[0] == '\r' || buf[0] == '\n')
362 if (delimited && *bufp == 0)
363 break; /* end of message */
370 reply_hack(bufp, pophost);
374 oldlen = strlen(bufp);
375 headers = malloc(oldlen + 1);
378 (void) strcpy(headers, bufp);
383 int newlen = oldlen + strlen(bufp);
385 headers = realloc(headers, newlen + 1);
388 strcpy(headers + oldlen, bufp);
389 bufp = headers + oldlen;
393 if (!strncmp(bufp,"From ",5))
395 else if (strncmp("From: ", bufp, 6))
397 else if (strncmp("To: ", bufp, 4))
399 else if (strncmp("Cc: ", bufp, 4))
401 else if (strncmp("Bcc: ", bufp, 5))
417 (void) strcpy(fromBuf, unixfrom);
421 sprintf(fromBuf,"From POPmail %s",ctime(&now));
424 if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) {
425 perror("gen_readmsg: write");
434 if (write(mboxfd,headers,oldlen) < 0)
438 perror("gen_readmsg: write");
445 /* write this line to the file */
446 if (write(mboxfd,bufp,strlen(bufp)) < 0)
448 perror("gen_readmsg: write");
454 sizeticker -= strlen(bufp);
457 if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
459 sizeticker = MSGBUFSIZE;
464 /* write message terminator, if any */
468 if (write(mboxfd,".\r\n",3) < 0) {
469 perror("gen_readmsg: write");
472 if (SMTP_ok(mboxfd, NULL) != SM_OK)
478 /* The server may not write the extra newline required by the Unix
479 mail folder format, so we write one here just in case */
480 if (write(mboxfd,"\n",1) < 0) {
481 perror("gen_readmsg: write");
487 /* The mail delivery agent may require a terminator. Write it if
488 it has been defined */
490 if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
491 perror("gen_readmsg: write");
494 #endif /* BINMAIL_TERM */
497 /* finish up display output */
498 if (outlevel == O_VERBOSE)
499 fprintf(stderr,"(%d lines of message content)\n",lines);
500 else if (outlevel > O_SILENT && mboxfd != 1)