setup.c only calls setlocale() et al if ENABLE_NLS is defined
[citadel.git] / citadel / utils / setup.c
1 /*
2  * Citadel setup utility
3  */
4
5 #define SHOW_ME_VAPPEND_PRINTF
6 #include "ctdl_module.h"
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <ctype.h>
12 #include <fcntl.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <sys/utsname.h>
16 #include <sys/wait.h>
17 #include <signal.h>
18 #include <netdb.h>
19 #include <errno.h>
20 #include <limits.h>
21 #include <pwd.h>
22 #include <time.h>
23 #include <libcitadel.h>
24 #include "citadel.h"
25 #include "axdefs.h"
26 #include "sysdep.h"
27 #include "config.h"
28 #include "citadel_dirs.h"
29 #if HAVE_BACKTRACE
30 #include <execinfo.h>
31 #endif
32
33 #ifdef ENABLE_NLS
34 #ifdef HAVE_XLOCALE_H
35 #include <xlocale.h>
36 #endif
37 #include <libintl.h>
38 #include <locale.h>
39 #define _(string)       gettext(string)
40 #else
41 #define _(string)       (string)
42 #endif
43
44
45 #define MAXSETUP 11     /* How many setup questions to ask */
46
47 #define UI_TEXT         0       /* Default setup type -- text only */
48 #define UI_DIALOG       2       /* Use the 'dialog' program */
49 #define UI_SILENT       3       /* Silent running, for use in scripts */
50
51 #define SERVICE_NAME    "citadel"
52 #define PROTO_NAME      "tcp"
53 #define NSSCONF         "/etc/nsswitch.conf"
54
55
56 typedef enum _SetupStep {
57         eCitadelHomeDir = 0,
58         eSysAdminName = 1,
59         eSysAdminPW = 2,
60         eUID = 3,
61         eIP_ADDR = 4,
62         eCTDL_Port = 5,
63         eAuthType = 6,
64         eLDAP_Host = 7,
65         eLDAP_Port = 8,
66         eLDAP_Base_DN = 9,
67         eLDAP_Bind_DN = 10,
68         eLDAP_Bind_PW = 11,
69         eMaxQuestions = 12
70 } eSteupStep;
71
72 ///"CREATE_XINETD_ENTRY";
73 /* Environment variables, don't translate! */
74 const char *EnvNames [eMaxQuestions] = {
75         "HOME_DIRECTORY",
76         "SYSADMIN_NAME",
77         "SYSADMIN_PW",
78         "CITADEL_UID",
79         "IP_ADDR",
80         "CITADEL_PORT",
81         "ENABLE_UNIX_AUTH",
82         "LDAP_HOST",
83         "LDAP_PORT",
84         "LDAP_BASE_DN",
85         "LDAP_BIND_DN",
86         "LDAP_BIND_PW"
87 };
88
89 int setup_type;
90 int using_web_installer = 0;
91 int enable_home = 1;
92 char admin_pass[SIZ];
93 char admin_cmd[SIZ];
94
95 const char *setup_titles[eMaxQuestions];
96 const char *setup_text[eMaxQuestions];
97
98 /* calculate all our path on a central place */
99 /* where to keep our config */
100         
101
102
103 void SetTitles(void)
104 {
105         int have_run_dir;
106 #ifndef HAVE_RUN_DIR
107         have_run_dir = 1;
108 #else
109         have_run_dir = 0;
110 #endif
111
112 #ifdef ENABLE_NLS
113         setlocale(LC_MESSAGES, getenv("LANG"));
114
115         bindtextdomain("citadel-setup", LOCALEDIR"/locale");
116         textdomain("citadel-setup");
117         bind_textdomain_codeset("citadel-setup","UTF8");
118 #endif
119
120         setup_titles[eCitadelHomeDir] = _("Citadel Home Directory");
121         if (have_run_dir)
122                 setup_text[eCitadelHomeDir] = _(
123 "Enter the full pathname of the directory in which the Citadel\n"
124 "installation you are creating or updating resides.  If you\n"
125 "specify a directory other than the default, you will need to\n"
126 "specify the -h flag to the server when you start it up.\n");
127         else
128                 setup_text[eCitadelHomeDir] = _(
129 "Enter the subdirectory name for an alternate installation of "
130 "Citadel. To do a default installation just leave it blank."
131 "If you specify a directory other than the default, you will need to\n"
132 "specify the -h flag to the server when you start it up.\n"
133 "note that it may not have a leading /");
134
135
136         setup_titles[eSysAdminName] = _("Citadel administrator username:");
137         setup_text[eSysAdminName] = _(
138 "Please enter the name of the Citadel user account that should be granted "
139 "administrative privileges once created. If using internal authentication "
140 "this user account will be created if it does not exist. For external "
141 "authentication this user account has to exist.");
142
143
144         setup_titles[eSysAdminPW] = _("Administrator password:");
145         setup_text[eSysAdminPW] = _(
146 "Enter a password for the system administrator. When setup\n"
147 "completes it will attempt to create the administrator user\n"
148 "and set the password specified here.\n");
149
150         setup_titles[eUID] = _("Citadel User ID:");
151         setup_text[eUID] = _(
152 "Citadel needs to run under its own user ID.  This would\n"
153 "typically be called \"citadel\", but if you are running Citadel\n"
154 "as a public BBS, you might also call it \"bbs\" or \"guest\".\n"
155 "The server will run under this user ID.  Please specify that\n"
156 "user ID here.  You may specify either a user name or a numeric\n"
157 "UID.\n");
158
159         setup_titles[eIP_ADDR] = _("Listening address for the Citadel server:");
160         setup_text[eIP_ADDR] = _(
161 "Please specify the IP address which the server should be listening to. "
162 "You can name a specific IPv4 or IPv6 address, or you can specify\n"
163 "\"*\" for \"any address\", \"::\" for \"any IPv6 address\", or \"0.0.0.0\"\n"
164 "for \"any IPv4 address\". If you leave this blank, Citadel will\n"
165 "listen on all addresses. "
166 "This can usually be left to the default unless multiple instances of Citadel "
167 "are running on the same computer.");
168
169         setup_titles[eCTDL_Port] = _("Server port number:");
170         setup_text[eCTDL_Port] = _(
171 "Specify the TCP port number on which your server will run.\n"
172 "Normally, this will be port 504, which is the official port\n"
173 "assigned by the IANA for Citadel servers.  You will only need\n"
174 "to specify a different port number if you run multiple instances\n"
175 "of Citadel on the same computer and there is something else\n"
176 "already using port 504.\n");
177
178         setup_titles[eAuthType] = _("Authentication method to use:");
179         setup_text[eAuthType] = _(
180 "Please choose the user authentication mode. By default Citadel will use its "
181 "own internal user accounts database. If you choose Host, Citadel users will "
182 "have accounts on the host system, authenticated via /etc/passwd or a PAM "
183 "source. LDAP chooses an RFC 2307 compliant directory server, the last option "
184 "chooses the nonstandard MS Active Directory LDAP scheme."
185 "\n"
186 "Do not change this option unless you are sure it is required, since changing "
187 "back requires a full reinstall of Citadel."
188 "\n"
189 " 0. Self contained authentication\n"
190 " 1. Host system integrated authentication\n"
191 " 2. External LDAP - RFC 2307 compliant directory\n"
192 " 3. External LDAP - nonstandard MS Active Directory\n"
193 "\n"
194 "For help: http://www.citadel.org/doku.php/faq:installation:authmodes\n"
195 "\n"
196 "ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n");
197
198         setup_titles[eLDAP_Host] = _("LDAP host:");
199         setup_text[eLDAP_Host] = _(
200 "Please enter the host name or IP address of your LDAP server.\n");
201
202         setup_titles[eLDAP_Port] = _("LDAP port number:");
203         setup_text[eLDAP_Port] = _(
204 "Please enter the port number of the LDAP service (usually 389).\n");
205
206         setup_titles[eLDAP_Base_DN] = _("LDAP base DN:");
207         setup_text[eLDAP_Base_DN] = _(
208 "Please enter the Base DN to search for authentication\n"
209 "(for example: dc=example,dc=com)\n");
210
211         setup_titles[eLDAP_Bind_DN] = _("LDAP bind DN:");
212         setup_text[eLDAP_Bind_DN] = _(
213 "Please enter the DN of an account to use for binding to the LDAP server for "
214 "performing queries. The account does not require any other privileges. If "
215 "your LDAP server allows anonymous queries, you can leave this blank."
216 "Please enter the DN of an account to use for binding to the LDAP server\n"
217 "for performing queries.  The account does not require any other\n"
218 "privileges.  If your LDAP server allows anonymous queries, you can\n"
219 "leave this blank.\n");
220
221         setup_titles[eLDAP_Bind_PW] = _("LDAP bind password:");
222         setup_text[eLDAP_Bind_PW] = _(
223 "If you entered a Bind DN in the previous question, you must now enter\n"
224 "the password associated with that account.  Otherwise, you can leave this\n"
225 "blank.\n");
226
227 #if 0
228 // Debug loading of locales... Strace does a better job though.
229         printf("Message catalog directory: %s\n", bindtextdomain("citadel-setup", LOCALEDIR"/locale"));
230         printf("Text domain: %s\n", textdomain("citadel-setup"));
231         printf("Text domain Charset: %s\n", bind_textdomain_codeset("citadel-setup","UTF8"));
232         {
233                 int i;
234                 for (i = 0; i < eMaxQuestions; i++)
235                         printf("%s - %s\n", setup_titles[i], _(setup_titles[i]));
236                 exit(0);
237         }
238 #endif
239 }
240
241 /*
242  * print the actual stack frame.
243  */
244 void cit_backtrace(void)
245 {
246 #ifdef HAVE_BACKTRACE
247         void *stack_frames[50];
248         size_t size, i;
249         char **strings;
250
251         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
252         strings = backtrace_symbols(stack_frames, size);
253         for (i = 0; i < size; i++) {
254                 if (strings != NULL)
255                         fprintf(stderr, "%s\n", strings[i]);
256                 else
257                         fprintf(stderr, "%p\n", stack_frames[i]);
258         }
259         free(strings);
260 #endif
261 }
262
263 struct config config;
264
265 int direction;
266
267
268 void cleanup(int exitcode)
269 {
270 //      printf("Exitcode: %d\n", exitcode);
271 //      cit_backtrace();
272         exit(exitcode);
273 }
274
275
276
277 void title(const char *text)
278 {
279         if (setup_type == UI_TEXT) {
280                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
281         }
282 }
283
284
285
286 int yesno(const char *question, int default_value)
287 {
288         int i = 0;
289         int answer = 0;
290         char buf[SIZ];
291
292         switch (setup_type) {
293
294         case UI_TEXT:
295                 do {
296                         printf("%s\n%s [%s] --> ",
297                                question,
298                                _("Yes/No"),
299                                ( default_value ? _("Yes") : _("No") )
300                         );
301                         if (fgets(buf, sizeof buf, stdin))
302                         {
303                                 answer = tolower(buf[0]);
304                                 if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10))
305                                         answer = default_value;
306                                 else if (answer == 'y')
307                                         answer = 1;
308                                 else if (answer == 'n')
309                                         answer = 0;
310                         }
311                 } while ((answer < 0) || (answer > 1));
312                 break;
313
314         case UI_DIALOG:
315                 sprintf(buf, "exec %s %s --yesno '%s' 15 75",
316                         getenv("CTDL_DIALOG"),
317                         ( default_value ? "" : "--defaultno" ),
318                         question);
319                 i = system(buf);
320                 if (i == 0) {
321                         answer = 1;
322                 }
323                 else {
324                         answer = 0;
325                 }
326                 break;
327         case UI_SILENT:
328                 break;
329
330         }
331         return (answer);
332 }
333
334
335 void important_message(const char *title, const char *msgtext)
336 {
337         char buf[SIZ];
338         int rv;
339
340         switch (setup_type) {
341
342         case UI_TEXT:
343                 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
344                 printf("       %s \n\n%s\n\n", title, msgtext);
345                 printf("%s", _("Press return to continue..."));
346                 if (fgets(buf, sizeof buf, stdin));
347                 break;
348
349         case UI_DIALOG:
350                 sprintf(buf, "exec %s --msgbox '%s' 19 72",
351                         getenv("CTDL_DIALOG"),
352                         msgtext);
353                 rv = system(buf);
354                 if (rv != 0)
355                         fprintf(stderr, _("failed to run the dialog command\n"));
356                 break;
357         case UI_SILENT:
358                 fprintf(stderr, "%s\n", msgtext);
359                 break;
360         }
361 }
362
363 void important_msgnum(int msgnum)
364 {
365         important_message(_("Important Message"), setup_text[msgnum]);
366 }
367
368 void display_error(char *error_message_format, ...)
369 {
370         StrBuf *Msg;
371         va_list arg_ptr;
372
373         Msg = NewStrBuf();
374         va_start(arg_ptr, error_message_format);
375         StrBufVAppendPrintf(Msg, 
376                             error_message_format, 
377                             arg_ptr);
378         va_end(arg_ptr);
379
380         important_message(_("Error"), ChrPtr(Msg));
381         FreeStrBuf(&Msg);
382 }
383
384 void progress(char *text, long int curr, long int cmax)
385 {
386         static long dots_printed = 0L;
387         long a = 0;
388         static FILE *fp = NULL;
389         char buf[SIZ];
390
391         switch (setup_type) {
392
393         case UI_TEXT:
394                 if (curr == 0) {
395                         printf("%s\n", text);
396                         printf("....................................................");
397                         printf("..........................\r");
398                         fflush(stdout);
399                         dots_printed = 0;
400                 } else if (curr == cmax) {
401                         printf("\r%79s\n", "");
402                 } else {
403                         a = (curr * 100) / cmax;
404                         a = a * 78;
405                         a = a / 100;
406                         while (dots_printed < a) {
407                                 printf("*");
408                                 ++dots_printed;
409                                 fflush(stdout);
410                         }
411                 }
412                 break;
413
414         case UI_DIALOG:
415                 if (curr == 0) {
416                         sprintf(buf, "exec %s --gauge '%s' 7 72 0",
417                                 getenv("CTDL_DIALOG"),
418                                 text);
419                         fp = popen(buf, "w");
420                         if (fp != NULL) {
421                                 fprintf(fp, "0\n");
422                                 fflush(fp);
423                         }
424                 } 
425                 else if (curr == cmax) {
426                         if (fp != NULL) {
427                                 fprintf(fp, "100\n");
428                                 pclose(fp);
429                                 fp = NULL;
430                         }
431                 }
432                 else {
433                         a = (curr * 100) / cmax;
434                         if (fp != NULL) {
435                                 fprintf(fp, "%ld\n", a);
436                                 fflush(fp);
437                         }
438                 }
439                 break;
440         case UI_SILENT:
441                 break;
442
443         }
444 }
445
446
447
448 /*
449  * check_services_entry()  -- Make sure "citadel" is in /etc/services
450  *
451  */
452 void check_services_entry(void)
453 {
454         int i;
455         FILE *sfp;
456         char errmsg[256];
457
458         if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
459                 for (i=0; i<=2; ++i) {
460                         progress(_("Adding service entry..."), i, 2);
461                         if (i == 0) {
462                                 sfp = fopen("/etc/services", "a");
463                                 if (sfp == NULL) {
464                                         sprintf(errmsg, "%s /etc/services: %s", _("Cannot open"), strerror(errno));
465                                         display_error(errmsg);
466                                 } else {
467                                         fprintf(sfp, "%s                504/tcp\n", SERVICE_NAME);
468                                         fclose(sfp);
469                                 }
470                         }
471                 }
472         }
473 }
474
475
476
477
478 /*
479  * delete_inittab_entry()  -- Remove obsolete /etc/inittab entry for Citadel
480  */
481 void delete_inittab_entry(void)
482 {
483         FILE *infp;
484         FILE *outfp;
485         char looking_for[256];
486         char buf[1024];
487         char outfilename[32];
488         int changes_made = 0;
489         int rv;
490
491         /* Determine the fully qualified path name of citserver */
492         snprintf(looking_for, 
493                  sizeof looking_for,
494                  "%s/citserver", 
495                  ctdl_sbin_dir
496                 );
497
498         /* Now tweak /etc/inittab */
499         infp = fopen("/etc/inittab", "r");
500         if (infp == NULL) {
501
502                 /* If /etc/inittab does not exist, return quietly.
503                  * Not all host platforms have it.
504                  */
505                 if (errno == ENOENT) {
506                         return;
507                 }
508
509                 /* Other errors might mean something really did go wrong.
510                  */
511                 sprintf(buf, "%s /etc/inittab: %s", _("Cannot open"), strerror(errno));
512                 display_error(buf);
513                 return;
514         }
515
516         strcpy(outfilename, "/tmp/ctdlsetup.XXXXXX");
517         outfp = fdopen(mkstemp(outfilename), "w+");
518         if (outfp == NULL) {
519                 sprintf(buf, "%s %s: %s", _("Cannot open"), outfilename, strerror(errno));
520                 display_error(buf);
521                 fclose(infp);
522                 return;
523         }
524
525         while (fgets(buf, sizeof buf, infp) != NULL) {
526                 if (strstr(buf, looking_for) != NULL) {
527                         rv = fwrite("#", 1, 1, outfp);
528                         if (rv == -1)
529                         {
530                                 display_error("%s %s\n",
531                                               _("failed to modify inittab"), 
532                                               strerror(errno));
533                         }
534                         ++changes_made;
535                 }
536                 rv = fwrite(buf, strlen(buf), 1, outfp);
537                 if (rv == -1)
538                 {
539                         display_error("%s %s\n",
540                                       _("failed to modify inittab"), 
541                                       strerror(errno));
542                 }
543         }
544
545         fclose(infp);
546         fclose(outfp);
547
548         if (changes_made) {
549                 sprintf(buf, "/bin/mv -f %s /etc/inittab 2>/dev/null", outfilename);
550                 rv = system(buf);
551                 rv = system("/sbin/init q 2>/dev/null");
552         }
553         else {
554                 unlink(outfilename);
555         }
556 }
557
558
559 /*
560  * install_init_scripts()  -- Try to configure to start Citadel at boot
561  */
562 void install_init_scripts(void)
563 {
564         struct stat etcinitd;
565         FILE *fp;
566         char *initfile = "/etc/init.d/citadel";
567         char command[SIZ];
568         int rv;
569
570         if ((stat("/etc/init.d/", &etcinitd) == -1) && 
571             (errno == ENOENT))
572         {
573                 if ((stat("/etc/rc.d/init.d/", &etcinitd) == -1) &&
574                     (errno == ENOENT))
575                         initfile = CTDLDIR"/citadel.init";
576                 else
577                         initfile = "/etc/rc.d/init.d/citadel";
578         }
579
580         fp = fopen(initfile, "r");
581         if (fp != NULL) {
582                 if (yesno(_("Citadel already appears to be configured to start at boot.\n"
583                             "Would you like to keep your boot configuration as is?\n"), 1) == 1) {
584                         return;
585                 }
586                 fclose(fp);
587                 
588         }
589
590         if (yesno(_("Would you like to automatically start Citadel at boot?\n"), 1) == 0) {
591                 return;
592         }
593
594         fp = fopen(initfile, "w");
595         if (fp == NULL) {
596                 display_error("%s /etc/init.d/citadel", _("Cannot create"));
597                 return;
598         }
599
600         fprintf(fp,     "#!/bin/sh\n"
601                 "#\n"
602                 "# Init file for Citadel\n"
603                 "#\n"
604                 "# chkconfig: - 79 30\n"
605                 "# description: Citadel service\n"
606                 "# processname: citserver\n"
607                 "# pidfile: %s/citadel.pid\n\n"
608                 "# uncomment this to create coredumps as described in\n"
609                 "# http://www.citadel.org/doku.php/faq:mastering_your_os:gdb#how.do.i.make.my.system.produce.core-files\n"
610                 "# ulimit -c unlimited\n"
611                 "\n"
612                 "CITADEL_DIR=%s\n"
613                 ,
614                 ctdl_run_dir,
615                 ctdl_sbin_dir
616                 );
617         fprintf(fp,     "\n"
618                 "test -d /var/run || exit 0\n"
619                 "\n"
620                 "case \"$1\" in\n"
621                 "\n"
622                 "start)         echo -n \"Starting Citadel... \"\n"
623                 "               if $CITADEL_DIR/citserver -lmail -d\n"
624                 "               then\n"
625                 "                       echo \"ok\"\n"
626                 "               else\n"
627                 "                       echo \"failed\"\n"
628                 "               fi\n");
629         fprintf(fp,     "               ;;\n"
630                 "stop)          echo -n \"Stopping Citadel... \"\n"
631                 "               if $CITADEL_DIR/sendcommand DOWN >/dev/null 2>&1 ; then\n"
632                 "                       echo \"ok\"\n"
633                 "               else\n"
634                 "                       echo \"failed\"\n"
635                 "               fi\n"
636                 "               rm -f %s/citadel.pid 2>/dev/null\n"
637                 ,
638                 ctdl_run_dir
639                 );
640         fprintf(fp,     "               ;;\n"
641                 "restart)       if $CITADEL_DIR/sendcommand DOWN 1 >/dev/null 2>&1 ; then\n"
642                 "                       echo \"ok\"\n"
643                 "               else\n"
644                 "                       echo \"failed\"\n"
645                 "               fi\n"
646                 "               ;;\n"
647                 "*)             echo \"Usage: $0 {start|stop|restart}\"\n"
648                 "               exit 1\n"
649                 "               ;;\n"
650                 "esac\n"
651                 );
652
653         fclose(fp);
654         chmod(initfile, 0755);
655
656         /* Set up the run levels. */
657         rv = system("/bin/rm -f /etc/rc?.d/[SK]??citadel 2>/dev/null");
658         if (rv != 0)
659                 display_error(_("failed to remove system V init links \n"));
660
661         snprintf(command, sizeof(command), "for x in 2 3 4 5 ; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/S79citadel ; done 2>/dev/null", initfile);
662         rv = system(command);
663         if (rv != 0)
664                 display_error(_("failed to set system V init links \n"));
665
666         snprintf(command, sizeof(command),"for x in 0 6 S; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/K30citadel ; done 2>/dev/null", initfile);
667         rv = system(command);
668         if (rv != 0)
669                 display_error(_("failed to set system V init links \n"));
670 }
671
672
673
674 /*
675  * On systems which use xinetd, see if we can offer to install Citadel as
676  * the default telnet target.
677  */
678 void check_xinetd_entry(void) {
679         char *filename = "/etc/xinetd.d/telnet";
680         FILE *fp;
681         char buf[SIZ];
682         int already_citadel = 0;
683         int rv;
684
685         fp = fopen(filename, "r+");
686         if (fp == NULL) return;         /* Not there.  Oh well... */
687
688         while (fgets(buf, sizeof buf, fp) != NULL) {
689                 if (strstr(buf, "/citadel") != NULL) already_citadel = 1;
690         }
691         fclose(fp);
692         if (already_citadel) return;    /* Already set up this way. */
693
694         /* Otherwise, prompt the user to create an entry. */
695         if (getenv("CREATE_XINETD_ENTRY") != NULL) {
696                 if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
697                         return;
698                 }
699         }
700         else {
701                 snprintf(buf, sizeof buf,
702                          _("Setup can configure the \"xinetd\" service to automatically\n"
703                            "connect incoming telnet sessions to Citadel, bypassing the\n"
704                            "host system login: prompt.  Would you like to do this?\n"
705                                  ));
706                 if (yesno(buf, 1) == 0) {
707                         return;
708                 }
709         }
710
711         fp = fopen(filename, "w");
712         fprintf(fp,
713                 "# description: telnet service for Citadel users\n"
714                 "service telnet\n"
715                 "{\n"
716                 "       disable = no\n"
717                 "       flags           = REUSE\n"
718                 "       socket_type     = stream\n"
719                 "       wait            = no\n"
720                 "       user            = root\n"
721                 "       server          = /usr/sbin/in.telnetd\n"
722                 "       server_args     = -h -L %s/citadel\n"
723                 "       log_on_failure  += USERID\n"
724                 "}\n",
725                 ctdl_bin_dir);
726         fclose(fp);
727
728         /* Now try to restart the service */
729         rv = system("/etc/init.d/xinetd restart >/dev/null 2>&1");
730         if (rv != 0)
731                 display_error(_("failed to restart xinetd.\n"));
732 }
733
734
735
736 /*
737  * Offer to disable other MTA's
738  */
739 void disable_other_mta(const char *mta) {
740         char buf[SIZ];
741         FILE *fp;
742         int lines = 0;
743         int rv;
744
745         sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
746                 "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
747                 mta, mta);
748         fp = popen(buf, "r");
749         if (fp == NULL) return;
750
751         while (fgets(buf, sizeof buf, fp) != NULL) {
752                 ++lines;
753         }
754         fclose(fp);
755         if (lines == 0) return;         /* Nothing to do. */
756
757
758         /* Offer to replace other MTA with the vastly superior Citadel :)  */
759
760         snprintf(buf, sizeof buf,
761                  "%s \"%s\" %s%s%s%s%s%s%s", 
762                  _("You appear to have the "), 
763                  mta, 
764                  _(" email program\n"
765                    "running on your system.  If you want Citadel mail\n"
766                    "connected with "), 
767                  mta,
768                  _(" you will have to manually integrate\n"
769                    "them.  It is preferable to disable "), 
770                  mta,
771                  _(", and use Citadel's\n"
772                    "SMTP, POP3, and IMAP services.\n\n"
773                    "May we disable "), 
774                  mta, 
775                  _("so that Citadel has access to ports\n"
776                    "25, 110, and 143?\n")
777                 );
778         if (yesno(buf, 1) == 0) {
779                 return;
780         }
781         
782
783         sprintf(buf, "for x in /etc/rc*.d/S*%s; do mv $x `echo $x |sed s/S/K/g`; done >/dev/null 2>&1", mta);
784         rv = system(buf);
785         if (rv != 0)
786                 display_error("%s %s.\n", _("failed to disable other mta"), mta);
787
788         sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
789         rv = system(buf);
790         if (rv != 0)
791                 display_error(" %s.\n", _("failed to disable other mta"), mta);
792 }
793
794 const char *other_mtas[] = {
795         "courier-authdaemon",
796         "courier-imap",
797         "courier-imap-ssl",
798         "courier-pop",
799         "courier-pop3",
800         "courier-pop3d",
801         "cyrmaster",
802         "cyrus",
803         "dovecot",
804         "exim",
805         "exim4",
806         "imapd",
807         "mta",
808         "pop3d",
809         "popd",
810         "postfix",
811         "qmail",
812         "saslauthd",
813         "sendmail",
814         "vmailmgrd",
815         ""
816 };
817
818 void disable_other_mtas(void)
819 {
820         int i = 0;
821         if ((getenv("ACT_AS_MTA") == NULL) || 
822             (getenv("ACT_AS_MTA") &&
823              strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
824                 /* Offer to disable other MTA's on the system. */
825                 while (!IsEmptyStr(other_mtas[i]))
826                 {
827                         disable_other_mta(other_mtas[i]);
828                         i++;
829                 }
830         }
831 }
832
833 /* 
834  * Check to see if our server really works.  Returns 0 on success.
835  */
836 int test_server(char *relhomestr, int relhome) {
837         char cmd[256];
838         char cookie[256];
839         FILE *fp;
840         char buf[4096];
841         int found_it = 0;
842
843         /* Generate a silly little cookie.  We're going to write it out
844          * to the server and try to get it back.  The cookie does not
845          * have to be secret ... just unique.
846          */
847         sprintf(cookie, "--test--%d--", getpid());
848
849         if (relhome)
850                 sprintf(cmd, "%s/sendcommand -h%s ECHO %s 2>&1",
851                         ctdl_sbin_dir,
852                         relhomestr,
853                         cookie);
854         else
855                 sprintf(cmd, "%s/sendcommand ECHO %s 2>&1",
856                         ctdl_sbin_dir,
857                         cookie);
858
859         fp = popen(cmd, "r");
860         if (fp == NULL) return(errno);
861
862         while (fgets(buf, sizeof buf, fp) != NULL) {
863                 if ( (buf[0]=='2')
864                      && (strstr(buf, cookie) != NULL) ) {
865                         ++found_it;
866                 }
867         }
868         pclose(fp);
869
870         if (found_it) {
871                 return(0);
872         }
873         return(-1);
874 }
875
876 void strprompt(const char *prompt_title, const char *prompt_text, char *Target, char *DefValue)
877 {
878         char buf[SIZ] = "";
879         char setupmsg[SIZ];
880         char dialog_result[PATH_MAX];
881         FILE *fp = NULL;
882         int rv;
883
884         strcpy(setupmsg, "");
885
886         switch (setup_type) {
887         case UI_TEXT:
888                 title(prompt_title);
889                 printf("\n%s\n", prompt_text);
890                 printf("%s\n%s\n", _("This is currently set to:"), Target);
891                 printf("%s\n", _("Enter new value or press return to leave unchanged:"));
892                 if (fgets(buf, sizeof buf, stdin)){
893                         buf[strlen(buf) - 1] = 0;
894                 }
895                 if (!IsEmptyStr(buf))
896                         strcpy(Target, buf);
897                 break;
898
899         case UI_DIALOG:
900                 CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
901                 sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
902                         getenv("CTDL_DIALOG"),
903                         prompt_text,
904                         Target,
905                         dialog_result);
906                 rv = system(buf);
907                 if (rv != 0)
908                         fprintf(stderr, "failed to run Dialog.\n");
909                 
910                 fp = fopen(dialog_result, "r");
911                 if (fp != NULL) {
912                         if (fgets(Target, sizeof buf, fp)) {
913                                 if (Target[strlen(Target)-1] == 10) {
914                                         Target[strlen(Target)-1] = 0;
915                                 }
916                         }
917                         fclose(fp);
918                         unlink(dialog_result);
919                 }
920                 break;
921         case UI_SILENT:
922                 strcpy(Target, DefValue);
923                 break;
924         }
925 }
926
927 void set_bool_val(int msgpos, int *ip, char *DefValue) 
928 {
929         title(setup_titles[msgpos]);
930         *ip = yesno(setup_text[msgpos], *ip);
931 }
932
933 void set_str_val(int msgpos, char *Target, char *DefValue) 
934 {
935         strprompt(setup_titles[msgpos], 
936                   setup_text[msgpos], 
937                   Target, 
938                   DefValue);
939 }
940
941 void set_int_val(int msgpos, int *ip, char *DefValue)
942 {
943         char buf[16];
944         snprintf(buf, sizeof buf, "%d", (int) *ip);
945         set_str_val(msgpos, buf, DefValue);
946         *ip = atoi(buf);
947 }
948
949
950 void set_char_val(int msgpos, char *ip, char *DefValue)
951 {
952         char buf[16];
953         snprintf(buf, sizeof buf, "%d", (int) *ip);
954         set_str_val(msgpos, buf, DefValue);
955         *ip = (char) atoi(buf);
956 }
957
958
959 void set_long_val(int msgpos, long int *ip, char *DefValue)
960 {
961         char buf[16];
962         snprintf(buf, sizeof buf, "%ld", *ip);
963         set_str_val(msgpos, buf, DefValue);
964         *ip = atol(buf);
965 }
966
967
968 void edit_value(int curr)
969 {
970         int i;
971         struct passwd *pw;
972         char ctdluidname[256];
973         char *Value = NULL;
974
975         if (setup_type == UI_SILENT)
976         {
977                 Value = getenv(EnvNames[curr]);
978         }
979         if (Value == NULL)
980                 Value = "";
981
982
983         switch (curr) {
984
985         case eSysAdminName:
986                 set_str_val(curr, config.c_sysadm, Value);
987                 break;
988
989         case eSysAdminPW:
990                 set_str_val(curr, admin_pass, Value);
991                 break;
992         
993         case eUID:
994                 if (setup_type == UI_SILENT)
995                 {               
996                         if (Value) {
997                                 config.c_ctdluid = atoi(Value);
998                         }                                       
999                 }
1000                 else
1001                 {
1002 #ifdef __CYGWIN__
1003                         config.c_ctdluid = 0;   /* XXX Windows hack, prob. insecure */
1004 #else
1005                         i = config.c_ctdluid;
1006                         pw = getpwuid(i);
1007                         if (pw == NULL) {
1008                                 set_int_val(curr, &i, Value);
1009                                 config.c_ctdluid = i;
1010                         }
1011                         else {
1012                                 strcpy(ctdluidname, pw->pw_name);
1013                                 set_str_val(curr, ctdluidname, Value);
1014                                 pw = getpwnam(ctdluidname);
1015                                 if (pw != NULL) {
1016                                         config.c_ctdluid = pw->pw_uid;
1017                                 }
1018                                 else if (atoi(ctdluidname) > 0) {
1019                                         config.c_ctdluid = atoi(ctdluidname);
1020                                 }
1021                         }
1022 #endif
1023                 }
1024                 break;
1025
1026         case eIP_ADDR:
1027                 set_str_val(curr, config.c_ip_addr, Value);
1028                 break;
1029
1030         case eCTDL_Port:
1031                 set_int_val(curr, &config.c_port_number, Value);
1032                 break;
1033
1034         case eAuthType:
1035                 if (setup_type == UI_SILENT)
1036                 {
1037                         const char *auth;
1038                         config.c_auth_mode = AUTHMODE_NATIVE;
1039                         auth = Value;
1040                         if (auth != NULL)
1041                         {
1042                                 if ((strcasecmp(auth, "yes") == 0) ||
1043                                     (strcasecmp(auth, "host") == 0))
1044                                 {
1045                                         config.c_auth_mode = AUTHMODE_HOST;
1046                                 }
1047                                 else if (strcasecmp(auth, "ldap") == 0){
1048                                         config.c_auth_mode = AUTHMODE_LDAP;
1049                                 }
1050                                 else if ((strcasecmp(auth, "ldap_ad") == 0) ||
1051                                          (strcasecmp(auth, "active directory") == 0)){
1052                                         config.c_auth_mode = AUTHMODE_LDAP_AD;
1053                                 }
1054                         }
1055                 }
1056                 else {
1057                         set_int_val(curr, &config.c_auth_mode, Value);
1058                 }
1059                 break;
1060
1061         case eLDAP_Host:
1062                 set_str_val(curr, config.c_ldap_host, Value);
1063                 break;
1064
1065         case eLDAP_Port:
1066                 if (config.c_ldap_port == 0) {
1067                         config.c_ldap_port = 389;
1068                 }
1069                 set_int_val(curr, &config.c_ldap_port, Value);
1070                 break;
1071
1072         case eLDAP_Base_DN:
1073                 set_str_val(curr, config.c_ldap_base_dn, Value);
1074                 break;
1075
1076         case eLDAP_Bind_DN:
1077                 set_str_val(curr, config.c_ldap_bind_dn, Value);
1078                 break;
1079
1080         case eLDAP_Bind_PW:
1081                 set_str_val(curr, config.c_ldap_bind_pw, Value);
1082                 break;
1083
1084         }
1085 }
1086
1087 /*
1088  * (re-)write the config data to disk
1089  */
1090 void write_config_to_disk(void)
1091 {
1092         FILE *fp;
1093         int fd;
1094         int rv;
1095
1096         if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) {
1097                 display_error("%s citadel.config [%s][%s]\n", _("setup: cannot open"), file_citadel_config, strerror(errno));
1098                 cleanup(1);
1099         }
1100         fp = fdopen(fd, "wb");
1101         if (fp == NULL) {
1102                 display_error("%s citadel.config [%s][%s]\n", _("setup: cannot open"), file_citadel_config, strerror(errno));
1103                 cleanup(1);
1104                 return;
1105         }
1106         rv = fwrite((char *) &config, sizeof(struct config), 1, fp);
1107
1108         if (rv == -1)
1109                 display_error("%s citadel.config [%s][%s]\n", _("setup: cannot write"), file_citadel_config, strerror(errno));
1110
1111         fclose(fp);
1112 }
1113
1114
1115
1116
1117 /*
1118  * Figure out what type of user interface we're going to use
1119  */
1120 int discover_ui(void)
1121 {
1122
1123         /* Use "dialog" if we have it */
1124         if (getenv("CTDL_DIALOG") != NULL) {
1125                 return UI_DIALOG;
1126         }
1127                 
1128         return UI_TEXT;
1129 }
1130
1131
1132
1133 void migrate_old_installs(void)
1134 {
1135         int rv;
1136         rv = system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
1137         if (rv != 0)
1138                 fprintf(stderr, _("failed to remove old style directories.\n"));
1139         unlink("citadel.log");
1140         unlink("weekly");
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         migrate_old_installs(); /* Delete files and directories used by older Citadel versions */
1614
1615         if (    ((setup_type == UI_SILENT)
1616                 && (getenv("ALTER_ETC_SERVICES")!=NULL))
1617                 || (setup_type != UI_SILENT)
1618         ) {
1619                 check_services_entry(); /* Check /etc/services */
1620         }
1621
1622 #ifndef __CYGWIN__
1623         delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
1624         check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
1625         disable_other_mtas();   /* Offer to disable other MTAs */
1626         fixnss();               /* Check for the 'db' nss and offer to disable it */
1627 #endif
1628
1629         progress(_("Setting file permissions"), 1, 3);
1630         rv = chown(file_citadel_config, config.c_ctdluid, gid);
1631         progress(_("Setting file permissions"), 2, 3);
1632         rv = chmod(file_citadel_config, S_IRUSR | S_IWUSR);
1633         progress(_("Setting file permissions"), 3, 3);
1634
1635         check_init_script(relhome);
1636         cleanup(0);
1637         return 0;
1638 }
1639
1640