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