]> mj.ucw.cz Git - umpf.git/blobdiff - ham.c
fix many little bugs, release 0.1
[umpf.git] / ham.c
diff --git a/ham.c b/ham.c
index 04153c5bb68ee9db8b3215d66398c8acd2a296c3..7028304dd14a37205898aba1428d3493eac8beae 100644 (file)
--- a/ham.c
+++ b/ham.c
@@ -1,26 +1,21 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sysexits.h>
+#include <time.h>
 
-#include "brum.h"
+#include <unistd.h>
 
-#define BUFSIZE 1024 
+#include "umpf.h"
+#define MAIL_LEN 65536
 
 int curbufsize;
 
-static char* //TODO: rewrite
-buf_double(char* buf, int size)
-{
-       buf = realloc(buf, 2*size);
-       
-       if (!buf)
-               die("Low memory");
-
-       return buf;
+char mbox_write_buf[BUFSIZE];
+int mbox_write_pos;
+int mbox_write_err;
 
-}
-
-static void
+void
 new_header(char* buf, struct list* h)
 {
        char* p;
@@ -33,41 +28,117 @@ new_header(char* buf, struct list* h)
                new->value = xstrdup("");
        else {
                *p = 0;
-               new->value = xstrdup(p+1);
+               if (*(p + 1) == ' ')
+                       new->value = xstrdup(p + 2);
+               else
+                       new->value = xstrdup(p + 1);
+               p = strrchr(new->value, '\n');
+               if (p && !*(p+1))
+                       *p = 0;
        }
        new->name = xstrdup(buf);
 
        list_add_last(h, &new->car);
 }
 
+int gmc_pos;
+char gmc_buf[BUFSIZE];
+int gmc_init;
+int gmc_read;
+int gmc_unget;
+
+static int
+give_me_char(int fd)
+{
+       if (! gmc_init) {
+               gmc_pos = 0;
+               gmc_read = read(fd, gmc_buf, BUFSIZE);
+               gmc_init = 1;
+               gmc_unget = EOF;
+               curr_email_len = 0;
+       }
+
+       if (gmc_unget != EOF) {
+               int a = gmc_unget;
+               gmc_unget = EOF;
+               curr_email_len++;
+               return a;
+       }
+
+       if (! gmc_read) {
+               gmc_init = 0;
+               return EOF;
+       }
+
+       if (gmc_pos < gmc_read) {
+               curr_email_len++;
+               return gmc_buf[gmc_pos++];
+       } else {
+               gmc_pos = 0;
+               gmc_read = read(fd, gmc_buf, BUFSIZE);
+               if (! gmc_read) {
+                       gmc_init = 0;
+                       return EOF;
+               }
+               else {
+                       curr_email_len++;
+                       return gmc_buf[gmc_pos++];
+               }
+       }
+}
+
+static void
+unget_me(int c)
+{
+       gmc_unget = c;
+       curr_email_len--;
+}
+
 struct list*
-make_hlist(void)
+make_hlist(int fd)
 {
        struct list* l = xmalloc(sizeof(struct list));
        char* buf;
        int i = 0; /* current position */
        int c, last = 0;
+       int first_line = 1;
+       int want_from_line = 0;
 
        list_init(l);
        buf = xmalloc(BUFSIZE);
        curbufsize = BUFSIZE;
        
-       while ((c = getchar()) != EOF){
+       while ((c = give_me_char(fd)) != EOF){
                if (c == '\r')
                        continue;
 
-               if (i >= curbufsize-2){
-                       buf = buf_double(buf, curbufsize);
-                       curbufsize *= 2;
+               if (i >= curbufsize-3)
+                       buf = xrealloc(buf, curbufsize *= 2);
+               
+               if (first_line && 
+                       ( (!i && c != 'F') || (i == 1 && c != 'r') || (i == 2 && c != 'o') 
+                               || (i == 3 && c != 'm') || (i == 4 && c != ' ') )
+                       )
+                       first_line = 0;
+               else {
+                       if (first_line && i == 4)
+                               want_from_line = 1;
                }
 
-               buf[i++] = c; 
+               buf[i++] = c;
                if (c == '\n'){
+                       if (want_from_line) {
+                               buf[i] = 0;
+                               fromline = xstrdup(buf);
+                               want_from_line = 0;
+                               i = 0;
+                               continue;
+                       }
                        if (last == '\n')
                                break;
-                       if ((c = getchar())!=' ' && c!='\t'){
+                       if ((c = give_me_char(fd)) != ' ' && c != '\t'){
                                if (c != EOF)
-                                       ungetc(c, stdin);
+                                       unget_me(c);
                                buf[i] = 0;
                                new_header(buf, l);
                                i = 0;
@@ -76,32 +147,322 @@ make_hlist(void)
                }
                        last = c;
        }
-       free(buf);
+       free_string(buf);
        return l;
 }
 
+struct email*
+get_body(int rfd)
+{
+       char* buf;
+       struct email* b;
+       int c, fd;
+       int will_save = 0;
+       int i = 0;
+       int curbufsize = MAIL_LEN;
+       char* tmpfile;
+       int res;
+       
+       buf = xmalloc(MAIL_LEN);
+       b = xmalloc(sizeof(struct email));
+       b->body = buf;
+       b->tmpfile = NULL;
+       b->fd = -1;
+
+       /* read mail body, if it is too big, try to make tmp file */
+       while ((c = give_me_char(rfd)) != EOF){
+               buf[i++] = c;
+               if (i >= curbufsize - 1) {
+                       tmpfile = xstrdup("/tmp/umpf.XXXXXX");
+                       fd = mkstemp(tmpfile);
+                       /* cannot create tmpfile, try to continue reading */
+                       if (fd < 0)
+                               bye(EX_TEMPFAIL, "Cannot create temporary file: %m");
+                       else {
+                               will_save = 1;
+                               b->body = NULL;
+                               b->tmpfile = tmpfile;
+                               b->fd = fd;
+                               res = write(fd, buf, MAIL_LEN);
+                               if (res < MAIL_LEN) {
+                                       unlink(b->tmpfile);
+                                       bye(EX_TEMPFAIL, "Cannot write to temporary file: %m");
+                               }
+                               break;  
+                       }
+               }
+       }
+       b->body_len = i;
+       /* save rest of the body to the tmpfile */
+       if (will_save) {
+               int j = 0;
+               while ((c = give_me_char(rfd)) != EOF){
+                       buf[j++] = c;
+                       b->body_len++;
+                       if (j >= MAIL_LEN) {
+                               j = 0;
+                               res = write(fd, buf, MAIL_LEN);
+                               if (res < MAIL_LEN) {
+                                       unlink(b->tmpfile);
+                                       bye(EX_TEMPFAIL, "Cannot write to temporary file: %m");
+                               }
+                       }
+               }
+               res = write(fd, buf, j);
+               if (res < j) {
+                       unlink(b->tmpfile);
+                       bye(EX_TEMPFAIL, "Cannot write to temporary file: %m");
+               }
+       }
+       return b; 
+}
+
 void
 print_headers(struct list* l)
 {
        struct hlist* p;
 
        LIST_FOREACH(p,l)
-               printf("%s:%s",p->name,p->value);
+               printf("%s:%s\n",p->name,p->value);
 }
 
+static void
+flush_mbox_buffer(int fd)
+{
+       if (mbox_write_err || !mbox_write_pos)
+               return;
+
+       int res;
+       res = write(fd, mbox_write_buf, mbox_write_pos);
+       if (res < 0)
+               mbox_write_err++;
+       mbox_write_pos = 0;
+}
+
+static void
+write_char_to_mailbox(char c, int fd)
+{
+       int res;
+
+       if (mbox_write_pos >= BUFSIZE){
+               res = write(fd, mbox_write_buf, BUFSIZE);
+               if (res < 0)
+                       mbox_write_err++;
+               mbox_write_pos = 0;
+               return;
+       }
+       mbox_write_buf[mbox_write_pos++] = c;
+}
+
+int email_pos;
+int headers_sent;
+int email_opened;
+
 void
-do_action(struct action* a)
+open_email(void)
 {
-       puts("--do action--");
-       if (a->l)
-               puts(a->l);
-       printf("->");
-       if (a->r)
-               puts(a->r);
-       putchar(' ');
-       if (a->s)
-               puts(a->s);
-       puts("with email\n");
-       print_headers(a->e.headers);
-       puts("\n--Muhehehechlemst!--\n");
+       email_pos = 0;
+       headers_sent = 0;
+       email_opened = 1;
+}
+
+char*
+read_email(struct email* em)
+{
+       char* buf;
+       int r, pos;
+
+       if (! email_opened) {
+               chars_written = 0;      
+               return NULL;
+       }
+
+       if (! headers_sent) {
+               struct hlist* ph;
+               int curbufsize = BUFSIZE;
+               buf = xmalloc(BUFSIZE);
+               pos = 0;
+
+               LIST_FOREACH(ph, em->headers){
+                       int needed = strlen(ph->name) + strlen(ph->value) + 4;
+                       while (curbufsize < pos + needed)
+                               buf = xrealloc(buf, curbufsize*=2);
+                       strcpy(buf + pos, ph->name);
+                       pos += strlen(ph->name);
+                       buf[pos++] = ':';
+                       buf[pos++] = ' ';
+                       strcpy(buf + pos, ph->value);
+                       pos += strlen(ph->value);
+                       buf[pos++] = '\n';
+       
+               }
+               buf[pos++] = '\n';
+               headers_sent = 1;
+               chars_written = pos;
+               return buf;
+       }
+
+       if (em->body) {
+               buf = xstrdup(em->body);
+               chars_written = em->body_len; 
+               //printf("%d: %s\n", em->body_len, em->body); 
+               email_opened = 0;
+               return buf;
+       }
+
+       if (! email_pos)
+               lseek(em->fd, 0, SEEK_SET);
+
+       buf = xmalloc(MAIL_LEN + 1);
+       r = read(em->fd, buf, MAIL_LEN);
+       if (r < MAIL_LEN)
+               email_opened = 0;
+       chars_written = r;
+       
+       return buf;
+}
+
+int
+write_email_to_fd(int fd, struct email* email)
+{
+       char* buf;
+       int wr, to_write;
+
+       open_email();
+       for (;;) {
+               buf = read_email(email);
+               if (!chars_written)
+                       break;
+
+               to_write = chars_written;
+               while (to_write) {
+                       wr = write(fd, buf, to_write);
+                       to_write -= wr;
+               }
+       };
+       
+       return 0;
+}
+
+/* try to copy e-mail to mailbox, if it fails,
+ truncate mailbox to the original size */
+static int
+copy_email(int fd, struct email* email)
+{
+       off_t mb_end;
+
+       mb_end = lseek(fd, 0, SEEK_END);
+       if (mb_end < 0)
+               return -1;
+
+       /* init globals */
+       mbox_write_err = 0;
+       mbox_write_pos = 0;
+
+       /* From line */
+       struct hlist* ph;
+       int i, len;
+       time_t t;
+       time(&t);
+       char* date = ctime(&t);
+       int datelen = strlen(date);
+
+       if (! fromline) {       
+               len = 5 + datelen + 1; 
+               fromline = xmalloc(len);
+               sprintf(fromline, "From %s", date);
+               len = strlen(fromline);
+       } else 
+               len = strlen(fromline);
+       for (i = 0; i < len; i++)
+               write_char_to_mailbox(fromline[i], fd);
+       
+       /* headers */
+       char* pc;
+       LIST_FOREACH(ph, email->headers){
+               for (pc = ph->name; *pc; pc++)
+                       write_char_to_mailbox(*pc, fd);
+               write_char_to_mailbox(':', fd); 
+               write_char_to_mailbox(' ', fd); 
+               for (pc = ph->value; *pc; pc++)
+                       write_char_to_mailbox(*pc, fd); 
+               write_char_to_mailbox('\n', fd);        
+       }
+
+       write_char_to_mailbox('\n', fd);
+       if (email->body) {
+               for (pc = email->body; pc < email->body + email->body_len; pc++){
+                               write_char_to_mailbox(*pc, fd);
+                               if (*pc == '\n'){
+                                       if ((pc + 5 < email->body + email->body_len)
+                                               && pc[1] == 'F' && pc[2] == 'r'
+                                               && pc[3] == 'o' && pc[4] == 'm'
+                                               && pc[5] == ' ')
+                                       write_char_to_mailbox('>', fd); 
+                               }
+               }
+       } else {
+               int i;
+               char buf[MAIL_LEN];
+               for(i = 0; i < email->body_len; i+=MAIL_LEN) {
+                       int len = MAIL_LEN;
+                       if (i >= email->body_len)
+                               len = len - (i - email->body_len);
+                       read(email->fd, buf, len); //FIXME: check it?
+                       for (pc = buf; pc < buf + len; pc++) {
+                               write_char_to_mailbox(*pc, fd);
+                               if (*pc == '\n'){
+                                       if ((pc + 5 < email->body + email->body_len)
+                                       && pc[1] == 'F' && pc[2] == 'r'
+                                       && pc[3] == 'o' && pc[4] == 'm'
+                                       && pc[5] == ' ')
+                                       write_char_to_mailbox('>', fd); 
+                               }
+                       }
+               }       
+       }
+
+       flush_mbox_buffer(fd);
+
+       /* errors? */
+       if (mbox_write_err){
+               /* try to truncate to the original length */
+               ftruncate(fd, mb_end);  
+               return -1;
+       }       
+
+       return 0; 
+}
+
+int
+deliver_local_email(char* folder, struct email* email)
+{
+       int res = -1;
+       int is_default_mailbox = 0;
+       int fd;
+
+       if (!strcmp(default_mailbox, folder))   
+               is_default_mailbox = 1;
+
+       fd = open_mailbox(folder, is_default_mailbox);
+       if (fd < 0){
+               if (is_default_mailbox)
+                       return res;
+               else /* try to save to default mailbox instead */
+                       return deliver_local_email(default_mailbox, email);
+       }
+
+       res = copy_email(fd, email);
+       if (res < 0){
+
+               /* try to deliver to the default mailbox */
+               if (is_default_mailbox)
+                       return res;
+               else
+                       return deliver_local_email(default_mailbox, email);
+       }
+
+       close_mailbox(fd, folder, is_default_mailbox);  
+
+       return res; 
 }