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