Back to home page

MITgcm

 
 

    


File indexing completed on 2018-03-02 18:44:55 UTC

view on githubraw file Latest commit ec6cf3b0 on 2003-08-26 20:45:25 UTC
ec6cf3b09d Ed H*0001 /* macnsmtp.c -- simple async SMTP client
                0002  */
                0003 /* (C) Copyright 1995 by Carnegie Mellon University
                0004  * All Rights Reserved.
                0005  *
                0006  * Permission to use, copy, modify, distribute, and sell this software
                0007  * and its documentation for any purpose is hereby granted without
                0008  * fee, provided that the above copyright notice appear in all copies
                0009  * and that both that copyright notice and this permission notice
                0010  * appear in supporting documentation, and that the name of Carnegie
                0011  * Mellon University not be used in advertising or publicity
                0012  * pertaining to distribution of the software without specific,
                0013  * written prior permission.  Carnegie Mellon University makes no
                0014  * representations about the suitability of this software for any
                0015  * purpose.  It is provided "as is" without express or implied
                0016  * warranty.
                0017  *
                0018  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
                0019  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
                0020  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
                0021  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                0022  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
                0023  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
                0024  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
                0025  * SOFTWARE.
                0026  */
                0027 /* (C) Copyright 1994-1995 by Christopher J. Newman
                0028  * All Rights Reserved.
                0029  *
                0030  * Permission to use, copy, modify, distribute, and sell this software and its
                0031  * documentation for any purpose is hereby granted without fee, provided that
                0032  * the above copyright notice appear in all copies and that both that
                0033  * copyright notice and this permission notice appear in supporting
                0034  * documentation, and that the name of Christopher J. Newman not be used in
                0035  * advertising or publicity pertaining to distribution of the software without
                0036  * specific, written prior permission.  Christopher J. Newman makes no
                0037  * representations about the suitability of this software for any purpose.  It
                0038  * is provided "as is" without express or implied warranty.
                0039  *
                0040  * CHRISTOPHER J. NEWMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
                0041  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
                0042  * SHALL CHRISTOPHER J. NEWMAN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
                0043  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
                0044  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
                0045  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
                0046  * OF THIS SOFTWARE.
                0047  *
                0048  * Author:  Christopher J. Newman
                0049  * Message: This is a nifty program.
                0050  */
                0051  
                0052 #include "macnapp.h"
                0053 
                0054 #define SMTP_PORT 25
                0055 
                0056 typedef struct {
                0057     na_win w;
                0058     void *context;      /* user context */
                0059     short num, rcpt;    /* recipient count (num), and sent (rcpt) */
                0060     na_smtpstat statf;  /* callback */
                0061     na_tcp tcpid;       /* TCP id */
                0062     short state;        /* SMTP state (see below) */
                0063     short count;        /* bytes used in linebuf */
                0064     short refnum;       /* input file */
                0065     short crfound:1;    /* found a CR in SMTP server data */
                0066     short crlf:1;       /* found a CRLF in SMTP server data */
                0067     short crtrans:1;    /* translate CR to CRLF in file */
                0068     long headsize;      /* size of extra headers starting at data */
                0069     long fsize, fdone;  /* file size & amount written */
                0070     long bufsize;       /* usable buffer size */
                0071     Ptr buf;            /* output buffer */
                0072     char linebuf[1024]; /* input line buffer */
                0073     char data[1];       /* header & envelope */
                0074 } na_smtpwin;
                0075  
                0076 #define sw ((na_smtpwin *) w)
                0077 
                0078 /* states: */
                0079 #define S_conn  0  /* connecting to server */
                0080 #define S_greet 1  /* waiting for greeting */
                0081 #define S_hello 2  /* waiting for local host lookup and HELO reply */
                0082 #define S_mailf 3  /* waiting for MAIL FROM reply */
                0083 #define S_rcpt  4  /* waiting for RCPT TO reply */
                0084 #define S_data  5  /* waiting for DATA continue reply */
                0085 #define S_send  6  /* transmitting data */
                0086 #define S_done  7  /* waiting for DATA success reply */
                0087 #define S_quit  8  /* waiting for QUIT reply */
                0088 #define S_wait  9  /* waiting for connection close */
                0089 #define S_close 10 /* closed */
                0090 
                0091 /* generate and submit SMTP command line (put command & data together with CRLF ending)
                0092  * returns NATCPwrite result code
                0093  */
                0094 static int SMTPsend(na_win *w, char *com, char *data)
                0095 {
                0096     char buf[512];
                0097     char *dest = buf;
                0098     int result = 0;
                0099     
                0100     while ((*dest = *com) != '\0') ++dest, ++com;
                0101     if (data) {
                0102         while ((*dest = *data++) != '\0') ++dest;
                0103         if (com[-1] == '<') *dest++ = '>';
                0104     }
                0105     *dest++ = '\r';
                0106     *dest++ = '\n';
                0107     result = NATCPwrite(sw->tcpid, buf, dest - buf, -1);
                0108     
                0109     return (result);
                0110 }
                0111 
                0112 /* do final callback
                0113  */
                0114 static void smtpclose(na_win *w, short code, short err, long size, char *data)
                0115 {
                0116     if (sw->state < S_wait) {
                0117         NATCPclose(sw->tcpid);
                0118         FSClose(sw->refnum);
                0119         sw->state = S_wait;
                0120         (*sw->statf)(sw->context, code, err, size, data);
                0121     }
                0122 }
                0123 
                0124 /* TCP read/write callback
                0125  */
                0126 static void readp(void *wh, na_tcp s, short status, long size, char *data)
                0127 {
                0128     na_win *w, **taskh;
                0129     char *dest;
                0130     short major, count, smtpcode;
                0131     
                0132     /* make sure our SMTP task still exists */
                0133     for (taskh = NAtask; taskh && taskh != wh; taskh = (*taskh)->task);
                0134     if (!taskh) return;
                0135     
                0136     /* handle TCP result */
                0137     w = NAlockWindow((na_win **) wh);
                0138     if (status == NATCP_connect) {
                0139         /* deal with new connection */
                0140         sw->state = S_greet;
                0141     } else if (status < 0) {
                0142         /* deal with TCP errors */
                0143         smtpclose(w, NASMTP_tcpfail, status, size, NULL);
                0144         if (status == NATCP_closed) sw->state = S_close;
                0145     } else if (status & NATCP_closing) {
                0146         /* deal with a closed connection */
                0147         if (sw->state < S_wait) {
                0148             smtpclose(w, NASMTP_conclosed, 0, 0, NULL);
                0149         }
                0150     } else if (status & NATCP_data) {
                0151         do {
                0152             /* buffer SMTP line */
                0153             dest = sw->linebuf + sw->count;
                0154             while (size && sw->count < sizeof (sw->linebuf)) {
                0155                 --size, ++sw->count;
                0156                 if (sw->crfound && *data == '\n') {
                0157                     *--dest = '\0';
                0158                     --sw->count;
                0159                     ++data;
                0160                     sw->crfound = 0;
                0161                     sw->crlf = 1;
                0162                     break;
                0163                 }
                0164                 sw->crfound = (*dest++ = *data++) == '\r';
                0165             }
                0166             if (!sw->crlf) {
                0167                 if (sw->count == sizeof (sw->linebuf)) {
                0168                     sw->linebuf[sw->count - 1] = '\0';
                0169                     smtpclose(w, NASMTP_badprot, 0, 0, sw->linebuf);
                0170                 }
                0171                 break;
                0172             }
                0173             sw->crlf = 0;
                0174             /* parse SMTP result code */
                0175             dest = sw->linebuf;
                0176             if (sw->count < 3 || !isdigit(dest[0])
                0177                 || !isdigit(dest[1]) || !isdigit(dest[2])) {
                0178                 smtpclose(w, NASMTP_badprot, 0, 0, dest);
                0179                 break;
                0180             }
                0181             sw->count = 0;
                0182             major = dest[0] - '0';
                0183             smtpcode = major * 100 + (dest[1] - '0') * 10 + (dest[2] - '0');
                0184             /* handle reply continuation */
                0185             if (dest[3] == '-') continue;
                0186             /* handle major errors */
                0187             if (major > 3) {
                0188                 if (sw->state != S_rcpt) {
                0189                     smtpclose(w, major == 4 ? NASMTP_temperr : NASMTP_permerr,
                0190                         smtpcode, sw->state, dest + 3);
                0191                     break;
                0192                 }
                0193                 (*sw->statf)(sw->context, NASMTP_badaddr, smtpcode, 0, sw->linebuf + 3);
                0194             }
                0195             dest = sw->data + sw->headsize;
                0196             /* state changes */
                0197             switch (sw->state) {
                0198                 case S_greet:
                0199                     if (sw->buf) {
                0200                         SMTPsend(w, "HELO ", sw->buf);
                0201                         if (sw->buf) DisposPtr(sw->buf);
                0202                     }
                0203                     sw->state = S_hello;
                0204                     break;
                0205                 case S_hello:
                0206                     SMTPsend(w, "MAIL FROM:<", dest);
                0207                     (*sw->statf)(sw->context, NASMTP_progress, 5, 0, 0);
                0208                     sw->state = S_mailf;
                0209                     break;
                0210                 case S_mailf:
                0211                 case S_rcpt:
                0212                     count = ++sw->rcpt;
                0213                     if (count < sw->num + 1) {
                0214                         while (count--) {
                0215                             while (*dest++);
                0216                         }
                0217                         SMTPsend(w, "RCPT TO:<", dest);
                0218                         (*sw->statf)(sw->context, NASMTP_progress, 5 + 10 * sw->rcpt / sw->num, 0, 0);
                0219                     } else {
                0220                         sw->state = S_data;
                0221                         SMTPsend(w, "DATA", 0);
                0222                         (*sw->statf)(sw->context, NASMTP_progress, 20, 0, 0);
                0223                     }
                0224                     break;
                0225                 case S_data:
                0226                     if (major != 3) {
                0227                         smtpclose(w, NASMTP_badprot, 0, 0, dest);
                0228                         break;
                0229                     }
                0230                     sw->state = S_send;
                0231                     if (sw->headsize) {
                0232                         sw->buf = NewPtr(sw->bufsize = sw->headsize);
                0233                         if (!sw->buf) {
                0234                             smtpclose(w, NASMTP_nomem, 0, 0, NULL);
                0235                             break;
                0236                         }
                0237                         memcpy(sw->buf, sw->data, sw->headsize);
                0238                     }
                0239                 case S_send:
                0240                     /* NOTE: this case should never happen */
                0241                     break;
                0242                 case S_done:
                0243                     sw->state = S_quit;
                0244                     SMTPsend(w, "QUIT", NULL);
                0245                     (*sw->statf)(sw->context, NASMTP_progress, 95, 0, 0);
                0246                     break;
                0247                 case S_quit:
                0248                     smtpclose(w, NASMTP_completed, 0, 0, 0);
                0249                     break;
                0250             }
                0251         } while (size);
                0252     }
                0253     NAunlockWindowh((na_win **) wh, w)
                0254 }
                0255 
                0256 /* TCP gethost callback
                0257  */
                0258 static void hostp(void *wh, na_tcp s, short status, long size, char *data)
                0259 {
                0260     na_win *w, **taskh;
                0261     
                0262     /* make sure our task still exists */
                0263     for (taskh = NAtask; taskh && taskh != wh; taskh = (*taskh)->task);
                0264     if (!taskh) return;
                0265     
                0266     /* store host/error */
                0267     w = NAlockWindow((na_win **) wh);
                0268     if (status == NATCP_connect) {
                0269         if (sw->state == S_hello) {
                0270             SMTPsend(w, "HELO ", data);
                0271         } else {
                0272             sw->buf = NewPtr(size + 1);
                0273             if (sw->buf == NULL) {
                0274                 smtpclose(w, NASMTP_nomem, 0, 0, NULL);
                0275             } else {
                0276                 memcpy(sw->buf, data, size + 1);
                0277             }
                0278         }
                0279     } else {
                0280         smtpclose(w, NASMTP_tcpfail, status, size, NULL);
                0281     }
                0282     NAunlockWindowh((na_win **) wh, w);
                0283 }
                0284 
                0285 /* translate CR to CRLF
                0286  */
                0287 static void crtocrlf(char *buf, long *size)
                0288 {
                0289     long crcount = 0;
                0290     char *src, *dst, *end = buf + *size;
                0291     
                0292     for (src = buf; src < end; ++src) {
                0293         if (src[0] == '\r') ++crcount;
                0294     }
                0295     src = end - 1;
                0296     for (dst = src + crcount; dst > src; *dst-- = *src--) {
                0297         if (*src == '\r') *dst-- = '\n';
                0298     }
                0299     *size += crcount;
                0300 }
                0301 
                0302 /* SMTP task
                0303  */
                0304 static short taskp(na_win *w)
                0305 {
                0306     OSErr oserr;
                0307     
                0308     if (sw->state == S_send || sw->state == S_done) {
                0309         /*XXX: could be generous with NewPtr() failure if a NATCPwritePending() */
                0310         if (!sw->bufsize) {
                0311             if ((sw->buf = NewPtr(8192)) == NULL) {
                0312                 smtpclose(w, NASMTP_nomem, 0, 0, NULL);
                0313                 return (NA_NOTPROCESSED);
                0314             }
                0315             sw->bufsize = sw->crtrans ? 4096 : 8192;
                0316             oserr = FSRead(sw->refnum, &sw->bufsize, sw->buf);
                0317             if (oserr != noErr && oserr != eofErr) sw->bufsize = 0;
                0318             if (!sw->bufsize) {
                0319                 if (oserr == eofErr && sw->state == S_send) {
                0320                     memcpy(sw->buf, "\r\n.\r\n", 5);
                0321                     sw->bufsize = 5;
                0322                     sw->state = S_done;
                0323                 } else {
                0324                     DisposPtr(sw->buf);
                0325                 }
                0326             } else {
                0327                 if (sw->crtrans) {
                0328                     crtocrlf(sw->buf, &sw->bufsize);
                0329                 }
                0330                 (*sw->statf)(sw->context, NASMTP_progress, 20
                0331                     + 70 * (sw->fdone += sw->bufsize) / sw->fsize, 0, 0);
                0332             }
                0333         }
                0334         if (sw->bufsize && NATCPwrite(sw->tcpid, sw->buf, sw->bufsize, 1) == NATCP_data) {
                0335             sw->bufsize = 0;
                0336         }
                0337     }
                0338     
                0339     return (sw->state == S_close ? NA_REQCLOSE : NA_NOTPROCESSED);
                0340 }
                0341 
                0342 /* SMTP close procedure
                0343  *  IMPORTANT: if the user quits during mail transmission, we want to
                0344  *  warn the user that mail will be lost.
                0345  */
                0346 static short closep(na_win *w)
                0347 {
                0348     if (sw->state < S_wait) {
                0349         /*XXX: put modal dialog here, allow abort of close */
                0350         if (sw->tcpid >= 0) NATCPclose(sw->tcpid);
                0351         FSClose(sw->refnum);
                0352     }
                0353     
                0354     return (NA_CLOSED);
                0355 }
                0356  
                0357 /* submit file to SMTP:
                0358  *  creates SMTP task, initializes data, starts TCP connection
                0359  *  copies from & dest addresses, so they don't need to persist
                0360  *  task is not completed until statf() is called
                0361  */
                0362 void NASMTPsubmit(na_smtpstat statf, char *server, FSSpec *fspec, Handle headers,
                0363                 Handle envelope, short flags, void *context)
                0364 {
                0365     long size;
                0366     na_win **wh, *w;
                0367     char *src, *dst;
                0368     OSErr oserr;
                0369 
                0370     /* create task */
                0371     size = sizeof (na_smtpwin);
                0372     if (headers) size += GetHandleSize(headers);
                0373     size += GetHandleSize(envelope);
                0374     wh = NAaddtask(taskp, size);
                0375     if (!wh) {
                0376         (*statf)(context, NASMTP_nomem, 0, 0, 0);
                0377         return;
                0378     }
                0379     
                0380     /* init task */
                0381     w = NAlockWindow(wh);
                0382     w->type = NA_SMTPTYPE;
                0383     w->closep = closep;
                0384     sw->context = context;
                0385     sw->statf = statf;
                0386     if (headers && (sw->headsize = GetHandleSize(headers))) {
                0387         memcpy(sw->data, (char *) *headers, sw->headsize);
                0388     }
                0389     size = GetHandleSize(envelope);
                0390     sw->num = -1;
                0391     dst = sw->data + sw->headsize;
                0392     for (src = (char *) *envelope; size; --size) {
                0393         if ((*dst++ = *src++) == '\0') ++sw->num;
                0394     }
                0395     if (flags & NASMTP_crtrans) sw->crtrans = 1;
                0396         
                0397     /* open file */
                0398     if ((oserr = HOpen(fspec->vRefNum, fspec->parID, fspec->name,
                0399         fsRdPerm, &sw->refnum)) != noErr) {
                0400         (*statf)(context, NASMTP_oserr, 0, oserr, 0);
                0401         NAcloseWindow(w, NA_CLOSED);
                0402         return;
                0403     }
                0404     GetEOF(sw->refnum, &sw->fsize);
                0405     
                0406     /* open MacTCP */
                0407     sw->tcpid = NATCPopen(readp, (void *) wh, server, SMTP_PORT, 0);
                0408     if (sw->tcpid != -1) NATCPgethost(hostp, (void *) wh);
                0409     NAunlockWindowh(wh, w);
                0410 }