Added our standard 'designed to piss off Richard Stallman' license declaration to...
[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 /* calculate all our path on a central place */
109 /* where to keep our config */
110         
111
112
113 void SetTitles(void)
114 {
115         int have_run_dir;
116 #ifndef HAVE_RUN_DIR
117         have_run_dir = 1;
118 #else
119         have_run_dir = 0;
120 #endif
121
122 #ifdef ENABLE_NLS
123         setlocale(LC_MESSAGES, getenv("LANG"));
124
125         bindtextdomain("citadel-setup", LOCALEDIR"/locale");
126         textdomain("citadel-setup");
127         bind_textdomain_codeset("citadel-setup","UTF8");
128 #endif
129
130         setup_titles[eCitadelHomeDir] = _("Citadel Home Directory");
131         if (have_run_dir)
132                 setup_text[eCitadelHomeDir] = _(
133 "Enter the full pathname of the directory in which the Citadel\n"
134 "installation you are creating or updating resides.  If you\n"
135 "specify a directory other than the default, you will need to\n"
136 "specify the -h flag to the server when you start it up.\n");
137         else
138                 setup_text[eCitadelHomeDir] = _(
139 "Enter the subdirectory name for an alternate installation of "
140 "Citadel. To do a default installation just leave it blank."
141 "If you specify a directory other than the default, you will need to\n"
142 "specify the -h flag to the server when you start it up.\n"
143 "note that it may not have a leading /");
144
145
146         setup_titles[eSysAdminName] = _("Citadel administrator username:");
147         setup_text[eSysAdminName] = _(
148 "Please enter the name of the Citadel user account that should be granted "
149 "administrative privileges once created. If using internal authentication "
150 "this user account will be created if it does not exist. For external "
151 "authentication this user account has to exist.");
152
153
154         setup_titles[eSysAdminPW] = _("Administrator password:");
155         setup_text[eSysAdminPW] = _(
156 "Enter a password for the system administrator. When setup\n"
157 "completes it will attempt to create the administrator user\n"
158 "and set the password specified here.\n");
159
160         setup_titles[eUID] = _("Citadel User ID:");
161         setup_text[eUID] = _(
162 "Citadel needs to run under its own user ID.  This would\n"
163 "typically be called \"citadel\", but if you are running Citadel\n"
164 "as a public BBS, you might also call it \"bbs\" or \"guest\".\n"
165 "The server will run under this user ID.  Please specify that\n"
166 "user ID here.  You may specify either a user name or a numeric\n"
167 "UID.\n");
168
169         setup_titles[eIP_ADDR] = _("Listening address for the Citadel server:");
170         setup_text[eIP_ADDR] = _(
171 "Please specify the IP address which the server should be listening to. "
172 "You can name a specific IPv4 or IPv6 address, or you can specify\n"
173 "\"*\" for \"any address\", \"::\" for \"any IPv6 address\", or \"0.0.0.0\"\n"
174 "for \"any IPv4 address\". If you leave this blank, Citadel will\n"
175 "listen on all addresses. "
176 "This can usually be left to the default unless multiple instances of Citadel "
177 "are running on the same computer.");
178
179         setup_titles[eCTDL_Port] = _("Server port number:");
180         setup_text[eCTDL_Port] = _(
181 "Specify the TCP port number on which your server will run.\n"
182 "Normally, this will be port 504, which is the official port\n"
183 "assigned by the IANA for Citadel servers.  You will only need\n"
184 "to specify a different port number if you run multiple instances\n"
185 "of Citadel on the same computer and there is something else\n"
186 "already using port 504.\n");
187
188         setup_titles[eAuthType] = _("Authentication method to use:");
189         setup_text[eAuthType] = _(
190 "Please choose the user authentication mode. By default Citadel will use its "
191 "own internal user accounts database. If you choose Host, Citadel users will "
192 "have accounts on the host system, authenticated via /etc/passwd or a PAM "
193 "source. LDAP chooses an RFC 2307 compliant directory server, the last option "
194 "chooses the nonstandard MS Active Directory LDAP scheme."
195 "\n"
196 "Do not change this option unless you are sure it is required, since changing "
197 "back requires a full reinstall of Citadel."
198 "\n"
199 " 0. Self contained authentication\n"
200 " 1. Host system integrated authentication\n"
201 " 2. External LDAP - RFC 2307 compliant directory\n"
202 " 3. External LDAP - nonstandard MS Active Directory\n"
203 "\n"
204 "For help: http://www.citadel.org/doku.php/faq:installation:authmodes\n"
205 "\n"
206 "ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n");
207
208         setup_titles[eLDAP_Host] = _("LDAP host:");
209         setup_text[eLDAP_Host] = _(
210 "Please enter the host name or IP address of your LDAP server.\n");
211
212         setup_titles[eLDAP_Port] = _("LDAP port number:");
213         setup_text[eLDAP_Port] = _(
214 "Please enter the port number of the LDAP service (usually 389).\n");
215
216         setup_titles[eLDAP_Base_DN] = _("LDAP base DN:");
217         setup_text[eLDAP_Base_DN] = _(
218 "Please enter the Base DN to search for authentication\n"
219 "(for example: dc=example,dc=com)\n");
220
221         setup_titles[eLDAP_Bind_DN] = _("LDAP bind DN:");
222         setup_text[eLDAP_Bind_DN] = _(
223 "Please enter the DN of an account to use for binding to the LDAP server for "
224 "performing queries. The account does not require any other privileges. If "
225 "your LDAP server allows anonymous queries, you can leave this blank."
226 "Please enter the DN of an account to use for binding to the LDAP server\n"
227 "for performing queries.  The account does not require any other\n"
228 "privileges.  If your LDAP server allows anonymous queries, you can\n"
229 "leave this blank.\n");
230
231         setup_titles[eLDAP_Bind_PW] = _("LDAP bind password:");
232         setup_text[eLDAP_Bind_PW] = _(
233 "If you entered a Bind DN in the previous question, you must now enter\n"
234 "the password associated with that account.  Otherwise, you can leave this\n"
235 "blank.\n");
236
237 #if 0
238 // Debug loading of locales... Strace does a better job though.
239         printf("Message catalog directory: %s\n", bindtextdomain("citadel-setup", LOCALEDIR"/locale"));
240         printf("Text domain: %s\n", textdomain("citadel-setup"));
241         printf("Text domain Charset: %s\n", bind_textdomain_codeset("citadel-setup","UTF8"));
242         {
243                 int i;
244                 for (i = 0; i < eMaxQuestions; i++)
245                         printf("%s - %s\n", setup_titles[i], _(setup_titles[i]));
246                 exit(0);
247         }
248 #endif
249 }
250
251 /*
252  * print the actual stack frame.
253  */
254 void cit_backtrace(void)
255 {
256 #ifdef HAVE_BACKTRACE
257         void *stack_frames[50];
258         size_t size, i;
259         char **strings;
260
261         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
262         strings = backtrace_symbols(stack_frames, size);
263         for (i = 0; i < size; i++) {
264                 if (strings != NULL)
265                         fprintf(stderr, "%s\n", strings[i]);
266                 else
267                         fprintf(stderr, "%p\n", stack_frames[i]);
268         }
269         free(strings);
270 #endif
271 }
272
273 struct config config;
274
275 int direction;
276
277
278 void cleanup(int exitcode)
279 {
280 //      printf("Exitcode: %d\n", exitcode);
281 //      cit_backtrace();
282         exit(exitcode);
283 }
284
285
286
287 void title(const char *text)
288 {
289         if (setup_type == UI_TEXT) {
290                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
291         }
292 }
293
294
295
296 int yesno(const char *question, int default_value)
297 {
298         int i = 0;
299         int answer = 0;
300         char buf[SIZ];
301
302         switch (setup_type) {
303
304         case UI_TEXT:
305                 do {
306                         printf("%s\n%s [%s] --> ",
307                                question,
308                                _("Yes/No"),
309                                ( default_value ? _("Yes") : _("No") )
310                         );
311                         if (fgets(buf, sizeof buf, stdin))
312                         {
313                                 answer = tolower(buf[0]);
314                                 if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10))
315                                         answer = default_value;
316                                 else if (answer == 'y')
317                                         answer = 1;
318                                 else if (answer == 'n')
319                                         answer = 0;
320                         }
321                 } while ((answer < 0) || (answer > 1));
322                 break;
323
324         case UI_DIALOG:
325                 sprintf(buf, "exec %s %s --yesno '%s' 15 75",
326                         getenv("CTDL_DIALOG"),
327                         ( default_value ? "" : "--defaultno" ),
328                         question);
329                 i = system(buf);
330                 if (i == 0) {
331                         answer = 1;
332                 }
333                 else {
334                         answer = 0;
335                 }
336                 break;
337         case UI_SILENT:
338                 break;
339
340         }
341         return (answer);
342 }
343
344
345 void important_message(const char *title, const char *msgtext)
346 {
347         char buf[SIZ];
348         int rv;
349
350         switch (setup_type) {
351
352         case UI_TEXT:
353                 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");
354                 printf("       %s \n\n%s\n\n", title, msgtext);
355                 printf("%s", _("Press return to continue..."));
356                 if (fgets(buf, sizeof buf, stdin));
357                 break;
358
359         case UI_DIALOG:
360                 sprintf(buf, "exec %s --msgbox '%s' 19 72",
361                         getenv("CTDL_DIALOG"),
362                         msgtext);
363                 rv = system(buf);
364                 if (rv != 0)
365                         fprintf(stderr, _("failed to run the dialog command\n"));
366                 break;
367         case UI_SILENT:
368                 fprintf(stderr, "%s\n", msgtext);
369                 break;
370         }
371 }
372
373 void important_msgnum(int msgnum)
374 {
375         important_message(_("Important Message"), setup_text[msgnum]);
376 }
377
378 void display_error(char *error_message_format, ...)
379 {
380         StrBuf *Msg;
381         va_list arg_ptr;
382
383         Msg = NewStrBuf();
384         va_start(arg_ptr, error_message_format);
385         StrBufVAppendPrintf(Msg, 
386                             error_message_format, 
387                             arg_ptr);
388         va_end(arg_ptr);
389
390         important_message(_("Error"), ChrPtr(Msg));
391         FreeStrBuf(&Msg);
392 }
393
394 void progress(char *text, long int curr, long int cmax)
395 {
396         static long dots_printed = 0L;
397         long a = 0;
398         static FILE *fp = NULL;
399         char buf[SIZ];
400
401         switch (setup_type) {
402
403         case UI_TEXT:
404                 if (curr == 0) {
405                         printf("%s\n", text);
406                         printf("....................................................");
407                         printf("..........................\r");
408                         fflush(stdout);
409                         dots_printed = 0;
410                 } else if (curr == cmax) {
411                         printf("\r%79s\n", "");
412                 } else {
413                         a = (curr * 100) / cmax;
414                         a = a * 78;
415                         a = a / 100;
416                         while (dots_printed < a) {
417                                 printf("*");
418                                 ++dots_printed;
419                                 fflush(stdout);
420                         }
421                 }
422                 break;
423
424         case UI_DIALOG:
425                 if (curr == 0) {
426                         sprintf(buf, "exec %s --gauge '%s' 7 72 0",
427                                 getenv("CTDL_DIALOG"),
428                                 text);
429                         fp = popen(buf, "w");
430                         if (fp != NULL) {
431                                 fprintf(fp, "0\n");
432                                 fflush(fp);
433                         }
434                 } 
435                 else if (curr == cmax) {
436                         if (fp != NULL) {
437                                 fprintf(fp, "100\n");
438                                 pclose(fp);
439                                 fp = NULL;
440                         }
441                 }
442                 else {
443                         a = (curr * 100) / cmax;
444                         if (fp != NULL) {
445                                 fprintf(fp, "%ld\n", a);
446                                 fflush(fp);
447                         }
448                 }
449                 break;
450         case UI_SILENT:
451                 break;
452
453         }
454 }
455
456
457
458 /*
459  * check_services_entry()  -- Make sure "citadel" is in /etc/services
460  *
461  */
462 void check_services_entry(void)
463 {
464         int i;
465         FILE *sfp;
466         char errmsg[256];
467
468         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
469                 for (i=0; i<=2; ++i) {
470                         progress(_("Adding service entry..."), i, 2);
471                         if (i == 0) {
472                                 sfp = fopen("/etc/services", "a");
473                                 if (sfp == NULL) {
474                                         sprintf(errmsg, "%s /etc/services: %s", _("Cannot open"), strerror(errno));
475                                         display_error(errmsg);
476                                 } else {
477                                         fprintf(sfp, "%s                504/tcp\n", SERVICE_NAME);
478                                         fclose(sfp);
479                                 }
480                         }
481                 }
482         }
483 }
484
485
486
487
488 /*
489  * delete_inittab_entry()  -- Remove obsolete /etc/inittab entry for Citadel
490  */
491 void delete_inittab_entry(void)
492 {
493         FILE *infp;
494         FILE *outfp;
495         char looking_for[256];
496         char buf[1024];
497         char outfilename[32];
498         int changes_made = 0;
499         int rv;
500
501         /* Determine the fully qualified path name of citserver */
502         snprintf(looking_for, 
503                  sizeof looking_for,
504                  "%s/citserver", 
505                  ctdl_sbin_dir
506                 );
507
508         /* Now tweak /etc/inittab */
509         infp = fopen("/etc/inittab", "r");
510         if (infp == NULL) {
511
512                 /* If /etc/inittab does not exist, return quietly.
513                  * Not all host platforms have it.
514                  */
515                 if (errno == ENOENT) {
516                         return;
517                 }
518
519                 /* Other errors might mean something really did go wrong.
520                  */
521                 sprintf(buf, "%s /etc/inittab: %s", _("Cannot open"), strerror(errno));
522                 display_error(buf);
523                 return;
524         }
525
526         strcpy(outfilename, "/tmp/ctdlsetup.XXXXXX");
527         outfp = fdopen(mkstemp(outfilename), "w+");
528         if (outfp == NULL) {
529                 sprintf(buf, "%s %s: %s", _("Cannot open"), outfilename, strerror(errno));
530                 display_error(buf);
531                 fclose(infp);
532                 return;
533         }
534
535         while (fgets(buf, sizeof buf, infp) != NULL) {
536                 if (strstr(buf, looking_for) != NULL) {
537                         rv = fwrite("#", 1, 1, outfp);
538                         if (rv == -1)
539                         {
540                                 display_error("%s %s\n",
541                                               _("failed to modify inittab"), 
542                                               strerror(errno));
543                         }
544                         ++changes_made;
545                 }
546                 rv = fwrite(buf, strlen(buf), 1, outfp);
547                 if (rv == -1)
548                 {
549                         display_error("%s %s\n",
550                                       _("failed to modify inittab"), 
551                                       strerror(errno));
552                 }
553         }
554
555         fclose(infp);
556         fclose(outfp);
557
558         if (changes_made) {
559                 sprintf(buf, "/bin/mv -f %s /etc/inittab 2>/dev/null", outfilename);
560                 rv = system(buf);
561                 rv = system("/sbin/init q 2>/dev/null");
562         }
563         else {
564                 unlink(outfilename);
565         }
566 }
567
568
569 /*
570  * install_init_scripts()  -- Try to configure to start Citadel at boot
571  */
572 void install_init_scripts(void)
573 {
574         struct stat etcinitd;
575         FILE *fp;
576         char *initfile = "/etc/init.d/citadel";
577         char command[SIZ];
578         int rv;
579
580         if ((stat("/etc/init.d/", &etcinitd) == -1) && 
581             (errno == ENOENT))
582         {
583                 if ((stat("/etc/rc.d/init.d/", &etcinitd) == -1) &&
584                     (errno == ENOENT))
585                         initfile = CTDLDIR"/citadel.init";
586                 else
587                         initfile = "/etc/rc.d/init.d/citadel";
588         }
589
590         fp = fopen(initfile, "r");
591         if (fp != NULL) {
592                 if (yesno(_("Citadel already appears to be configured to start at boot.\n"
593                             "Would you like to keep your boot configuration as is?\n"), 1) == 1) {
594                         return;
595                 }
596                 fclose(fp);
597                 
598         }
599
600         if (yesno(_("Would you like to automatically start Citadel at boot?\n"), 1) == 0) {
601                 return;
602         }
603
604         fp = fopen(initfile, "w");
605         if (fp == NULL) {
606                 display_error("%s /etc/init.d/citadel", _("Cannot create"));
607                 return;
608         }
609
610         fprintf(fp,     "#!/bin/sh\n"
611                 "#\n"
612                 "# Init file for Citadel\n"
613                 "#\n"
614                 "# chkconfig: - 79 30\n"
615                 "# description: Citadel service\n"
616                 "# processname: citserver\n"
617                 "# pidfile: %s/citadel.pid\n\n"
618                 "# uncomment this to create coredumps as described in\n"
619                 "# http://www.citadel.org/doku.php/faq:mastering_your_os:gdb#how.do.i.make.my.system.produce.core-files\n"
620                 "# ulimit -c unlimited\n"
621                 "\n"
622                 "CITADEL_DIR=%s\n"
623                 ,
624                 ctdl_run_dir,
625                 ctdl_sbin_dir
626                 );
627         fprintf(fp,     "\n"
628                 "test -d /var/run || exit 0\n"
629                 "\n"
630                 "case \"$1\" in\n"
631                 "\n"
632                 "start)         echo -n \"Starting Citadel... \"\n"
633                 "               if $CITADEL_DIR/citserver -lmail -d\n"
634                 "               then\n"
635                 "                       echo \"ok\"\n"
636                 "               else\n"
637                 "                       echo \"failed\"\n"
638                 "               fi\n");
639         fprintf(fp,     "               ;;\n"
640                 "stop)          echo -n \"Stopping Citadel... \"\n"
641                 "               if $CITADEL_DIR/sendcommand DOWN >/dev/null 2>&1 ; then\n"
642                 "                       echo \"ok\"\n"
643                 "               else\n"
644                 "                       echo \"failed\"\n"
645                 "               fi\n"
646                 "               rm -f %s/citadel.pid 2>/dev/null\n"
647                 ,
648                 ctdl_run_dir
649                 );
650         fprintf(fp,     "               ;;\n"
651                 "restart)       if $CITADEL_DIR/sendcommand DOWN 1 >/dev/null 2>&1 ; then\n"
652                 "                       echo \"ok\"\n"
653                 "               else\n"
654                 "                       echo \"failed\"\n"
655                 "               fi\n"
656                 "               ;;\n"
657                 "*)             echo \"Usage: $0 {start|stop|restart}\"\n"
658                 "               exit 1\n"
659                 "               ;;\n"
660                 "esac\n"
661                 );
662
663         fclose(fp);
664         chmod(initfile, 0755);
665
666         /* Set up the run levels. */
667         rv = system("/bin/rm -f /etc/rc?.d/[SK]??citadel 2>/dev/null");
668         if (rv != 0)
669                 display_error(_("failed to remove system V init links \n"));
670
671         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);
672         rv = system(command);
673         if (rv != 0)
674                 display_error(_("failed to set system V init links \n"));
675
676         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);
677         rv = system(command);
678         if (rv != 0)
679                 display_error(_("failed to set system V init links \n"));
680 }
681
682
683
684 /*
685  * On systems which use xinetd, see if we can offer to install Citadel as
686  * the default telnet target.
687  */
688 void check_xinetd_entry(void) {
689         char *filename = "/etc/xinetd.d/telnet";
690         FILE *fp;
691         char buf[SIZ];
692         int already_citadel = 0;
693         int rv;
694
695         fp = fopen(filename, "r+");
696         if (fp == NULL) return;         /* Not there.  Oh well... */
697
698         while (fgets(buf, sizeof buf, fp) != NULL) {
699                 if (strstr(buf, "/citadel") != NULL) already_citadel = 1;
700         }
701         fclose(fp);
702         if (already_citadel) return;    /* Already set up this way. */
703
704         /* Otherwise, prompt the user to create an entry. */
705         if (getenv("CREATE_XINETD_ENTRY") != NULL) {
706                 if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
707                         return;
708                 }
709         }
710         else {
711                 snprintf(buf, sizeof buf,
712                          _("Setup can configure the \"xinetd\" service to automatically\n"
713                            "connect incoming telnet sessions to Citadel, bypassing the\n"
714                            "host system login: prompt.  Would you like to do this?\n"
715                                  ));
716                 if (yesno(buf, 1) == 0) {
717                         return;
718                 }
719         }
720
721         fp = fopen(filename, "w");
722         fprintf(fp,
723                 "# description: telnet service for Citadel users\n"
724                 "service telnet\n"
725                 "{\n"
726                 "       disable = no\n"
727                 "       flags           = REUSE\n"
728                 "       socket_type     = stream\n"
729                 "       wait            = no\n"
730                 "       user            = root\n"
731                 "       server          = /usr/sbin/in.telnetd\n"
732                 "       server_args     = -h -L %s/citadel\n"
733                 "       log_on_failure  += USERID\n"
734                 "}\n",
735                 ctdl_bin_dir);
736         fclose(fp);
737
738         /* Now try to restart the service */
739         rv = system("/etc/init.d/xinetd restart >/dev/null 2>&1");
740         if (rv != 0)
741                 display_error(_("failed to restart xinetd.\n"));
742 }
743
744
745
746 /*
747  * Offer to disable other MTA's
748  */
749 void disable_other_mta(const char *mta) {
750         char buf[SIZ];
751         FILE *fp;
752         int lines = 0;
753         int rv;
754
755         sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
756                 "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
757                 mta, mta);
758         fp = popen(buf, "r");
759         if (fp == NULL) return;
760
761         while (fgets(buf, sizeof buf, fp) != NULL) {
762                 ++lines;
763         }
764         fclose(fp);
765         if (lines == 0) return;         /* Nothing to do. */
766
767
768         /* Offer to replace other MTA with the vastly superior Citadel :)  */
769
770         snprintf(buf, sizeof buf,
771                  "%s \"%s\" %s%s%s%s%s%s%s", 
772                  _("You appear to have the "), 
773                  mta, 
774                  _(" email program\n"
775                    "running on your system.  If you want Citadel mail\n"
776                    "connected with "), 
777                  mta,
778                  _(" you will have to manually integrate\n"
779                    "them.  It is preferable to disable "), 
780                  mta,
781                  _(", and use Citadel's\n"
782                    "SMTP, POP3, and IMAP services.\n\n"
783                    "May we disable "), 
784                  mta, 
785                  _("so that Citadel has access to ports\n"
786                    "25, 110, and 143?\n")
787                 );
788         if (yesno(buf, 1) == 0) {
789                 return;
790         }
791         
792
793         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);
794         rv = system(buf);
795         if (rv != 0)
796                 display_error("%s %s.\n", _("failed to disable other mta"), mta);
797
798         sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
799         rv = system(buf);
800         if (rv != 0)
801                 display_error(" %s.\n", _("failed to disable other mta"), mta);
802 }
803
804 const char *other_mtas[] = {
805         "courier-authdaemon",
806         "courier-imap",
807         "courier-imap-ssl",
808         "courier-pop",
809         "courier-pop3",
810         "courier-pop3d",
811         "cyrmaster",
812         "cyrus",
813         "dovecot",
814         "exim",
815         "exim4",
816         "imapd",
817         "mta",
818         "pop3d",
819         "popd",
820         "postfix",
821         "qmail",
822         "saslauthd",
823         "sendmail",
824         "vmailmgrd",
825         ""
826 };
827
828 void disable_other_mtas(void)
829 {
830         int i = 0;
831         if ((getenv("ACT_AS_MTA") == NULL) || 
832             (getenv("ACT_AS_MTA") &&
833              strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
834                 /* Offer to disable other MTA's on the system. */
835                 while (!IsEmptyStr(other_mtas[i]))
836                 {
837                         disable_other_mta(other_mtas[i]);
838                         i++;
839                 }
840         }
841 }
842
843 /* 
844  * Check to see if our server really works.  Returns 0 on success.
845  */
846 int test_server(char *relhomestr, int relhome) {
847         char cmd[256];
848         char cookie[256];
849         FILE *fp;
850         char buf[4096];
851         int found_it = 0;
852
853         /* Generate a silly little cookie.  We're going to write it out
854          * to the server and try to get it back.  The cookie does not
855          * have to be secret ... just unique.
856          */
857         sprintf(cookie, "--test--%d--", getpid());
858
859         if (relhome)
860                 sprintf(cmd, "%s/sendcommand -h%s ECHO %s 2>&1",
861                         ctdl_sbin_dir,
862                         relhomestr,
863                         cookie);
864         else
865                 sprintf(cmd, "%s/sendcommand ECHO %s 2>&1",
866                         ctdl_sbin_dir,
867                         cookie);
868
869         fp = popen(cmd, "r");
870         if (fp == NULL) return(errno);
871
872         while (fgets(buf, sizeof buf, fp) != NULL) {
873                 if ( (buf[0]=='2')
874                      && (strstr(buf, cookie) != NULL) ) {
875                         ++found_it;
876                 }
877         }
878         pclose(fp);
879
880         if (found_it) {
881                 return(0);
882         }
883         return(-1);
884 }
885
886 void strprompt(const char *prompt_title, const char *prompt_text, char *Target, char *DefValue)
887 {
888         char buf[SIZ] = "";
889         char setupmsg[SIZ];
890         char dialog_result[PATH_MAX];
891         FILE *fp = NULL;
892         int rv;
893
894         strcpy(setupmsg, "");
895
896         switch (setup_type) {
897         case UI_TEXT:
898                 title(prompt_title);
899                 printf("\n%s\n", prompt_text);
900                 printf("%s\n%s\n", _("This is currently set to:"), Target);
901                 printf("%s\n", _("Enter new value or press return to leave unchanged:"));
902                 if (fgets(buf, sizeof buf, stdin)){
903                         buf[strlen(buf) - 1] = 0;
904                 }
905                 if (!IsEmptyStr(buf))
906                         strcpy(Target, buf);
907                 break;
908
909         case UI_DIALOG:
910                 CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
911                 sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
912                         getenv("CTDL_DIALOG"),
913                         prompt_text,
914                         Target,
915                         dialog_result);
916                 rv = system(buf);
917                 if (rv != 0)
918                         fprintf(stderr, "failed to run Dialog.\n");
919                 
920                 fp = fopen(dialog_result, "r");
921                 if (fp != NULL) {
922                         if (fgets(Target, sizeof buf, fp)) {
923                                 if (Target[strlen(Target)-1] == 10) {
924                                         Target[strlen(Target)-1] = 0;
925                                 }
926                         }
927                         fclose(fp);
928                         unlink(dialog_result);
929                 }
930                 break;
931         case UI_SILENT:
932                 if (*DefValue != '\0')
933                         strcpy(Target, DefValue);
934                 break;
935         }
936 }
937
938 void set_bool_val(int msgpos, int *ip, char *DefValue) 
939 {
940         title(setup_titles[msgpos]);
941         *ip = yesno(setup_text[msgpos], *ip);
942 }
943
944 void set_str_val(int msgpos, char *Target, char *DefValue) 
945 {
946         strprompt(setup_titles[msgpos], 
947                   setup_text[msgpos], 
948                   Target, 
949                   DefValue);
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;   /* XXX Windows hack, prob. insecure */
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 "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
1569         set_default_values();
1570
1571         /* Go through a series of dialogs prompting for config info */
1572         for (curr = 1; curr <= MAXSETUP; ++curr) {
1573                 edit_value(curr);
1574                 if ((curr == 6) && (config.c_auth_mode != AUTHMODE_LDAP) && (config.c_auth_mode != AUTHMODE_LDAP_AD)) {
1575                         curr += 5;      /* skip LDAP questions if we're not authenticating against LDAP */
1576                 }
1577         }
1578
1579         /***** begin version update section *****/
1580
1581         old_setup_level = config.c_setup_level;
1582
1583         if (old_setup_level == 0) {
1584                 goto NEW_INST;
1585         }
1586
1587         if (old_setup_level < 555) {
1588                 important_message(
1589                         _("Citadel Setup"),
1590                         _("This Citadel installation is too old to be upgraded.")
1591                 );
1592                 cleanup(1);
1593         }
1594         write_config_to_disk();
1595
1596         old_setup_level = config.c_setup_level;
1597
1598         /***** end of version update section *****/
1599
1600 NEW_INST:
1601         config.c_setup_level = REV_LEVEL;
1602
1603         if ((pw = getpwuid(config.c_ctdluid)) == NULL) {
1604                 gid = getgid();
1605         } else {
1606                 gid = pw->pw_gid;
1607         }
1608
1609         create_run_directories(config.c_ctdluid, gid);
1610
1611         write_config_to_disk();
1612
1613         if (    ((setup_type == UI_SILENT)
1614                 && (getenv("ALTER_ETC_SERVICES")!=NULL))
1615                 || (setup_type != UI_SILENT)
1616         ) {
1617                 check_services_entry(); /* Check /etc/services */
1618         }
1619
1620 #ifndef __CYGWIN__
1621         delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
1622         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1623         disable_other_mtas();   /* Offer to disable other MTAs */
1624         fixnss();               /* Check for the 'db' nss and offer to disable it */
1625 #endif
1626
1627         progress(_("Setting file permissions"), 1, 3);
1628         rv = chown(file_citadel_config, config.c_ctdluid, gid);
1629         progress(_("Setting file permissions"), 2, 3);
1630         rv = chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1631         progress(_("Setting file permissions"), 3, 3);
1632
1633         check_init_script(relhome);
1634         cleanup(0);
1635         return 0;
1636 }
1637
1638