add free_field as standard for struct field
[mmh] / sbr / m_getfld2.c
1 /*
2 ** m_getfld2 -- replacement for m_getfld()
3 **              read an RFC 822 message
4 */
5
6 #define _WITH_GETLINE
7 #define _POSIX_C_SOURCE 200809L
8
9 #include <h/mh.h>
10 #include <h/utils.h>
11 #include <stdio.h>
12 #include <ctype.h>
13 #include <stdbool.h>
14
15
16 enum threestate {
17         B_TRUE = 1,
18         B_FALSE = 0,
19         FAIL = -1,
20 };
21
22 /*
23 ** static prototypes
24 */
25 static enum threestate is_falted(FILE *);
26 static size_t copyname(char *, char *);
27 static bool is_separator(char *);
28
29 struct field free_field = { "\0", 0, NULL, 0, 0 };
30
31
32 /*
33 ** FLD2:        We read a (complete) header field
34 ** BODY2:       We read a body line
35 ** LENERR2:     Line is too long (>998, as defined by RFC 822)
36 ** FMTERR2:     Header field invalid
37 ** IOERR2:      Failure to read
38 ** FILEEOF2:    We're at the end of the file
39 **
40 ** f->name is only filled in FLD2.
41 **
42 ** In FLD2, f->value contains the field's (complete) value only;
43 ** in BODY2, LENERR2 and FMTERR2 f->value contains the whole line;
44 ** in IOERR2 and FILEEOF2 f->value is not set.
45 */
46 enum state
47 m_getfld2(enum state s, struct field *f, FILE *msg)
48 {
49         char *tmpline = NULL;
50         size_t len = 0;
51         ssize_t nchars;
52         enum threestate falted = B_FALSE;
53         enum state ret = s;
54
55         switch (s) {
56         case FLD2:
57                 nchars = getline(&tmpline, &len, msg);
58                 if (nchars < 1) {
59                         free(f->value);
60                         *f = free_field;
61                         if (feof(msg)) {
62                                 return FILEEOF2;
63                         } else {
64                                 return IOERR2;
65                         }
66                 }
67
68                 if (nchars >= NAMESZ) {
69                         ret = LENERR2;
70                 }
71
72                 if (*(tmpline + nchars - 1) != '\n') {
73                         ret = FMTERR2;
74                 }
75
76                 if (ret == FLD2 && is_separator(tmpline)) {
77                         /* header/body separator found */
78                         free(tmpline);
79                         return m_getfld2(BODY2, f, msg);
80                 }
81
82                 f->namelen = copyname(f->name, tmpline);
83                 if (f->namelen < 1) {
84                         *f->name = '\0';
85                         f->namelen = 0;
86                         ret = FMTERR2;
87                 }
88
89                 /* copy the field's value */
90                 if (f->alloclen <= nchars - f->namelen) {
91                         f->value = mh_xrealloc(f->value, f->alloclen + len);
92                         f->alloclen += len;
93                 }
94                 if (f->namelen != 0) {
95                         strcpy(f->value, tmpline + f->namelen + 1);
96                         f->valuelen = nchars - f->namelen - 1;
97                 } else {
98                         strcpy(f->value, tmpline);
99                         f->valuelen = nchars;
100                 }
101
102                 while (ret == FLD2 && (falted = is_falted(msg)) == B_TRUE) {
103                         nchars = getline(&tmpline, &len, msg);
104                         if (nchars <= 0) {
105                                 free(tmpline);
106                                 return IOERR2;
107                         }
108
109                         if (nchars >= NAMESZ) {
110                                 ret = LENERR2;
111                         }
112
113                         if (*(tmpline + nchars - 1) != '\n') {
114                                 ret = FMTERR2;
115                         }
116
117                         if (f->alloclen - f->valuelen <= nchars) {
118                                 f->value = mh_xrealloc(f->value,
119                                                 f->alloclen + len);
120                                 f->alloclen += len;
121                         }
122                         strcpy(f->value + f->valuelen, tmpline);
123                         f->valuelen += nchars;
124
125                 }
126
127                 if (falted == FAIL) {
128                         ret = IOERR2;
129                 }
130
131                 free(tmpline);
132                 return ret;
133
134         case BODY2:
135                 free(f->value);
136                 *f = free_field;
137
138                 nchars = getline(&tmpline, &len, msg);
139                 if (nchars < 1) {
140                         free(tmpline);
141                         if (feof(msg)) {
142                                 return FILEEOF2;
143                         } else {
144                                 return IOERR2;
145                         }
146                 }
147
148                 if (nchars >= NAMESZ) {
149                         ret = LENERR2;
150                 }
151
152                 f->value = tmpline;
153                 f->valuelen = nchars;
154                 f->alloclen = len;
155                 return ret;
156
157         default:
158                 /* give error states back as received */
159                 return s;
160         }
161 }
162
163 static enum threestate
164 is_falted(FILE *msg)
165 {
166         enum threestate ret;
167         int c;
168
169         if ((c = getc(msg)) < 0) {
170                 if (feof(msg)) {
171                         clearerr(msg);
172                         return B_FALSE;
173                 } else {
174                         return FAIL;
175                 }
176         }
177         if (isblank(c)) {
178                 ret = B_TRUE;
179         } else {
180                 ret = B_FALSE;
181         }
182         if (ungetc(c, msg) != c) {
183                 return FAIL;
184         }
185         return ret;
186 }
187
188 static size_t
189 copyname(char *dst, char *src)
190 {
191         size_t len;
192         char *cp, *sep;
193
194         if (!(sep = strchr(src, ':'))) {
195                 return 0;
196         }
197         /* whitespace is forbidden in name */
198         for (cp=src; cp<sep; cp++) {
199                 if (isspace(*cp)) {
200                         return 0;
201                 }
202         }
203
204         len = sep - src;
205         if (len >= NAMESZ) {
206                 return 0;
207         }
208
209         src[len] = '\0';
210         strcpy(dst, src);
211
212         return strlen(dst);
213 }
214
215 static bool
216 is_separator(char *line)
217 {
218         /*
219         ** In MH, lines that are consists of dashes only are
220         ** separators as well ... not so in RFC 822.
221         */
222         while (*line == '-') {
223                 line++;
224         }
225         if (strcmp("\n", line) == 0 || strcmp("\r\n", line) == 0 ) {
226                 return true;
227         }
228         return false;
229 }