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