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