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