]> diplodocus.org Git - nmh/blob - test/fakepop.c
lock_file.c: close(2) file descriptor on failure, avoiding leak.
[nmh] / test / fakepop.c
1 /*
2 * fakepop - A fake POP server used by the nmh test suite
3 *
4 * This code is Copyright (c) 2012, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <sys/types.h>
15 #include <limits.h>
16
17 #define PIDFILE "/tmp/fakepop.pid"
18 #define LINESIZE 1024
19 #define BUFALLOC 4096
20
21 #define CHECKUSER() if (!user) { \
22 putcrlf(s, "-ERR Aren't you forgetting " \
23 "something? Like the USER command?"); \
24 continue; \
25 }
26 #define CHECKAUTH() if (!auth) { \
27 putcrlf(s, "-ERR Um, hello? Forget to " \
28 "log in?"); \
29 continue; \
30 }
31
32 void putcrlf(int, char *);
33 int serve(const char *, const char *);
34
35 static void putpopbulk(int, char *);
36 static int getpop(int, char *, ssize_t);
37 static char *readmessage(FILE *);
38
39 int
40 main(int argc, char *argv[])
41 {
42 FILE **mfiles;
43 char line[LINESIZE];
44 int rc, s, user = 0, auth = 0, i, j;
45 int numfiles;
46 size_t *octets;
47 const char *xoauth;
48
49 if (argc < 5) {
50 fprintf(stderr, "Usage: %s port username "
51 "password mail-file [mail-file ...]\n", argv[0]);
52 exit(1);
53 }
54
55 if (strcmp(argv[2], "XOAUTH") == 0) {
56 xoauth = argv[3];
57 } else {
58 xoauth = NULL;
59 }
60
61 numfiles = argc - 4;
62
63 mfiles = malloc(sizeof(FILE *) * numfiles);
64
65 if (! mfiles) {
66 fprintf(stderr, "Unable to allocate %d bytes for file "
67 "array\n", (int) (sizeof(FILE *) * numfiles));
68 exit(1);
69 }
70
71 octets = malloc(sizeof(*octets) * numfiles);
72
73 if (! octets) {
74 fprintf(stderr, "Unable to allocate %d bytes for size "
75 "array\n", (int) (sizeof(FILE *) * numfiles));
76 exit(1);
77 }
78
79 for (i = 4, j = 0; i < argc; i++, j++) {
80 if (!(mfiles[j] = fopen(argv[i], "r"))) {
81 fprintf(stderr, "Unable to open message file \"%s\""
82 ": %s\n", argv[i], strerror(errno));
83 exit(1);
84 }
85
86 /*
87 * POP wants the size of the maildrop in bytes, but
88 * with \r\n line endings. Calculate that.
89 */
90
91 octets[j] = 0;
92
93 while (fgets(line, sizeof(line), mfiles[j])) {
94 octets[j] += strlen(line);
95 if (strrchr(line, '\n'))
96 octets[j]++;
97 }
98
99 rewind(mfiles[j]);
100 }
101
102 s = serve(PIDFILE, argv[1]);
103
104 /*
105 * Pretend to be a POP server
106 */
107
108 putcrlf(s, "+OK Not really a POP server, but we play one on TV");
109
110 for (;;) {
111 char linebuf[LINESIZE];
112
113 rc = getpop(s, linebuf, sizeof(linebuf));
114
115 if (rc <= 0)
116 break; /* Error or EOF */
117
118 if (strcasecmp(linebuf, "CAPA") == 0) {
119 putpopbulk(s, "+OK We have no capabilities, really\r\n"
120 "FAKE-CAPABILITY\r\n");
121 if (xoauth != NULL) {
122 putcrlf(s, "SASL XOAUTH2");
123 }
124 putcrlf(s, ".");
125 } else if (strncasecmp(linebuf, "USER ", 5) == 0) {
126 if (strcmp(linebuf + 5, argv[2]) == 0) {
127 putcrlf(s, "+OK Niiiice!");
128 user = 1;
129 } else {
130 putcrlf(s, "-ERR Don't play me, bro!");
131 }
132 } else if (strncasecmp(linebuf, "PASS ", 5) == 0) {
133 CHECKUSER();
134 if (strcmp(linebuf + 5, argv[3]) == 0) {
135 putcrlf(s, "+OK Aren't you a sight "
136 "for sore eyes!");
137 auth = 1;
138 } else {
139 putcrlf(s, "-ERR C'mon!");
140 }
141 } else if (xoauth != NULL
142 && strncasecmp(linebuf, "AUTH XOAUTH2", 12) == 0) {
143 if (strstr(linebuf, xoauth) == NULL) {
144 putcrlf(s, "+ base64-json-err");
145 rc = getpop(s, linebuf, sizeof(linebuf));
146 if (rc != 0)
147 break; /* Error or EOF */
148 putcrlf(s, "-ERR [AUTH] Invalid credentials.");
149 continue;
150 }
151 putcrlf(s, "+OK Welcome.");
152 auth = 1;
153 } else if (strcasecmp(linebuf, "STAT") == 0) {
154 size_t total = 0;
155 CHECKAUTH();
156 for (i = 0, j = 0; i < numfiles; i++) {
157 if (mfiles[i]) {
158 total += octets[i];
159 j++;
160 }
161 }
162 snprintf(linebuf, sizeof(linebuf),
163 "+OK %d %d", i, (int) total);
164 putcrlf(s, linebuf);
165 } else if (strncasecmp(linebuf, "RETR ", 5) == 0) {
166 CHECKAUTH();
167 rc = sscanf(linebuf + 5, "%d", &i);
168 if (rc != 1) {
169 putcrlf(s, "-ERR Whaaaa...?");
170 continue;
171 }
172 if (i < 1 || i > numfiles) {
173 putcrlf(s, "-ERR That message number is "
174 "out of range, jerkface!");
175 continue;
176 }
177 if (mfiles[i - 1] == NULL) {
178 putcrlf(s, "-ERR Sorry, don't have it anymore");
179 } else {
180 char *buf = readmessage(mfiles[i - 1]);
181 putcrlf(s, "+OK Here you go ...");
182 putpopbulk(s, buf);
183 free(buf);
184 }
185 } else if (strncasecmp(linebuf, "DELE ", 5) == 0) {
186 CHECKAUTH();
187 rc = sscanf(linebuf + 5, "%d", &i);
188 if (rc != 1) {
189 putcrlf(s, "-ERR Whaaaa...?");
190 continue;
191 }
192 if (i < 1 || i > numfiles) {
193 putcrlf(s, "-ERR That message number is "
194 "out of range, jerkface!");
195 continue;
196 }
197 if (mfiles[i - 1] == NULL) {
198 putcrlf(s, "-ERR Um, didn't you tell me "
199 "to delete it already?");
200 } else {
201 fclose(mfiles[i - 1]);
202 mfiles[i - 1] = NULL;
203 putcrlf(s, "+OK Alright man, I got rid of it");
204 }
205 } else if (strcasecmp(linebuf, "QUIT") == 0) {
206 putcrlf(s, "+OK See ya, wouldn't want to be ya!");
207 close(s);
208 break;
209 } else {
210 putcrlf(s, "-ERR Um, what?");
211 }
212 }
213
214 exit(0);
215 }
216
217 /*
218 * Put one big buffer to the POP server. Should have already had the line
219 * endings set up and dot-stuffed if necessary.
220 */
221
222 static void
223 putpopbulk(int socket, char *data)
224 {
225 ssize_t datalen = strlen(data);
226
227 if (write(socket, data, datalen) < 0) {
228 perror ("write");
229 }
230 }
231
232 /*
233 * Get one line from the POP server. We don't do any buffering here.
234 */
235
236 static int
237 getpop(int socket, char *data, ssize_t len)
238 {
239 int cc;
240 int offset = 0;
241
242 for (;;) {
243 cc = read(socket, data + offset, len - offset);
244
245 if (cc < 0) {
246 fprintf(stderr, "Read failed: %s\n", strerror(errno));
247 exit(1);
248 }
249
250 if (cc == 0) {
251 return 0;
252 }
253
254 offset += cc;
255
256 if (offset >= len) {
257 fprintf(stderr, "Input buffer overflow "
258 "(%d bytes)\n", (int) len);
259 exit(1);
260 }
261
262 if (data[offset - 1] == '\n' && data[offset - 2] == '\r') {
263 data[offset - 2] = '\0';
264 return offset - 2;
265 }
266 }
267 }
268
269 #define HAVEROOM(buf, size, used, new) do { \
270 if (used + new > size - 1) { \
271 buf = realloc(buf, size += BUFALLOC); \
272 } \
273 } while (0)
274
275 /*
276 * Read a file and return it as one malloc()'d buffer. Convert \n to \r\n
277 * and dot-stuff if necessary.
278 */
279
280 static char *
281 readmessage(FILE *file)
282 {
283 char *buffer = malloc(BUFALLOC);
284 ssize_t bufsize = BUFALLOC, used = 0;
285 char linebuf[LINESIZE];
286 int i;
287
288 buffer[0] = '\0';
289
290 while (fgets(linebuf, sizeof(linebuf), file)) {
291 if (strcmp(linebuf, ".\n") == 0) {
292 HAVEROOM(buffer, bufsize, used, 4);
293 strcat(buffer, "..\r\n");
294 } else {
295 i = strlen(linebuf);
296 if (i && linebuf[i - 1] == '\n') {
297 HAVEROOM(buffer, bufsize, used, i + 1);
298 linebuf[i - 1] = '\0';
299 strcat(buffer, linebuf);
300 strcat(buffer, "\r\n");
301 } else {
302 HAVEROOM(buffer, bufsize, used, i);
303 strcat(buffer, linebuf);
304 }
305 }
306 }
307
308 /*
309 * Put a terminating dot at the end
310 */
311
312 HAVEROOM(buffer, bufsize, used, 3);
313
314 strcat(buffer, ".\r\n");
315
316 rewind(file);
317
318 return buffer;
319 }