home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / network / src_1218.zip / POPSERV.C < prev    next >
C/C++ Source or Header  |  1991-07-14  |  15KB  |  723 lines

  1. /* POP Server state machine - see RFC 937
  2.  *
  3.  *  also see other credits in popcli.c
  4.  *  10/89 Mike Stockett wa7dyx
  5.  *  Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
  6.  *  Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <time.h>
  11. #include <sys/stat.h>
  12. #ifdef UNIX
  13. #include <sys/types.h>
  14. #endif
  15. #if    defined(__STDC__) || defined(__TURBOC__)
  16. #include <stdarg.h>
  17. #endif
  18. #include <ctype.h>
  19. #include <setjmp.h>
  20. #include "global.h"
  21. #include "mbuf.h"
  22. #include "cmdparse.h"
  23. #include "socket.h"
  24. #include "proc.h"
  25. #include "files.h"
  26. #include "pop.h"
  27.  
  28. extern char Nospace[];
  29.  
  30. static struct pop_scb *create_scb __ARGS((void));
  31. static void delete_scb __ARGS((struct pop_scb *scb));
  32. static void popserv __ARGS((int s,void *unused,void *p));
  33. static int poplogin __ARGS((char *pass,char *username));
  34.  
  35. /* I don't know why this isn't static, it isn't called anywhere else {was} */
  36. void pop_sm __ARGS((struct pop_scb *scb));
  37.  
  38. static int Spop = -1; /* prototype socket for service */
  39.  
  40. /* Start up POP receiver service */
  41. int
  42. pop1(argc,argv,p)
  43.  
  44. int argc;
  45. char *argv[];
  46. void *p;
  47.  
  48. {
  49.     struct sockaddr_in lsocket;
  50.     int s;
  51.  
  52.     if (Spop != -1) {
  53.         return 0;
  54.     }
  55.  
  56.     psignal(Curproc,0);        /* Don't keep the parser waiting */
  57.     chname(Curproc,"POP listener");
  58.  
  59.     lsocket.sin_family = AF_INET;
  60.     lsocket.sin_addr.s_addr = INADDR_ANY;
  61.     if(argc < 2)
  62.         lsocket.sin_port = IPPORT_POP;
  63.     else
  64.         lsocket.sin_port = atoi(argv[1]);
  65.  
  66.     Spop = socket(AF_INET,SOCK_STREAM,0);
  67.  
  68.     bind(Spop,(char *)&lsocket,sizeof(lsocket));
  69.  
  70.     listen(Spop,1);
  71.  
  72.     for (;;) {
  73.         if((s = accept(Spop,NULLCHAR,(int *)NULL)) == -1)
  74.             break;    /* Service is shutting down */
  75.  
  76.         /* Spawn a server */
  77.  
  78.         newproc("POP server",2048,popserv,s,NULL,NULL,0);
  79.     }
  80.     return 0;
  81. }
  82.  
  83. /* Shutdown POP service (existing connections are allowed to finish) */
  84.  
  85. int
  86. pop0(argc,argv,p)
  87. int argc;
  88. char *argv[];
  89. void *p;
  90.  
  91. {
  92.     close_s(Spop);
  93.     Spop = -1;
  94.     return 0;
  95. }
  96.  
  97. static void
  98. popserv(s,unused,p)
  99. int s;
  100. void *unused;
  101. void *p;
  102. {
  103.     struct pop_scb *scb;
  104.  
  105.     sockowner(s,Curproc);        /* We own it now */
  106.     log(s,"open POP");
  107.  
  108.     if((scb = create_scb()) == NULLSCB) {
  109.         tprintf(Nospace);
  110.         log(s,"close POP - no space");
  111.         close_s(s);
  112.         return;
  113.     }
  114.  
  115.     scb->socket = s;
  116.     scb->state  = AUTH;
  117.  
  118.     (void) usprintf(s,greeting_msg,Hostname);
  119.  
  120. loop:    if ((scb->count = recvline(s,scb->buf,BUF_LEN)) == -1){
  121.         /* He closed on us */
  122.  
  123.         goto quit;
  124.     }
  125.  
  126.     rip(scb->buf);
  127.     if (strlen(scb->buf) == 0)        /* Ignore blank cmd lines */
  128.         goto loop;
  129.     pop_sm(scb);
  130.     if (scb->state == DONE)
  131.         goto quit;
  132.  
  133.     goto loop;
  134.  
  135. quit:
  136.     log(scb->socket,"close POP");
  137.     close_s(scb->socket);
  138.     delete_scb(scb);
  139. }
  140.  
  141.  
  142. /* Create control block, initialize */
  143.  
  144. static struct
  145. pop_scb *create_scb()
  146. {
  147.     register struct pop_scb *scb;
  148.  
  149.     if((scb = (struct pop_scb *)callocw(1,sizeof (struct pop_scb))) == NULLSCB)
  150.         return NULLSCB;
  151.  
  152.     scb->username[0] = '\0';
  153.     scb->msg_status = NULL;
  154.     scb->wf = NULL;
  155.  
  156.     scb->count = scb->folder_file_size = scb->msg_num = 0;
  157.  
  158.     scb->folder_modified = FALSE;
  159.     return scb;
  160. }
  161.  
  162.  
  163. /* Free resources, delete control block */
  164.  
  165. static void
  166. delete_scb(scb)
  167. register struct pop_scb *scb;
  168. {
  169.  
  170.     if (scb == NULLSCB)
  171.         return;
  172.     if (scb->wf != NULL)
  173.         fclose(scb->wf);
  174.     if (scb->msg_status  != NULL)
  175.         free((char *)scb->msg_status);
  176.  
  177.     free((char *)scb);
  178. }
  179.  
  180. /* replace terminating end of line marker(s) (\r and \n) with null */
  181. void
  182. rrip(s)
  183. register char *s;
  184. {
  185.     register char *cp;
  186.  
  187.     if((cp = strchr(s,'\r')) != NULLCHAR)
  188.         *cp = '\0';
  189.     if((cp = strchr(s,'\n')) != NULLCHAR)
  190.         *cp = '\0';
  191. }
  192.  
  193. /* --------------------- start of POP server code ------------------------ */
  194.  
  195. #define    BITS_PER_WORD        16
  196.  
  197. #define isSOM(x)        ((strncmp(x,"From ",5) == 0))
  198.  
  199. /* Command string specifications */
  200.  
  201. static char    ackd_cmd[] = "ACKD",
  202.         acks_cmd[] = "ACKS",
  203. #ifdef POP_FOLDERS
  204.         fold_cmd[] = "FOLD ",
  205. #endif
  206.         login_cmd[] = "HELO ",
  207.         nack_cmd[] = "NACK",
  208.         quit_cmd[] = "QUIT",
  209.         read_cmd[] = "READ",
  210.         retr_cmd[] = "RETR";
  211.  
  212. void
  213. pop_sm(scb)
  214. struct pop_scb *scb;
  215. {
  216.     char password[40];
  217.     void state_error(struct pop_scb *,char *);
  218.     void open_folder(struct pop_scb *);
  219.     void do_cleanup(struct pop_scb *);
  220.     void read_message(struct pop_scb *);
  221.     void retrieve_message(struct pop_scb *);
  222.     void deletemsg(struct pop_scb *,int);
  223.     void get_message(struct pop_scb *,int);
  224.     void print_message_length(struct pop_scb *);
  225.     void close_folder(struct pop_scb *);
  226. #ifdef POP_FOLDERS
  227.     void select_folder(struct pop_scb *);
  228. #endif
  229.  
  230.     if (scb == NULLSCB)    /* be certain it is good -- wa6smn */
  231.         return;
  232.  
  233.     switch(scb->state) {
  234.     case AUTH:
  235.         if (strncmp(scb->buf,login_cmd,strlen(login_cmd)) == 0){
  236.             sscanf(scb->buf,"HELO %s%s",scb->username,password);
  237.  
  238.             if (!poplogin(scb->username,password)) {
  239.                 log(scb->socket,"POP access DENIED to %s",
  240.                         scb->username);
  241.                 state_error(scb,"Access DENIED!!");
  242.                 return;
  243.             }
  244.  
  245.             log(scb->socket,"POP access granted to %s",
  246.                     scb->username);
  247.             open_folder(scb);
  248.         } else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
  249.             do_cleanup(scb);
  250.         } else
  251.             state_error(scb,"(AUTH) Expected HELO or QUIT command");
  252.         break;
  253.  
  254.     case MBOX:
  255.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  256.             read_message(scb);
  257.  
  258. #ifdef POP_FOLDERS
  259.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  260.             select_folder(scb);
  261.  
  262. #endif
  263.  
  264.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
  265.             do_cleanup(scb);
  266.         } else
  267.             state_error(scb,
  268. #ifdef POP_FOLDERS
  269.                     "(MBOX) Expected FOLD, READ, or QUIT command");
  270. #else
  271.                     "(MBOX) Expected READ or QUIT command");
  272. #endif
  273.         break;
  274.  
  275.     case ITEM:
  276.         if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
  277.             read_message(scb);
  278.  
  279. #ifdef POP_FOLDERS
  280.  
  281.         else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
  282.             select_folder(scb);
  283. #endif
  284.  
  285.         else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
  286.             retrieve_message(scb);
  287.         else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
  288.             do_cleanup(scb);
  289.         else
  290.             state_error(scb,
  291. #ifdef POP_FOLDERS
  292.                "(ITEM) Expected FOLD, READ, RETR, or QUIT command");
  293. #else
  294.                "(ITEM) Expected READ, RETR, or QUIT command");
  295. #endif
  296.         break;
  297.  
  298.     case NEXT:
  299.         if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
  300.                 /* ACKD processing */
  301.             deletemsg(scb,scb->msg_num);
  302.             scb->msg_num++;
  303.             get_message(scb,scb->msg_num);
  304.         } else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
  305.                 /* ACKS processing */
  306.             scb->msg_num++;
  307.             get_message(scb,scb->msg_num);
  308.         } else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
  309.                 /* NACK processing */
  310.             fseek(scb->wf,scb->curpos,SEEK_SET);
  311.         } else {
  312.             state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
  313.             return;
  314.         }
  315.  
  316.         print_message_length(scb);
  317.         scb->state  = ITEM;
  318.         break;
  319.  
  320.     case DONE:
  321.         do_cleanup(scb);
  322.         break;
  323.  
  324.     default:
  325.         state_error(scb,"(TOP) State Error!!");
  326.         break;
  327.     }
  328. }
  329.  
  330. void
  331. do_cleanup(scb)
  332. struct pop_scb *scb;
  333. {
  334.     void close_folder(struct pop_scb *);
  335.  
  336.     close_folder(scb);
  337.     (void) usprintf(scb->socket,signoff_msg);
  338.     scb->state = DONE;
  339. }
  340.  
  341. void
  342. state_error(scb,msg)
  343. struct pop_scb *scb;
  344. char *msg;
  345. {
  346.     (void) usprintf(scb->socket,error_rsp,msg);
  347.     scb->state = DONE;
  348. }
  349.  
  350. #ifdef POP_FOLDERS
  351.  
  352. select_folder(scb)
  353. struct pop_scb    *scb;
  354. {
  355.     sscanf(scb->buf,"FOLD %s",scb->username);
  356.  
  357.     if (scb->wf != NULL)
  358.         close_folder(scb);
  359.  
  360.     open_folder(scb);
  361. }
  362.  
  363. #endif
  364.  
  365.  
  366. void
  367. close_folder(scb)
  368. struct pop_scb *scb;
  369. {
  370.     char folder_pathname[64];
  371.     char line[BUF_LEN];
  372.     FILE *fd;
  373.     int deleted = FALSE;
  374.     int msg_no = 0;
  375.     struct stat folder_stat;
  376.     int newmail(struct pop_scb *);
  377.     int isdeleted(struct pop_scb *,int);
  378.  
  379.     if (scb->wf == NULL)
  380.         return;
  381.  
  382.     if (!scb->folder_modified) {
  383.         /* no need to re-write the folder if we have not modified it */
  384.  
  385.         fclose(scb->wf);
  386.         scb->wf = NULL;
  387.  
  388.         free((char *)scb->msg_status);
  389.         scb->msg_status = NULL;
  390.         return;
  391.     }
  392.  
  393.  
  394.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  395.  
  396.     if (newmail(scb)) {
  397.         /* copy new mail into the work file and save the
  398.            message count for later */
  399.  
  400.         if ((fd = fopen(folder_pathname,"r")) == NULL) {
  401.             state_error(scb,"Unable to add new mail to folder");
  402.             return;
  403.         }
  404.  
  405.         fseek(scb->wf,0,SEEK_END);
  406.         fseek(fd,scb->folder_file_size,SEEK_SET);
  407.         while (!feof(fd)) {
  408.             fgets(line,BUF_LEN,fd);
  409.             fputs(line,scb->wf);
  410.         }
  411.  
  412.         fclose(fd);
  413.     }
  414.  
  415.     /* now create the updated mail folder */
  416.  
  417.     if ((fd = fopen(folder_pathname,"w")) == NULL){
  418.         state_error(scb,"Unable to update mail folder");
  419.         return;
  420.     }
  421.  
  422.     rewind(scb->wf);
  423.     while (!feof(scb->wf)){
  424.         fgets(line,BUF_LEN,scb->wf);
  425.  
  426.         if (isSOM(line)){
  427.             msg_no++;
  428.             if (msg_no <= scb->folder_len)
  429.                 deleted = isdeleted(scb,msg_no);
  430.             else
  431.                 deleted = FALSE;
  432.         }
  433.  
  434.         if (deleted)
  435.             continue;
  436.  
  437.         fputs(line,fd);
  438.     }
  439.  
  440.     fclose(fd);
  441.  
  442.     /* trash the updated mail folder if it is empty */
  443.  
  444.     if ((stat(folder_pathname,&folder_stat) == 0) && (folder_stat.st_size == 0))
  445.         unlink(folder_pathname);
  446.  
  447.     fclose(scb->wf);
  448.     scb->wf = NULL;
  449.  
  450.     free((char *)scb->msg_status);
  451.     scb->msg_status = NULL;
  452. }
  453.  
  454. void
  455. open_folder(scb)
  456. struct pop_scb    *scb;
  457. {
  458.     char folder_pathname[64];
  459.     char line[BUF_LEN];
  460.     FILE *fd;
  461.     FILE *tmpfile();
  462.     struct stat folder_stat;
  463.  
  464.  
  465.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  466.     scb->folder_len       = 0;
  467.     scb->folder_file_size = 0;
  468.     if (stat(folder_pathname,&folder_stat)){
  469.          (void) usprintf(scb->socket,no_mail_rsp);
  470.          return;
  471.     }
  472.  
  473.     scb->folder_file_size = folder_stat.st_size;
  474.     if ((fd = fopen(folder_pathname,"r")) == NULL){
  475.         state_error(scb,"Unable to open mail folder");
  476.         return;
  477.     }
  478.  
  479.     if ((scb->wf = tmpfile()) == NULL) {
  480.         state_error(scb,"Unable to create work folder");
  481.         return;
  482.     }
  483.  
  484.     while(!feof(fd)) {
  485.         fgets(line,BUF_LEN,fd);
  486.  
  487.         /* scan for begining of a message */
  488.  
  489.         if (isSOM(line))
  490.             scb->folder_len++;
  491.  
  492.         /* now put  the line in the work file */
  493.  
  494.         fputs(line,scb->wf);
  495.     }
  496.  
  497.     fclose(fd);
  498.  
  499.     scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
  500.  
  501.     if ((((scb->folder_len) % BITS_PER_WORD) != 0) ||
  502.         (scb->msg_status_size == 0))
  503.         scb->msg_status_size++;
  504.  
  505.     if ((scb->msg_status = (unsigned int *) callocw(scb->msg_status_size,
  506.                 sizeof(unsigned int))) == NULL) {
  507.         state_error(scb,"Unable to create message status array");
  508.         return;
  509.     }
  510.  
  511.     (void) usprintf(scb->socket,count_rsp,scb->folder_len);
  512.  
  513.     scb->state  = MBOX;
  514. }
  515.  
  516. void
  517. read_message(scb)
  518. struct pop_scb    *scb;
  519. {
  520.     void get_message(struct pop_scb *,int);
  521.     void print_message_length(struct pop_scb *);
  522.  
  523.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  524.         return;
  525.     if (scb->buf[sizeof(read_cmd) - 1] == ' ')
  526.         scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
  527.     else
  528.         scb->msg_num++;
  529.  
  530.     get_message(scb,scb->msg_num);
  531.     print_message_length(scb);
  532.     scb->state  = ITEM;
  533. }
  534.  
  535. void
  536. retrieve_message(scb)
  537. struct pop_scb    *scb;
  538. {
  539.     char line[BUF_LEN];
  540.     long cnt;
  541.     void rrip(char *);
  542.  
  543.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  544.         return;
  545.     if (scb->msg_len == 0) {
  546.         state_error(scb,"Attempt to access a DELETED message!");
  547.         return;
  548.     }
  549.  
  550.     cnt  = scb->msg_len;
  551.     while(!feof(scb->wf) && (cnt > 0)) {
  552.         fgets(line,BUF_LEN,scb->wf);
  553.         rrip(line);
  554.  
  555.         (void) usprintf(scb->socket,msg_line,line);
  556.         cnt -= (strlen(line)+2);    /* Compensate for CRLF */
  557.     }
  558.  
  559.     scb->state = NEXT;
  560. }
  561.  
  562. void
  563. get_message(scb,msg_no)
  564. struct pop_scb    *scb;
  565. int msg_no;
  566. {
  567.     char line[BUF_LEN];
  568.     long ftell();
  569.     void rrip(char *);
  570.  
  571.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  572.         return;
  573.     scb->msg_len = 0;
  574.     if (msg_no > scb->folder_len) {
  575.         scb->curpos  = 0;
  576.         scb->nextpos = 0;
  577.         return;
  578.     } else {
  579.         /* find the message and its length */
  580.  
  581.         rewind(scb->wf);
  582.         while (!feof(scb->wf) && (msg_no > -1)) {
  583.             if (msg_no > 0)
  584.                 scb->curpos = ftell(scb->wf);
  585.             
  586.             fgets(line,BUF_LEN,scb->wf);
  587.             rrip(line);
  588.  
  589.             if (isSOM(line))
  590.                 msg_no--;
  591.  
  592.             if (msg_no != 0)
  593.                 continue;
  594.  
  595.             scb->nextpos  = ftell(scb->wf);
  596.             scb->msg_len += (strlen(line)+2);    /* Add CRLF */
  597.         }
  598.     }
  599.  
  600.     if (scb->msg_len > 0)
  601.         fseek(scb->wf,scb->curpos,SEEK_SET);
  602.  
  603.     /* we need the pointers even if the message was deleted */
  604.  
  605.     if  (isdeleted(scb,scb->msg_num))
  606.         scb->msg_len = 0;
  607. }
  608.  
  609. static int
  610. poplogin(username,pass)
  611. char *pass;
  612. char *username;
  613. {
  614.     char buf[80];
  615.     char *cp;
  616.     char *cp1;
  617.     FILE *fp;
  618.  
  619.     if((fp = fopen(Popusers,"r")) == NULLFILE) {
  620.         /* User file doesn't exist */
  621.         tprintf("POP users file %s not found\n",Popusers);
  622.         return(FALSE);
  623.     }
  624.  
  625.     while(fgets(buf,sizeof(buf),fp),!feof(fp)) {
  626.         if(buf[0] == '#')
  627.             continue;    /* Comment */
  628.  
  629.         if((cp = strchr(buf,':')) == NULLCHAR)
  630.             /* Bogus entry */
  631.             continue;
  632.  
  633.         *cp++ = '\0';        /* Now points to password */
  634.         if(strcmp(username,buf) == 0)
  635.             break;        /* Found user name */
  636.     }
  637.  
  638.     if(feof(fp)) {
  639.         /* User name not found in file */
  640.  
  641.         fclose(fp);
  642.         return(FALSE);
  643.     }
  644.     fclose(fp);
  645.  
  646.     if ((cp1 = strchr(cp,':')) == NULLCHAR)
  647.         return(FALSE);
  648.  
  649.     *cp1 = '\0';
  650.     if(strcmp(cp,pass) != 0) {
  651.         /* Password required, but wrong one given */
  652.  
  653.         return(FALSE);
  654.     }
  655.  
  656.     /* whew! finally made it!! */
  657.  
  658.     return(TRUE);
  659. }
  660.  
  661. int
  662. isdeleted(scb,msg_no)
  663. struct pop_scb *scb;
  664. int msg_no;
  665. {
  666.     unsigned int mask = 1,offset;
  667.  
  668.     msg_no--;
  669.     offset = msg_no / BITS_PER_WORD;
  670.     mask <<= msg_no % BITS_PER_WORD;
  671.     return (((scb->msg_status[offset]) & mask)? TRUE:FALSE);
  672. }
  673.  
  674. void
  675. deletemsg(scb,msg_no)
  676. struct pop_scb *scb;
  677. int msg_no;
  678. {
  679.     unsigned int mask = 1,offset;
  680.  
  681.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  682.         return;
  683.     msg_no--;
  684.     offset = msg_no / BITS_PER_WORD;
  685.     mask <<= msg_no % BITS_PER_WORD;
  686.     scb->msg_status[offset] |= mask;
  687.     scb->folder_modified = TRUE;
  688. }
  689.  
  690. int
  691. newmail(scb)
  692. struct pop_scb *scb;
  693. {
  694.     char folder_pathname[64];
  695.     struct stat folder_stat;
  696.  
  697.     sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
  698.  
  699.     if (stat(folder_pathname,&folder_stat)) {
  700.         state_error(scb,"Unable to get old mail folder's status");
  701.         return(FALSE);
  702.     } else
  703.         return ((folder_stat.st_size > scb->folder_file_size)? TRUE:FALSE);
  704. }
  705.  
  706. void
  707. print_message_length(scb)
  708. struct pop_scb *scb;
  709. {
  710.     char *print_control_string;
  711.  
  712.     if (scb == NULLSCB)    /* check for null -- wa6smn */
  713.         return;
  714.     if (scb->msg_len > 0)
  715.         print_control_string = length_rsp;
  716.     else if (scb->msg_num <= scb->folder_len)
  717.         print_control_string = length_rsp;
  718.     else
  719.         print_control_string = no_more_rsp;
  720.  
  721.     (void)usprintf(scb->socket,print_control_string,scb->msg_len,scb->msg_num);
  722. }
  723.