]>
diplodocus.org Git - nmh/blob - test/fakepop.c
2 * fakepop - A fake POP server used by the nmh test suite
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.
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <sys/types.h>
18 #include <sys/select.h>
24 #define PIDFILE "/tmp/fakepop.pid"
28 #define CHECKUSER() if (!user) { \
29 putpop(s, "-ERR Aren't you forgetting " \
30 "something? Like the USER command?"); \
33 #define CHECKUSERPASS() CHECKUSER() \
35 putpop(s, "-ERR Um, hello? Forget to " \
40 static void killpidfile(void);
41 static void handleterm(int);
42 static void putpop(int, char *);
43 static void putpopbulk(int, char *);
44 static int getpop(int, char *, ssize_t
);
45 static char *readmessage(FILE *);
48 main(int argc
, char *argv
[])
50 struct addrinfo hints
, *res
;
57 int rc
, l
, s
, on
, user
= 0, pass
= 0, i
, j
;
62 fprintf(stderr
, "Usage: %s port username "
63 "password mail-file [mail-file ...]\n", argv
[0]);
69 mfiles
= malloc(sizeof(FILE *) * numfiles
);
72 fprintf(stderr
, "Unable to allocate %d bytes for file "
73 "array\n", (int) (sizeof(FILE *) * numfiles
));
77 octets
= malloc(sizeof(*octets
) * numfiles
);
80 fprintf(stderr
, "Unable to allocate %d bytes for size "
81 "array\n", (int) (sizeof(FILE *) * numfiles
));
85 for (i
= 4, j
= 0; i
< argc
; i
++, j
++) {
86 if (!(mfiles
[j
] = fopen(argv
[i
], "r"))) {
87 fprintf(stderr
, "Unable to open message file \"%s\""
88 ": %s\n", argv
[i
], strerror(errno
));
93 * POP wants the size of the maildrop in bytes, but
94 * with \r\n line endings. Calculate that.
99 while (fgets(line
, sizeof(line
), mfiles
[j
])) {
100 octets
[j
] += strlen(line
);
101 if (strrchr(line
, '\n'))
109 * If there is a pid file around, kill the previously running
113 if (stat(PIDFILE
, &st
) == 0) {
116 if (!(pid
= fopen(PIDFILE
, "r"))) {
117 fprintf(stderr
, "Cannot open " PIDFILE
118 " (%s), continuing ...\n", strerror(errno
));
120 rc
= fscanf(pid
, "%ld", &oldpid
);
124 fprintf(stderr
, "Unable to parse pid in "
125 PIDFILE
", continuing ...\n");
127 kill((pid_t
) oldpid
, SIGTERM
);
134 memset(&hints
, 0, sizeof(hints
));
136 hints
.ai_family
= PF_INET
;
137 hints
.ai_socktype
= SOCK_STREAM
;
138 hints
.ai_protocol
= IPPROTO_TCP
;
139 hints
.ai_flags
= AI_PASSIVE
;
141 rc
= getaddrinfo("127.0.0.1", argv
[1], &hints
, &res
);
144 fprintf(stderr
, "Unable to resolve localhost/%s: %s\n",
145 argv
[1], gai_strerror(rc
));
149 l
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
152 fprintf(stderr
, "Unable to create listening socket: %s\n",
159 if (setsockopt(l
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1) {
160 fprintf(stderr
, "Unable to set SO_REUSEADDR: %s\n",
165 if (bind(l
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
166 fprintf(stderr
, "Unable to bind socket: %s\n", strerror(errno
));
170 if (listen(l
, 1) == -1) {
171 fprintf(stderr
, "Unable to listen on socket: %s\n",
177 * Fork off a copy of ourselves, print out our child pid, then
181 switch (child
= fork()) {
183 fprintf(stderr
, "Unable to fork child: %s\n", strerror(errno
));
188 * Close stdin and stdout so $() in the shell will get an
189 * EOF. For now leave stderr open.
195 printf("%ld\n", (long) child
);
200 * Now that our socket and files are set up, wait 30 seconds for
201 * a connection. If there isn't one, then exit.
204 if (!(pid
= fopen(PIDFILE
, "w"))) {
205 fprintf(stderr
, "Cannot open " PIDFILE
": %s\n",
210 fprintf(pid
, "%ld\n", (long) getpid());
213 signal(SIGTERM
, handleterm
);
222 rc
= select(l
+ 1, &readfd
, NULL
, NULL
, &tv
);
225 fprintf(stderr
, "select() failed: %s\n", strerror(errno
));
230 * If we get a timeout, just silently exit
238 * We got a connection; accept it. Right after that close our
239 * listening socket so we won't get any more connections on it.
242 if ((s
= accept(l
, NULL
, NULL
)) == -1) {
243 fprintf(stderr
, "Unable to accept connection: %s\n",
251 * Pretend to be a POP server
254 putpop(s
, "+OK Not really a POP server, but we play one on TV");
257 char linebuf
[LINESIZE
];
259 rc
= getpop(s
, linebuf
, sizeof(linebuf
));
262 break; /* Error or EOF */
264 if (strcasecmp(linebuf
, "CAPA") == 0) {
265 putpopbulk(s
, "+OK We have no capabilities, really\r\n"
266 "FAKE-CAPABILITY\r\n.\r\n");
267 } else if (strncasecmp(linebuf
, "USER ", 5) == 0) {
268 if (strcmp(linebuf
+ 5, argv
[2]) == 0) {
269 putpop(s
, "+OK Niiiice!");
272 putpop(s
, "-ERR Don't play me, bro!");
274 } else if (strncasecmp(linebuf
, "PASS ", 5) == 0) {
276 if (strcmp(linebuf
+ 5, argv
[3]) == 0) {
277 putpop(s
, "+OK Aren't you a sight "
281 putpop(s
, "-ERR C'mon!");
283 } else if (strcasecmp(linebuf
, "STAT") == 0) {
286 for (i
= 0, j
= 0; i
< numfiles
; i
++) {
292 snprintf(linebuf
, sizeof(linebuf
),
293 "+OK %d %d", i
, (int) total
);
295 } else if (strncasecmp(linebuf
, "RETR ", 5) == 0) {
297 rc
= sscanf(linebuf
+ 5, "%d", &i
);
299 putpop(s
, "-ERR Whaaaa...?");
302 if (i
< 1 || i
> numfiles
) {
303 putpop(s
, "-ERR That message number is "
304 "out of range, jerkface!");
307 if (mfiles
[i
- 1] == NULL
) {
308 putpop(s
, "-ERR Sorry, don't have it anymore");
310 char *buf
= readmessage(mfiles
[i
- 1]);
311 putpop(s
, "+OK Here you go ...");
315 } else if (strncasecmp(linebuf
, "DELE ", 5) == 0) {
317 rc
= sscanf(linebuf
+ 5, "%d", &i
);
319 putpop(s
, "-ERR Whaaaa...?");
322 if (i
< 1 || i
> numfiles
) {
323 putpop(s
, "-ERR That message number is "
324 "out of range, jerkface!");
327 if (mfiles
[i
- 1] == NULL
) {
328 putpop(s
, "-ERR Um, didn't you tell me "
329 "to delete it already?");
331 fclose(mfiles
[i
- 1]);
332 mfiles
[i
- 1] = NULL
;
333 putpop(s
, "+OK Alright man, I got rid of it");
335 } else if (strcasecmp(linebuf
, "QUIT") == 0) {
336 putpop(s
, "+OK See ya, wouldn't want to be ya!");
340 putpop(s
, "-ERR Um, what?");
348 * Send one line to the POP client
352 putpop(int socket
, char *data
)
356 iov
[0].iov_base
= data
;
357 iov
[0].iov_len
= strlen(data
);
358 iov
[1].iov_base
= "\r\n";
361 if (writev(socket
, iov
, 2) < 0) {
367 * Put one big buffer to the POP server. Should have already had the line
368 * endings set up and dot-stuffed if necessary.
372 putpopbulk(int socket
, char *data
)
374 ssize_t datalen
= strlen(data
);
376 if (write(socket
, data
, datalen
) < 0) {
382 * Get one line from the POP server. We don't do any buffering here.
386 getpop(int socket
, char *data
, ssize_t len
)
392 cc
= read(socket
, data
+ offset
, len
- offset
);
395 fprintf(stderr
, "Read failed: %s\n", strerror(errno
));
406 fprintf(stderr
, "Input buffer overflow "
407 "(%d bytes)\n", (int) len
);
411 if (data
[offset
- 1] == '\n' && data
[offset
- 2] == '\r') {
412 data
[offset
- 2] = '\0';
418 #define HAVEROOM(buf, size, used, new) do { \
419 if (used + new > size - 1) { \
420 buf = realloc(buf, size += BUFALLOC); \
425 * Read a file and return it as one malloc()'d buffer. Convert \n to \r\n
426 * and dot-stuff if necessary.
430 readmessage(FILE *file
)
432 char *buffer
= malloc(BUFALLOC
);
433 ssize_t bufsize
= BUFALLOC
, used
= 0;
434 char linebuf
[LINESIZE
];
439 while (fgets(linebuf
, sizeof(linebuf
), file
)) {
440 if (strcmp(linebuf
, ".\n") == 0) {
441 HAVEROOM(buffer
, bufsize
, used
, 4);
442 strcat(buffer
, "..\r\n");
445 if (i
&& linebuf
[i
- 1] == '\n') {
446 HAVEROOM(buffer
, bufsize
, used
, i
+ 1);
447 linebuf
[i
- 1] = '\0';
448 strcat(buffer
, linebuf
);
449 strcat(buffer
, "\r\n");
451 HAVEROOM(buffer
, bufsize
, used
, i
);
452 strcat(buffer
, linebuf
);
458 * Put a terminating dot at the end
461 HAVEROOM(buffer
, bufsize
, used
, 3);
463 strcat(buffer
, ".\r\n");
475 handleterm(int signal
)
485 * Get rid of our pid file