]> Pileus Git - ~andy/fetchmail/blob - driver.c
Initial revision
[~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
29 #include  "socket.h"
30 #include  "popclient.h"
31
32 static struct method *protocol;
33
34 char tag[TAGLEN];
35 static int tagnum;
36 #define GENSYM  (sprintf(tag, "a%04d", ++tagnum), tag)
37
38 /*********************************************************************
39   function:      do_protocol
40   description:   retrieve messages from the specified mail server
41                  using a given set of methods
42
43   arguments:     
44     queryctl     fully-specified options (i.e. parsed, defaults invoked,
45                  etc).
46     proto        protocol method pointer
47
48   return value:  exit code from the set of PS_.* constants defined in 
49                  popclient.h
50   calls:
51   globals:       reads outlevel.
52  *********************************************************************/
53
54 int do_protocol(queryctl, proto)
55 struct hostrec *queryctl;
56 struct method *proto;
57 {
58     int ok, len;
59     int mboxfd;
60     char buf [POPBUFSIZE];
61     int socket;
62     int first,number,count;
63
64     tagnum = 0;
65     protocol = proto;
66
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) 
70             return(PS_IOERR);
71     
72     /* open the socket */
73     if ((socket = Socket(queryctl->servername,protocol->port)) < 0) {
74         perror("do_protocol: socket");
75         ok = PS_SOCKET;
76         goto closeUp;
77     }
78
79     /* accept greeting message from IMAP server */
80     ok = imap_ok(buf,socket);
81     if (ok != 0) {
82         if (ok != PS_SOCKET)
83             gen_transact(socket, protocol->exit_cmd);
84         close(socket);
85         goto closeUp;
86     }
87
88     /* print the greeting */
89     if (outlevel > O_SILENT && outlevel < O_VERBOSE) 
90         fprintf(stderr,"%s greeting: %s\n", protocol->name, buf);
91
92     /* try to get authorized to fetch mail */
93     ok = (protocol->getauth)(socket, queryctl, buf);
94     if (ok == PS_ERROR)
95         ok = PS_AUTHFAIL;
96     if (ok != 0)
97         goto cleanUp;
98
99     /* compute count and first */
100     if ((*protocol->getrange)(socket, queryctl, &count, &first) != 0)
101         goto cleanUp;
102
103     /* show them how many messages we'll be downloading */
104     if (outlevel > O_SILENT && outlevel < O_VERBOSE)
105         if (first > 1) 
106             fprintf(stderr,"%d messages in folder, %d new messages.\n", 
107                     count, count - first + 1);
108         else
109             fprintf(stderr,"%d %smessages in folder.\n", count, ok ? "" : "new ");
110
111     if (count > 0) { 
112         for (number = queryctl->flush ? 1 : first;  number<=count; number++) {
113
114             char *cp;
115
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;
120                 if (ok != 0)
121                     goto cleanUp;
122             }
123            
124             if (queryctl->flush && number < first && !queryctl->fetchall) 
125                 ok = 0;  /* no command to send here, will delete message below */
126             else
127             {
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);
132                 else
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, 
137                                   queryctl->rewrite);
138                 if (protocol->trail)
139                     (*protocol->trail)(socket, queryctl, number);
140                 if (ok != 0)
141                     goto cleanUp;
142             }
143
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);
148                 else
149                     ;
150                 ok = gen_transact(socket, protocol->delete_cmd, number);
151                 if (ok != 0)
152                     goto cleanUp;
153             }
154
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);
159                 if (ok != 0)
160                     goto cleanUp;
161             }
162         }
163
164         /* remove all messages flagged for deletion */
165         if (!queryctl->keep && protocol->expunge_cmd)
166         {
167             ok = gen_transact(socket, protocol->expunge_cmd);
168             if (ok != 0)
169                 goto cleanUp;
170         }
171
172         ok = gen_transact(socket, protocol->exit_cmd);
173         if (ok == 0)
174             ok = PS_SUCCESS;
175         close(socket);
176         goto closeUp;
177     }
178     else {
179         ok = gen_transact(socket, protocol->exit_cmd);
180         if (ok == 0)
181             ok = PS_NOMAIL;
182         close(socket);
183         goto closeUp;
184     }
185
186 cleanUp:
187     if (ok != 0 && ok != PS_SOCKET)
188         gen_transact(socket, protocol->exit_cmd);
189
190 closeUp:
191     if (queryctl->output == TO_FOLDER)
192         if (closeuserfolder(mboxfd) < 0 && ok == 0)
193             ok = PS_IOERR;
194     
195     if (ok == PS_IOERR || ok == PS_SOCKET) 
196         perror("do_protocol: cleanUp");
197
198     return(ok);
199 }
200
201 /*********************************************************************
202   function:      gen_send
203   description:   Assemble command in print style and send to the server
204
205   arguments:     
206     socket       socket to which the server is connected.
207     fmt          printf-style format
208
209   return value:  none.
210   calls:         SockPuts.
211   globals:       reads outlevel.
212  *********************************************************************/
213
214 void gen_send(socket, fmt, va_alist)
215 int socket;
216 const char *fmt;
217 va_dcl {
218
219   char buf [POPBUFSIZE];
220   va_list ap;
221
222   if (protocol->tagged)
223       (void) sprintf(buf, "%s ", GENSYM);
224   else
225       buf[0] = '\0';
226
227   va_start(ap);
228   vsprintf(buf + strlen(buf), fmt, ap);
229   va_end(ap);
230
231   SockPuts(socket, buf);
232
233   if (outlevel == O_VERBOSE)
234     fprintf(stderr,"> %s\n", buf);
235 }
236
237 /*********************************************************************
238   function:      gen_transact
239   description:   Assemble command in print style and send to the server.
240                  then accept a protocol-dependent response.
241
242   arguments:     
243     socket       socket to which the server is connected.
244     fmt          printf-style format
245
246   return value:  none.
247   calls:         SockPuts, imap_ok.
248   globals:       reads outlevel.
249  *********************************************************************/
250
251 int gen_transact(socket, fmt, va_alist)
252 int socket;
253 const char *fmt;
254 va_dcl {
255
256   int ok;
257   char buf [POPBUFSIZE];
258   va_list ap;
259
260   if (protocol->tagged)
261       (void) sprintf(buf, "%s ", GENSYM);
262   else
263       buf[0] = '\0';
264
265   va_start(ap);
266   vsprintf(buf + strlen(buf), fmt, ap);
267   va_end(ap);
268
269   SockPuts(socket, buf);
270
271   if (outlevel == O_VERBOSE)
272     fprintf(stderr,"> %s\n", buf);
273
274   ok = (protocol->parse_response)(buf,socket);
275   if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
276     fprintf(stderr,"%s\n",buf);
277
278   return(ok);
279 }
280
281 /*********************************************************************
282   function:      gen_readmsg
283   description:   Read the message content 
284
285  as described in RFC 1225.
286   arguments:     
287     socket       ... to which the server is connected.
288     mboxfd       open file descriptor to which the retrieved message will
289                  be written.
290     len          length of text 
291     pophost      name of the POP host 
292     topipe       true if we're writing to the system mailbox pipe.
293
294   return value:  zero if success else PS_* return code.
295   calls:         SockGets.
296   globals:       reads outlevel. 
297  *********************************************************************/
298
299 int gen_readmsg (socket,mboxfd,len,delimited,pophost,topipe,rewrite)
300 int socket;
301 int mboxfd;
302 long len;
303 int delimited;
304 char *pophost;
305 int topipe;
306 int rewrite;
307
308   char buf [MSGBUFSIZE]; 
309   char *bufp;
310   char savec;
311   char fromBuf[MSGBUFSIZE];
312   int n;
313   int needFrom;
314   int inheaders;
315   int lines,sizeticker;
316   time_t now;
317   /* This keeps the retrieved message count for display purposes */
318   static int msgnum = 0;  
319
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 */
324     if (mboxfd == 1)
325       fputs(".\n",stderr);
326     else
327       ;
328   }
329   else
330     ;
331
332   /* read the message content from the server */
333   inheaders = 1;
334   lines = 0;
335   sizeticker = MSGBUFSIZE;
336   while (delimited || len > 0) {
337     if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
338       return(PS_SOCKET);
339     len -= n;
340     bufp = buf;
341     if (buf[0] == '\r' || buf[0] == '\n')
342       inheaders = 0;
343     if (*bufp == '.') {
344       bufp++;
345       if (delimited && *bufp == 0)
346         break;  /* end of message */
347     }
348     strcat(bufp,"\n");
349      
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. 
354     */
355     if (!topipe && lines == 0) {
356       if (strlen(bufp) >= strlen("From ")) {
357         savec = *(bufp + 5);
358         *(bufp + 5) = 0;
359         needFrom = strcmp(bufp,"From ") != 0;
360         *(bufp + 5) = savec;
361       }
362       else
363         needFrom = 1;
364       if (needFrom) {
365         now = time(NULL);
366         sprintf(fromBuf,"From POPmail %s",ctime(&now));
367         if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) {
368           perror("gen_readmsg: write");
369           return(PS_IOERR);
370         }
371       }
372     }
373
374     /*
375      * Edit some headers so that replies will work properly.
376      */
377     if (inheaders && rewrite)
378       reply_hack(bufp, pophost);
379
380     /* write this line to the file */
381     if (write(mboxfd,bufp,strlen(bufp)) < 0) {
382       perror("gen_readmsg: write");
383       return(PS_IOERR);
384     }
385
386     sizeticker -= strlen(bufp);
387     if (sizeticker <= 0) {
388       if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
389         fputc('.',stderr);
390       sizeticker = MSGBUFSIZE;
391     }
392     lines++;
393   }
394
395   if (!topipe) {
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");
400       return(PS_IOERR);
401     }
402   }
403   else {
404     /* The mail delivery agent may require a terminator.  Write it if
405        it has been defined */
406 #ifdef BINMAIL_TERM
407     if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
408       perror("gen_readmsg: write");
409       return(PS_IOERR);
410     }
411 #endif
412     }
413
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) 
418     fputs(".\n",stderr);
419   else
420     ;
421   return(0);
422 }