]> diplodocus.org Git - nmh/blob - test/fakesmtp.c
449fdec0ee908d5b9ccc8aa7d2a80ccdccc5a607
[nmh] / test / fakesmtp.c
1 /*
2 * fakesmtp - A fake SMTP 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/socket.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17
18 #define PIDFILE "/tmp/fakesmtp.pid"
19
20 #define LINESIZE 1024
21
22 enum {
23 /* Processing top-level SMTP commands (e.g. EHLO, DATA). */
24 SMTP_TOP,
25
26 /* Processing payload of a DATA command. */
27 SMTP_DATA,
28
29 /* Looking for the blank line required by XOAUTH2 after 334 response. */
30 SMTP_XOAUTH_ERR
31 };
32
33 void putcrlf(int, char *);
34 int serve(const char *, const char *);
35
36 static int getsmtp(int, char *);
37
38 int
39 main(int argc, char *argv[])
40 {
41 int rc, conn, smtp_state;
42 FILE *f;
43 const char *xoauth = getenv("XOAUTH");
44
45 if (argc != 3) {
46 fprintf(stderr, "Usage: %s output-filename port\n", argv[0]);
47 exit(1);
48 }
49
50 if (!(f = fopen(argv[1], "w"))) {
51 fprintf(stderr, "Unable to open output file \"%s\": %s\n",
52 argv[1], strerror(errno));
53 exit(1);
54 }
55
56 conn = serve(PIDFILE, argv[2]);
57
58 /*
59 * Pretend to be an SMTP server.
60 */
61
62 putcrlf(conn, "220 Not really an ESMTP server");
63 smtp_state = SMTP_TOP;
64
65 for (;;) {
66 char line[LINESIZE];
67
68 rc = getsmtp(conn, line);
69
70 if (rc == -1)
71 break; /* EOF */
72
73 fprintf(f, "%s\n", line);
74
75 switch (smtp_state) {
76 case SMTP_DATA:
77 if (strcmp(line, ".") == 0) {
78 smtp_state = SMTP_TOP;
79 putcrlf(conn, "250 Thanks for the info!");
80 }
81 continue;
82 case SMTP_XOAUTH_ERR:
83 smtp_state = SMTP_TOP;
84 putcrlf(conn, "535 Not no way, not no how!");
85 continue;
86 }
87
88 /*
89 * Most commands we ignore and send the same response to.
90 */
91
92 if (strcmp(line, "QUIT") == 0) {
93 fclose(f);
94 f = NULL;
95 putcrlf(conn, "221 Later alligator!");
96 close(conn);
97 break;
98 }
99 if (strcmp(line, "DATA") == 0) {
100 putcrlf(conn, "354 Go ahead");
101 smtp_state = SMTP_DATA;
102 continue;
103 }
104 if (xoauth == NULL) {
105 putcrlf(conn, "250-ready");
106 putcrlf(conn, "250-8BITMIME");
107 putcrlf(conn, "250-SMTPUTF8");
108 } else {
109 /* XOAUTH2 support enabled; handle EHLO and AUTH. */
110 if (strncmp(line, "EHLO", 4) == 0) {
111 putcrlf(conn, "250-ready");
112 putcrlf(conn, "250 AUTH XOAUTH2");
113 continue;
114 }
115 if (strncmp(line, "AUTH", 4) == 0) {
116 if (strncmp(line, "AUTH XOAUTH2", 12) == 0
117 && strstr(line, xoauth) != NULL) {
118 putcrlf(conn, "235 OK come in");
119 continue;
120 }
121 putcrlf(conn, "334 base64-json-err");
122 smtp_state = SMTP_XOAUTH_ERR;
123 continue;
124 }
125 }
126 putcrlf(conn, "250 I'll buy that for a dollar!");
127 }
128
129 if (f)
130 fclose(f);
131
132 exit(0);
133 }
134
135 /*
136 * Read a line (up to the \r\n)
137 */
138
139 static int
140 getsmtp(int socket, char *data)
141 {
142 int cc;
143 static unsigned int bytesinbuf = 0;
144 static char buffer[LINESIZE * 2], *p;
145
146 for (;;) {
147 /*
148 * Find our \r\n
149 */
150
151 if (bytesinbuf > 0 && (p = strchr(buffer, '\r')) &&
152 *(p + 1) == '\n') {
153 *p = '\0';
154 strncpy(data, buffer, LINESIZE);
155 data[LINESIZE - 1] = '\0';
156 cc = strlen(buffer);
157
158 /*
159 * Shuffle leftover bytes back to the beginning
160 */
161
162 bytesinbuf -= cc + 2; /* Don't forget \r\n */
163 if (bytesinbuf > 0) {
164 memmove(buffer, buffer + cc + 2, bytesinbuf);
165 }
166 return cc;
167 }
168
169 if (bytesinbuf >= sizeof(buffer)) {
170 fprintf(stderr, "Buffer overflow in getsmtp()!\n");
171 exit(1);
172 }
173
174 memset(buffer + bytesinbuf, 0, sizeof(buffer) - bytesinbuf);
175 cc = read(socket, buffer + bytesinbuf,
176 sizeof(buffer) - bytesinbuf);
177
178 if (cc < 0) {
179 fprintf(stderr, "Read failed: %s\n", strerror(errno));
180 exit(1);
181 }
182
183 if (cc == 0)
184 return -1;
185
186 bytesinbuf += cc;
187 }
188 }