w3mail

program to send a web page by email
git clone https://logand.com/git/w3mail.git/
Log | Files | Refs | README | LICENSE

dirpop3d.c (5428B)


      1 /*
      2 
      3   dirpop3d -- pop3 server for w3mail
      4 
      5   Copyright (C) 2011 Tomas Hlavaty
      6 
      7   This program is free software: you can redistribute it and/or modify
      8   it under the terms of the GNU General Public License as published by
      9   the Free Software Foundation, either version 3 of the License, or
     10   (at your option) any later version.
     11 
     12   This program is distributed in the hope that it will be useful, but
     13   WITHOUT ANY WARRANTY; without even the implied warranty of
     14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15   General Public License for more details.
     16 
     17   You should have received a copy of the GNU General Public License
     18   along with this program.  If not, see
     19   <http://www.gnu.org/licenses/>.
     20 
     21 */
     22 
     23 #include <stdlib.h>
     24 #include <string.h>
     25 #include <stdarg.h>
     26 #include <unistd.h>
     27 
     28 #include "utils.h"
     29 
     30 struct msg {
     31   int deleted;
     32   int nbytes;
     33   char *fname;
     34 };
     35 
     36 struct flist {
     37   int len;
     38   int n;
     39   int nbytes;
     40   struct msg msg[];
     41 };
     42 
     43 static int sizeof_flist(int len) {
     44   return 3 * sizeof(int) + sizeof(struct msg) * len;
     45 }
     46 
     47 static struct flist *flist_new(int len) {
     48   struct flist *x = malloc(sizeof_flist(len));
     49   x->len = len;
     50   x->n = 0;
     51   x->nbytes = 0;
     52   return x;
     53 }
     54 
     55 static void flist_free(struct flist *x) {
     56   for(int i = 0; i < x->n; i++)
     57     free(x->msg[i].fname);
     58   free(x);
     59 }
     60 
     61 static struct flist *flist_realloc(struct flist *x, int len) {
     62   if(x->len < len) {
     63     struct flist *y = realloc(x, sizeof_flist(len));
     64     y->len = len;
     65     return y;
     66   }
     67   return x;
     68 }
     69 
     70 static struct flist *flist_add(struct flist *x, char *fname, int nbytes) {
     71   if(x->len <= x->n)
     72     x = flist_realloc(x, x->len + 100);
     73   x->nbytes += nbytes;
     74   struct msg *y = &x->msg[x->n++];
     75   y->deleted = 0;
     76   y->nbytes = nbytes;
     77   y->fname = malloc(strlen(fname) + 1);
     78   strcpy(y->fname, fname);
     79   return x;
     80 }
     81 
     82 struct xlist {
     83   struct flist *flist;
     84 };
     85 
     86 static int list_cb(void *udata, char *path, char *fname, struct dirent *e, struct stat *s) {
     87   struct xlist *x = udata;
     88   if(S_ISREG(s->st_mode))
     89     x->flist = flist_add(x->flist, fname, s->st_size);
     90   return 1;
     91 }
     92 
     93 static int line(int fd, char *buf, char *end) {
     94   int n = 0;
     95   while(buf < end) {
     96     char c;
     97     int m = read(fd, &c, sizeof(char));
     98     if(m <= 0) break;
     99     if(m != sizeof(char)) exit(-1);
    100     *buf++ = c;
    101     n++;
    102     if('\n' == c) break;
    103   }
    104   return n;
    105 }
    106 
    107 static inline void crlf() {
    108   printf("\r\n");
    109   fflush(stdout);
    110 }
    111 
    112 static void pr(char *fmt, ...) {
    113   va_list v;
    114   va_start(v, fmt);
    115   vprintf(fmt, v);
    116   crlf();
    117   va_end(v);
    118 }
    119 
    120 int main(int argc, char *argv[]) {
    121   if(argc != 2) quit(1, "Usage: %s rootdir", argv[0]);
    122   char *rootdir = argv[1];
    123   pr("+OK dirpop3d ready");
    124   enum {START, USER, PASS, STAT, LIST} state = START;
    125   char cmd[BLEN], usr[BLEN], pwd[BLEN];
    126   struct flist *flist = NULL;
    127   char buf[BLEN];
    128   for(int n; 0 < (n = line(0, buf, buf + BLEN));) {
    129     rtrim(buf);
    130     sscanf(buf, "%s", cmd);
    131     if(0 == strcmp("QUIT", cmd)) {
    132       if(flist) {
    133         for(int i = 0; i < flist->n; i++) {
    134           struct msg *x = &flist->msg[i];
    135           if(x->deleted) unlink(x->fname);
    136         }
    137         flist_free(flist);
    138         flist = NULL;
    139       }
    140       pr("+OK dirpop3d bye");
    141       break;
    142     }
    143     else if(0 == strcmp("USER", cmd)) {
    144       if(state != START) pr("-ERR");
    145       else {
    146         sscanf(buf, "%s %s", cmd, usr);
    147         pr("+OK hi %s", usr);
    148         state = USER;
    149       }
    150     }
    151     else if(0 == strcmp("PASS", cmd)) {
    152       if(state != USER) pr("-ERR");
    153       else {
    154         sscanf(buf, "%s %s", cmd, pwd);
    155         pr("+OK welcome %s", usr);
    156         state = PASS;
    157       }
    158     }
    159     else if(0 == strcmp("STAT", cmd)) {
    160       if(state != PASS) pr("-ERR");
    161       else {
    162         if(!flist) {
    163           flist = flist_new(100);
    164           struct xlist x = {flist};
    165           dir(&x, list_cb, "%s/%s", rootdir, usr);
    166           flist = x.flist;
    167         }
    168         pr("+OK %d %d", flist->n, flist->nbytes);
    169         state = STAT;
    170       }
    171     }
    172     else if(0 == strcmp("LIST", cmd)) {
    173       if(state != STAT) pr("-ERR");
    174       else {
    175         pr("+OK %d messages (%d octets)", flist->n, flist->nbytes);
    176         for(int i = 0; i < flist->n; i++)
    177           pr("%d %d", i + 1, flist->msg[i].nbytes);
    178         pr(".");
    179         state = LIST;
    180       }
    181     }
    182     else if(0 == strcmp("RETR", cmd)) {
    183       if(state != STAT) pr("-ERR");
    184       else {
    185         int n = 0;
    186         sscanf(buf, "%s %d", cmd, &n);
    187         if(0 < n && n <= flist->n) {
    188           struct msg *x = &flist->msg[n - 1];
    189           pr("+OK %d octets", x->nbytes);
    190           FILE *in = fopen(x->fname, "r");
    191           if(!in) die(1, "echo(): cannot open input file '%s'", x->fname);
    192           /* Note that the served files must have lines shorter than
    193              BLEN and no single dot char on its own line.  Therefore
    194              use base64 for the message body. */
    195           char buf[BLEN];
    196           while(fgets(buf, BLEN, in)) {
    197             rtrim(buf);
    198             pr("%s", buf);
    199           }
    200           fclose(in);
    201           pr(".");
    202         }
    203         else pr("-ERR");
    204       }
    205     }
    206     else if(0 == strcmp("DELE", cmd)) {
    207       if(state != STAT) pr("-ERR");
    208       else {
    209         int n = 0;
    210         sscanf(buf, "%s %d", cmd, &n);
    211         if(0 < n && n <= flist->n) {
    212           struct msg *x = &flist->msg[n - 1];
    213           x->deleted = 1;
    214           pr("+OK message %d deleted", n);
    215         }
    216         else pr("-ERR");
    217       }
    218     }
    219     else pr("-ERR wrong command");
    220   }
    221 }