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