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