/* smtpcli.c * Client routines for Simple Mail Transfer Protocol ala RFC821 * A.D. Barksdale Garbee II, aka Bdale, N3EUA * Copyright 1986 Bdale Garbee, All Rights Reserved. * Permission granted for non-commercial copying and use, provided * this notice is retained. */ #include #include "machdep.h" #include "netuser.h" #include "mbuf.h" #include "timer.h" #include "tcp.h" #include "smtp.h" extern int16 lport; /* local port placeholder */ int32 aton(); static void sendit(); static struct timer smtpcli_t; char *index(),*rindex(); /* init routine called when program fired up */ smtpclinit() { int dosmtptick(); smtpcli_t.func = (void (*)())dosmtptick;/* what to call on timeout */ smtpcli_t.arg = 0; /* dummy value */ smtpcli_t.start = SMTPCLITIME; /* set timer duration */ start_timer(&smtpcli_t); /* and fire it up */ } /* this is the routine that gets called every so often to do outgoing mail processing */ int dosmtptick() { char lfilename[LINELEN], tmpstring[LINELEN], wfilename[13], *ptr; FILE *lfile; struct smtp_msg *mp; struct socket lsocket, fsocket; char *calloc(),*malloc(); void smtp_rec(), smtp_cts(), smtp_state(); /* printf("DOSMTPTICK() entered\n"); */ lsocket.address = ip_addr; /* our ip address */ fsocket.port = SMTP_PORT; /* if lock file exists in mqueue dir, return */ sprintf(lfilename,"%s%s",MAILQDIR,"lockfile"); if ((lfile = fopen(lfilename,"x")) == NULL) return; /* get next work filename from mqueue directory */ sprintf(tmpstring,"%s%s",MAILQDIR,"*.wrk"); #ifndef AMIGA filedir(tmpstring,0,wfilename); #endif if (wfilename[0] == '\0') return; /* no work to be done */ /* if we have work, rebuild the exact (non-wild) filename */ mp = (struct smtp_msg *)calloc(1,sizeof (struct smtp_msg)); sprintf(tmpstring,"%s%s",MAILQDIR,wfilename); ptr = &tmpstring[0]; mp->filename = malloc((unsigned)strlen(ptr)+1); strcpy(mp->filename,ptr); /* printf("work file name: %s\n",mp->filename); /* debug only */ mp->wfile = fopen(mp->filename,"r"); /* get ip address, from stuff, to stuff */ fgets(tmpstring,LINELEN,mp->wfile); /* read target ip addr */ /* printf("target ip addr: %s\n",tmpstring); */ fgets(mp->toaddr,LINELEN,mp->wfile); /* who to */ rip(mp->toaddr); /* printf("addressee: %s\n",mp->toaddr); */ fgets(mp->fromaddr,LINELEN,mp->wfile); /* who from */ rip(mp->fromaddr); /* printf("sender: %s\n",mp->fromaddr); */ fclose(mp->wfile); /* set up the rest of the socket info from what we got */ fsocket.address = aton(tmpstring); /* destination ip address */ lsocket.port = lport++; /* next unused port */ /* open smtp connection */ mp->state = CLI_OPEN_STATE; /* init state placeholder */ /* printf("Opening TCP connection for SMTP client\n"); */ mp->tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,1024, smtp_rec,smtp_cts,smtp_state,0,(int *)mp); mp->tcb->user = (int *)mp; /* Upward pointer */ /* printf("releasing lock\n"); */ if (lfile != NULL) { /* release lock */ fclose(lfile); unlink(lfilename); } } /* replace terminating end of line marker(s) with null */ rip(s) char *s; { char *c; c=s; while (*c != '\0') { switch (*c) { case '\r': case '\n': *c='\0'; break; default: c++; break; } } } /* this is the master state machine that handles a single SMTP transaction */ smtp_transaction(mp) struct smtp_msg *mp; { char tmpstring[LINELEN]; /* where we build command lines */ /* printf("SMTP_TRANSACTION() called, state=%u\n",mp->state); */ if (affirmative(mp)) { switch(mp->state) { case CLI_OPEN_STATE: mp->state = CLI_MAIL_STATE; /* issue MAIL command */ /* printf("FROMADDR = %s\n",mp->fromaddr); */ sprintf(tmpstring,"mail from:<%s>\r\n",mp->fromaddr); sendit(mp,tmpstring); break; case CLI_MAIL_STATE: mp->state = CLI_RCPT_STATE; /* issue RCPT command */ sprintf(tmpstring,"rcpt to:<%s>\r\n",mp->toaddr); sendit(mp,tmpstring); break; case CLI_RCPT_STATE: mp->state = CLI_SEND_STATE; /* open text file */ strcpy(tmpstring,mp->filename); strcpy(index(tmpstring,'.'),".txt"); /* printf("text filename: %s",tmpstring); */ mp->tfile = fopen(tmpstring,"r"); /* issue DATA command */ sprintf(tmpstring,"data\r\n"); sendit(mp,tmpstring); break; case CLI_SEND_STATE: /* the transmitter upcall routine will advance the state pointer on end of file, so we do nada... */ break; case CLI_UNLK_STATE: unlink(mp->filename); /* unlink workfile */ /* close and unlink the textfile */ fclose(mp->tfile); strcpy(tmpstring,mp->filename); strcpy(index(tmpstring,'.'),".txt"); unlink(tmpstring); mp->state = CLI_QUIT_STATE; /* issue a quit command */ sprintf(tmpstring,"quit\r\n"); sendit(mp,tmpstring); break; case CLI_QUIT_STATE: /* either start next transaction, or quit */ close_tcp(mp->tcb); /* close up connection */ break; } } else { /* if we get here, means we got a negative reply */ /* for the moment, just let that hose us... */ mp->state = CLI_QUIT_STATE; /* issue a quit command */ sprintf(tmpstring,"quit\r\n"); sendit(mp,tmpstring); } } /* return true if the passed string contains a positive response code */ affirmative(mp) struct smtp_msg *mp; { /* 2 is always good, 3 is ok if we've just sent 'data' command */ if ((*mp->buf = '2') || ((*mp->buf = '3') && (mp->state = CLI_DATA_STATE))) return 1; else return 0; } /* smtp receiver upcall routine. fires up the state machine to parse input */ static void smtp_rec(tcb,cnt) struct tcb *tcb; int16 cnt; { register struct smtp_msg *mp; char *inet_ntoa(), c; struct mbuf *bp; /* may want a void line here for procedures used */ /* printf("SMTP_REC called\n"); */ mp = (struct smtp_msg *)tcb->user; /* point to our struct */ recv_tcp(tcb,&bp,cnt); /* suck up chars from low level routine */ /* Assemble input line in buffer, return if incomplete */ while(pullup(&bp,&c,1) == 1) { switch(c) { case '\r': /* strip cr's */ continue; case '\n': /* line is finished, go do it! */ mp->buf[mp->cnt] = '\0'; smtp_transaction(mp); break; default: /* other chars get added to buffer */ mp->buf[mp->cnt++] = c; break; } } } /* smtp transmitter ready upcall routine. twiddles cts flag */ static void smtp_cts(tcb,cnt) struct tcb *tcb; int16 cnt; { register struct smtp_msg *mp; struct mbuf *bp; char tmpstring[LINELEN]; char *cp; int c; /* printf("SMTP_CTS called\n"); */ mp = (struct smtp_msg *)tcb->user; /* point to our struct */ /* don't do anything until/unless we're supposed to be sending */ if(mp->state != CLI_SEND_STATE) return; if((bp = alloc_mbuf(cnt)) == NULLBUF){ /* Hard to know what to do here */ return; } cp = bp->data; while(cnt > 1 && (c = getc(mp->tfile)) != EOF){ *cp++ = c; bp->cnt++; cnt--; } if(bp->cnt != 0) send_tcp(tcb,bp); else free_p(bp); if(cnt > 1){ /* EOF seen */ sprintf(tmpstring,"\r\n.\r\n"); sendit(mp,tmpstring); mp->state = CLI_UNLK_STATE; } } /* smtp state change upcall routine. cans connection on error */ static void smtp_state(tcb,old,new) struct tcb *tcb; char old,new; { struct smtp_msg *mp; /* printf("SMTP_STATE called, state=%u\n",new); */ mp = (struct smtp_msg *)tcb->user; switch(new) { case ESTABLISHED: mp->state = CLI_OPEN_STATE; /* shouldn't be needed */ break; case CLOSE_WAIT: close_tcp(tcb); /* shut things down */ /* may want to do something here to shut down the rest of the transaction? */ break; case CLOSED: del_tcp(tcb); /* hosed for good */ if(mp->filename != NULLCHAR) free(mp->filename); free((char *)mp); break; } } /* Send message back to server */ static void sendit(mp,message) struct smtp_msg *mp; char *message; { struct mbuf *bp,*qdata(); /* printf("SENDIT called: %s",message); */ bp = qdata(message,(int16)strlen(message)); send_tcp(mp->tcb,bp); }