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