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