]>
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 octets
= 0, rc
, l
, s
, on
, user
= 0, pass
= 0, deleted
= 0;
60 fprintf(stderr
, "Usage: %s mail-file port username "
61 "password\n", argv
[0]);
65 if (!(f
= fopen(argv
[1], "r"))) {
66 fprintf(stderr
, "Unable to open message file \"%s\": %s\n",
67 argv
[1], strerror(errno
));
72 * POP wants the size of the maildrop in bytes, but with \r\n line
73 * endings. Calculate that.
76 while (fgets(line
, sizeof(line
), f
)) {
77 octets
+= strlen(line
);
78 if (strrchr(line
, '\n'))
85 * If there is a pid file around, kill the previously running
89 if (stat(PIDFILE
, &st
) == 0) {
92 if (!(pid
= fopen(PIDFILE
, "r"))) {
93 fprintf(stderr
, "Cannot open " PIDFILE
94 " (%s), continuing ...\n", strerror(errno
));
96 rc
= fscanf(pid
, "%ld", &oldpid
);
100 fprintf(stderr
, "Unable to parse pid in "
101 PIDFILE
", continuing ...\n");
103 kill((pid_t
) oldpid
, SIGTERM
);
110 memset(&hints
, 0, sizeof(hints
));
112 hints
.ai_family
= PF_INET
;
113 hints
.ai_socktype
= SOCK_STREAM
;
114 hints
.ai_protocol
= IPPROTO_TCP
;
115 hints
.ai_flags
= AI_PASSIVE
;
117 rc
= getaddrinfo("127.0.0.1", argv
[2], &hints
, &res
);
120 fprintf(stderr
, "Unable to resolve localhost/%s: %s\n",
121 argv
[2], gai_strerror(rc
));
125 l
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
128 fprintf(stderr
, "Unable to create listening socket: %s\n",
135 if (setsockopt(l
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1) {
136 fprintf(stderr
, "Unable to set SO_REUSEADDR: %s\n",
141 if (bind(l
, res
->ai_addr
, res
->ai_addrlen
) == -1) {
142 fprintf(stderr
, "Unable to bind socket: %s\n", strerror(errno
));
146 if (listen(l
, 1) == -1) {
147 fprintf(stderr
, "Unable to listen on socket: %s\n",
153 * Fork off a copy of ourselves, print out our child pid, then
157 switch (child
= fork()) {
159 fprintf(stderr
, "Unable to fork child: %s\n", strerror(errno
));
164 * Close stdin and stdout so $() in the shell will get an
165 * EOF. For now leave stderr open.
171 printf("%ld\n", (long) child
);
176 * Now that our socket and files are set up, wait 30 seconds for
177 * a connection. If there isn't one, then exit.
180 if (!(pid
= fopen(PIDFILE
, "w"))) {
181 fprintf(stderr
, "Cannot open " PIDFILE
": %s\n",
186 fprintf(pid
, "%ld\n", (long) getpid());
189 signal(SIGTERM
, handleterm
);
198 rc
= select(l
+ 1, &readfd
, NULL
, NULL
, &tv
);
201 fprintf(stderr
, "select() failed: %s\n", strerror(errno
));
206 * If we get a timeout, just silently exit
214 * We got a connection; accept it. Right after that close our
215 * listening socket so we won't get any more connections on it.
218 if ((s
= accept(l
, NULL
, NULL
)) == -1) {
219 fprintf(stderr
, "Unable to accept connection: %s\n",
227 * Pretend to be a POP server
230 putpop(s
, "+OK Not really a POP server, but we play one on TV");
233 char linebuf
[LINESIZE
];
235 rc
= getpop(s
, linebuf
, sizeof(linebuf
));
238 break; /* Error or EOF */
240 if (strcasecmp(linebuf
, "CAPA") == 0) {
241 putpopbulk(s
, "+OK We have no capabilities, really\r\n"
242 "FAKE-CAPABILITY\r\n.\r\n");
243 } else if (strncasecmp(linebuf
, "USER ", 5) == 0) {
244 if (strcmp(linebuf
+ 5, argv
[3]) == 0) {
245 putpop(s
, "+OK Niiiice!");
248 putpop(s
, "-ERR Don't play me, bro!");
250 } else if (strncasecmp(linebuf
, "PASS ", 5) == 0) {
252 if (strcmp(linebuf
+ 5, argv
[4]) == 0) {
253 putpop(s
, "+OK Aren't you a sight "
257 putpop(s
, "-ERR C'mon!");
259 } else if (strcasecmp(linebuf
, "STAT") == 0) {
262 strncpy(linebuf
, "+OK 0 0", sizeof(linebuf
));
264 snprintf(linebuf
, sizeof(linebuf
),
268 } else if (strcasecmp(linebuf
, "RETR 1") == 0) {
271 putpop(s
, "-ERR Sorry, don't have it anymore");
273 char *buf
= readmessage(f
);
274 putpop(s
, "+OK Here you go ...");
278 } else if (strncasecmp(linebuf
, "RETR ", 5) == 0) {
280 putpop(s
, "-ERR Sorry man, out of range!");
281 } else if (strcasecmp(linebuf
, "DELE 1") == 0) {
284 putpop(s
, "-ERR Um, didn't you tell me "
285 "to delete it already?");
287 putpop(s
, "+OK Alright man, I got rid of it");
290 } else if (strncasecmp(linebuf
, "DELE ", 5) == 0) {
292 putpop(s
, "-ERR Sorry man, out of range!");
293 } else if (strcasecmp(linebuf
, "QUIT") == 0) {
294 putpop(s
, "+OK See ya, wouldn't want to be ya!");
298 putpop(s
, "-ERR Um, what?");
306 * Send one line to the POP client
310 putpop(int socket
, char *data
)
314 iov
[0].iov_base
= data
;
315 iov
[0].iov_len
= strlen(data
);
316 iov
[1].iov_base
= "\r\n";
319 writev(socket
, iov
, 2);
323 * Put one big buffer to the POP server. Should have already had the line
324 * endings set up and dot-stuffed if necessary.
328 putpopbulk(int socket
, char *data
)
330 ssize_t datalen
= strlen(data
);
332 write(socket
, data
, datalen
);
336 * Get one line from the POP server. We don't do any buffering here.
340 getpop(int socket
, char *data
, ssize_t len
)
346 cc
= read(socket
, data
+ offset
, len
- offset
);
349 fprintf(stderr
, "Read failed: %s\n", strerror(errno
));
360 fprintf(stderr
, "Input buffer overflow "
361 "(%d bytes)\n", (int) len
);
365 if (data
[offset
- 1] == '\n' && data
[offset
- 2] == '\r') {
366 data
[offset
- 2] = '\0';
372 #define HAVEROOM(buf, size, used, new) do { \
373 if (used + new > size - 1) { \
374 buf = realloc(buf, size += BUFALLOC); \
379 * Read a file and return it as one malloc()'d buffer. Convert \n to \r\n
380 * and dot-stuff if necessary.
384 readmessage(FILE *file
)
386 char *buffer
= malloc(BUFALLOC
);
387 ssize_t bufsize
= BUFALLOC
, used
= 0;
388 char linebuf
[LINESIZE
];
393 while (fgets(linebuf
, sizeof(linebuf
), file
)) {
394 if (strcmp(linebuf
, ".\n") == 0) {
395 HAVEROOM(buffer
, bufsize
, used
, 4);
396 strcat(buffer
, "..\r\n");
399 if (i
&& linebuf
[i
- 1] == '\n') {
400 HAVEROOM(buffer
, bufsize
, used
, i
+ 1);
401 linebuf
[i
- 1] = '\0';
402 strcat(buffer
, linebuf
);
403 strcat(buffer
, "\r\n");
405 HAVEROOM(buffer
, bufsize
, used
, i
);
406 strcat(buffer
, linebuf
);
412 * Put a terminating dot at the end
415 HAVEROOM(buffer
, bufsize
, used
, 3);
417 strcat(buffer
, ".\r\n");
427 handleterm(int signal
)
437 * Get rid of our pid file