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