]> code.citadel.org Git - citadel.git/blob - citadel/setup.c
* Citadel setup: if the environment
[citadel.git] / citadel / setup.c
1 /*
2  * $Id$
3  *
4  * Citadel setup utility
5  *
6  */
7
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <fcntl.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/utsname.h>
17 #include <sys/wait.h>
18 #include <signal.h>
19 #include <netdb.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <pwd.h>
23 #include <time.h>
24
25 #include "citadel.h"
26 #include "axdefs.h"
27 #include "sysdep.h"
28 #include "config.h"
29 #include "tools.h"
30 #include "citadel_dirs.h"
31
32 #ifdef HAVE_NEWT
33 #include <newt.h>
34 #endif
35
36
37 #define MAXSETUP 4      /* How many setup questions to ask */
38
39 #define UI_TEXT         0       /* Default setup type -- text only */
40 #define UI_DIALOG       2       /* Use the 'dialog' program */
41 #define UI_SILENT       3       /* Silent running, for use in scripts */
42 #define UI_NEWT         4       /* Use the "newt" window library */
43
44 #define SERVICE_NAME    "citadel"
45 #define PROTO_NAME      "tcp"
46 #define NSSCONF         "/etc/nsswitch.conf"
47
48 int setup_type;
49 char setup_directory[PATH_MAX];
50 int using_web_installer = 0;
51 int enable_home = 1;
52
53 char *setup_titles[] =
54 {
55         "Citadel Home Directory",
56         "System Administrator",
57         "Citadel User ID",
58         "Server IP address",
59         "Server port number",
60 };
61
62
63 struct config config;
64
65         /* calculate all our path on a central place */
66     /* where to keep our config */
67         
68
69 char *setup_text[] = {
70 #ifndef HAVE_RUN_DIR
71 "Enter the full pathname of the directory in which the Citadel\n"
72 "installation you are creating or updating resides.  If you\n"
73 "specify a directory other than the default, you will need to\n"
74 "specify the -h flag to the server when you start it up.\n",
75 #else
76 "Enter the subdirectory name for an alternate installation of "
77 "Citadel. To do a default installation just leave it blank."
78 "If you specify a directory other than the default, you will need to\n"
79 "specify the -h flag to the server when you start it up.\n"
80 "note that it may not have a leading /",
81 #endif
82
83 "Enter the name of the system administrator (which is probably\n"
84 "you).  When an account is created with this name, it will\n"
85 "automatically be given administrator-level access.\n",
86
87 "Citadel needs to run under its own user ID.  This would\n"
88 "typically be called \"citadel\", but if you are running Citadel\n"
89 "as a public BBS, you might also call it \"bbs\" or \"guest\".\n"
90 "The server will run under this user ID.  Please specify that\n"
91 "user ID here.  You may specify either a user name or a numeric\n"
92 "UID.\n",
93
94 "Specify the IP address on which your server will run.  If you\n"
95 "leave this blank, or if you specify 0.0.0.0, Citadel will listen\n"
96 "on all addresses.  You can usually skip this unless you are\n"
97 "running multiple instances of Citadel on the same computer.\n",
98
99 "Specify the TCP port number on which your server will run.\n"
100 "Normally, this will be port 504, which is the official port\n"
101 "assigned by the IANA for Citadel servers.  You will only need\n"
102 "to specify a different port number if you run multiple instances\n"
103 "of Citadel on the same computer and there is something else\n"
104 "already using port 504.\n",
105
106 };
107
108 struct config config;
109 int direction;
110
111
112 void cleanup(int exitcode)
113 {
114 #ifdef HAVE_NEWT
115         newtCls();
116         newtRefresh();
117         newtFinished();
118 #endif
119         exit(exitcode);
120 }
121
122
123
124 void title(char *text)
125 {
126         if (setup_type == UI_TEXT) {
127                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
128         }
129 }
130
131
132
133 int yesno(char *question)
134 {
135 #ifdef HAVE_NEWT
136         newtComponent form = NULL;
137         newtComponent yesbutton = NULL;
138         newtComponent nobutton = NULL;
139         int prompt_window_height = 0;
140 #endif
141         int i = 0;
142         int answer = 0;
143         char buf[SIZ];
144
145         switch (setup_type) {
146
147         case UI_TEXT:
148                 do {
149                         printf("%s\nYes/No --> ", question);
150                         fgets(buf, sizeof buf, stdin);
151                         answer = tolower(buf[0]);
152                         if (answer == 'y')
153                                 answer = 1;
154                         else if (answer == 'n')
155                                 answer = 0;
156                 } while ((answer < 0) || (answer > 1));
157                 break;
158
159         case UI_DIALOG:
160                 sprintf(buf, "exec %s --yesno '%s' 10 72",
161                         getenv("CTDL_DIALOG"),
162                         question);
163                 i = system(buf);
164                 if (i == 0) {
165                         answer = 1;
166                 }
167                 else {
168                         answer = 0;
169                 }
170                 break;
171
172 #ifdef HAVE_NEWT
173         case UI_NEWT:
174                 prompt_window_height = num_tokens(question, '\n') + 5;
175                 newtCenteredWindow(76, prompt_window_height, "Question");
176                 form = newtForm(NULL, NULL, 0);
177                 for (i=0; i<num_tokens(question, '\n'); ++i) {
178                         extract_token(buf, question, i, '\n', sizeof buf);
179                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
180                 }
181                 yesbutton = newtButton(10, (prompt_window_height - 4), "Yes");
182                 nobutton = newtButton(60, (prompt_window_height - 4), "No");
183                 newtFormAddComponent(form, yesbutton);
184                 newtFormAddComponent(form, nobutton);
185                 if (newtRunForm(form) == yesbutton) {
186                         answer = 1;
187                 }
188                 else {
189                         answer = 0;
190                 }
191                 newtPopWindow();
192                 newtFormDestroy(form);  
193
194                 break;
195 #endif
196
197         }
198         return (answer);
199 }
200
201
202 void important_message(char *title, char *msgtext)
203 {
204 #ifdef HAVE_NEWT
205         newtComponent form = NULL;
206         int i = 0;
207 #endif
208         char buf[SIZ];
209
210         switch (setup_type) {
211
212         case UI_TEXT:
213                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
214                 printf("       %s \n\n%s\n\n", title, msgtext);
215                 printf("Press return to continue...");
216                 fgets(buf, sizeof buf, stdin);
217                 break;
218
219         case UI_DIALOG:
220                 sprintf(buf, "exec %s --msgbox '%s' 19 72",
221                         getenv("CTDL_DIALOG"),
222                         msgtext);
223                 system(buf);
224                 break;
225
226 #ifdef HAVE_NEWT
227         case UI_NEWT:
228                 newtCenteredWindow(76, 10, title);
229                 form = newtForm(NULL, NULL, 0);
230                 for (i=0; i<num_tokens(msgtext, '\n'); ++i) {
231                         extract_token(buf, msgtext, i, '\n', sizeof buf);
232                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
233                 }
234                 newtFormAddComponent(form, newtButton(35, 5, "OK"));
235                 newtRunForm(form);
236                 newtPopWindow();
237                 newtFormDestroy(form);  
238                 break;
239 #endif
240
241         }
242 }
243
244 void important_msgnum(int msgnum)
245 {
246         important_message("Important Message", setup_text[msgnum]);
247 }
248
249 void display_error(char *error_message)
250 {
251         important_message("Error", error_message);
252 }
253
254 void progress(char *text, long int curr, long int cmax)
255 {
256 #ifdef HAVE_NEWT
257
258         /* These variables are static because progress() gets called
259          * multiple times during the course of whatever operation is
260          * being performed.  This makes setup non-threadsafe, but who
261          * cares?
262          */
263         static newtComponent form = NULL;
264         static newtComponent scale = NULL;
265 #endif
266         static long dots_printed = 0L;
267         long a = 0;
268         static FILE *fp = NULL;
269         char buf[SIZ];
270
271         switch (setup_type) {
272
273         case UI_TEXT:
274                 if (curr == 0) {
275                         printf("%s\n", text);
276                         printf("..........................");
277                         printf("..........................");
278                         printf("..........................\r");
279                         fflush(stdout);
280                         dots_printed = 0;
281                 } else if (curr == cmax) {
282                         printf("\r%79s\n", "");
283                 } else {
284                         a = (curr * 100) / cmax;
285                         a = a * 78;
286                         a = a / 100;
287                         while (dots_printed < a) {
288                                 printf("*");
289                                 ++dots_printed;
290                                 fflush(stdout);
291                         }
292                 }
293                 break;
294
295         case UI_DIALOG:
296                 if (curr == 0) {
297                         sprintf(buf, "exec %s --gauge '%s' 7 72 0",
298                                 getenv("CTDL_DIALOG"),
299                                 text);
300                         fp = popen(buf, "w");
301                         if (fp != NULL) {
302                                 fprintf(fp, "0\n");
303                                 fflush(fp);
304                         }
305                 } 
306                 else if (curr == cmax) {
307                         if (fp != NULL) {
308                                 fprintf(fp, "100\n");
309                                 pclose(fp);
310                                 fp = NULL;
311                         }
312                 }
313                 else {
314                         a = (curr * 100) / cmax;
315                         if (fp != NULL) {
316                                 fprintf(fp, "%ld\n", a);
317                                 fflush(fp);
318                         }
319                 }
320                 break;
321
322 #ifdef HAVE_NEWT
323         case UI_NEWT:
324                 if (curr == 0) {
325                         newtCenteredWindow(76, 8, text);
326                         form = newtForm(NULL, NULL, 0);
327                         scale = newtScale(1, 3, 74, cmax);
328                         newtFormAddComponent(form, scale);
329                         newtDrawForm(form);
330                         newtRefresh();
331                 }
332                 if ((curr > 0) && (curr <= cmax)) {
333                         newtScaleSet(scale, curr);
334                         newtRefresh();
335                 }
336                 if (curr == cmax) {
337                         newtFormDestroy(form);  
338                         newtPopWindow();
339                         newtRefresh();
340                 }
341                 break;
342 #endif
343
344         }
345 }
346
347
348
349 /*
350  * check_services_entry()  -- Make sure "citadel" is in /etc/services
351  *
352  */
353 void check_services_entry(void)
354 {
355         int i;
356         FILE *sfp;
357
358         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
359                 for (i=0; i<=2; ++i) {
360                         progress("Adding service entry...", i, 2);
361                         if (i == 0) {
362                                 sfp = fopen("/etc/services", "a");
363                                 if (sfp == NULL) {
364                                         display_error(strerror(errno));
365                                 } else {
366                                         fprintf(sfp, "%s                504/tcp\n",
367                                                 SERVICE_NAME);
368                                         fclose(sfp);
369                                 }
370                         }
371                         sleep(1);
372                 }
373         }
374 }
375
376
377
378
379 /*
380  * delete_inittab_entry()  -- Remove obsolete /etc/inittab entry for Citadel
381  *
382  */
383 void delete_inittab_entry(void)
384 {
385         FILE *infp;
386         FILE *outfp;
387         char looking_for[256];
388         char buf[1024];
389         char outfilename[32];
390         int changes_made = 0;
391
392         /* Determine the fully qualified path name of citserver */
393         snprintf(looking_for, 
394                          sizeof looking_for,
395                          "%s/citserver", 
396 #ifndef HAVE_RUN_DIR
397                          setup_directory
398 #else
399                          CTDLDIR
400 #endif
401          );
402
403         /* Now tweak /etc/inittab */
404         infp = fopen("/etc/inittab", "r");
405         if (infp == NULL) {
406                 display_error(strerror(errno));
407                 return;
408         }
409
410         strcpy(outfilename, "/tmp/ctdlsetup.XXXXXX");
411         outfp = fdopen(mkstemp(outfilename), "w+");
412         if (outfp == NULL) {
413                 display_error(strerror(errno));
414                 fclose(infp);
415                 return;
416         }
417
418         while (fgets(buf, sizeof buf, infp) != NULL) {
419                 if (strstr(buf, looking_for) == NULL) {
420                         fwrite(buf, strlen(buf), 1, outfp);
421                 }
422                 else {
423                         ++changes_made;
424                 }
425         }
426
427         fclose(infp);
428         fclose(outfp);
429
430         if (changes_made) {
431                 sprintf(buf, "/bin/mv -f %s /etc/inittab 2>/dev/null", outfilename);
432                 system(buf);
433                 system("/sbin/init q 2>/dev/null");
434         }
435         else {
436                 unlink(outfilename);
437         }
438 }
439
440
441 /*
442  * install_init_scripts()  -- Try to configure to start Citadel at boot
443  *
444  */
445 void install_init_scripts(void)
446 {
447         FILE *fp;
448
449         if (yesno("Would you like to automatically start Citadel at boot?\n") == 0) {
450                 return;
451         }
452
453         fp = fopen("/etc/init.d/citadel", "w");
454         if (fp == NULL) {
455                 display_error("Cannot create /etc/init.d/citadel");
456                 return;
457         }
458
459         fprintf(fp,     "#!/bin/sh\n"
460                         "\n"
461                         "CITADEL_DIR=%s\n", setup_directory);
462         fprintf(fp,     "\n"
463                         "test -x $CITADEL_DIR/ctdlsvc || exit 0\n"
464                         "test -d /var/run || exit 0\n"
465                         "\n"
466                         "case \"$1\" in\n"
467                         "\n"
468                         "start)         echo -n \"Starting Citadel... \"\n"
469                         "               if $CITADEL_DIR/ctdlsvc /var/run/citadel.pid "
470                                                         "$CITADEL_DIR/citserver "
471                                                         "-t/dev/null\n"
472                         "               then\n"
473                         "                       echo \"ok\"\n"
474                         "               else\n"
475                         "                       echo \"failed\"\n"
476                         "               fi\n");
477         fprintf(fp,     "               ;;\n"
478                         "stop)          echo -n \"Stopping Citadel... \"\n"
479                         "               if $CITADEL_DIR/sendcommand DOWN >/dev/null 2>&1 ; then\n"
480                         "                       echo \"ok\"\n"
481                         "               else\n"
482                         "                       echo \"failed\"\n"
483                         "               fi\n"
484                         "               rm -f /var/run/citadel.pid 2>/dev/null\n");
485         fprintf(fp,     "               ;;\n"
486                         "*)             echo \"Usage: $0 {start|stop}\"\n"
487                         "               exit 1\n"
488                         "               ;;\n"
489                         "esac\n"
490         );
491
492         fclose(fp);
493         chmod("/etc/init.d/citadel", 0755);
494
495         /* Set up the run levels. */
496         system("/bin/rm -f /etc/rc?.d/[SK]??citadel 2>/dev/null");
497         system("for x in 2 3 4 5 ; do [ -d /etc/rc$x.d ] && ln -s /etc/init.d/citadel /etc/rc$x.d/S79citadel ; done 2>/dev/null");
498         system("for x in 0 6 S; do [ -d /etc/rc$x.d ] && ln -s /etc/init.d/citadel /etc/rc$x.d/K30citadel ; done 2>/dev/null");
499
500 }
501
502
503
504
505
506
507 /*
508  * On systems which use xinetd, see if we can offer to install Citadel as
509  * the default telnet target.
510  */
511 void check_xinetd_entry(void) {
512         char *filename = "/etc/xinetd.d/telnet";
513         FILE *fp;
514         char buf[SIZ];
515         int already_citadel = 0;
516
517         fp = fopen(filename, "r+");
518         if (fp == NULL) return;         /* Not there.  Oh well... */
519
520         while (fgets(buf, sizeof buf, fp) != NULL) {
521                 if (strstr(buf, setup_directory) != NULL) already_citadel = 1;
522         }
523         fclose(fp);
524         if (already_citadel) return;    /* Already set up this way. */
525
526         /* Otherwise, prompt the user to create an entry. */
527         if (getenv("CREATE_XINETD_ENTRY") != NULL) {
528                 if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
529                         return;
530                 }
531         }
532         else {
533                 snprintf(buf, sizeof buf,
534                         "Setup can configure the \"xinetd\" service to automatically\n"
535                         "connect incoming telnet sessions to Citadel, bypassing the\n"
536                         "host system login: prompt.  Would you like to do this?\n"
537                 );
538                 if (yesno(buf) == 0) {
539                         return;
540                 }
541         }
542
543         fp = fopen(filename, "w");
544         fprintf(fp,
545                 "# description: telnet service for Citadel users\n"
546                 "service telnet\n"
547                 "{\n"
548                 "       disable = no\n"
549                 "       flags           = REUSE\n"
550                 "       socket_type     = stream\n"
551                 "       wait            = no\n"
552                 "       user            = root\n"
553                 "       server          = /usr/sbin/in.telnetd\n"
554                 "       server_args     = -h -L %s/citadel\n"
555                 "       log_on_failure  += USERID\n"
556                 "}\n",
557 #ifndef HAVE_RUN_DIR
558                         setup_directory
559 #else
560                         RUN_DIR
561 #endif
562                         );
563         fclose(fp);
564
565         /* Now try to restart the service */
566         system("/etc/init.d/xinetd restart >/dev/null 2>&1");
567 }
568
569
570
571 /*
572  * Offer to disable other MTA's
573  */
574 void disable_other_mta(char *mta) {
575         char buf[SIZ];
576         FILE *fp;
577         int lines = 0;
578
579         sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
580                 "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
581                 mta, mta);
582         fp = popen(buf, "r");
583         if (fp == NULL) return;
584
585         while (fgets(buf, sizeof buf, fp) != NULL) {
586                 ++lines;
587         }
588         fclose(fp);
589         if (lines == 0) return;         /* Nothing to do. */
590
591
592         /* Offer to replace other MTA with the vastly superior Citadel :)  */
593
594         if (getenv("ACT_AS_MTA")) {
595                 if (strcasecmp(getenv("ACT_AS_MTA"), "yes")) {
596                         return;
597                 }
598         }
599         else {
600                 snprintf(buf, sizeof buf,
601                         "You appear to have the \"%s\" email program\n"
602                         "running on your system.  If you want Citadel mail\n"
603                         "connected with %s, you will have to manually integrate\n"
604                         "them.  It is preferable to disable %s, and use Citadel's\n"
605                         "SMTP, POP3, and IMAP services.\n\n"
606                         "May we disable %s so that Citadel has access to ports\n"
607                         "25, 110, and 143?\n",
608                         mta, mta, mta, mta
609                 );
610                 if (yesno(buf) == 0) {
611                         return;
612                 }
613         }
614
615         sprintf(buf, "for x in /etc/rc*.d/S*%s; do mv $x `echo $x |sed s/S/K/g`; done >/dev/null 2>&1", mta);
616         system(buf);
617         sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
618         system(buf);
619 }
620
621
622
623
624 /* 
625  * Check to see if our server really works.  Returns 0 on success.
626  */
627 int test_server(void) {
628         char cmd[256];
629         char cookie[256];
630         FILE *fp;
631         char buf[4096];
632         int found_it = 0;
633
634         /* Generate a silly little cookie.  We're going to write it out
635          * to the server and try to get it back.  The cookie does not
636          * have to be secret ... just unique.
637          */
638         sprintf(cookie, "--test--%d--", getpid());
639
640         sprintf(cmd, "%s/sendcommand %s%s ECHO %s 2>&1",
641 #ifndef HAVE_RUN_DIR
642                         setup_directory,
643 #else
644                         CTDLDIR,
645 #endif
646                         (enable_home)?"-h":"", 
647                         (enable_home)?setup_directory:"",
648                         cookie);
649
650         fp = popen(cmd, "r");
651         if (fp == NULL) return(errno);
652
653         while (fgets(buf, sizeof buf, fp) != NULL) {
654                 if ( (buf[0]=='2')
655                    && (strstr(buf, cookie) != NULL) ) {
656                         ++found_it;
657                 }
658         }
659         pclose(fp);
660
661         if (found_it) {
662                 return(0);
663         }
664         return(-1);
665 }
666
667 void strprompt(char *prompt_title, char *prompt_text, char *str)
668 {
669 #ifdef HAVE_NEWT
670         newtComponent form;
671         char *result;
672         int i;
673         int prompt_window_height = 0;
674 #endif
675         char buf[SIZ];
676         char setupmsg[SIZ];
677         char dialog_result[PATH_MAX];
678         FILE *fp = NULL;
679
680         strcpy(setupmsg, "");
681
682         switch (setup_type) {
683         case UI_TEXT:
684                 title(prompt_title);
685                 printf("\n%s\n", prompt_text);
686                 printf("This is currently set to:\n%s\n", str);
687                 printf("Enter new value or press return to leave unchanged:\n");
688                 fgets(buf, sizeof buf, stdin);
689                 buf[strlen(buf) - 1] = 0;
690                 if (strlen(buf) != 0)
691                         strcpy(str, buf);
692                 break;
693
694         case UI_DIALOG:
695                 CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
696                 sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
697                         getenv("CTDL_DIALOG"),
698                         prompt_text,
699                         str,
700                         dialog_result);
701                 system(buf);
702                 fp = fopen(dialog_result, "r");
703                 if (fp != NULL) {
704                         fgets(str, sizeof buf, fp);
705                         if (str[strlen(str)-1] == 10) {
706                                 str[strlen(str)-1] = 0;
707                         }
708                         fclose(fp);
709                         unlink(dialog_result);
710                 }
711                 break;
712
713 #ifdef HAVE_NEWT
714         case UI_NEWT:
715
716                 prompt_window_height = num_tokens(prompt_text, '\n') + 5 ;
717                 newtCenteredWindow(76,
718                                 prompt_window_height,
719                                 prompt_title);
720                 form = newtForm(NULL, NULL, 0);
721                 for (i=0; i<num_tokens(prompt_text, '\n'); ++i) {
722                         extract_token(buf, prompt_text, i, '\n', sizeof buf);
723                         newtFormAddComponent(form, newtLabel(1, 1+i, buf));
724                 }
725                 newtFormAddComponent(form,
726                         newtEntry(1,
727                                 (prompt_window_height - 2),
728                                 str,
729                                 74,
730                                 (const char **) &result,
731                                 NEWT_FLAG_RETURNEXIT)
732                 );
733                 newtRunForm(form);
734                 strcpy(str, result);
735
736                 newtPopWindow();
737                 newtFormDestroy(form);  
738
739 #endif
740         }
741 }
742
743 void set_str_val(int msgpos, char *str) {
744         strprompt(setup_titles[msgpos], setup_text[msgpos], str);
745 }
746
747
748
749 void set_int_val(int msgpos, int *ip)
750 {
751         char buf[16];
752         snprintf(buf, sizeof buf, "%d", (int) *ip);
753         set_str_val(msgpos, buf);
754         *ip = atoi(buf);
755 }
756
757
758 void set_char_val(int msgpos, char *ip)
759 {
760         char buf[16];
761         snprintf(buf, sizeof buf, "%d", (int) *ip);
762         set_str_val(msgpos, buf);
763         *ip = (char) atoi(buf);
764 }
765
766
767 void set_long_val(int msgpos, long int *ip)
768 {
769         char buf[16];
770         snprintf(buf, sizeof buf, "%ld", *ip);
771         set_str_val(msgpos, buf);
772         *ip = atol(buf);
773 }
774
775
776 void edit_value(int curr)
777 {
778         int i;
779         struct passwd *pw;
780         char ctdluidname[SIZ];
781
782         switch (curr) {
783
784         case 1:
785                 if (getenv("SYSADMIN_NAME")) {
786                         strcpy(config.c_sysadm, getenv("SYSADMIN_NAME"));
787                 }
788                 else {
789                         set_str_val(curr, config.c_sysadm);
790                 }
791                 break;
792
793         case 2:
794 #ifdef __CYGWIN__
795                 config.c_ctdluid = 0;   /* XXX Windows hack, prob. insecure */
796 #else
797                 i = config.c_ctdluid;
798                 pw = getpwuid(i);
799                 if (pw == NULL) {
800                         set_int_val(curr, &i);
801                         config.c_ctdluid = i;
802                 }
803                 else {
804                         strcpy(ctdluidname, pw->pw_name);
805                         set_str_val(curr, ctdluidname);
806                         pw = getpwnam(ctdluidname);
807                         if (pw != NULL) {
808                                 config.c_ctdluid = pw->pw_uid;
809                         }
810                         else if (atoi(ctdluidname) > 0) {
811                                 config.c_ctdluid = atoi(ctdluidname);
812                         }
813                 }
814 #endif
815                 break;
816
817         case 3:
818                 set_str_val(curr, config.c_ip_addr);
819                 break;
820
821         case 4:
822                 set_int_val(curr, &config.c_port_number);
823                 break;
824
825
826         }
827 }
828
829 /*
830  * (re-)write the config data to disk
831  */
832 void write_config_to_disk(void)
833 {
834         FILE *fp;
835         int fd;
836
837         if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) {
838                 display_error("setup: cannot open citadel.config");
839                 cleanup(1);
840         }
841         fp = fdopen(fd, "wb");
842         if (fp == NULL) {
843                 display_error("setup: cannot open citadel.config");
844                 cleanup(1);
845         }
846         fwrite((char *) &config, sizeof(struct config), 1, fp);
847         fclose(fp);
848 }
849
850
851
852
853 /*
854  * Figure out what type of user interface we're going to use
855  */
856 int discover_ui(void)
857 {
858
859         /* Use "dialog" if we have it */
860         if (getenv("CTDL_DIALOG") != NULL) {
861                 return UI_DIALOG;
862         }
863                 
864
865 #ifdef HAVE_NEWT
866         newtInit();
867         newtCls();
868         newtDrawRootText(0, 0, "Citadel Setup");
869         return UI_NEWT;
870 #endif
871         return UI_TEXT;
872 }
873
874
875
876
877
878 /*
879  * Strip "db" entries out of /etc/nsswitch.conf
880  */
881 void fixnss(void) {
882         FILE *fp_read;
883         int fd_write;
884         char buf[256];
885         char buf_nc[256];
886         char question[512];
887         int i;
888         int changed = 0;
889         int file_changed = 0;
890         char new_filename[64];
891
892         fp_read = fopen(NSSCONF, "r");
893         if (fp_read == NULL) {
894                 return;
895         }
896
897         strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX");
898         fd_write = mkstemp(new_filename);
899         if (fd_write < 0) {
900                 fclose(fp_read);
901                 return;
902         }
903
904         while (fgets(buf, sizeof buf, fp_read) != NULL) {
905                 changed = 0;
906                 strcpy(buf_nc, buf);
907                 for (i=0; i<strlen(buf_nc); ++i) {
908                         if (buf_nc[i] == '#') {
909                                 buf_nc[i] = 0;
910                         }
911                 }
912                 for (i=0; i<strlen(buf_nc); ++i) {
913                         if (!strncasecmp(&buf_nc[i], "db", 2)) {
914                                 if (i > 0) {
915                                         if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
916                                                 changed = 1;
917                                                 file_changed = 1;
918                                                 strcpy(&buf_nc[i], &buf_nc[i+2]);
919                                                 strcpy(&buf[i], &buf[i+2]);
920                                                 if (buf[i]==32) {
921                                                         strcpy(&buf_nc[i], &buf_nc[i+1]);
922                                                         strcpy(&buf[i], &buf[i+1]);
923                                                 }
924                                         }
925                                 }
926                         }
927                 }
928                 if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
929                         fclose(fp_read);
930                         close(fd_write);
931                         unlink(new_filename);
932                         return;
933                 }
934         }
935
936         fclose(fp_read);
937         
938         if (!file_changed) {
939                 unlink(new_filename);
940                 return;
941         }
942
943         snprintf(question, sizeof question,
944                 "\n"
945                 "/etc/nsswitch.conf is configured to use the 'db' module for\n"
946                 "one or more services.  This is not necessary on most systems,\n"
947                 "and it is known to crash the Citadel server when delivering\n"
948                 "mail to the Internet.\n"
949                 "\n"
950                 "Do you want this module to be automatically disabled?\n"
951                 "\n"
952         );
953
954         if (yesno(question)) {
955                 sprintf(buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
956                 system(buf);
957         }
958         unlink(new_filename);
959 }
960
961
962
963
964
965
966
967
968 int main(int argc, char *argv[])
969 {
970         int a;
971         int curr; 
972         char aaa[128];
973         FILE *fp;
974         int old_setup_level = 0;
975         int info_only = 0;
976         struct utsname my_utsname;
977         struct passwd *pw;
978         struct hostent *he;
979         gid_t gid;
980         int relh=0;
981         int home=0;
982         char relhome[PATH_MAX]="";
983         char ctdldir[PATH_MAX]=CTDLDIR;
984         
985         /* set an invalid setup type */
986         setup_type = (-1);
987
988         /* Check to see if we're running the web installer */
989         if (getenv("CITADEL_INSTALLER") != NULL) {
990                 using_web_installer = 1;
991         }
992
993         /* parse command line args */
994         for (a = 0; a < argc; ++a) {
995                 if (!strncmp(argv[a], "-u", 2)) {
996                         strcpy(aaa, argv[a]);
997                         strcpy(aaa, &aaa[2]);
998                         setup_type = atoi(aaa);
999                 }
1000                 if (!strcmp(argv[a], "-i")) {
1001                         info_only = 1;
1002                 }
1003                 if (!strcmp(argv[a], "-q")) {
1004                         setup_type = UI_SILENT;
1005                 }
1006         }
1007
1008
1009         /* If a setup type was not specified, try to determine automatically
1010          * the best one to use out of all available types.
1011          */
1012         if (setup_type < 0) {
1013                 setup_type = discover_ui();
1014         }
1015         if (info_only == 1) {
1016                 important_message("Citadel Setup", CITADEL);
1017                 cleanup(0);
1018         }
1019
1020         /* Get started in a valid setup directory. */
1021         strcpy(setup_directory, 
1022 #ifdef HAVE_RUN_DIR
1023                    ""
1024 #else
1025                    CTDLDIR
1026 #endif
1027                    );
1028         if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
1029                 strcpy(setup_directory, getenv("CITADEL"));
1030         }
1031         else {
1032                 set_str_val(0, setup_directory);
1033         }
1034
1035         home=(setup_directory[1]!='\0');
1036         relh=home&(setup_directory[1]!='/');
1037         if (!relh) {
1038                 safestrncpy(ctdl_home_directory, setup_directory, sizeof ctdl_home_directory);
1039         }
1040         else {
1041                 safestrncpy(relhome, ctdl_home_directory, sizeof relhome);
1042         }
1043
1044         calc_dirs_n_files(relh, home, relhome, ctdldir);
1045         
1046         enable_home=(relh|home);
1047
1048         if (home) {
1049                 if (chdir(setup_directory) == 0) {
1050                         strcpy(file_citadel_config, "./citadel.config");
1051                 }
1052                 else {
1053                         important_message("Citadel Setup",
1054                                 "The directory you specified does not exist.");
1055                         cleanup(errno);
1056                 }
1057         }
1058
1059         /* Determine our host name, in case we need to use it as a default */
1060         uname(&my_utsname);
1061
1062         /* Try to stop Citadel if we can */
1063         if (!access("/etc/init.d/citadel", X_OK)) {
1064                 for (a=0; a<=2; ++a) {
1065                         progress("Stopping the Citadel service...", a, 2);
1066                         if (a == 0) system("/etc/init.d/citadel stop >/dev/null 2>&1");
1067                         sleep(1);
1068                 }
1069         }
1070
1071         /* Make sure Citadel is not running. */
1072         if (test_server() == 0) {
1073                 important_message("Citadel Setup",
1074                         "The Citadel service is still running.\n"
1075                         "Please stop the service manually and run "
1076                         "setup again.");
1077                 cleanup(1);
1078         }
1079
1080         /* Now begin. */
1081         switch (setup_type) {
1082
1083         case UI_TEXT:
1084                 printf("\n\n\n"
1085                         "              *** Citadel setup program ***\n\n");
1086                 break;
1087
1088         }
1089
1090         /*
1091          * What we're going to try to do here is append a whole bunch of
1092          * nulls to the citadel.config file, so we can keep the old config
1093          * values if they exist, but if the file is missing or from an
1094          * earlier version with a shorter config structure, when setup tries
1095          * to read the old config parameters, they'll all come up zero.
1096          * The length of the config file will be set to what it's supposed
1097          * to be when we rewrite it, because we replace the old file with a
1098          * completely new copy.
1099          */
1100         if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
1101                       S_IRUSR | S_IWUSR)) == -1) {
1102                 display_error("setup: cannot append citadel.config");
1103                 cleanup(errno);
1104         }
1105         fp = fdopen(a, "ab");
1106         if (fp == NULL) {
1107                 display_error("setup: cannot append citadel.config");
1108                 cleanup(errno);
1109         }
1110         for (a = 0; a < sizeof(struct config); ++a)
1111                 putc(0, fp);
1112         fclose(fp);
1113
1114         /* now we re-open it, and read the old or blank configuration */
1115         fp = fopen(file_citadel_config, "rb");
1116         if (fp == NULL) {
1117                 display_error("setup: cannot open citadel.config");
1118                 cleanup(errno);
1119         }
1120         fread((char *) &config, sizeof(struct config), 1, fp);
1121         fclose(fp);
1122
1123         /* set some sample/default values in place of blanks... */
1124         if (strlen(config.c_nodename) == 0)
1125                 safestrncpy(config.c_nodename, my_utsname.nodename,
1126                             sizeof config.c_nodename);
1127         strtok(config.c_nodename, ".");
1128         if (strlen(config.c_fqdn) == 0) {
1129                 if ((he = gethostbyname(my_utsname.nodename)) != NULL)
1130                         safestrncpy(config.c_fqdn, he->h_name,
1131                                     sizeof config.c_fqdn);
1132                 else
1133                         safestrncpy(config.c_fqdn, my_utsname.nodename,
1134                                     sizeof config.c_fqdn);
1135         }
1136         if (strlen(config.c_humannode) == 0)
1137                 strcpy(config.c_humannode, "My System");
1138         if (strlen(config.c_phonenum) == 0)
1139                 strcpy(config.c_phonenum, "US 800 555 1212");
1140         if (config.c_initax == 0) {
1141                 config.c_initax = 4;
1142         }
1143         if (strlen(config.c_moreprompt) == 0)
1144                 strcpy(config.c_moreprompt, "<more>");
1145         if (strlen(config.c_twitroom) == 0)
1146                 strcpy(config.c_twitroom, "Trashcan");
1147         if (strlen(config.c_baseroom) == 0)
1148                 strcpy(config.c_baseroom, BASEROOM);
1149         if (strlen(config.c_aideroom) == 0)
1150                 strcpy(config.c_aideroom, "Aide");
1151         if (config.c_port_number == 0) {
1152                 config.c_port_number = 504;
1153         }
1154         if (config.c_sleeping == 0) {
1155                 config.c_sleeping = 900;
1156         }
1157         if (config.c_ctdluid == 0) {
1158                 pw = getpwnam("citadel");
1159                 if (pw != NULL)
1160                         config.c_ctdluid = pw->pw_uid;
1161         }
1162         if (config.c_ctdluid == 0) {
1163                 pw = getpwnam("bbs");
1164                 if (pw != NULL)
1165                         config.c_ctdluid = pw->pw_uid;
1166         }
1167         if (config.c_ctdluid == 0) {
1168                 pw = getpwnam("guest");
1169                 if (pw != NULL)
1170                         config.c_ctdluid = pw->pw_uid;
1171         }
1172         if (config.c_createax == 0) {
1173                 config.c_createax = 3;
1174         }
1175         /*
1176          * Negative values for maxsessions are not allowed.
1177          */
1178         if (config.c_maxsessions < 0) {
1179                 config.c_maxsessions = 0;
1180         }
1181         /* We need a system default message expiry policy, because this is
1182          * the top level and there's no 'higher' policy to fall back on.
1183          * By default, do not expire messages at all.
1184          */
1185         if (config.c_ep.expire_mode == 0) {
1186                 config.c_ep.expire_mode = EXPIRE_MANUAL;
1187                 config.c_ep.expire_value = 0;
1188         }
1189
1190         /*
1191          * Default port numbers for various services
1192          */
1193         if (config.c_smtp_port == 0) config.c_smtp_port = 25;
1194         if (config.c_pop3_port == 0) config.c_pop3_port = 110;
1195         if (config.c_imap_port == 0) config.c_imap_port = 143;
1196         if (config.c_msa_port == 0) config.c_msa_port = 587;
1197         if (config.c_smtps_port == 0) config.c_smtps_port = 465;
1198         if (config.c_pop3s_port == 0) config.c_pop3s_port = 995;
1199         if (config.c_imaps_port == 0) config.c_imaps_port = 993;
1200         if (config.c_pftcpdict_port == 0) config.c_pftcpdict_port = -1;
1201         if (config.c_managesieve_port == 0) config.c_managesieve_port = 2020;
1202
1203         /* Go through a series of dialogs prompting for config info */
1204         if (setup_type != UI_SILENT) {
1205                 for (curr = 1; curr <= MAXSETUP; ++curr) {
1206                         edit_value(curr);
1207                 }
1208         }
1209
1210 /***** begin version update section ***** */
1211         /* take care of any updating that is necessary */
1212
1213         old_setup_level = config.c_setup_level;
1214
1215         if (old_setup_level == 0) {
1216                 goto NEW_INST;
1217         }
1218
1219         if (old_setup_level < 555) {
1220                 important_message("Citadel Setup",
1221                                   "This Citadel installation is too old "
1222                                   "to be upgraded.");
1223                 cleanup(1);
1224         }
1225         write_config_to_disk();
1226
1227         old_setup_level = config.c_setup_level;
1228
1229         /* end of version update section */
1230
1231 NEW_INST:
1232         config.c_setup_level = REV_LEVEL;
1233
1234 /******************************************/
1235
1236         write_config_to_disk();
1237
1238         mkdir(ctdl_info_dir, 0700);
1239         chmod(ctdl_info_dir, 0700);
1240         chown(ctdl_info_dir, config.c_ctdluid, -1);
1241
1242         mkdir(ctdl_bio_dir, 0700);
1243         chmod(ctdl_bio_dir, 0700);
1244         chown(ctdl_bio_dir, config.c_ctdluid, -1);
1245
1246         mkdir(ctdl_usrpic_dir, 0700);
1247         chmod(ctdl_usrpic_dir, 0700);
1248         chown(ctdl_usrpic_dir, config.c_ctdluid, -1);
1249
1250         mkdir(ctdl_message_dir, 0700);
1251         chmod(ctdl_message_dir, 0700);
1252         chown(ctdl_message_dir, config.c_ctdluid, -1);
1253
1254         mkdir(ctdl_hlp_dir, 0700);
1255         chmod(ctdl_hlp_dir, 0700);
1256         chown(ctdl_hlp_dir, config.c_ctdluid, -1);
1257
1258         mkdir(ctdl_image_dir, 0700);
1259         chmod(ctdl_image_dir, 0700);
1260         chown(ctdl_image_dir, config.c_ctdluid, -1);
1261
1262         mkdir(ctdl_bb_dir, 0700);
1263         chmod(ctdl_bb_dir, 0700);
1264         chown(ctdl_bb_dir, config.c_ctdluid, -1);
1265
1266         mkdir(ctdl_file_dir, 0700);
1267         chmod(ctdl_file_dir, 0700);
1268         chown(ctdl_file_dir, config.c_ctdluid, -1);
1269
1270         mkdir(ctdl_netcfg_dir, 0700);
1271         chmod(ctdl_netcfg_dir, 0700);
1272         chown(ctdl_netcfg_dir, config.c_ctdluid, -1);
1273
1274         /* TODO: where to put this? */
1275         mkdir("netconfigs", 0700);
1276         chmod("netconfigs", 0700);
1277         chown("netconfigs", config.c_ctdluid, -1);
1278
1279         /* Delete files and directories used by older Citadel versions */
1280         system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
1281         unlink("citadel.log");
1282         unlink("weekly");
1283
1284         check_services_entry(); /* Check /etc/services */
1285 #ifndef __CYGWIN__
1286         delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
1287         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1288
1289         /* Offer to disable other MTA's on the system. */
1290         disable_other_mta("courier-authdaemon");
1291         disable_other_mta("courier-imap");
1292         disable_other_mta("courier-imap-ssl");
1293         disable_other_mta("courier-pop");
1294         disable_other_mta("courier-pop3");
1295         disable_other_mta("courier-pop3d");
1296         disable_other_mta("cyrmaster");
1297         disable_other_mta("cyrus");
1298         disable_other_mta("dovecot");
1299         disable_other_mta("exim");
1300         disable_other_mta("exim4");
1301         disable_other_mta("hula");
1302         disable_other_mta("imapd");
1303         disable_other_mta("mta");
1304         disable_other_mta("pop3d");
1305         disable_other_mta("popd");
1306         disable_other_mta("postfix");
1307         disable_other_mta("qmail");
1308         disable_other_mta("saslauthd");
1309         disable_other_mta("sendmail");
1310         disable_other_mta("vmailmgrd");
1311         disable_other_mta("zimbra");
1312 #endif
1313
1314         /* Check for the 'db' nss and offer to disable it */
1315         fixnss();
1316
1317         if ((pw = getpwuid(config.c_ctdluid)) == NULL)
1318                 gid = getgid();
1319         else
1320                 gid = pw->pw_gid;
1321
1322         progress("Setting file permissions", 0, 4);
1323         chown(".", config.c_ctdluid, gid);
1324         sleep(1);
1325         progress("Setting file permissions", 1, 4);
1326         chown(file_citadel_config, config.c_ctdluid, gid);
1327         sleep(1);
1328         progress("Setting file permissions", 2, 4);
1329
1330         snprintf(aaa, sizeof aaa,
1331                          "%schkpwd",
1332                          ctdl_sbin_dir);
1333         chown(aaa,0,0); /*  config.c_ctdluid, gid); chkpwd needs to be root owned*/
1334         sleep(1);
1335         progress("Setting file permissions", 3, 4);
1336         chmod(aaa, 04755); 
1337
1338         sleep(1);
1339         progress("Setting file permissions", 3, 4);
1340         chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1341         sleep(1);
1342         progress("Setting file permissions", 4, 4);
1343
1344         /* 
1345          * If we're running on SysV, install init scripts.
1346          */
1347         if (!access("/var/run", W_OK)) {
1348
1349                 if (getenv("NO_INIT_SCRIPTS") == NULL) {
1350                         install_init_scripts();
1351                 }
1352
1353                 if (!access("/etc/init.d/citadel", X_OK)) {
1354                         for (a=0; a<=2; ++a) {
1355                                 progress("Starting the Citadel service...", a, 2);
1356                                 if (a == 0) system("/etc/init.d/citadel start >/dev/null 2>&1");
1357                                 sleep(1);
1358                         }
1359                 }
1360
1361                 if (test_server() == 0) {
1362                         important_message("Setup finished",
1363                                 "Setup of the Citadel server is complete.\n"
1364                                 "If you will be using WebCit, please run its\n"
1365                                 "setup program now; otherwise, run './citadel'\n"
1366                                 "to log in.\n");
1367                 }
1368                 else {
1369                         important_message("Setup failed",
1370                                 "Setup is finished, but the Citadel server failed to start.\n"
1371                                 "Go back and check your configuration.\n"
1372                         );
1373                 }
1374
1375         }
1376
1377         else {
1378                 important_message("Setup finished",
1379                         "Setup is finished.  You may now start the server.");
1380         }
1381
1382         cleanup(0);
1383         return 0;
1384 }
1385
1386