]> diplodocus.org Git - nmh/blob - test/fakesmtp.c
Wrapped code block of conditional statement with {}, so that the
[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 /* XOAUTH2 support enabled; handle EHLO and AUTH. */
106 if (strncmp(line, "EHLO", 4) == 0) {
107 putcrlf(conn, "250-ready");
108 putcrlf(conn, "250 AUTH XOAUTH2");
109 continue;
110 }
111 if (strncmp(line, "AUTH", 4) == 0) {
112 if (strncmp(line, "AUTH XOAUTH2", 12) == 0
113 && strstr(line, xoauth) != NULL) {
114 putcrlf(conn, "235 OK come in");
115 continue;
116 }
117 putcrlf(conn, "334 base64-json-err");
118 smtp_state = SMTP_XOAUTH_ERR;
119 continue;
120 }
121 }
122 putcrlf(conn, "250 I'll buy that for a dollar!");
123 }
124
125 if (f)
126 fclose(f);
127
128 exit(0);
129 }
130
131 /*
132 * Read a line (up to the \r\n)
133 */
134
135 static int
136 getsmtp(int socket, char *data)
137 {
138 int cc;
139 static unsigned int bytesinbuf = 0;
140 static char buffer[LINESIZE * 2], *p;
141
142 for (;;) {
143 /*
144 * Find our \r\n
145 */
146
147 if (bytesinbuf > 0 && (p = strchr(buffer, '\r')) &&
148 *(p + 1) == '\n') {
149 *p = '\0';
150 strncpy(data, buffer, LINESIZE);
151 data[LINESIZE - 1] = '\0';
152 cc = strlen(buffer);
153
154 /*
155 * Shuffle leftover bytes back to the beginning
156 */
157
158 bytesinbuf -= cc + 2; /* Don't forget \r\n */
159 if (bytesinbuf > 0) {
160 memmove(buffer, buffer + cc + 2, bytesinbuf);
161 }
162 return cc;
163 }
164
165 if (bytesinbuf >= sizeof(buffer)) {
166 fprintf(stderr, "Buffer overflow in getsmtp()!\n");
167 exit(1);
168 }
169
170 memset(buffer + bytesinbuf, 0, sizeof(buffer) - bytesinbuf);
171 cc = read(socket, buffer + bytesinbuf,
172 sizeof(buffer) - bytesinbuf);
173
174 if (cc < 0) {
175 fprintf(stderr, "Read failed: %s\n", strerror(errno));
176 exit(1);
177 }
178
179 if (cc == 0)
180 return -1;
181
182 bytesinbuf += cc;
183 }
184 }