* Created the framework for the "first time setup wizard"
[citadel.git] / webcit / auth.c
1 /*
2  * $Id$
3  *
4  * Handles authentication of users to a Citadel server.
5  *
6  */
7
8
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <sys/socket.h>
18 #include <sys/time.h>
19 #include <limits.h>
20 #include <netinet/in.h>
21 #include <netdb.h>
22 #include <string.h>
23 #include <pwd.h>
24 #include <errno.h>
25 #include <stdarg.h>
26 #include <pthread.h>
27 #include <signal.h>
28 #include "webcit.h"
29
30 char *axdefs[] =
31 {
32         "Deleted",
33         "New User",
34         "Problem User",
35         "Local User",
36         "Network User",
37         "Preferred User",
38         "Aide"
39 };
40
41 /*
42  * Display the login screen
43  */
44 void display_login(char *mesg)
45 {
46         char buf[SIZ];
47
48         output_headers(1, 1, 2, 0, 0, 0, 0);
49         wprintf("<div style=\"position:absolute; top:20px; left:20px; right:20px\">\n");
50
51         if (mesg != NULL) if (strlen(mesg) > 0) {
52                 stresc(buf, mesg, 0, 0);
53                 svprintf("mesg", WCS_STRING, "%s", buf);
54         }
55
56         svprintf("hello", WCS_SERVCMD, "MESG hello");
57         svprintf("BOXTITLE", WCS_STRING, "%s - powered by Citadel",
58                 serv_info.serv_humannode);
59
60         do_template("login");
61
62         wDumpContent(2);
63 }
64
65
66
67
68 /*
69  * This function needs to get called whenever the session changes from
70  * not-logged-in to logged-in, either by an explicit login by the user or
71  * by a timed-out session automatically re-establishing with a little help
72  * from the browser cookie.  Either way, we need to load access controls and
73  * preferences from the server.
74  */
75 void become_logged_in(char *user, char *pass, char *serv_response)
76 {
77         char buf[SIZ];
78
79         WC->logged_in = 1;
80         extract(WC->wc_username, &serv_response[4], 0);
81         strcpy(WC->wc_password, pass);
82         WC->axlevel = extract_int(&serv_response[4], 1);
83         if (WC->axlevel >= 6) {
84                 WC->is_aide = 1;
85         }
86
87         load_preferences();
88
89         serv_puts("CHEK");
90         serv_gets(buf);
91         if (buf[0] == '2') {
92                 WC->new_mail = extract_int(&buf[4], 0);
93                 WC->need_regi = extract_int(&buf[4], 1);
94                 WC->need_vali = extract_int(&buf[4], 2);
95                 extract(WC->cs_inet_email, &buf[4], 3);
96         }
97 }
98
99
100 void do_login(void)
101 {
102         char buf[SIZ];
103
104         if (!strcasecmp(bstr("action"), "Exit")) {
105                 do_logout();
106                 return;
107         }
108         if (!strcasecmp(bstr("action"), "Login")) {
109                 serv_printf("USER %s", bstr("name"));
110                 serv_gets(buf);
111                 if (buf[0] == '3') {
112                         serv_printf("PASS %s", bstr("pass"));
113                         serv_gets(buf);
114                         if (buf[0] == '2') {
115                                 become_logged_in(bstr("name"),
116                                                  bstr("pass"), buf);
117                         } else {
118                                 display_login(&buf[4]);
119                                 return;
120                         }
121                 } else {
122                         display_login(&buf[4]);
123                         return;
124                 }
125         }
126         if (!strcasecmp(bstr("action"), "New User")) {
127                 if (strlen(bstr("pass")) == 0) {
128                         display_login("Blank passwords are not allowed.");
129                         return;
130                 }
131                 serv_printf("NEWU %s", bstr("name"));
132                 serv_gets(buf);
133                 if (buf[0] == '2') {
134                         become_logged_in(bstr("name"), bstr("pass"), buf);
135                         serv_printf("SETP %s", bstr("pass"));
136                         serv_gets(buf);
137                 } else {
138                         display_login(&buf[4]);
139                         return;
140                 }
141         }
142         if (WC->logged_in) {
143                 if (WC->need_regi) {
144                         display_reg(1);
145                 } else {
146                         do_welcome();
147                 }
148         } else {
149                 display_login("Your password was not accepted.");
150         }
151
152 }
153
154 void do_welcome(void)
155 {
156         char buf[SIZ];
157         FILE *fp;
158         int i;
159
160         /*
161          * See if we have to run the first-time setup wizard
162          */
163         if (WC->is_aide) {
164                 if (!setup_wizard) {
165                         sprintf(wizard_filename, "setupwiz.%s.%s",
166                                 ctdlhost, ctdlport);
167                         for (i=0; i<strlen(wizard_filename); ++i) {
168                                 if (    (wizard_filename[i]==' ')
169                                         || (wizard_filename[i] == '/')
170                                 ) {
171                                         wizard_filename[i] = '_';
172                                 }
173                         }
174         
175                         fp = fopen(wizard_filename, "r");
176                         if (fp != NULL) {
177                                 fgets(buf, sizeof buf, fp);
178                                 buf[strlen(buf)-1] = 0;
179                                 fclose(fp);
180                                 if (atoi(buf) == serv_info.serv_rev_level) {
181                                         setup_wizard = 1; /* already run */
182                                 }
183                         }
184                 }
185
186                 if (!setup_wizard) {
187                         http_redirect("/setup_wizard");
188                 }
189         }
190
191         /*
192          * Go to the user's preferred start page
193          */
194         get_preference("startpage", buf);
195         if (strlen(buf)==0) {
196                 strcpy(buf, "/dotskip&room=_BASEROOM_");
197                 set_preference("startpage", buf);
198         }
199         http_redirect(buf);
200 }
201
202
203 /*
204  * Disconnect from the Citadel server, and end this WebCit session
205  */
206 void end_webcit_session(void) {
207         serv_puts("QUIT");
208         WC->killthis = 1;
209         /* close() of citadel socket will be done by do_housekeeping() */
210 }
211
212
213 void do_logout(void)
214 {
215         char buf[SIZ];
216
217         strcpy(WC->wc_username, "");
218         strcpy(WC->wc_password, "");
219         strcpy(WC->wc_roomname, "");
220
221         /* Calling output_headers() this way causes the cookies to be un-set */
222         output_headers(1, 1, 0, 1, 0, 0, 0);
223
224         wprintf("<center>");
225         serv_puts("MESG goodbye");
226         serv_gets(buf);
227
228         if (WC->serv_sock >= 0) {
229                 if (buf[0] == '1') {
230                         fmout(NULL, "CENTER");
231                 } else {
232                         wprintf("Goodbye\n");
233                 }
234         }
235         else {
236                 wprintf("This program was unable to connect or stay "
237                         "connected to the Citadel server.  Please report "
238                         "this problem to your system administrator."
239                 );
240         }
241
242         wprintf("<hr /><a href=\"/\">Log in again</A>&nbsp;&nbsp;&nbsp;"
243                 "<a href=\"javascript:window.close();\">Close window</A>"
244                 "</center>\n");
245         wDumpContent(2);
246         end_webcit_session();
247 }
248
249
250 /* 
251  * validate new users
252  */
253 void validate(void)
254 {
255         char cmd[SIZ];
256         char user[SIZ];
257         char buf[SIZ];
258         int a;
259
260         output_headers(1, 1, 2, 0, 0, 0, 0);
261         wprintf("<div id=\"banner\">\n"
262                 "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
263                 "<SPAN CLASS=\"titlebar\">Validate new users</SPAN>"
264                 "</TD></TR></TABLE>\n"
265                 "</div>\n<div id=\"content\">\n"
266         );
267                                                                                                                              
268         strcpy(buf, bstr("user"));
269         if (strlen(buf) > 0)
270                 if (strlen(bstr("axlevel")) > 0) {
271                         serv_printf("VALI %s|%s", buf, bstr("axlevel"));
272                         serv_gets(buf);
273                         if (buf[0] != '2') {
274                                 wprintf("<b>%s</b><br />\n", &buf[4]);
275                         }
276                 }
277         serv_puts("GNUR");
278         serv_gets(buf);
279
280         if (buf[0] != '3') {
281                 wprintf("<b>%s</b><br />\n", &buf[4]);
282                 wDumpContent(1);
283                 return;
284         }
285
286         wprintf("<div id=\"fix_scrollbar_bug\">"
287                 "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
288         wprintf("<center>");
289
290         strcpy(user, &buf[4]);
291         serv_printf("GREG %s", user);
292         serv_gets(cmd);
293         if (cmd[0] == '1') {
294                 a = 0;
295                 do {
296                         serv_gets(buf);
297                         ++a;
298                         if (a == 1)
299                                 wprintf("User #%s<br /><H1>%s</H1>",
300                                         buf, &cmd[4]);
301                         if (a == 2)
302                                 wprintf("PW: %s<br />\n", buf);
303                         if (a == 3)
304                                 wprintf("%s<br />\n", buf);
305                         if (a == 4)
306                                 wprintf("%s<br />\n", buf);
307                         if (a == 5)
308                                 wprintf("%s, ", buf);
309                         if (a == 6)
310                                 wprintf("%s ", buf);
311                         if (a == 7)
312                                 wprintf("%s<br />\n", buf);
313                         if (a == 8)
314                                 wprintf("%s<br />\n", buf);
315                         if (a == 9)
316                                 wprintf("Current access level: %d (%s)\n",
317                                         atoi(buf), axdefs[atoi(buf)]);
318                 } while (strcmp(buf, "000"));
319         } else {
320                 wprintf("<H1>%s</H1>%s<br />\n", user, &cmd[4]);
321         }
322
323         wprintf("<hr />Select access level for this user:<br />\n");
324         for (a = 0; a <= 6; ++a) {
325                 wprintf("<A HREF=\"/validate&user=");
326                 urlescputs(user);
327                 wprintf("&axlevel=%d\">%s</A>&nbsp;&nbsp;&nbsp;\n",
328                         a, axdefs[a]);
329         }
330         wprintf("<br />\n");
331
332         wprintf("</CENTER>\n");
333         wprintf("</td></tr></table></div>\n");
334         wDumpContent(1);
335 }
336
337
338
339 /* 
340  * Display form for registration.
341  * (Set during_login to 1 if this registration is being performed during
342  * new user login and will require chaining to the proper screen.)
343  */
344 void display_reg(int during_login)
345 {
346         long vcard_msgnum;
347
348         if (goto_config_room() != 0) {
349                 if (during_login) do_welcome();
350                 else display_main_menu();
351                 return;
352         }
353
354         vcard_msgnum = locate_user_vcard(WC->wc_username, -1);
355         if (vcard_msgnum < 0L) {
356                 if (during_login) do_welcome();
357                 else display_main_menu();
358                 return;
359         }
360
361         if (during_login) {
362                 do_edit_vcard(vcard_msgnum, "1", "/do_welcome");
363         }
364         else {
365                 do_edit_vcard(vcard_msgnum, "1", "/display_main_menu");
366         }
367
368 }
369
370
371
372
373 /* 
374  * display form for changing your password
375  */
376 void display_changepw(void)
377 {
378         char buf[SIZ];
379
380         output_headers(1, 1, 2, 0, 0, 0, 0);
381         wprintf("<div id=\"banner\">\n"
382                 "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
383                 "<SPAN CLASS=\"titlebar\">Change your password</SPAN>"
384                 "</TD></TR></TABLE>\n"
385                 "</div>\n<div id=\"content\">\n"
386         );
387
388         if (strlen(WC->ImportantMessage) > 0) {
389                 do_template("beginbox_nt");
390                 wprintf("<SPAN CLASS=\"errormsg\">"
391                         "%s</SPAN><br />\n", WC->ImportantMessage);
392                 do_template("endbox");
393                 strcpy(WC->ImportantMessage, "");
394         }
395
396         wprintf("<div id=\"fix_scrollbar_bug\">"
397                 "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
398
399         wprintf("<CENTER><br />");
400         serv_puts("MESG changepw");
401         serv_gets(buf);
402         if (buf[0] == '1') {
403                 fmout(NULL, "CENTER");
404         }
405
406         wprintf("<form name=\"changepwform\" action=\"changepw\" method=\"post\">\n");
407         wprintf("<CENTER>"
408                 "<table border=\"0\" cellspacing=\"5\" cellpadding=\"5\" "
409                 "BGCOLOR=\"#EEEEEE\">"
410                 "<TR><TD>Enter new password:</TD>\n");
411         wprintf("<TD><INPUT TYPE=\"password\" NAME=\"newpass1\" VALUE=\"\" MAXLENGTH=\"20\"></TD></TR>\n");
412         wprintf("<TR><TD>Enter it again to confirm:</TD>\n");
413         wprintf("<TD><INPUT TYPE=\"password\" NAME=\"newpass2\" VALUE=\"\" MAXLENGTH=\"20\"></TD></TR>\n");
414
415         wprintf("</TABLE><br />\n");
416         wprintf("<INPUT type=\"submit\" name=\"action\" value=\"Change\">"
417                 "&nbsp;"
418                 "<INPUT type=\"submit\" name=\"action\" value=\"Cancel\">\n");
419         wprintf("</form></center>\n");
420         wprintf("</td></tr></table></div>\n");
421         wDumpContent(1);
422 }
423
424 /*
425  * change password
426  */
427 void changepw(void)
428 {
429         char buf[SIZ];
430         char newpass1[32], newpass2[32];
431
432         if (strcmp(bstr("action"), "Change")) {
433                 strcpy(WC->ImportantMessage, 
434                         "Cancelled.  Password was not changed.");
435                 display_main_menu();
436                 return;
437         }
438
439         strcpy(newpass1, bstr("newpass1"));
440         strcpy(newpass2, bstr("newpass2"));
441
442         if (strcasecmp(newpass1, newpass2)) {
443                 strcpy(WC->ImportantMessage, 
444                         "They don't match.  Password was not changed.");
445                 display_changepw();
446                 return;
447         }
448
449         if (strlen(newpass1) == 0) {
450                 strcpy(WC->ImportantMessage, 
451                         "Blank passwords are not allowed.");
452                 display_changepw();
453                 return;
454         }
455
456         serv_printf("SETP %s", newpass1);
457         serv_gets(buf);
458         sprintf(WC->ImportantMessage, "%s", &buf[4]);
459         if (buf[0] == '2') {
460                 safestrncpy(WC->wc_password, buf, sizeof WC->wc_password);
461                 display_main_menu();
462         }
463         else {
464                 display_changepw();
465         }
466 }