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)
30 #include "popclient.h"
32 static struct method *protocol;
36 #define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag)
38 /*********************************************************************
40 description: retrieve messages from the specified mail server
41 using a given set of methods
44 queryctl fully-specified options (i.e. parsed, defaults invoked,
46 proto protocol method pointer
48 return value: exit code from the set of PS_.* constants defined in
51 globals: reads outlevel.
52 *********************************************************************/
54 int do_protocol(queryctl, proto)
55 struct hostrec *queryctl;
60 char buf [POPBUFSIZE];
62 int first,number,count;
67 /* open stdout or the mailbox, locking it if it is a folder */
68 if (queryctl->output == TO_FOLDER || queryctl->output == TO_STDOUT)
69 if ((mboxfd = openuserfolder(queryctl)) < 0)
73 if ((socket = Socket(queryctl->servername,protocol->port)) < 0) {
74 perror("do_protocol: socket");
79 /* accept greeting message from IMAP server */
80 ok = imap_ok(buf,socket);
83 gen_transact(socket, protocol->exit_cmd);
88 /* print the greeting */
89 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
90 fprintf(stderr,"%s greeting: %s\n", protocol->name, buf);
92 /* try to get authorized to fetch mail */
93 ok = (protocol->getauth)(socket, queryctl, buf);
99 /* compute count and first */
100 if ((*protocol->getrange)(socket, queryctl, &count, &first) != 0)
103 /* show them how many messages we'll be downloading */
104 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
106 fprintf(stderr,"%d messages in folder, %d new messages.\n",
107 count, count - first + 1);
109 fprintf(stderr,"%d %smessages in folder.\n", count, ok ? "" : "new ");
112 for (number = queryctl->flush ? 1 : first; number<=count; number++) {
116 /* open the mail pipe if we're using an MDA */
117 if (queryctl->output == TO_MDA
118 && (queryctl->fetchall || number >= first)) {
119 ok = (mboxfd = openmailpipe(queryctl)) < 0 ? -1 : 0;
124 if (queryctl->flush && number < first && !queryctl->fetchall)
125 ok = 0; /* no command to send here, will delete message below */
128 (*protocol->fetch)(socket, number, linelimit, &len);
129 if (outlevel == O_VERBOSE)
130 if (protocol->delimited)
131 fprintf(stderr,"fetching message %d (delimited)\n",number);
133 fprintf(stderr,"fetching message %d (%d bytes)\n",number,len);
134 ok = gen_readmsg(socket,mboxfd,len,protocol->delimited,
135 queryctl->servername,
136 queryctl->output == TO_MDA,
139 (*protocol->trail)(socket, queryctl, number);
144 /* maybe we delete this message now? */
145 if ((number < first && queryctl->flush) || !queryctl->keep) {
146 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
147 fprintf(stderr,"flushing message %d\n", number);
150 ok = gen_transact(socket, protocol->delete_cmd, number);
155 /* close the mail pipe, we'll reopen before next message */
156 if (queryctl->output == TO_MDA
157 && (queryctl->fetchall || number >= first)) {
158 ok = closemailpipe(mboxfd);
164 /* remove all messages flagged for deletion */
165 if (!queryctl->keep && protocol->expunge_cmd)
167 ok = gen_transact(socket, protocol->expunge_cmd);
172 ok = gen_transact(socket, protocol->exit_cmd);
179 ok = gen_transact(socket, protocol->exit_cmd);
187 if (ok != 0 && ok != PS_SOCKET)
188 gen_transact(socket, protocol->exit_cmd);
191 if (queryctl->output == TO_FOLDER)
192 if (closeuserfolder(mboxfd) < 0 && ok == 0)
195 if (ok == PS_IOERR || ok == PS_SOCKET)
196 perror("do_protocol: cleanUp");
201 /*********************************************************************
203 description: Assemble command in print style and send to the server
206 socket socket to which the server is connected.
207 fmt printf-style format
211 globals: reads outlevel.
212 *********************************************************************/
214 void gen_send(socket, fmt, va_alist)
219 char buf [POPBUFSIZE];
222 if (protocol->tagged)
223 (void) sprintf(buf, "%s ", GENSYM);
228 vsprintf(buf + strlen(buf), fmt, ap);
231 SockPuts(socket, buf);
233 if (outlevel == O_VERBOSE)
234 fprintf(stderr,"> %s\n", buf);
237 /*********************************************************************
238 function: gen_transact
239 description: Assemble command in print style and send to the server.
240 then accept a protocol-dependent response.
243 socket socket to which the server is connected.
244 fmt printf-style format
247 calls: SockPuts, imap_ok.
248 globals: reads outlevel.
249 *********************************************************************/
251 int gen_transact(socket, fmt, va_alist)
257 char buf [POPBUFSIZE];
260 if (protocol->tagged)
261 (void) sprintf(buf, "%s ", GENSYM);
266 vsprintf(buf + strlen(buf), fmt, ap);
269 SockPuts(socket, buf);
271 if (outlevel == O_VERBOSE)
272 fprintf(stderr,"> %s\n", buf);
274 ok = (protocol->parse_response)(buf,socket);
275 if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
276 fprintf(stderr,"%s\n",buf);
281 /*********************************************************************
282 function: gen_readmsg
283 description: Read the message content
285 as described in RFC 1225.
287 socket ... to which the server is connected.
288 mboxfd open file descriptor to which the retrieved message will
291 pophost name of the POP host
292 topipe true if we're writing to the system mailbox pipe.
294 return value: zero if success else PS_* return code.
296 globals: reads outlevel.
297 *********************************************************************/
299 int gen_readmsg (socket,mboxfd,len,delimited,pophost,topipe,rewrite)
308 char buf [MSGBUFSIZE];
311 char fromBuf[MSGBUFSIZE];
315 int lines,sizeticker;
317 /* This keeps the retrieved message count for display purposes */
318 static int msgnum = 0;
320 /* set up for status message if outlevel allows it */
321 if (outlevel > O_SILENT && outlevel < O_VERBOSE) {
322 fprintf(stderr,"reading message %d",++msgnum);
323 /* won't do the '...' if retrieved messages are being sent to stdout */
332 /* read the message content from the server */
335 sizeticker = MSGBUFSIZE;
336 while (delimited || len > 0) {
337 if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
341 if (buf[0] == '\r' || buf[0] == '\n')
345 if (delimited && *bufp == 0)
346 break; /* end of message */
350 /* Check for Unix 'From' header, and add a bogus one if it's not
351 present -- only if not using an MDA.
352 XXX -- should probably parse real From: header and use its
353 address field instead of bogus 'POPmail' string.
355 if (!topipe && lines == 0) {
356 if (strlen(bufp) >= strlen("From ")) {
359 needFrom = strcmp(bufp,"From ") != 0;
366 sprintf(fromBuf,"From POPmail %s",ctime(&now));
367 if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) {
368 perror("gen_readmsg: write");
375 * Edit some headers so that replies will work properly.
377 if (inheaders && rewrite)
378 reply_hack(bufp, pophost);
380 /* write this line to the file */
381 if (write(mboxfd,bufp,strlen(bufp)) < 0) {
382 perror("gen_readmsg: write");
386 sizeticker -= strlen(bufp);
387 if (sizeticker <= 0) {
388 if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
390 sizeticker = MSGBUFSIZE;
396 /* The server may not write the extra newline required by the Unix
397 mail folder format, so we write one here just in case */
398 if (write(mboxfd,"\n",1) < 0) {
399 perror("gen_readmsg: write");
404 /* The mail delivery agent may require a terminator. Write it if
405 it has been defined */
407 if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
408 perror("gen_readmsg: write");
414 /* finish up display output */
415 if (outlevel == O_VERBOSE)
416 fprintf(stderr,"(%d lines of message content)\n",lines);
417 else if (outlevel > O_SILENT && mboxfd != 1)