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