Handle hostnames with upper-case letters
[webmin.git] / makedebian.pl
1 #!/usr/local/bin/perl
2 # Build a Debian package of Webmin
3
4 use POSIX;
5
6 if ($ARGV[0] eq "--webmail" || $ARGV[0] eq "-webmail") {
7         $webmail = 1;
8         shift(@ARGV);
9         }
10 if ($0 =~ /useradmin|usermin/ || `pwd` =~ /useradmin|usermin/) {
11         if ($webmail) {
12                 $product = "usermin-webmail";
13                 }
14         else {
15                 $product = "usermin";
16                 }
17         $baseproduct = "usermin";
18         $port = 20000;
19         }
20 else {
21         $product = "webmin";
22         $baseproduct = "webmin";
23         $port = 10000;
24         }
25 $ucproduct = ucfirst($baseproduct);
26 $tmp_dir = "/tmp/debian";
27 $debian_dir = "$tmp_dir/DEBIAN";
28 $control_file = "$debian_dir/control";
29 $doc_dir = "$tmp_dir/usr/share/doc/$baseproduct";
30 $copyright_file = "$doc_dir/copyright";
31 $usr_dir = "$tmp_dir/usr/share/$baseproduct";
32 $pam_dir = "$tmp_dir/etc/pam.d";
33 $init_dir = "$tmp_dir/etc/init.d";
34 $pam_file = "$pam_dir/$baseproduct";
35 $preinstall_file = "$debian_dir/preinst";
36 $postinstall_file = "$debian_dir/postinst";
37 $preuninstall_file = "$debian_dir/prerm";
38 $postuninstall_file = "$debian_dir/postrm";
39 $debian_copyright_file = "$debian_dir/copyright";
40 $changelog_file = "$debian_dir/changelog";
41 $changelog_doc_file = "$doc_dir/changelog";
42 $conffiles_file = "$debian_dir/conffiles";
43
44 -d "tarballs" || die "makedebian.pl must be run in the $ucproduct root directory";
45 -r "/etc/debian_version" || die "makedebian.pl must be run on Debian";
46 chop($webmin_dir = `pwd`);
47
48 @ARGV == 1 || @ARGV == 2 ||
49         die "usage: makedebian.pl [--webmail] <version> [release]";
50 $ver = $ARGV[0];
51 if ($ARGV[1]) {
52         $rel = "-".$ARGV[1];
53         }
54 -r "tarballs/$product-$ver.tar.gz" || die "tarballs/$product-$ver.tar.gz not found";
55
56 # Create the base directories
57 print "Creating Debian package of ",ucfirst($product)," ",$ver,$rel," ...\n";
58 system("rm -rf $tmp_dir");
59 mkdir($tmp_dir, 0755);
60 chmod(0755, $tmp_dir);
61 mkdir($debian_dir, 0755);
62 system("mkdir -p $pam_dir");
63 system("mkdir -p $init_dir");
64 system("mkdir -p $usr_dir");
65 system("mkdir -p $doc_dir");
66
67 # Un-tar the package to the correct locations
68 system("gunzip -c tarballs/$product-$ver.tar.gz | (cd $tmp_dir ; tar xf -)") &&
69         die "un-tar failed!";
70 system("mv $tmp_dir/$product-$ver/* $usr_dir");
71 rmdir("$tmp_dir/$product-$ver");
72 system("mv $usr_dir/$baseproduct-debian-pam $pam_file");
73 system("cd $usr_dir && (find . -name '*.cgi' ; find . -name '*.pl') | perl perlpath.pl /usr/bin/perl -");
74 system("cd $usr_dir && rm -f mount/freebsd-mounts*");
75 system("cd $usr_dir && rm -f mount/openbsd-mounts*");
76 if ($product eq "webmin") {
77         system("cd $usr_dir && rm -f mount/macos-mounts*");
78         system("cd $usr_dir && rm -f webmin-gentoo-init");
79         system("cd $usr_dir && rm -rf format bsdexports hpuxexports sgiexports zones rbac");
80         system("cd $usr_dir && rm -rf acl/Authen-SolarisRBAC-0.1*");
81         }
82
83 # Create init script
84 system("mv $usr_dir/$baseproduct-init $init_dir/$baseproduct");
85 chmod(0755, "$init_dir/$baseproduct");
86 system("echo deb >$usr_dir/install-type");
87 system("echo $product >$usr_dir/deb-name");
88 system("cd $usr_dir && chmod -R og-w .");
89 if ($< == 0) {
90         system("cd $usr_dir && chown -R root:bin .");
91         }
92 $size = int(`du -sk $tmp_dir`);
93
94 # Create the control file
95 @deps = ( "perl", "libnet-ssleay-perl", "openssl", "libauthen-pam-perl", "libpam-runtime", "libio-pty-perl", "apt-show-versions" );
96 if ($baseproduct eq "webmin") {
97         push(@deps, "python");
98         }
99 $deps = join(", ", @deps);
100 open(CONTROL, ">$control_file");
101 print CONTROL <<EOF;
102 Package: $product
103 Version: $ver$rel
104 Section: admin
105 Priority: optional
106 Architecture: all
107 Depends: $deps
108 Pre-Depends: perl
109 Installed-Size: $size
110 Maintainer: Jamie Cameron <jcameron\@webmin.com>
111 Provides: $baseproduct
112 EOF
113 if ($product eq "webmin") {
114         print CONTROL <<EOF;
115 Replaces: webmin-adsl, webmin-apache, webmin-bandwidth, webmin-bind, webmin-burner, webmin-cfengine, webmin-cluster, webmin-core, webmin-cpan, webmin-dhcpd, webmin-exim, webmin-exports, webmin-fetchmail, webmin-firewall, webmin-freeswan, webmin-frox, webmin-fsdump, webmin-grub, webmin-heartbeat, webmin-htaccess, webmin-inetd, webmin-jabber, webmin-ldap-netgroups, webmin-ldap-user-simple, webmin-ldap-useradmin, webmin-lilo, webmin-logrotate, webmin-lpadmin, webmin-lvm, webmin-mailboxes, webmin-mon, webmin-mysql, webmin-nis, webmin-openslp, webmin-postfix, webmin-postgresql, webmin-ppp, webmin-pptp-client, webmin-pptp-server, webmin-procmail, webmin-proftpd, webmin-pserver, webmin-quota, webmin-samba, webmin-sarg, webmin-sendmail, webmin-shorewall, webmin-slbackup, webmin-smart-status, webmin-snort, webmin-software, webmin-spamassassin, webmin-squid, webmin-sshd, webmin-status, webmin-stunnel, webmin-updown, webmin-usermin, webmin-vgetty, webmin-webalizer, webmin-wuftpd, webmin-wvdial, webmin-xinetd
116 Description: web-based administration interface for Unix systems
117   Using Webmin you can configure DNS, Samba, NFS, local/remote filesystems
118   and more using your web browser.  After installation, enter the URL
119   https://localhost:10000/ into your browser and login as root with your root
120   password.
121 EOF
122         }
123 else {
124         print CONTROL <<EOF;
125 Replaces: usermin-at, usermin-changepass, usermin-chfn, usermin-commands, usermin-cron, usermin-cshrc, usermin-fetchmail, usermin-forward, usermin-gnupg, usermin-htaccess, usermin-htpasswd, usermin-mailbox, usermin-man, usermin-mysql, usermin-plan, usermin-postgresql, usermin-proc, usermin-procmail, usermin-quota, usermin-schedule, usermin-shell, usermin-spamassassin, usermin-ssh, usermin-tunnel, usermin-updown, usermin-usermount
126 Description: web-based user account administration interface for Unix systems
127   After installation, enter the URL http://localhost:20000/ into your browser
128   and login as any user on your system.
129 EOF
130         }
131 close(CONTROL);
132
133 # Create the copyright file
134 $nowstr = strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time()));
135 open(COPY, ">$copyright_file");
136 print COPY <<EOF;
137 This package was debianized by Jamie Cameron <jcameron\@webmin.com> on
138 $nowstr.
139
140 It was downloaded from: http://www.webmin.com/
141
142 Upstream author: Jamie Cameron <jcameron\@webmin.com>
143
144 Copyright:
145
146 EOF
147 open(BSD, "$usr_dir/LICENCE");
148 while(<BSD>) {
149         print COPY $_;
150         }
151 close(BSD);
152 close(COPY);
153 system("cp $copyright_file $debian_copyright_file");
154
155 # Create the config files file, for those we don't want to replace
156 open(CONF, ">$conffiles_file");
157 print CONF "/etc/pam.d/$baseproduct\n";
158 print CONF "/etc/init.d/$baseproduct\n";
159 close(CONF);
160 chmod(0644, $conffiles_file);
161
162 # Get the changes for each module and version
163 $changes = { };
164 foreach $f (sort { $a cmp $b } ( glob("*/CHANGELOG"), "CHANGELOG" )) {
165         # Get the module name and type
166         local $mod = $f =~ /^(\S+)\/CHANGELOG/ ? $1 : "core";
167         next if ($mod ne "core" && -l $mod);
168         local $desc;
169         if ($mod eq "core") {
170                 $desc = "$ucproduct Core";
171                 }
172         else {
173                 local $m = $f;
174                 local %minfo;
175                 $m =~ s/CHANGELOG/module.info/;
176                 &read_file($m, \%minfo);
177                 next if (!$minfo{'longdesc'});
178                 $desc = $minfo{'desc'};
179                 }
180
181         # Read its change log file
182         local $inversion;
183         open(LOG, $f);
184         while(<LOG>) {
185                 s/\r|\n//g;
186                 if (/^----\s+Changes\s+since\s+(\S+)\s+----/) {
187                         $inversion = $1;
188                         }
189                 elsif ($inversion && /\S/) {
190                         push(@{$changes->{$inversion}->{$desc}}, $_);
191                         }
192                 }
193         }
194
195 # Create the changelog file from actual changes, plus the historical changelog
196 open(CHANGELOG, ">$changelog_file");
197 foreach $v (sort { $a <=> $b } (keys %$changes)) {
198         if ($ver > $v && sprintf("%.2f0", $ver) == $v) {
199                 $forv = $ver;
200                 }
201         else {
202                 $forv = sprintf("%.2f0", $v+0.01);
203                 }
204         @st = stat("tarballs/webmin-$forv.tar.gz");
205         $vtimestr = strftime("%a, %d %b %Y %H:%M:%S %z", localtime($st[9]));
206         print CHANGELOG "$baseproduct ($forv) stable; urgency=low\n";
207         print CHANGELOG "\n";
208         foreach $desc (keys %{$changes->{$v}}) {
209                 foreach $c (@{$changes->{$v}->{$desc}}) {
210                         @lines = &wrap_lines("$desc : $c", 65);
211                         print CHANGELOG " * $lines[0]\n";
212                         foreach $l (@lines[1 .. $#lines]) {
213                                 print CHANGELOG "   $l\n";
214                                 }
215                         }
216                 }
217         print CHANGELOG "\n";
218         print CHANGELOG "-- Jamie Cameron <jcameron\@webmin.com> $vtimestr\n";
219         print CHANGELOG "\n";
220         }
221 close(CHANGELOG);
222 system("iconv -f ISO-8859-1 -t UTF-8 $changelog_file >$changelog_doc_file");
223
224 # Get the temp-directory creator script
225 open(TEMP, "maketemp.pl");
226 while(<TEMP>) {
227         $maketemp .= $_;
228         }
229 close(TEMP);
230 $maketemp =~ s/\\/\\\\/g;
231 $maketemp =~ s/`/\\`/g;
232 $maketemp =~ s/\$/\\\$/g;
233
234 # Create the pre-install script
235 # No need for an OS check, as all debians are supported.
236 open(SCRIPT, ">$preinstall_file");
237 print SCRIPT <<EOF;
238 #!/bin/sh
239 perl <<EOD;
240 $maketemp
241 EOD
242 if [ "\$1" != "upgrade" ]; then
243         if [ "\$WEBMIN_PORT\" != \"\" ]; then
244                 port=\$WEBMIN_PORT
245         else
246                 port=$port
247         fi
248         perl -e 'use Socket; socket(FOO, PF_INET, SOCK_STREAM, getprotobyname("tcp")); setsockopt(FOO, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)); bind(FOO, pack_sockaddr_in(\$ARGV[0], INADDR_ANY)) || exit(1); exit(0);' \$port
249         if [ "\$?" != "0" ]; then
250                 echo Port \$port is already in use
251                 exit 2
252         fi
253 fi
254 EOF
255 close(SCRIPT);
256 system("chmod 755 $preinstall_file");
257
258 # Create the post-install script
259 open(SCRIPT, ">$postinstall_file");
260 print SCRIPT <<EOF;
261 #!/bin/sh
262 inetd=`grep "^inetd=" /etc/$baseproduct/miniserv.conf 2>/dev/null | sed -e 's/inetd=//g'`
263 if [ "\$1" = "upgrade" ]; then
264         # Upgrading the package, so stop the old webmin properly
265         if [ "\$inetd" != "1" ]; then
266                 /etc/$baseproduct/stop >/dev/null 2>&1 </dev/null
267         fi
268 fi
269 cd /usr/share/$baseproduct
270 config_dir=/etc/$baseproduct
271 var_dir=/var/$baseproduct
272 perl=/usr/bin/perl
273 autoos=3
274 if [ "\$WEBMIN_PORT\" != \"\" ]; then
275         port=\$WEBMIN_PORT
276 else
277         port=$port
278 fi
279 login=root
280 if [ -r /etc/shadow ]; then
281         crypt=x
282 else
283         crypt=`grep "^root:" /etc/passwd | cut -f 2 -d :`
284 fi
285 host=`hostname`
286 ssl=1
287 atboot=1
288 makeboot=1
289 nochown=1
290 autothird=1
291 noperlpath=1
292 nouninstall=1
293 nostart=1
294 export config_dir var_dir perl autoos port login crypt host ssl nochown autothird noperlpath nouninstall nostart allow atboot makeboot
295 tempdir=/tmp/.webmin
296 if [ ! -d \$tempdir ]; then
297         tempdir=/tmp
298 fi
299 ./setup.sh >$tempdir/$product-setup.out 2>&1
300 if [ "$product" = "webmin" ]; then
301         grep sudo= /etc/$product/miniserv.conf >/dev/null 2>&1
302         if [ "\$?" = 1 ]; then
303                 # Allow sudo-based logins for Ubuntu
304                 echo sudo=1 >>/etc/$product/miniserv.conf
305         fi
306 fi
307 rm -f /var/lock/subsys/$baseproduct
308 if [ "$inetd" != "1" ]; then
309         if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
310                 invoke-rc.d $baseproduct start >/dev/null 2>&1 </dev/null
311         else
312                 /etc/$baseproduct/start >/dev/null 2>&1 </dev/null
313         fi
314 fi
315 if [ "$product" = "usermin" ]; then
316         (insserv $baseproduct || update-rc.d $baseproduct defaults) >/dev/null 2>&1
317 fi
318 cat >/etc/$baseproduct/uninstall.sh <<EOFF
319 #!/bin/sh
320 printf "Are you sure you want to uninstall $ucproduct? (y/n) : "
321 read answer
322 printf "\\n"
323 if [ "\\\$answer" = "y" ]; then
324         echo "Removing $ucproduct package .."
325         dpkg --remove $product
326         echo "Done!"
327 fi
328 EOFF
329 chmod +x /etc/$baseproduct/uninstall.sh
330 port=`grep "^port=" /etc/$baseproduct/miniserv.conf | sed -e 's/port=//g'`
331 perl -e 'use Net::SSLeay' >/dev/null 2>/dev/null
332 sslmode=0
333 if [ "\$?" = "0" ]; then
334         grep ssl=1 /etc/$baseproduct/miniserv.conf >/dev/null 2>/dev/null
335         if [ "\$?" = "0" ]; then
336                 sslmode=1
337         fi
338 fi
339 if [ "\$sslmode" = "1" ]; then
340         echo "$ucproduct install complete. You can now login to https://\$host:\$port/"
341 else
342         echo "$ucproduct install complete. You can now login to http://\$host:\$port/"
343 fi
344 if [ "$product" = "webmin" ]; then
345         echo "as root with your root password, or as any user who can use sudo"
346         echo "to run commands as root."
347 else
348         echo "as any user on the system."
349 fi
350 EOF
351 close(SCRIPT);
352 system("chmod 755 $postinstall_file");
353
354 # Create the pre-uninstall script
355 open(SCRIPT, ">$preuninstall_file");
356 print SCRIPT <<EOF;
357 #!/bin/sh
358 if [ "\$1" != "upgrade" ]; then
359         grep root=/usr/share/$baseproduct /etc/$baseproduct/miniserv.conf >/dev/null 2>&1
360         if [ "\$?" = 0 ]; then
361                 # Package is being removed, and no new version of webmin
362                 # has taken it's place. Run uninstalls and stop the server
363                 if [ "$product" = "webmin" ]; then
364                         echo "Running uninstall scripts .."
365                         (cd /usr/share/$baseproduct ; WEBMIN_CONFIG=/etc/$baseproduct WEBMIN_VAR=/var/$baseproduct LANG= /usr/share/$baseproduct/run-uninstalls.pl)
366                 fi
367                 /etc/$baseproduct/stop >/dev/null 2>&1 </dev/null
368                 /bin/true
369         fi
370 fi
371 EOF
372 close(SCRIPT);
373 system("chmod 755 $preuninstall_file");
374
375 # Create the post-uninstall script
376 open(SCRIPT, ">$postuninstall_file");
377 print SCRIPT <<EOF;
378 #!/bin/sh
379 if [ "\$1" != "upgrade" ]; then
380         grep root=/usr/share/$baseproduct /etc/$baseproduct/miniserv.conf >/dev/null 2>&1
381         if [ "\$?" = 0 ]; then
382                 # Package is being removed, and no new version of webmin
383                 # has taken it's place. Delete the config files
384                 rm -rf /etc/$baseproduct /var/$baseproduct
385         fi
386 fi
387 EOF
388 close(SCRIPT);
389 system("chmod 755 $postuninstall_file");
390
391 # Run the actual build command
392 system("fakeroot dpkg --build $tmp_dir deb/${product}_${ver}${rel}_all.deb") &&
393         die "dpkg failed";
394 #system("rm -rf $tmp_dir");
395 print "Wrote deb/${product}_${ver}${rel}_all.deb\n";
396 $md5 = `md5sum tarballs/$product-$ver$rel.tar.gz`;
397 $md5 =~ s/\s+.*\n//g;
398 @st = stat("tarballs/$product-$ver.tar.gz");
399
400 # Create the .diff file, which just contains the debian directory
401 $diff_orig_dir = "$tmp_dir/$product-$ver-orig";
402 $diff_new_dir = "$tmp_dir/$product-$ver";
403 mkdir($diff_orig_dir, 0755);
404 mkdir($diff_new_dir, 0755);
405 system("cp -r $debian_dir $diff_new_dir");
406 system("cd $tmp_dir && diff -r -N -u $product-$ver-orig $product-$ver >$webmin_dir/deb/${product}_${ver}${rel}.diff");
407 $diffmd5 = `md5sum deb/${product}_${ver}${rel}.diff`;
408 $diffmd5 =~ s/\s+.*\n//g;
409 @diffst = stat("deb/${product}_${ver}${rel}.diff");
410
411 # Create the .dsc file
412 open(DSC, ">deb/${product}_$ver$rel.plain");
413 print DSC <<EOF;
414 Format: 1.0
415 Source: $product
416 Version: $ver$rel
417 Binary: $product
418 Maintainer: Jamie Cameron <jcameron\@webmin.com>
419 Architecture: all
420 Standards-Version: 3.6.1
421 Build-Depends-Indep: debhelper (>= 4.1.16), debconf (>= 0.5.00), perl
422 Uploaders: Jamie Cameron <jcameron\@webmin.com>
423 Files:
424  $md5 $st[7] ${product}-${ver}.tar.gz
425  $diffmd5 $diffst[7] ${product}_${ver}.diff
426
427 EOF
428 close(DSC);
429 unlink("deb/${product}_$ver$rel.dsc");
430 system("gpg --output deb/${product}_$ver$rel.dsc --clearsign deb/${product}_$ver$rel.plain");
431 unlink("deb/${product}_$ver$rel.plain");
432 print "Wrote source deb/${product}_$ver$rel.dsc\n";
433
434 $dir = "sarge";
435 if (-d "/usr/local/webadmin/deb/repository") {
436         # Add to our repository
437         chdir("/usr/local/webadmin/deb/repository");
438         system("reprepro -Vb . remove $dir $product");
439         system("reprepro -Vb . includedeb $dir ../${product}_${ver}${rel}_all.deb");
440         chdir("/usr/local/webadmin");
441         }
442
443 # Create PGP signature
444 unlink("sigs/${product}_${ver}${rel}_all.deb-sig.asc");
445 system("gpg --armor --output sigs/${product}_${ver}${rel}_all.deb-sig.asc --default-key jcameron\@webmin.com --detach-sig deb/${product}_${ver}${rel}_all.deb");
446
447 # read_file(file, &assoc, [&order], [lowercase])
448 # Fill an associative array with name=value pairs from a file
449 sub read_file
450 {
451 local $_;
452 open(ARFILE, $_[0]) || return 0;
453 while(<ARFILE>) {
454         chomp;
455         local $hash = index($_, "#");
456         local $eq = index($_, "=");
457         if ($hash != 0 && $eq >= 0) {
458                 local $n = substr($_, 0, $eq);
459                 local $v = substr($_, $eq+1);
460                 $_[1]->{$_[3] ? lc($n) : $n} = $v;
461                 push(@{$_[2]}, $n) if ($_[2]);
462                 }
463         }
464 close(ARFILE);
465 if (defined($main::read_file_cache{$_[0]})) {
466         %{$main::read_file_cache{$_[0]}} = %{$_[1]};
467         }
468 return 1;
469 }
470
471 # wrap_lines(text, width)
472 # Given a multi-line string, return an array of lines wrapped to
473 # the given width
474 sub wrap_lines
475 {
476 local @rv;
477 local $w = $_[1];
478 local $rest;
479 foreach $rest (split(/\n/, $_[0])) {
480         if ($rest =~ /\S/) {
481                 while($rest =~ /^(.{1,$w}\S*)\s*([\0-\377]*)$/) {
482                         push(@rv, $1);
483                         $rest = $2;
484                         }
485                 }
486         else {
487                 # Empty line .. keep as it is
488                 push(@rv, $rest);
489                 }
490         }
491 return @rv;
492 }
493
494