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