]> Pileus Git - ~andy/fetchmail/blob - driver.c
Correct the rewrite logic.
[~andy/fetchmail] / driver.c
1 /* Copyright 1996 by Eric S. Raymond
2  * All rights reserved.
3  * For license terms, see the file COPYING in this directory.
4  */
5
6 /***********************************************************************
7   module:       driver.c
8   project:      popclient
9   programmer:   Eric S. Raymond
10   description:  Generic driver for mail fetch method protocols
11
12  ***********************************************************************/
13
14 #include  <config.h>
15 #include  <varargs.h>
16
17 #include  <stdio.h>
18 #if defined(STDC_HEADERS)
19 #include  <string.h>
20 #endif
21 #if defined(HAVE_UNISTD_H)
22 #include  <unistd.h>
23 #endif
24
25 #include  <sys/time.h>
26 #include  <ctype.h>
27 #include  <errno.h>
28 #include  <malloc.h>
29
30 #include  "socket.h"
31 #include  "popclient.h"
32 #include  "smtp.h"
33
34 static struct method *protocol;
35
36 #define SMTP_PORT       25      /* standard SMTP service port */
37
38 char tag[TAGLEN];
39 static int tagnum;
40 #define GENSYM  (sprintf(tag, "a%04d", ++tagnum), tag)
41
42 static int gen_readmsg (int socket, int mboxfd, long len, int delimited,
43        char *host, int topipe, int rewrite);
44
45 /*********************************************************************
46   function:      do_protocol
47   description:   retrieve messages from the specified mail server
48                  using a given set of methods
49
50   arguments:     
51     queryctl     fully-specified options (i.e. parsed, defaults invoked,
52                  etc).
53     proto        protocol method pointer
54
55   return value:  exit code from the set of PS_.* constants defined in 
56                  popclient.h
57   calls:
58   globals:       reads outlevel.
59  *********************************************************************/
60
61 int do_protocol(queryctl, proto)
62 struct hostrec *queryctl;
63 struct method *proto;
64 {
65     int ok, len;
66     int mboxfd;
67     char buf [POPBUFSIZE], host[HOSTLEN];
68     int socket;
69     int first,number,count;
70
71     tagnum = 0;
72     protocol = proto;
73
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) 
77             return(PS_IOERR);
78     } else if (queryctl->output == TO_SMTP) {
79         if ((mboxfd = Socket(queryctl->smtphost,SMTP_PORT)) < 0) 
80             return(PS_SOCKET);
81     
82         /* make it look like mail is coming from the server */
83         if (SMTP_helo(mboxfd,queryctl->servername) != SM_OK) {
84             close(mboxfd);
85             mboxfd = 0;
86             return(PS_SMTP);
87         }
88     }
89
90     /* open a socket to the mail server */
91     if ((socket = Socket(queryctl->servername,protocol->port)) < 0) {
92         perror("do_protocol: socket");
93         ok = PS_SOCKET;
94         goto closeUp;
95     }
96
97     /* accept greeting message from mail server */
98     ok = (protocol->parse_response)(buf, socket);
99     if (ok != 0) {
100         if (ok != PS_SOCKET)
101             gen_transact(socket, protocol->exit_cmd);
102         close(socket);
103         goto closeUp;
104     }
105
106     /* print the greeting */
107     if (outlevel > O_SILENT && outlevel < O_VERBOSE) 
108         fprintf(stderr,"%s greeting: %s\n", protocol->name, buf);
109
110     /* try to get authorized to fetch mail */
111     ok = (protocol->getauth)(socket, queryctl, buf);
112     if (ok == PS_ERROR)
113         ok = PS_AUTHFAIL;
114     if (ok != 0)
115         goto cleanUp;
116
117     /* compute count and first */
118     if ((*protocol->getrange)(socket, queryctl, &count, &first) != 0)
119         goto cleanUp;
120
121     /* show them how many messages we'll be downloading */
122     if (outlevel > O_SILENT && outlevel < O_VERBOSE)
123         if (first > 1) 
124             fprintf(stderr,"%d messages in folder, %d new messages.\n", 
125                     count, count - first + 1);
126         else
127             fprintf(stderr,"%d %smessages in folder.\n", count, ok ? "" : "new ");
128
129     if (count > 0) { 
130         for (number = queryctl->flush ? 1 : first;  number<=count; number++) {
131
132             char *cp;
133
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;
138                 if (ok != 0)
139                     goto cleanUp;
140             }
141            
142             if (queryctl->flush && number < first && !queryctl->fetchall) 
143                 ok = 0;  /* no command to send here, will delete message below */
144             else
145             {
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);
150                     else
151                         fprintf(stderr,"fetching message %d (%d bytes)\n",number,len);
152                 ok = gen_readmsg(socket,mboxfd,len,protocol->delimited,
153                                  queryctl->servername,
154                                  queryctl->output, 
155                                  !queryctl->norewrite);
156                 if (protocol->trail)
157                     (*protocol->trail)(socket, queryctl, number);
158                 if (ok != 0)
159                     goto cleanUp;
160             }
161
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);
166                 else
167                     ;
168                 ok = gen_transact(socket, protocol->delete_cmd, number);
169                 if (ok != 0)
170                     goto cleanUp;
171             }
172
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);
177                 if (ok != 0)
178                     goto cleanUp;
179             }
180         }
181
182         /* remove all messages flagged for deletion */
183         if (!queryctl->keep && protocol->expunge_cmd)
184         {
185             ok = gen_transact(socket, protocol->expunge_cmd);
186             if (ok != 0)
187                 goto cleanUp;
188         }
189
190         ok = gen_transact(socket, protocol->exit_cmd);
191         if (ok == 0)
192             ok = PS_SUCCESS;
193         close(socket);
194         goto closeUp;
195     }
196     else {
197         ok = gen_transact(socket, protocol->exit_cmd);
198         if (ok == 0)
199             ok = PS_NOMAIL;
200         close(socket);
201         goto closeUp;
202     }
203
204 cleanUp:
205     if (ok != 0 && ok != PS_SOCKET)
206         gen_transact(socket, protocol->exit_cmd);
207
208 closeUp:
209     if (queryctl->output == TO_FOLDER)
210     {
211         if (closeuserfolder(mboxfd) < 0 && ok == 0)
212             ok = PS_IOERR;
213     }
214     else if (queryctl->output == TO_SMTP && mboxfd  > 0)
215         close(mboxfd);
216
217     if (ok == PS_IOERR || ok == PS_SOCKET) 
218         perror("do_protocol: cleanUp");
219
220     return(ok);
221 }
222
223 /*********************************************************************
224   function:      gen_send
225   description:   Assemble command in print style and send to the server
226
227   arguments:     
228     socket       socket to which the server is connected.
229     fmt          printf-style format
230
231   return value:  none.
232   calls:         SockPuts.
233   globals:       reads outlevel.
234  *********************************************************************/
235
236 void gen_send(socket, fmt, va_alist)
237 int socket;
238 const char *fmt;
239 va_dcl {
240
241   char buf [POPBUFSIZE];
242   va_list ap;
243
244   if (protocol->tagged)
245       (void) sprintf(buf, "%s ", GENSYM);
246   else
247       buf[0] = '\0';
248
249   va_start(ap);
250   vsprintf(buf + strlen(buf), fmt, ap);
251   va_end(ap);
252
253   SockPuts(socket, buf);
254
255   if (outlevel == O_VERBOSE)
256     fprintf(stderr,"> %s\n", buf);
257 }
258
259 /*********************************************************************
260   function:      gen_transact
261   description:   Assemble command in print style and send to the server.
262                  then accept a protocol-dependent response.
263
264   arguments:     
265     socket       socket to which the server is connected.
266     fmt          printf-style format
267
268   return value:  none.
269   calls:         SockPuts, imap_ok.
270   globals:       reads outlevel.
271  *********************************************************************/
272
273 int gen_transact(socket, fmt, va_alist)
274 int socket;
275 const char *fmt;
276 va_dcl {
277
278   int ok;
279   char buf [POPBUFSIZE];
280   va_list ap;
281
282   if (protocol->tagged)
283       (void) sprintf(buf, "%s ", GENSYM);
284   else
285       buf[0] = '\0';
286
287   va_start(ap);
288   vsprintf(buf + strlen(buf), fmt, ap);
289   va_end(ap);
290
291   SockPuts(socket, buf);
292
293   if (outlevel == O_VERBOSE)
294     fprintf(stderr,"> %s\n", buf);
295
296   ok = (protocol->parse_response)(buf,socket);
297   if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
298     fprintf(stderr,"%s\n",buf);
299
300   return(ok);
301 }
302
303 /*********************************************************************
304   function:      gen_readmsg
305   description:   Read the message content 
306
307  as described in RFC 1225.
308   arguments:     
309     socket       ... to which the server is connected.
310     mboxfd       open file descriptor to which the retrieved message will
311                  be written.
312     len          length of text 
313     pophost      name of the POP host 
314     output       output mode
315
316   return value:  zero if success else PS_* return code.
317   calls:         SockGets.
318   globals:       reads outlevel. 
319  *********************************************************************/
320
321 int gen_readmsg (socket,mboxfd,len,delimited,pophost,output,rewrite)
322 int socket;
323 int mboxfd;
324 long len;
325 int delimited;
326 char *pophost;
327 int output;
328 int rewrite;
329
330     char buf [MSGBUFSIZE]; 
331     char fromBuf[MSGBUFSIZE];
332     char *bufp, *headers, *unixfrom, *fromhdr, *tohdr, *cchdr, *bcchdr;
333     int n, oldlen;
334     int inheaders;
335     int lines,sizeticker;
336     time_t now;
337     /* This keeps the retrieved message count for display purposes */
338     static int msgnum = 0;  
339
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 */
344         if (mboxfd == 1)
345             fputs(".\n",stderr);
346     }
347
348     /* read the message content from the server */
349     inheaders = 1;
350     headers = unixfrom = fromhdr = tohdr = cchdr = bcchdr = NULL;
351     lines = 0;
352     sizeticker = MSGBUFSIZE;
353     while (delimited || len > 0) {
354         if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
355             return(PS_SOCKET);
356         len -= n;
357         bufp = buf;
358         if (buf[0] == '\0' || buf[0] == '\r' || buf[0] == '\n')
359             inheaders = 0;
360         if (*bufp == '.') {
361             bufp++;
362             if (delimited && *bufp == 0)
363                 break;  /* end of message */
364         }
365         strcat(bufp,"\n");
366      
367         if (inheaders)
368         {
369             if (rewrite)
370                 reply_hack(bufp, pophost);
371
372             if (!lines)
373             {
374                 oldlen = strlen(bufp);
375                 headers = malloc(oldlen + 1);
376                 if (headers == NULL)
377                     return(PS_IOERR);
378                 (void) strcpy(headers, bufp);
379                 bufp = headers;
380             }
381             else
382             {
383                 int     newlen = oldlen + strlen(bufp);
384
385                 headers = realloc(headers, newlen + 1);
386                 if (headers == NULL)
387                     return(PS_IOERR);
388                 strcpy(headers + oldlen, bufp);
389                 bufp = headers + oldlen;
390                 oldlen = newlen;
391             }
392
393             if (!strncmp(bufp,"From ",5))
394                 unixfrom = bufp;
395             else if (strncmp("From: ", bufp, 6))
396                 tohdr = bufp;
397             else if (strncmp("To: ", bufp, 4))
398                 fromhdr = bufp;
399             else if (strncmp("Cc: ", bufp, 4))
400                 cchdr = bufp;
401             else if (strncmp("Bcc: ", bufp, 5))
402                 bcchdr = bufp;
403
404             goto skipwrite;
405         }
406         else if (headers)
407         {
408             switch (output)
409             {
410             case TO_SMTP:
411                 SMTP_data(mboxfd);
412                 break;
413
414             case TO_FOLDER:
415             case TO_STDOUT:
416                 if (unixfrom)
417                     (void) strcpy(fromBuf, unixfrom);
418                 else
419                 {
420                     now = time(NULL);
421                     sprintf(fromBuf,"From POPmail %s",ctime(&now));
422                 }
423
424                 if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) {
425                     perror("gen_readmsg: write");
426                     return(PS_IOERR);
427                 }
428                 break;
429
430             case TO_MDA:
431                 break;
432             }
433
434             if (write(mboxfd,headers,oldlen) < 0)
435             {
436                 free(headers);
437                 headers = NULL;
438                 perror("gen_readmsg: write");
439                 return(PS_IOERR);
440             }
441             free(headers);
442             headers = NULL;
443         }
444
445         /* write this line to the file */
446         if (write(mboxfd,bufp,strlen(bufp)) < 0)
447         {
448             perror("gen_readmsg: write");
449             return(PS_IOERR);
450         }
451
452     skipwrite:;
453
454         sizeticker -= strlen(bufp);
455         if (sizeticker <= 0)
456         {
457             if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
458                 fputc('.',stderr);
459             sizeticker = MSGBUFSIZE;
460         }
461         lines++;
462     }
463
464     /* write message terminator, if any */
465     switch (output)
466     {
467     case TO_SMTP:
468         if (write(mboxfd,".\r\n",3) < 0) {
469             perror("gen_readmsg: write");
470             return(PS_IOERR);
471         }
472         if (SMTP_ok(mboxfd, NULL) != SM_OK)
473             return(PS_SMTP);
474         break;
475
476     case TO_FOLDER:
477     case TO_STDOUT:
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");
482             return(PS_IOERR);
483         }
484         break;
485
486     case TO_MDA:
487         /* The mail delivery agent may require a terminator.  Write it if
488            it has been defined */
489 #ifdef BINMAIL_TERM
490         if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
491             perror("gen_readmsg: write");
492             return(PS_IOERR);
493         }
494 #endif /* BINMAIL_TERM */
495     }
496
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) 
501         fputs(".\n",stderr);
502     else
503         ;
504     return(0);
505 }