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