30a1daf1053560ac4da23c87a76521c7681e9a58
[citadel.git] / citadel / serv_smtp.c
1 /* $Id$ */
2
3 #define SMTP_PORT       2525
4
5 #include "sysdep.h"
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <stdio.h>
9 #include <fcntl.h>
10 #include <signal.h>
11 #include <pwd.h>
12 #include <errno.h>
13 #include <sys/types.h>
14 #include <sys/time.h>
15 #include <sys/wait.h>
16 #include <string.h>
17 #include <limits.h>
18 #include "citadel.h"
19 #include "server.h"
20 #include <time.h>
21 #include "sysdep_decls.h"
22 #include "citserver.h"
23 #include "support.h"
24 #include "config.h"
25 #include "dynloader.h"
26 #include "room_ops.h"
27 #include "user_ops.h"
28 #include "policy.h"
29 #include "database.h"
30 #include "msgbase.h"
31 #include "tools.h"
32 #include "internet_addressing.h"
33
34 struct citsmtp {
35         int command_state;
36         struct usersupp vrfy_buffer;
37         int vrfy_count;
38         char vrfy_match[256];
39 };
40
41 enum {
42         smtp_command,
43         smtp_user,
44         smtp_password
45 };
46
47 #define SMTP ((struct citsmtp *)CtdlGetUserData(SYM_SMTP))
48
49 long SYM_SMTP;
50
51
52 /*
53  * Here's where our SMTP session begins its happy day.
54  */
55 void smtp_greeting(void) {
56
57         strcpy(CC->cs_clientname, "Citadel SMTP");
58         CtdlAllocUserData(SYM_SMTP, sizeof(struct citsmtp));
59
60         cprintf("220 Welcome to the Citadel/UX ESMTP server at %s\n",
61                 config.c_fqdn);
62 }
63
64
65 /*
66  * Implement HELO and EHLO commands.
67  */
68 void smtp_hello(char *argbuf, int is_esmtp) {
69
70         if (!is_esmtp) {
71                 cprintf("250 Greetings and joyous salutations.\n");
72         }
73         else {
74                 cprintf("250-Greetings and joyous salutations.\n");
75                 cprintf("250-HELP\n");
76                 cprintf("250-SIZE %ld\n", config.c_maxmsglen);
77                 cprintf("250 AUTH=LOGIN\n");
78         }
79 }
80
81
82 /*
83  * Implement HELP command.
84  */
85 void smtp_help(void) {
86         cprintf("214-Here's the frequency, Kenneth:\n");
87         cprintf("214-    EHLO\n");
88         cprintf("214-    EXPN\n");
89         cprintf("214-    HELO\n");
90         cprintf("214-    HELP\n");
91         cprintf("214-    NOOP\n");
92         cprintf("214-    QUIT\n");
93         cprintf("214-    RSET\n");
94         cprintf("214-    VRFY\n");
95         cprintf("214 I could tell you more, but then I'd have to kill you.\n");
96 }
97
98
99 /*
100  *
101  */
102 void smtp_get_user(char *argbuf) {
103         char buf[256];
104         char username[256];
105
106         decode_base64(username, argbuf);
107         lprintf(9, "Trying <%s>\n", username);
108         if (CtdlLoginExistingUser(username) == login_ok) {
109                 encode_base64(buf, "Password:");
110                 cprintf("334 %s\n", buf);
111                 SMTP->command_state = smtp_password;
112         }
113         else {
114                 cprintf("500 No such user.\n");
115                 SMTP->command_state = smtp_command;
116         }
117 }
118
119
120 /*
121  *
122  */
123 void smtp_get_pass(char *argbuf) {
124         char password[256];
125
126         decode_base64(password, argbuf);
127         lprintf(9, "Trying <%s>\n", password);
128         if (CtdlTryPassword(password) == pass_ok) {
129                 cprintf("235 Authentication successful.\n");
130                 lprintf(9, "SMTP auth login successful\n");
131         }
132         else {
133                 cprintf("500 Authentication failed.\n");
134         }
135         SMTP->command_state = smtp_command;
136 }
137
138
139 /*
140  *
141  */
142 void smtp_auth(char *argbuf) {
143         char buf[256];
144
145         if (strncasecmp(argbuf, "login", 5) ) {
146                 cprintf("550 We only support LOGIN authentication.\n");
147                 return;
148         }
149
150         if (strlen(argbuf) >= 7) {
151                 smtp_get_user(&argbuf[6]);
152         }
153
154         else {
155                 encode_base64(buf, "Username:");
156                 cprintf("334 %s\n", buf);
157                 SMTP->command_state = smtp_user;
158         }
159 }
160
161
162 /*
163  * Back end for smtp_vrfy() command
164  */
165 void smtp_vrfy_backend(struct usersupp *us) {
166
167         if (!fuzzy_match(us, SMTP->vrfy_match)) {
168                 ++SMTP->vrfy_count;
169                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
170         }
171 }
172
173
174 /* 
175  * Implements the VRFY (verify user name) command.
176  * Performs fuzzy match on full user names.
177  */
178 void smtp_vrfy(char *argbuf) {
179         SMTP->vrfy_count = 0;
180         strcpy(SMTP->vrfy_match, argbuf);
181         ForEachUser(smtp_vrfy_backend);
182
183         if (SMTP->vrfy_count < 1) {
184                 cprintf("550 String does not match anything.\n");
185         }
186         else if (SMTP->vrfy_count == 1) {
187                 cprintf("250 %s <cit%ld@%s>\n",
188                         SMTP->vrfy_buffer.fullname,
189                         SMTP->vrfy_buffer.usernum,
190                         config.c_fqdn);
191         }
192         else if (SMTP->vrfy_count > 1) {
193                 cprintf("553 Request ambiguous: %d users matched.\n",
194                         SMTP->vrfy_count);
195         }
196
197 }
198
199
200
201 /*
202  * Back end for smtp_expn() command
203  */
204 void smtp_expn_backend(struct usersupp *us) {
205
206         if (!fuzzy_match(us, SMTP->vrfy_match)) {
207
208                 if (SMTP->vrfy_count >= 1) {
209                         cprintf("250-%s <cit%ld@%s>\n",
210                                 SMTP->vrfy_buffer.fullname,
211                                 SMTP->vrfy_buffer.usernum,
212                                 config.c_fqdn);
213                 }
214
215                 ++SMTP->vrfy_count;
216                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct usersupp));
217         }
218 }
219
220
221 /* 
222  * Implements the EXPN (expand user name) command.
223  * Performs fuzzy match on full user names.
224  */
225 void smtp_expn(char *argbuf) {
226         SMTP->vrfy_count = 0;
227         strcpy(SMTP->vrfy_match, argbuf);
228         ForEachUser(smtp_expn_backend);
229
230         if (SMTP->vrfy_count < 1) {
231                 cprintf("550 String does not match anything.\n");
232         }
233         else if (SMTP->vrfy_count >= 1) {
234                 cprintf("250 %s <cit%ld@%s>\n",
235                         SMTP->vrfy_buffer.fullname,
236                         SMTP->vrfy_buffer.usernum,
237                         config.c_fqdn);
238         }
239 }
240
241
242 /*
243  * Implements the RSET (reset state) command.
244  * Currently this just zeroes out the state buffer.  If pointers to data
245  * allocated with mallok() are ever placed in the state buffer, we have to
246  * be sure to phree() them first!
247  */
248 void smtp_rset(void) {
249         memset(SMTP, 0, sizeof(struct citsmtp));
250         cprintf("250 Zap!\n");
251 }
252
253
254
255 /* 
256  * Main command loop for SMTP sessions.
257  */
258 void smtp_command_loop(void) {
259         char cmdbuf[256];
260
261         time(&CC->lastcmd);
262         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
263         if (client_gets(cmdbuf) < 1) {
264                 lprintf(3, "SMTP socket is broken.  Ending session.\n");
265                 CC->kill_me = 1;
266                 return;
267         }
268         lprintf(5, "citserver[%3d]: %s\n", CC->cs_pid, cmdbuf);
269         while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
270
271         if (SMTP->command_state == smtp_user) {
272                 smtp_get_user(cmdbuf);
273         }
274
275         else if (SMTP->command_state == smtp_password) {
276                 smtp_get_pass(cmdbuf);
277         }
278
279         else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
280                 smtp_auth(&cmdbuf[5]);
281         }
282
283         else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
284                 smtp_hello(&cmdbuf[5], 1);
285         }
286
287         else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
288                 smtp_expn(&cmdbuf[5]);
289         }
290
291         else if (!strncasecmp(cmdbuf, "HELO", 4)) {
292                 smtp_hello(&cmdbuf[5], 0);
293         }
294
295         else if (!strncasecmp(cmdbuf, "HELP", 4)) {
296                 smtp_help();
297         }
298
299         else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
300                 cprintf("250 This command successfully did nothing.\n");
301         }
302
303         else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
304                 cprintf("221 Goodbye...\n");
305                 CC->kill_me = 1;
306                 return;
307                 }
308
309         else if (!strncasecmp(cmdbuf, "RSET", 4)) {
310                 smtp_rset();
311         }
312
313         else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
314                 smtp_vrfy(&cmdbuf[5]);
315         }
316
317         else {
318                 cprintf("500 I'm afraid I can't do that, Dave.\n");
319         }
320
321 }
322
323
324
325 char *Dynamic_Module_Init(void)
326 {
327         SYM_SMTP = CtdlGetDynamicSymbol();
328         CtdlRegisterServiceHook(SMTP_PORT,
329                                 smtp_greeting,
330                                 smtp_command_loop);
331         return "$Id$";
332 }