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