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