]> Pileus Git - ~andy/fetchmail/blob - smtp.c
c46128e1402b69c2559366aa1eae1ef91db3dc39
[~andy/fetchmail] / smtp.c
1 /* Copyright 1996 Harry Hochheiser
2  * All rights reserved.
3  * For license terms, see the file COPYING in this directory.
4  */
5
6 /***********************************************************************
7   module:       smtp.c
8   project:      popforward
9   programmer:   Harry Hochheiser
10   description:  Handling of SMTP connections, and processing of mail 
11                  to be forwarded via SMTP connections.
12
13   7/30/96.  Note: since this file is new from scratch, I'll assume
14   that  I'm working on a modern (ANSI) compiler, and I'll use
15   prototypes.
16
17
18  ***********************************************************************/
19
20 #include <config.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include "socket.h"
25 #include "popforward.h"
26 #include "smtp.h"
27
28 static int POP3_parseHeaders(int number, int socket,char **from,int *replFlag);
29 static int SMTP_sendMessageHeaders(int mboxfd,struct optrec *option,
30                                    char *from);
31 static int SendData(int f,char *buf,int check);
32
33
34 /*********************************************************************
35   function:      SMTP_helo
36   description:   Send a "HELO" message to the SMTP server.
37
38   arguments:     
39     socket       TCP/IP socket for connection to SMTP
40   return value:  Result of SMTP_OK: based on codes in popforward.h.
41                  
42  *********************************************************************/
43
44 int SMTP_helo(int socket,char *host)
45 {
46   int ok;
47   char buf[SMTPBUFSIZE];
48   sprintf(buf,"HELO %s\r\n",host);
49   SockPrintf(socket,"%s",buf);
50   ok = SMTP_ok(socket,buf);
51   return ok;
52 }
53
54
55 /*********************************************************************
56   function:      SMTP_from
57   description:   Send a "MAIL FROM:" message to the SMTP server.
58
59   arguments:     
60     socket       TCP/IP socket for connection to SMTP
61     fromuser:    user name of originator
62     fromhost:    host name of originator.  
63
64     Note: these args are likely to change, as we get fancier about
65     handling the names.
66
67   return value:  Result of SMTP_ok: based on codes in popforward.h.
68                  
69  *********************************************************************/
70 int SMTP_from(int socket,char *fromuser,char *fromhost)
71 {
72   char buf[SMTPBUFSIZE];  /* it's as good as size as any... */
73   int ok;
74   SockPrintf(socket,"MAIL FROM %s@%s\n",fromuser,fromhost);
75   ok= SMTP_ok(socket,buf);
76
77   return ok;
78 }
79
80
81 /*********************************************************************
82   function:      SMTP_rcpt
83   description:   Send a "RCPT TO:" message to the SMTP server.
84
85   arguments:     
86     socket       TCP/IP socket for connection to SMTP
87     toser:    user name of recipient
88     tohost:    host name of recipient
89
90   return value:  Result of SMTP_OK: based on codes in popforward.h.
91                  
92  *********************************************************************/
93 int SMTP_rcpt(int socket,char *touser,char *tohost)
94 {
95   char buf[SMTPBUFSIZE];  /* it's as good as size as any... */
96   int ok;
97
98   SockPrintf(socket,"RCPT TO: %s@%s\n",touser,tohost);
99   ok = SMTP_ok(socket,buf);
100   
101   return ok;
102 }
103
104
105 /*********************************************************************
106   function:      SMTP_data
107   description:   Send a "DATA" message to the SMTP server.
108
109   arguments:     
110     socket       TCP/IP socket for connection to SMTP
111
112   return value:  Result of SMTP_OK: based on codes in popforward.h.
113                  
114  *********************************************************************/
115 int SMTP_data(int socket)
116 {
117   SockPrintf(socket,"DATA\n");
118 }
119
120
121 /*********************************************************************
122   function:      SMTP_rset
123   description:   Send a "DATA" message to the SMTP server.
124
125   arguments:     
126     socket       TCP/IP socket for connection to SMTP
127
128   return value:  Result of SMTP_OK: based on codes in popforward.h.
129                  
130  *********************************************************************/
131 void SMTP_rset(int socket)
132 {
133   SockPrintf(socket,"RSET\n");
134 }
135
136
137
138 /*********************************************************************
139   function:      SMTP_check
140   description:   Returns the status of the smtp connection
141                  8/13/96, HSH
142   arguments:     
143     socket       TCP/IP socket for connection to SMTP
144
145   return value:  based on codes in popforward.h.
146                  Do the dirty work of seeing what the status is..
147  *********************************************************************/
148 static int SMTP_check(int socket,char *argbuf)
149 {
150   int  ok;  
151   char buf[SMTPBUFSIZE];
152   
153   if (SMTP_Gets(socket, buf, sizeof(buf))  > 0) {
154     if (argbuf)
155       strcpy(argbuf,buf);
156     if (buf[0] == '1' || buf[0] == '2' || buf[0] == '3')
157       ok = SM_OK;
158     else 
159       ok = SM_ERROR;
160   }
161   else
162     ok= SM_UNRECOVERABLE;
163   return (ok);
164 }
165
166 /*********************************************************************
167   function:      SMTP_ok
168   description:   Returns the statsus of the smtp connection
169                  7/31/96, HSH
170   arguments:     
171     socket       TCP/IP socket for connection to SMTP
172
173   return value:  based on codes in popforward.h.
174                  
175   NOTE:  As of 7/31/96 Initial implementation, we're just returning 
176   a dummy value of SM_OK. Eventually, we should really implement this.
177  *********************************************************************/
178 int SMTP_ok(int socket,char *argbuf)
179 {
180   int  ok;  
181   char buf[SMTPBUFSIZE];
182
183   /* I can tell that the SMTP server connection is ok if I can read a
184      status message that starts with "1xx" ,"2xx" or "3xx".
185      Therefore, it can't be ok if there's no data waiting to be read
186      
187      Tried to deal with this with a call to SockDataWaiting, but 
188      it failed badly.
189
190     */
191
192   ok = SMTP_check(socket,argbuf);
193   if (ok == SM_ERROR) /* if we got an error, */
194     {
195       SMTP_rset(socket);
196       ok = SMTP_check(socket,argbuf);  /* how does it look now ? */
197       if (ok == SM_OK)  
198         ok = SM_ERROR;                /* It's just a simple error, for*/
199                                       /*         the current message  */
200       else
201         ok = SM_UNRECOVERABLE;       /* if It still says error, we're */
202                                      /* in bad shape                  */ 
203     }
204   return ok;
205 }
206
207 /*********************************************************************
208   function:      SMTP_Gets
209   description:   Gets  a line from the SMTP connection
210                  7/31/96, HSH
211   arguments:     
212     socket       TCP/IP socket for connection to SMTP
213
214   return value:  number of bytes read.
215                  
216  *********************************************************************/
217 int SMTP_Gets(int socket,char *buf,int sz)
218 {
219   return read(socket,buf,sz);
220 }
221
222
223 /*********************************************************************
224   function:      POP3_readSMTP
225   description:   Read the message content as described in RFC 1225.
226   arguments:     
227     number       message number.
228     socket       ... to which the server is connected.
229     mboxfd       open file descriptor to which the retrieved message will
230                  be written. 
231     options      added 7/30/96, HSH send in the whole options package...
232     server:      originating pop server.  7/30/96, HSH
233
234   This procedure is the SMTP version of the original POP3_readmsg that 
235   is found in the original popforward.  8/2/96, HSH
236   return value:  zero if success else PS_* return code.
237   calls:         SockGets.
238   globals:       reads outlevel. 
239  *********************************************************************/
240
241 int POP3_readSMTP(int number,int socket,int mboxfd,struct optrec *options,
242                   char *server)
243
244   char buf [MSGBUFSIZE]; 
245   char smtpbuf[SMTPBUFSIZE];
246   char *bufp;
247   char fromBuf[MSGBUFSIZE];
248   char *summaryHeaders[3];
249   int  sumLines =0;
250   int needFrom;
251   int inheaders;
252   int lines,sizeticker;
253   int n;
254   time_t now;
255
256   char *from = NULL;
257   int  replFlag = 0;
258   
259
260   /* HSH 8/19/96, Archive file  */
261
262   int archive = 0;
263
264   int msgnum = 0;
265
266
267   /* This keeps the retrieved message count for display purposes */
268   int ok=0;
269
270   /* set up for status message if outlevel allows it */
271   /* Get this into log file as well. */
272
273
274   ok = POP3_parseHeaders(number,socket,&from,&replFlag);
275   if (ok != 0)
276     {
277       if (from) free(from);
278       return(PS_IOERR);
279     }
280       
281   ok = POP3_sendGet(number,options,socket);
282   if (ok != 0)
283     {
284       if (from) free(from);
285       return(PS_IOERR);
286     }
287
288   /* 8/19/96 HSH open the archive file.. */
289
290   if ((archive =archGetFile(options,&msgnum)) <= 0)
291     {
292      if (from) free(from);
293       return(PS_IOERR);
294     }
295   
296   /* reads the message content from the server */
297   inheaders = 1;
298   lines = 0;
299   sizeticker = MSGBUFSIZE;
300   while (1) {
301     if (SockGets(socket,buf,sizeof(buf)) < 0)
302       {
303         if (from) free(from);
304         return(PS_SOCKET);
305       }
306     bufp = buf;
307     if (buf[0] == '\r' || buf[0] == '\n')
308       inheaders = 0;
309     if (*bufp == '.') {
310       bufp++;
311       if (*bufp == 0)
312         break;  /* end of message */
313     }
314     strcat(bufp,"\n");
315      
316     /* Check for Unix 'From' header, and add a bogus one if it's not
317        present -- only if not using an MDA.
318        XXX -- should probably parse real From: header and use its 
319               address field instead of bogus 'POPmail' string. 
320     */
321     
322
323  
324    if (lines == 0) {
325       if (strlen(bufp) >= strlen("From ")) 
326         needFrom = strncasecmp(bufp,"From ",strlen("From "));
327       else
328         needFrom = 1;
329
330       if ((ok = SMTP_sendMessageHeaders(mboxfd,options,from)) != SM_OK)
331           goto smtperr;
332    
333       if (needFrom) {
334         now = time(NULL);
335         sprintf(fromBuf,"From POPmail %s",ctime(&now));
336         if ((ok =SendData(mboxfd,fromBuf,0)) != SM_OK) 
337           goto smtperr;
338       }
339    }
340
341    n = write(archive,bufp,strlen(bufp));
342
343     /* write this line to the file */
344     if ((ok =SendData(mboxfd,bufp,0)) != SM_OK)
345       {
346         /* Abort the message, so we'll be clear.. */
347         SendData(mboxfd,BINMAIL_TERM,0); 
348         goto smtperr;
349       }
350
351
352     sizeticker -= strlen(bufp);
353     lines++;
354   }
355
356   if ((ok =SendData(mboxfd,BINMAIL_TERM,0)) !=SM_OK) 
357     goto smtperr;
358
359
360   /* finish up display output */
361   
362   if (from) free(from);
363   if (archive != 0) close(archive);
364   return(0);
365
366 smtperr:
367     if (archive != 0) close(archive);
368     SMTP_rset(mboxfd);
369     if (from) free(from);
370     return(ok);
371 }
372
373 /******************************************************************
374   function:     POP3_parseHeaders
375   description:  Read the headers of the mail message, in order to grab the 
376                 "From" and "reply to" fields, to be used for proper
377                 mail processing.
378   arguments:
379       number    message number
380       socket    TCP socket for POP connection
381       from      character pointer to hold value of message "FROM" field
382       replFlag  indicates whether or not we've seen a reply flag.
383
384   ret. value:   non-zero on success, else zero.
385   globals:      SockGets POP3_OK.
386   calls:        reads outlevel.
387  *****************************************************************/
388 int POP3_parseHeaders(number,socket,from,replFlag)
389 int number;
390 int socket;
391 char **from;
392 int  *replFlag;
393 {
394
395   int ok;
396   char buf[MSGBUFSIZE];
397   char *bufp;
398   int  len;
399
400   ok = POP3_sendTOP(number,0,socket);
401   if (ok != 0)
402       return(ok);
403   
404   ok = -1; /* we're not ok until we find "FROM: " */
405   /* read lines in until we're done.. */
406   while (1)
407     {
408
409       if (SockGets(socket,buf,sizeof(buf)) < 0)
410         {
411           return(PS_SOCKET);
412         }
413       bufp = buf;
414
415       if (*bufp == '.') {
416         bufp++;
417         if (*bufp == 0)
418           break;  /* end of message */
419       }
420
421       len = strlen(buf);
422       if (len < strlen(HEADER_FROM)) /* since From header is shorter than reply-to, it */
423         continue;                    /* can't be either type. */
424
425       /* if it starts with "FROM: ", grab from */
426       if (strncasecmp(buf,HEADER_FROM,strlen(HEADER_FROM)) == 0)
427         {
428           bufp = buf + strlen(HEADER_FROM);
429           *from = strdup(bufp); 
430           ok =0;
431         }
432       if (strncasecmp(buf,HEADER_REPLY,strlen(HEADER_REPLY)) == 0)
433           *replFlag = 1;
434     }
435
436   return(ok);
437 }
438
439
440 /******************************************************************
441   function:     SMTP_sendMessageHeaders
442   description:  Send the headers for the smtp message along to the mailbox..
443   arguments:
444       number    message number
445       socket    TCP socket for POP connection
446       from      character pointer to hold value of message "FROM" field
447       replFlag  indicates whether or not we've seen a reply flag.
448
449   ret. value:   non-zero on success, else zero.
450   globals:      SockGets POP3_OK.
451   calls:        reads outlevel.
452  *****************************************************************/
453 int SMTP_sendMessageHeaders(int mboxfd,struct optrec *options,char *from)
454 {
455   char smtpbuf[SMTPBUFSIZE];
456   char fromBuf[MSGBUFSIZE];
457
458   int ok;
459
460   /* 7/30/96, HSH add stuff to print out the SMTP commands. */
461   ok  = SMTP_ok(mboxfd,smtpbuf);
462   if (ok != SM_OK)
463     {
464       return ok;
465     }
466   /* mail is from whoever the headers said it was from */
467   sprintf(fromBuf,"MAIL FROM: %s\r\n",from);
468   if ((ok = SendData(mboxfd,fromBuf,1)) != SM_OK) 
469     return ok;
470   
471 /* Now here, add something for the receipt field.  7/30/96,
472    HSH */
473   sprintf(fromBuf,"RCPT TO: %s@%s\r\n",options->forwarduser,
474           options->forwardhost);
475   if ((ok=SendData(mboxfd,fromBuf,1)) != SM_OK) 
476     return ok;
477
478   sprintf(fromBuf,"DATA\r\n");
479   ok =SendData(mboxfd,fromBuf,1);
480   return ok;
481    
482 }
483
484 /******************************************************************
485   function:     SendData
486   description:  Write to socket or file, as appropriate for destination
487   arguments:
488     f           socket or file descriptor
489     buf         buffer to write
490     dest        options destination.
491     check       1 if we should check for SMTP_ok, 0 if not...
492                 ignored if DEST is not TO_SMTP
493   7/30/96 HSH added
494
495   ret. value:   0 if ok, otherwise, non-zero..
496   globals:      none.
497   calls:        SockWrite
498  *****************************************************************/
499
500 static int SendData(int f,char *buf,int check)
501
502   int res;
503   char smtpbuf[SMTPBUFSIZE];
504   int len = strlen(buf);
505
506   res = SockWrite(f,buf,len);
507   if (check != 0)
508     {
509           res  = SMTP_ok(f,smtpbuf);
510     }
511    return res;
512 }
513
514