add free_field as standard for struct field
[mmh] / sbr / m_getfld2.c
index 91164f3..6545d0f 100644 (file)
@@ -10,6 +10,7 @@
 #include <h/utils.h>
 #include <stdio.h>
 #include <ctype.h>
+#include <stdbool.h>
 
 
 enum threestate {
@@ -23,11 +24,24 @@ enum threestate {
 */
 static enum threestate is_falted(FILE *);
 static size_t copyname(char *, char *);
+static bool is_separator(char *);
+
+struct field free_field = { "\0", 0, NULL, 0, 0 };
 
 
 /*
-** For the states FLD2, BODY2 and FMTERR2 memory is allocated for f->value.
-** For the states LENERR2, ERR2 and FILEEOF2 no memory is allocated.
+** FLD2:       We read a (complete) header field
+** BODY2:      We read a body line
+** LENERR2:    Line is too long (>998, as defined by RFC 822)
+** FMTERR2:    Header field invalid
+** IOERR2:     Failure to read
+** FILEEOF2:   We're at the end of the file
+**
+** f->name is only filled in FLD2.
+**
+** In FLD2, f->value contains the field's (complete) value only;
+** in BODY2, LENERR2 and FMTERR2 f->value contains the whole line;
+** in IOERR2 and FILEEOF2 f->value is not set.
 */
 enum state
 m_getfld2(enum state s, struct field *f, FILE *msg)
@@ -35,38 +49,31 @@ m_getfld2(enum state s, struct field *f, FILE *msg)
        char *tmpline = NULL;
        size_t len = 0;
        ssize_t nchars;
-       enum threestate falted;
+       enum threestate falted = B_FALSE;
+       enum state ret = s;
 
-       nchars = getline(&tmpline, &len, msg);
-       if (nchars == -1) {
-               if (feof(msg)) {
-                       return FILEEOF2;
-               } else {
-                       return ERR2;
+       switch (s) {
+       case FLD2:
+               nchars = getline(&tmpline, &len, msg);
+               if (nchars < 1) {
+                       free(f->value);
+                       *f = free_field;
+                       if (feof(msg)) {
+                               return FILEEOF2;
+                       } else {
+                               return IOERR2;
+                       }
                }
-       }
 
-       if (nchars >= NAMESZ) {
-               free(tmpline);
-               return LENERR2;
-       }
-
-       *f->name = '\0';
-       f->namelen = 0;
+               if (nchars >= NAMESZ) {
+                       ret = LENERR2;
+               }
 
-       switch (s) {
-       case FLD2:
                if (*(tmpline + nchars - 1) != '\n') {
-                       if (f->value) {
-                               free(f->value);
-                       }
-                       f->value = tmpline;
-                       f->valuelen = nchars;
-                       f->alloclen = len;
-                       return FMTERR2;
+                       ret = FMTERR2;
                }
 
-               if (nchars > 0 && (*tmpline == '\n' || *tmpline == '-')) {
+               if (ret == FLD2 && is_separator(tmpline)) {
                        /* header/body separator found */
                        free(tmpline);
                        return m_getfld2(BODY2, f, msg);
@@ -74,13 +81,9 @@ m_getfld2(enum state s, struct field *f, FILE *msg)
 
                f->namelen = copyname(f->name, tmpline);
                if (f->namelen < 1) {
-                       if (f->value) {
-                               free(f->value);
-                       }
-                       f->value = tmpline;
-                       f->valuelen = nchars;
-                       f->alloclen = len;
-                       return FMTERR2;
+                       *f->name = '\0';
+                       f->namelen = 0;
+                       ret = FMTERR2;
                }
 
                /* copy the field's value */
@@ -88,29 +91,27 @@ m_getfld2(enum state s, struct field *f, FILE *msg)
                        f->value = mh_xrealloc(f->value, f->alloclen + len);
                        f->alloclen += len;
                }
-               strcpy(f->value, tmpline + f->namelen + 1);
-               f->valuelen = nchars - f->namelen - 1;
+               if (f->namelen != 0) {
+                       strcpy(f->value, tmpline + f->namelen + 1);
+                       f->valuelen = nchars - f->namelen - 1;
+               } else {
+                       strcpy(f->value, tmpline);
+                       f->valuelen = nchars;
+               }
 
-               while ((falted = is_falted(msg)) == B_TRUE) {
+               while (ret == FLD2 && (falted = is_falted(msg)) == B_TRUE) {
                        nchars = getline(&tmpline, &len, msg);
                        if (nchars <= 0) {
                                free(tmpline);
-                               return ERR2;
+                               return IOERR2;
                        }
 
                        if (nchars >= NAMESZ) {
-                               free(tmpline);
-                               return LENERR2;
+                               ret = LENERR2;
                        }
 
                        if (*(tmpline + nchars - 1) != '\n') {
-                               if (f->value) {
-                                       free(f->value);
-                               }
-                               f->value = tmpline;
-                               f->valuelen = nchars;
-                               f->alloclen = len;
-                               return FMTERR2;
+                               ret = FMTERR2;
                        }
 
                        if (f->alloclen - f->valuelen <= nchars) {
@@ -124,21 +125,34 @@ m_getfld2(enum state s, struct field *f, FILE *msg)
                }
 
                if (falted == FAIL) {
-                       free(tmpline);
-                       return ERR2;
+                       ret = IOERR2;
                }
 
                free(tmpline);
-               return FLD2;
+               return ret;
 
        case BODY2:
-               if (f->value) {
-                       free(f->value);
+               free(f->value);
+               *f = free_field;
+
+               nchars = getline(&tmpline, &len, msg);
+               if (nchars < 1) {
+                       free(tmpline);
+                       if (feof(msg)) {
+                               return FILEEOF2;
+                       } else {
+                               return IOERR2;
+                       }
+               }
+
+               if (nchars >= NAMESZ) {
+                       ret = LENERR2;
                }
+
                f->value = tmpline;
                f->valuelen = nchars;
                f->alloclen = len;
-               return BODY2;
+               return ret;
 
        default:
                /* give error states back as received */
@@ -175,11 +189,17 @@ static size_t
 copyname(char *dst, char *src)
 {
        size_t len;
-       char *sep = strchr(src, ':');
+       char *cp, *sep;
 
-       if (!sep) {
+       if (!(sep = strchr(src, ':'))) {
                return 0;
        }
+       /* whitespace is forbidden in name */
+       for (cp=src; cp<sep; cp++) {
+               if (isspace(*cp)) {
+                       return 0;
+               }
+       }
 
        len = sep - src;
        if (len >= NAMESZ) {
@@ -191,3 +211,19 @@ copyname(char *dst, char *src)
 
        return strlen(dst);
 }
+
+static bool
+is_separator(char *line)
+{
+       /*
+       ** In MH, lines that are consists of dashes only are
+       ** separators as well ... not so in RFC 822.
+       */
+       while (*line == '-') {
+               line++;
+       }
+       if (strcmp("\n", line) == 0 || strcmp("\r\n", line) == 0 ) {
+               return true;
+       }
+       return false;
+}