]> git.treefish.org Git - fex.git/blob - install
Original release 20150729
[fex.git] / install
1 #!/usr/bin/perl -w
2
3 use 5.006;
4 use Getopt::Std;
5 use File::Basename;
6 use Socket;
7 use IO::Socket::INET;
8 use Digest::MD5 'md5_hex';
9
10 $ENV{PATH} .= ':/sbin:/usr/sbin';
11
12 $usage = "usage: $0 [-p port] [IP-address]\n";
13 $xinetd = '/etc/xinetd.d/fex';
14
15 umask 022;
16
17 if ($<) {
18   die "you must be root to install F*EX\n";
19 }
20
21 $fex = 'fex.rus.uni-stuttgart.de';
22 if (system("host $fex >/dev/null") != 0) {
23   die "host $fex is not resolvable - check /etc/resolv.conf\n";
24 }
25
26 $opt_p = 80;
27
28 if (open $xinetd,$xinetd) {
29   while (<$xinetd>) {
30     if (/^\s*port\s*=\s*(\d+)/) {
31       $opt_p = $fexport = $1;
32     }
33     if (/^\s*bind\s*=\s*([\d.]+)$/) {
34       $fexip = $ip = $1;
35     }
36   }
37   close $xinetd;
38 }
39
40 goto INSTALL if $0 =~ /upgrade$/;
41
42 if (`uname` =~ /^SunOS/) {
43   die "Solaris is currently not supported. "
44      ."Please contact framstag\@rus.uni-stuttgart.de for details.\n";
45 }
46
47 getopts('p:') or die $usage;
48
49 $arg = shift;
50 if ($arg and -f "locale/$arg/lib/fup.pl") {
51   exec 'locale/translate',$arg;
52 } else {
53   $ip = $arg || $fexip || 0;
54 }
55
56 # if (not $ip and open P,"ifconfig 2>/dev/null |") {
57 if (not $ip and open P,'host $(hostname)|') {
58   $guessed_ip = 0;
59   while (<P>) {
60     if (/(\d+\.\d+\.\d+\.\d+)/) { 
61       $guessed_ip = $1;
62       last;
63     }
64   }
65   close P;
66   unless (-f $xinetd) {
67     print "Your IP [$guessed_ip] : ";
68     chomp($ip = <STDIN>);
69   }
70   $ip ||= $guessed_ip;
71 }
72
73
74 ($hostname) = gethostbyaddr(gethostbyname($ip),AF_INET);
75 die "cannot find hostname for IP $ip\n" unless $hostname;
76
77 print "checking prerequisites\n";
78
79 if (`which xinetd` =~ m{^/}) {
80   print "found xinetd\n";
81 } else {
82   print "xinetd executable NOT found\n";
83   $premiss++;
84 }
85
86 foreach (qw'/usr/lib/sendmail /usr/sbin/sendmail') {
87   if (-x) {
88     $sendmail = $_;
89     print "found $sendmail\n";
90     last;
91   }
92 }
93 unless ($sendmail) {
94   print "sendmail NOT found\n";
95   $premiss++;  
96 }
97
98 if ($premiss) {
99   print "installation aborted, nothing has been touched yet\n";
100   print "what now? ==> see doc/installation\n";
101   exit 1;
102 }
103
104 unless ($fexport) {
105   
106   $SH = IO::Socket::INET->new(
107     PeerAddr => $ip,
108     PeerPort => $opt_p,
109     Proto    => 'tcp',
110   );
111   
112   if ($SH) {
113     print "There is already a tcp-service running on $ip:$opt_p !\n";
114     print "Select another port for F*EX by running $0 -p OTHERPORT $ip\n";
115     print "or an alternative IP-address by running $0 OTHERADDRESS\n";
116     exit 5;
117   }
118 }
119
120 print "prerequisites checked, ok\n";
121
122 unless (getpwnam('fex')) {
123   print "creating user fex\n";
124   system 'useradd -s /bin/bash -c "File EXchange" -m fex';
125   exit $? if $?;
126 }
127
128 if (open F,'/etc/passwd') {
129   while (<F>) {
130     $fexbash = $_ if /^fex:.*\/bash/;
131   }
132   close F;
133 }
134 unless ($fexbash) {
135   die "no bash login shell for user fex\n";
136 }
137
138 INSTALL:
139
140 umask 077;
141
142 @FEX = getpwnam('fex') or die "no user fex\n";
143 $FEXHOME  = $FEX[7];
144 $ENV{HOME} = $FEXHOME; # needed for later eval fex.ph
145
146 die "no HOME directory for user fex\n" unless -d $FEXHOME;
147 if ($FEXHOME !~ /fex/) {
148   print "HOME=$FEXHOME for user fex does not contain \"fex\"\n";
149   print "REALLY continue?! ";
150   $_ = <STDIN>;
151   exit unless /^y/i;
152 }
153
154 print "Installing:\n";
155
156 $pecl = "$FEXHOME/perl/Encode/ConfigLocal.pm";
157 unless (-f $pecl) {
158   mkdir "$FEXHOME/perl";
159   mkdir "$FEXHOME/perl/Encode";
160   open $pecl,'>',$pecl or die "$0: cannot write $pecl - $!\n";
161   print {$pecl} 
162     "# hack for broken Perl in SuSe and Solaris, used via \@INC in fexsrv\n",
163     "1;\n";
164   close $pecl;
165   print $pecl,"\n";
166   chownr('fex:root',"$FEXHOME/perl");
167 }
168
169 @save = (
170   "lib/fex.ph",
171   "lib/fup.pl",
172   "lib/reactivation.txt",
173   "etc/mime.types",
174   "htdocs/index.html",
175   "htdocs/robots.txt",
176   "htdocs/FAQ/local.faq",
177 );
178
179 foreach $s (@save) {
180   $f = "$FEXHOME/$s";
181   if (-e $f) {
182     $fs = $f.'_save';
183     rename $f,$fs and print "$f --> $fs\n";
184   }
185 }
186
187 cpav(qw'bin cgi-bin lib etc htdocs doc',$FEXHOME);
188 unlink "$FEXHOME/doc/License";
189 unlink "$FEXHOME/htdocs/License";
190
191 $hl = "$FEXHOME/htdocs/locale";
192 unless (-d $hl) { mkdir $hl or die "$0: cannot mkdir $hl - $!\n" }
193
194 foreach $s (@save) {
195   $f = "$FEXHOME/$s";
196   $fs = $f.'_save';
197   $fn = $f.'_new';
198   if (-e $fs) {
199     unlink $fn;
200     rename $f,$fn and print "$f --> $fn\n";
201     rename $fs,$f and print "$fs --> $f\n";
202   }
203 }
204
205 if (-d "$FEXHOME/spool") {
206   warn "checking $FEXHOME/spool ...\n";
207   &convert_spool;
208 } else {
209   $newinstall = $FEXHOME;
210   chmod 0700,$FEXHOME;
211   mkdir "$FEXHOME/spool",0700 or die "cannot mkdir $FEXHOME/spool - $!\n";
212   mkdir "$FEXHOME/spool/.error",0700;
213 }
214 chownr('fex',"$FEXHOME/spool/.");
215
216 # fex-VM?
217 if (open my $setup,'/root/bin/setup') {
218   while (<$setup>) {
219     exit if /#.*X-VM/;  
220   }
221   close $setup;
222 }
223
224 system(qw'perl -p -i -e',
225   's:href="/?FAQ.html":href="/FAQ/FAQ.html":',
226   "$FEXHOME/lib/fup.pl"
227 );
228   
229 $fph = "$FEXHOME/lib/fex.ph";
230 open $fph,$fph or die "cannot read $fph - $!\n";
231 while (<$fph>) {
232   s/'MYHOSTNAME.MYDOMAIN'/'$hostname'/;
233   $conf .= $_;
234 }
235 close $fph;
236
237 eval $conf;
238
239 # die "no \$spooldir in $fph\n" unless $spooldir;
240 $spooldir ||= '/home/fex/spool';
241 die "\$spooldir=$spooldir is not a directory, see $fph\n" unless -d $spooldir;
242 symlink $spooldir,"$FEXHOME/spool" unless -e "$FEXHOME/spool";
243 @sds1 = stat "$spooldir/.";
244 @sds2 = stat "$FEXHOME/spool/.";
245 if ("@sds1" ne "@sds2") {
246   die "$FEXHOME/spool is not a symbolic link to \$spooldir=$spooldir\n";
247 }
248
249 $fid = "$FEXHOME/.fex/id";
250 $aa = "$spooldir/$admin/@";
251
252 if ($newinstall or not -s $aa) {
253   print "\n";
254   for (;;) {
255     print "Server hostname [$hostname] : ";
256     $_ = <STDIN>;
257     s/\s//g;
258     $hostname = $_ if $_;
259     last if gethostbyname($hostname);
260     print "No DNS for $hostname\n";
261   }
262   for (;;) {
263     print "F*EX admin [$admin] : ";
264     $_ = <STDIN>;
265     s/\s//g;
266     $admin = $_ if $_;
267     last if $admin =~ /.\@./;
268     print "admin must be a valid email address!\n";
269   }
270   $aa = "$spooldir/$admin/@";
271   while (not $admin_pw) {
272     print "F*EX admin password: ";
273     $admin_pw = <STDIN>;
274     $admin_pw =~ s/\s//g;
275   }
276   mkfid();
277   print "(admin password is in $aa)\n";
278   $conf =~ s/^\s*\$hostname\s*=.*/\$hostname = '$hostname';/m;
279   $conf =~ s/^\s*\$admin\s*=.*/\$admin = '$admin';/m;
280 } else {
281   if ($admin_pw) {
282     print "\nFound old \$admin_pw in $fph !\n";
283     print "This is no longer supported for security reason.\n";
284     if (open $aa,$aa) {
285       $_ = <$aa>||'';
286       chomp;
287       close $aa;
288       if ($_ ne $admin_pw) {
289         print "\nYou have to delete \$admin_pw in $fph and run\n";
290         print "$FEXHOME/bin/fac -u $admin $admin_pw\n";
291         print "\nThen rerun $0\n";
292         exit 2;
293       }
294     }
295     mkfid();
296     print "\$admin_pw is transfered to auth-ID in $aa\n\n";
297     $conf =~ s/^\s*(\$admin_pw)\s*=.*/# $1 is now auth_ID of user \$admin/m;
298   }
299 }
300
301 open $fph,">$fph.new" or die "$0: cannot write $fph.new - $!\n";
302 print {$fph} $conf;
303 close $fph;
304 system "chown fex $fph.new";
305 rename "$fph.new",$fph or die "$0: cannot rename $fph.new to $fph - $!\n"; 
306
307 do $fph or die "$0: error in new $fph - $!\n";
308
309 if (@locales = glob "locale/*/lib/fup.pl") {
310   foreach (@locales) {
311     m{locale/(.+?)/} and $locale = $1;
312     if (-f "$FEXHOME/$_") { 
313       system 'locale/translate',$locale;
314       chownr('fex',"$FEXHOME/locale/$locale");
315       $hl = "$FEXHOME/htdocs/locale/$locale";
316       symlink "$FEXHOME/locale/$locale/htdocs",$hl unless -l $hl;
317     } else { 
318       push @nlocales,"./install $1\n";
319     }
320   }
321   if (@nlocales) {
322     if (glob "$FEXHOME/locale/*/lib/fup.pl") {
323       print "\nTo install another localized version, type:\n";
324     } else {
325       print "\nTo install a localized version, type:\n";
326     }
327     print @nlocales;
328   }
329 }
330
331 $fph = "$FEXHOME/lib/fex.ph";
332 do $fph;
333
334 unless (-f $xinetd) {
335   my $xc = '/etc/xinetd.conf';
336   if (open $xc,$xc) {
337     while (<$xc>) {
338       if (/^\s*only_from/) {
339         print "WARNING: found \"only_from\" in $xc : fexsrv is restricted!\n";
340       }
341     }
342     close $xc;
343   }
344   if (-d '/etc/xinetd.d') {
345     unless (-f $xinetd) {
346       open $xinetd,">$xinetd" or die "cannot write $xinetd - $!\n";
347       open F,'etc/xinetd_fex' or die "cannot read etc/xinetd_fex - $!\n";
348       while (<F>) {
349         s/FEXHOME/$FEXHOME/;
350         s/PORT/$opt_p/;
351         s/ADDRESS/$ip/;
352         print {$xinetd} $_;
353       }
354       close F;
355       close $xinetd;
356       system qw'/etc/init.d/xinetd restart';
357       print "WARNING: cannot restart xinetd\n" if $?;
358     }
359   } else {
360     print "WARNING: No /etc/xinetd.d found.\n";
361     print "WARNING: You have to install etc/xinetd_fex manually.\n";
362   }
363
364   $crontab = `crontab -u fex -l 2>/dev/null`;
365   if ($crontab !~ /fex_cleanup/) {
366     open $crontab,">fex.cron" or die "cannot create fex.cron - $!\n";
367     print {$crontab} $crontab,"\n";
368     print {$crontab} " 3 2 * * * exec $FEXHOME/bin/backup\n";
369     print {$crontab} " 3 3 * * * exec $FEXHOME/bin/fex_cleanup\n";
370     close $crontab;
371     system qw'crontab -u fex fex.cron';
372   }
373
374   chownr('fex:root',$FEXHOME,"$FEXHOME/spool/.");
375   chmodr('go-r',"$FEXHOME/lib","$FEXHOME/cgi-bin","$FEXHOME/spool/.");
376
377   print "\n";
378   print "Now check configuration file $FEXHOME/lib/fex.ph and run\n";
379   print "$FEXHOME/bin/fac for further configuration and user management.\n";
380   print "(You can do this as user \"fex\")\n";
381 } else {
382   
383   chmodr('go-r',"$FEXHOME/lib","$FEXHOME/cgi-bin");
384   
385   print "\n";
386   print "F*EX update installed.\n";
387   print "You can inform your users about the new features with:\n";
388   print "$FEXHOME/bin/fexwall 'new F*EX features on $hostname' ".
389         "< $FEXHOME/doc/newfeatures\n";
390 }
391
392 if (@local_rdomains and not @local_rhosts) {
393   print "\nWARNING:\n";
394   print "In $fph you have @local_rdomains but not @local_rhosts!\n";
395   print "Selfregistrating of external users will not work!\n";
396   print "See ${fph}_new/\n";
397 }
398
399 if (`$sendmail -h 2>&1` =~ /exim/ and 
400     `grep trusted_users /etc/exim4/exim4.conf 2>/dev/null` !~ /\bfex\b/) {
401   print "\nWARNING:\n";
402   print "$sendmail is exim\n";
403   print "You MUST set in your exim4.conf:\n";
404   print "trusted_users = mail : uucp : fex\n";
405 }
406
407 exit;
408
409 sub mkfid {
410   my $ad = dirname($aa);
411   mkdir $ad;
412   open $aa,'>',$aa or die "$0: cannot create $aa - $!\n";
413   print {$aa} "$admin_pw\n";
414   close $aa;
415   my $fd = dirname($fid);
416   mkdir $fd;
417   rename $fid,$fid.'_save';
418   open $fid,'>',$fid or die "$0: cannot create $fid - $!\n";
419   print {$fid} "$hostname:$opt_p\n";
420   print {$fid} "$admin\n";
421   print {$fid} "$admin_pw\n";
422   close $fid;
423   chownr('fex',$ad,$fd);
424   chmod 0700,$ad,$fd;
425 }
426
427 sub chownr {
428   my $user = shift;
429   local $_;
430   foreach (@_) {
431     if (m:^/*(lib|usr|home)?/*$:) {
432       die "ERROR: short path in chownr $user @_\n";
433     }
434   }
435   system qw'chown -R',$user,@_;
436 }
437
438 sub chmodr {
439   my $mod = shift;
440   local $_;
441   foreach (@_) {
442     if (m:^/*(lib|usr|home)?/*$:) {
443       die "ERROR: short path in chmodr $mod @_\n";
444     }
445   }
446   system qw'chmod -R',$mod,@_;
447 }
448
449 sub convert_spool {
450   my ($f,$d,$to,$from,$link);
451   
452   local $) = $FEX[3];
453   local $> = $FEX[2]; 
454
455   our ($spooldir,$skeydir,$gkeydir);
456   $ENV{FEXLIB} = $FEXLIB = "$FEXHOME/lib";
457   require "$FEXLIB/fex.pp" or die "$0: cannot load $FEXLIB/fex.pp - $!\n";
458   die "no \$spooldir in $FEXLIB/fex.pp\n" unless $spooldir;
459   die "\$spooldir=$spooldir/" if $spooldir =~ m:^/*(root)?$:;
460
461   # User --> user@maildomain
462   if ($mdomain) {
463     foreach $f (glob "$spooldir/.dkeys/*") {
464       if ($link = readlink $f) {
465         (undef,$to,$from,$file) = split('/',$link);
466         if ($file) {
467           $to   .= '@'.$mdomain if $to   !~ /@/;
468           $from .= '@'.$mdomain if $from !~ /@/;
469           if ($link ne "../$to/$from/$file") {
470             symlink "../$to/$from/$file",$f;
471           }
472         }
473       }
474     }
475   }
476
477   # fix spool layout: FROM and TO must have domains and must be lower case
478   foreach $d ((glob "$spooldir/*/*"),(glob "$spooldir/*")) {
479     if (not -l $d and -d $d and $d =~ m:(.+)/(.+):) {
480       $p = $1;
481       $b = $2;
482       if ($b !~ /^@/ and $b !~ /^[A-Z_-]+$/) {
483         if ($mdomain and $b !~ /@/) {
484           rename $d,sprintf("%s/%s@%s",$p,lc($b),$mdomain);
485         } elsif ($b ne lc($b)) {
486           rename $d,sprintf("%s/%s",$p,lc($b));
487         }
488       }
489     }
490   }
491
492   # split auth-ID and subuser file: @ --> @ @SUBUSER
493   foreach my $u (glob "$spooldir/*@*") {
494     next if -f "$u/\@SUBUSER";
495     open my $idf,"$u/\@" or next;
496     $id = <$idf>;
497     if (defined ($su = <$idf>) and $su =~ /\w/
498         and open my $suf,">$u/\@SUBUSER") {
499       print {$suf} $su;
500       while (defined ($su = <$idf>)) { print {$suf} $su }
501       close $suf;
502       close $idf;
503       if (open my $idf,">$u/\@") {
504         print {$idf} $id;
505         close $idf;
506       }
507     }
508   }
509
510   # create new SKEYs
511   foreach my $sf (glob "$spooldir/*/\@SUBUSER") {
512     $user = (split '/',$sf)[-2];
513     if (open $sf,$sf) {
514       while (<$sf>) {
515         s/#.*//;
516         if (/(.+\@.+):(.+)/) {
517           ($subuser,$id) = ($1,$2);
518           next if $subuser =~ /\*/;
519           $skey = md5_hex("$user:$subuser:$id");
520           if (open $skey,'>',"$skeydir/$skey") {
521             print {$skey} "from=$subuser\n",
522                           "to=$user\n",
523                           "id=$id\n";
524             close $skey;
525           }
526           mkdirp("$spooldir/$subuser/\@MAINUSER");
527           symlink $skey,"$spooldir/$subuser/\@MAINUSER/$user";
528         }
529       }
530     }
531     close $sf;
532   }
533
534   # create new GKEYs
535   foreach my $gf (glob "$spooldir/*/\@GROUP/*") {
536     next unless -f $gf;
537     # normalize group name
538     if ($gf =~ m:(.+)/(.+):) {
539       my $gd = $1;
540       my $g1 = $2;
541       my $g2 = $2;
542       $g2 =~ s/[^\w\*%^+=:,.!-]/_/g;
543       if ($g1 ne $g2) {
544         rename "$gd/$g1","$gd/$g2" and $gf = "$gd/$g2";
545       }
546     }
547     $group = (split '/',$gf)[-1];
548     $user  = (split '/',$gf)[-3];
549     if (open $gf,$gf) {
550       while (<$gf>) {
551         s/#.*//;
552         if (/(.+\@.+):(.+)/) {
553           ($gm,$id) = ($1,$2);
554           $gkey = md5_hex("$user:$group:$gm:$id");
555           if (open $gkey,'>',"$gkeydir/$gkey") {
556             print {$gkey} "from=$gm\n",
557                           "to=\@$group\n",
558                           "user=$user\n",
559                           "id=$id\n";
560             close $gkey;
561           }
562           mkdirp("$spooldir/$gm/\@GROUP");
563           symlink "../../$user/\@GROUP/$group","$spooldir/$gm/\@GROUP/$group";
564         }
565       }
566     }
567     close $gf;
568   }
569 }
570
571 sub cpav {
572   my $dd = pop @_;
573   local *P;
574   
575   die "cpav: $dd is not a directory" unless -d $dd;
576   open P,"tar cf - @_ | su -c 'cd $dd; umask 022; tar xvf - 2>&1' fex |" 
577     or die "cpav: cannot tar - $!\n";
578   while (<P>) {
579     chomp;
580     print "$_ --> $dd/$_\n" unless /\/$/;
581   }
582   close P;
583 }