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