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