X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/afee8ed17a56617618fb7f963b111d7cf0fb56e7..886127936eae6a5bff449d797abb4e8aa10f9186:/test/fakepop.c?ds=inline diff --git a/test/fakepop.c b/test/fakepop.c index bca565d1..bc2baae0 100644 --- a/test/fakepop.c +++ b/test/fakepop.c @@ -10,36 +10,28 @@ #include #include #include -#include #include -#include -#include #include -#include -#include -#include #include -#include #define PIDFILE "/tmp/fakepop.pid" #define LINESIZE 1024 #define BUFALLOC 4096 #define CHECKUSER() if (!user) { \ - putpop(s, "-ERR Aren't you forgetting " \ + putcrlf(s, "-ERR Aren't you forgetting " \ "something? Like the USER command?"); \ continue; \ } -#define CHECKUSERPASS() CHECKUSER() \ - if (! pass) { \ - putpop(s, "-ERR Um, hello? Forget to " \ +#define CHECKAUTH() if (!auth) { \ + putcrlf(s, "-ERR Um, hello? Forget to " \ "log in?"); \ continue; \ } -static void killpidfile(void); -static void handleterm(int); -static void putpop(int, char *); +void putcrlf(int, char *); +int serve(const char *, const char *); + static void putpopbulk(int, char *); static int getpop(int, char *, ssize_t); static char *readmessage(FILE *); @@ -47,187 +39,73 @@ static char *readmessage(FILE *); int main(int argc, char *argv[]) { - struct addrinfo hints, *res; - struct stat st; - FILE *f, *pid; + FILE **mfiles; char line[LINESIZE]; - fd_set readfd; - struct timeval tv; - pid_t child; - int octets = 0, rc, l, s, on, user = 0, pass = 0, deleted = 0; - - if (argc != 5) { - fprintf(stderr, "Usage: %s mail-file port username " - "password\n", argv[0]); - exit(1); - } - - if (!(f = fopen(argv[1], "r"))) { - fprintf(stderr, "Unable to open message file \"%s\": %s\n", - argv[1], strerror(errno)); + int rc, s, user = 0, auth = 0, i, j; + int numfiles; + size_t *octets; + const char *xoauth; + + if (argc < 5) { + fprintf(stderr, "Usage: %s port username " + "password mail-file [mail-file ...]\n", argv[0]); exit(1); } - /* - * POP wants the size of the maildrop in bytes, but with \r\n line - * endings. Calculate that. - */ - - while (fgets(line, sizeof(line), f)) { - octets += strlen(line); - if (strrchr(line, '\n')) - octets++; - } - - rewind(f); - - /* - * If there is a pid file around, kill the previously running - * fakepop process. - */ - - if (stat(PIDFILE, &st) == 0) { - long oldpid; - - if (!(pid = fopen(PIDFILE, "r"))) { - fprintf(stderr, "Cannot open " PIDFILE - " (%s), continuing ...\n", strerror(errno)); - } else { - rc = fscanf(pid, "%ld", &oldpid); - fclose(pid); - - if (rc != 1) { - fprintf(stderr, "Unable to parse pid in " - PIDFILE ", continuing ...\n"); - } else { - kill((pid_t) oldpid, SIGTERM); - } - } - - unlink(PIDFILE); + if (strcmp(argv[2], "XOAUTH") == 0) { + xoauth = argv[3]; + } else { + xoauth = NULL; } - memset(&hints, 0, sizeof(hints)); - - hints.ai_family = PF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - - rc = getaddrinfo("127.0.0.1", argv[2], &hints, &res); - - if (rc) { - fprintf(stderr, "Unable to resolve localhost/%s: %s\n", - argv[2], gai_strerror(rc)); - exit(1); - } + numfiles = argc - 4; - l = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + mfiles = malloc(sizeof(FILE *) * numfiles); - if (l == -1) { - fprintf(stderr, "Unable to create listening socket: %s\n", - strerror(errno)); + if (! mfiles) { + fprintf(stderr, "Unable to allocate %d bytes for file " + "array\n", (int) (sizeof(FILE *) * numfiles)); exit(1); } - on = 1; + octets = malloc(sizeof(*octets) * numfiles); - if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { - fprintf(stderr, "Unable to set SO_REUSEADDR: %s\n", - strerror(errno)); + if (! octets) { + fprintf(stderr, "Unable to allocate %d bytes for size " + "array\n", (int) (sizeof(FILE *) * numfiles)); exit(1); } - if (bind(l, res->ai_addr, res->ai_addrlen) == -1) { - fprintf(stderr, "Unable to bind socket: %s\n", strerror(errno)); - exit(1); - } - - if (listen(l, 1) == -1) { - fprintf(stderr, "Unable to listen on socket: %s\n", - strerror(errno)); - exit(1); - } - - /* - * Fork off a copy of ourselves, print out our child pid, then - * exit. - */ + for (i = 4, j = 0; i < argc; i++, j++) { + if (!(mfiles[j] = fopen(argv[i], "r"))) { + fprintf(stderr, "Unable to open message file \"%s\"" + ": %s\n", argv[i], strerror(errno)); + exit(1); + } - switch (child = fork()) { - case -1: - fprintf(stderr, "Unable to fork child: %s\n", strerror(errno)); - exit(1); - break; - case 0: /* - * Close stdin and stdout so $() in the shell will get an - * EOF. For now leave stderr open. + * POP wants the size of the maildrop in bytes, but + * with \r\n line endings. Calculate that. */ - fclose(stdin); - fclose(stdout); - break; - default: - printf("%ld\n", (long) child); - exit(0); - } - - /* - * Now that our socket and files are set up, wait 30 seconds for - * a connection. If there isn't one, then exit. - */ - - if (!(pid = fopen(PIDFILE, "w"))) { - fprintf(stderr, "Cannot open " PIDFILE ": %s\n", - strerror(errno)); - exit(1); - } - - fprintf(pid, "%ld\n", (long) getpid()); - fclose(pid); - - signal(SIGTERM, handleterm); - atexit(killpidfile); - - FD_ZERO(&readfd); - FD_SET(l, &readfd); - - tv.tv_sec = 30; - tv.tv_usec = 0; - - rc = select(l + 1, &readfd, NULL, NULL, &tv); - - if (rc < 0) { - fprintf(stderr, "select() failed: %s\n", strerror(errno)); - exit(1); - } - /* - * If we get a timeout, just silently exit - */ + octets[j] = 0; - if (rc == 0) { - exit(1); - } - - /* - * We got a connection; accept it. Right after that close our - * listening socket so we won't get any more connections on it. - */ + while (fgets(line, sizeof(line), mfiles[j])) { + octets[j] += strlen(line); + if (strrchr(line, '\n')) + octets[j]++; + } - if ((s = accept(l, NULL, NULL)) == -1) { - fprintf(stderr, "Unable to accept connection: %s\n", - strerror(errno)); - exit(1); + rewind(mfiles[j]); } - close(l); + s = serve(PIDFILE, argv[1]); /* * Pretend to be a POP server */ - putpop(s, "+OK Not really a POP server, but we play one on TV"); + putcrlf(s, "+OK Not really a POP server, but we play one on TV"); for (;;) { char linebuf[LINESIZE]; @@ -239,86 +117,103 @@ main(int argc, char *argv[]) if (strcasecmp(linebuf, "CAPA") == 0) { putpopbulk(s, "+OK We have no capabilities, really\r\n" - "FAKE-CAPABILITY\r\n.\r\n"); + "FAKE-CAPABILITY\r\n"); + if (xoauth != NULL) { + putcrlf(s, "SASL XOAUTH2"); + } + putcrlf(s, "."); } else if (strncasecmp(linebuf, "USER ", 5) == 0) { - if (strcmp(linebuf + 5, argv[3]) == 0) { - putpop(s, "+OK Niiiice!"); + if (strcmp(linebuf + 5, argv[2]) == 0) { + putcrlf(s, "+OK Niiiice!"); user = 1; } else { - putpop(s, "-ERR Don't play me, bro!"); + putcrlf(s, "-ERR Don't play me, bro!"); } } else if (strncasecmp(linebuf, "PASS ", 5) == 0) { CHECKUSER(); - if (strcmp(linebuf + 5, argv[4]) == 0) { - putpop(s, "+OK Aren't you a sight " + if (strcmp(linebuf + 5, argv[3]) == 0) { + putcrlf(s, "+OK Aren't you a sight " "for sore eyes!"); - pass = 1; + auth = 1; } else { - putpop(s, "-ERR C'mon!"); + putcrlf(s, "-ERR C'mon!"); } + } else if (xoauth != NULL + && strncasecmp(linebuf, "AUTH XOAUTH2", 12) == 0) { + if (strstr(linebuf, xoauth) == NULL) { + putcrlf(s, "+ base64-json-err"); + rc = getpop(s, linebuf, sizeof(linebuf)); + if (rc != 0) + break; /* Error or EOF */ + putcrlf(s, "-ERR [AUTH] Invalid credentials."); + continue; + } + putcrlf(s, "+OK Welcome."); + auth = 1; } else if (strcasecmp(linebuf, "STAT") == 0) { - CHECKUSERPASS(); - if (deleted) { - strncpy(linebuf, "+OK 0 0", sizeof(linebuf)); - } else { - snprintf(linebuf, sizeof(linebuf), - "+OK 1 %d", octets); + size_t total = 0; + CHECKAUTH(); + for (i = 0, j = 0; i < numfiles; i++) { + if (mfiles[i]) { + total += octets[i]; + j++; + } } - putpop(s, linebuf); - } else if (strcasecmp(linebuf, "RETR 1") == 0) { - CHECKUSERPASS(); - if (deleted) { - putpop(s, "-ERR Sorry, don't have it anymore"); + snprintf(linebuf, sizeof(linebuf), + "+OK %d %d", i, (int) total); + putcrlf(s, linebuf); + } else if (strncasecmp(linebuf, "RETR ", 5) == 0) { + CHECKAUTH(); + rc = sscanf(linebuf + 5, "%d", &i); + if (rc != 1) { + putcrlf(s, "-ERR Whaaaa...?"); + continue; + } + if (i < 1 || i > numfiles) { + putcrlf(s, "-ERR That message number is " + "out of range, jerkface!"); + continue; + } + if (mfiles[i - 1] == NULL) { + putcrlf(s, "-ERR Sorry, don't have it anymore"); } else { - char *buf = readmessage(f); - putpop(s, "+OK Here you go ..."); + char *buf = readmessage(mfiles[i - 1]); + putcrlf(s, "+OK Here you go ..."); putpopbulk(s, buf); free(buf); } - } else if (strncasecmp(linebuf, "RETR ", 5) == 0) { - CHECKUSERPASS(); - putpop(s, "-ERR Sorry man, out of range!"); - } else if (strcasecmp(linebuf, "DELE 1") == 0) { - CHECKUSERPASS(); - if (deleted) { - putpop(s, "-ERR Um, didn't you tell me " + } else if (strncasecmp(linebuf, "DELE ", 5) == 0) { + CHECKAUTH(); + rc = sscanf(linebuf + 5, "%d", &i); + if (rc != 1) { + putcrlf(s, "-ERR Whaaaa...?"); + continue; + } + if (i < 1 || i > numfiles) { + putcrlf(s, "-ERR That message number is " + "out of range, jerkface!"); + continue; + } + if (mfiles[i - 1] == NULL) { + putcrlf(s, "-ERR Um, didn't you tell me " "to delete it already?"); } else { - putpop(s, "+OK Alright man, I got rid of it"); - deleted = 1; + fclose(mfiles[i - 1]); + mfiles[i - 1] = NULL; + putcrlf(s, "+OK Alright man, I got rid of it"); } - } else if (strncasecmp(linebuf, "DELE ", 5) == 0) { - CHECKUSERPASS(); - putpop(s, "-ERR Sorry man, out of range!"); } else if (strcasecmp(linebuf, "QUIT") == 0) { - putpop(s, "+OK See ya, wouldn't want to be ya!"); + putcrlf(s, "+OK See ya, wouldn't want to be ya!"); close(s); break; } else { - putpop(s, "-ERR Um, what?"); + putcrlf(s, "-ERR Um, what?"); } } exit(0); } -/* - * Send one line to the POP client - */ - -static void -putpop(int socket, char *data) -{ - struct iovec iov[2]; - - iov[0].iov_base = data; - iov[0].iov_len = strlen(data); - iov[1].iov_base = "\r\n"; - iov[1].iov_len = 2; - - writev(socket, iov, 2); -} - /* * Put one big buffer to the POP server. Should have already had the line * endings set up and dot-stuffed if necessary. @@ -329,7 +224,9 @@ putpopbulk(int socket, char *data) { ssize_t datalen = strlen(data); - write(socket, data, datalen); + if (write(socket, data, datalen) < 0) { + perror ("write"); + } } /* @@ -416,29 +313,7 @@ readmessage(FILE *file) strcat(buffer, ".\r\n"); - return buffer; -} + rewind(file); -/* - * Handle a SIGTERM - */ - -static void -handleterm(int signal) -{ - (void) signal; - - killpidfile(); - fflush(NULL); - _exit(1); -} - -/* - * Get rid of our pid file - */ - -static void -killpidfile(void) -{ - unlink(PIDFILE); + return buffer; }