File: bugzilla.postinst

package info (click to toggle)
bugzilla 2.16.7-7sarge2
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 4,696 kB
  • ctags: 492
  • sloc: perl: 20,289; xml: 6,856; sh: 338; makefile: 211; python: 172
file content (540 lines) | stat: -rw-r--r-- 15,346 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
#!/usr/bin/perl -w

use strict;
use vars qw( $dsn $dbh $skipped
	     $mysql_host $mysql_port
	     $mysql_name $mysql_user $mysql_user_pwd
	     $mysql_root_name $mysql_root_pwd $mysql_need_root);

use Debconf::Client::ConfModule ':all';
use DBI;

# Prototypes
sub debug($);
sub error($;$);
sub alter_localconfig;
sub exists_database;
sub create_database;
sub grant;
sub create_profiles_tables;
sub populate_profiles;
sub dbi_connect($$$);
sub dbi_disconnect();
sub exists_bugzilla_db();
sub init_default_vars();
sub random_string($);
sub update_debconf_keys();

# Default MySQL vars
my $BUGZILLA_DBNAME = 'bugzilla';
my $BUGZILLA_DBUSER = 'bugzilla';
my $BUGZILLA_DBPASS = random_string(16);
my $BUGZILLA_DBHOST = 'localhost';
my $BUGZILLA_DBPORT = '3306';

# Debian MySQL user (debian-sys-maint defined in /etc/mysql/debian.cnf) 
my $DEBIAN_DBHOST = undef;
my $DEBIAN_DBUSER = undef;
my $DEBIAN_DBPASS = undef;
my $DEBIAN_DBPORT = undef;

$skipped = 0;

# First look at the context 
my $mode = $ARGV[0];
if ($mode eq 'configure') {
	# continue below
}
elsif ( $mode eq 'abort-upgrade' or 
	$mode eq 'abort-remove' or
	$mode eq 'abort-deconfigure') {
	exit 0;
}
else {
	warn "postinst called with unknown argument : $mode\n";
	exit 0;
}

# according to installation way user choosed, 
# we'll do different things.
my $install_way;
my $default_file = '/etc/mysql/debian.cnf';	
if (-f $default_file) {
	$install_way = get('bugzilla/bugzilla_installation_way');
}
else {
	$install_way = get('bugzilla/bugzilla_installation_way_single');
}

# The Automatic way will read /etc/mysql/default.cnf
# and will use the values found there.
if ($install_way eq 'Automatic') {

	# At this time, Debian MySQL user must be available.
	init_default_vars();
	debug "Debian MySQL maintainer is : $DEBIAN_DBUSER";
	
	# At this time, we have to set the good values
	# for accessing the MySQL database.
	$mysql_host = $BUGZILLA_DBHOST;
	$mysql_port = $BUGZILLA_DBPORT;
	$mysql_name = $BUGZILLA_DBNAME;
	$mysql_user = $BUGZILLA_DBUSER;
	$mysql_user_pwd = $BUGZILLA_DBPASS;
	$mysql_need_root = 'true';
	$mysql_root_name = $DEBIAN_DBUSER;
	$mysql_root_pwd = $DEBIAN_DBPASS;

	# check if we have already a db configured in Debconf
	# and available with MySQL.
	my $exists = exists_bugzilla_db();
	if ($exists == 1) {
		warn "Database $mysql_name already exists, skipping database creation.\n";
		$skipped = 1;
	}
	
	# The connection was not possible, this must mean that MySQL wasn't running.
	elsif ($exists == -1) {
		warn "Connection to MySQL failed, make sure your local MySQL server is running.\n";
		exit 0;
	}
	
	# No existing db was found, let's create a fresh one.
	else {
		warn "Creating a new default bugzilla database...\n";
	}
}

# The Manual way is the same as before, everything 
# was asked with debconf and is used here.
elsif ($install_way eq 'Manual') {
	$mysql_host = get('bugzilla/mysql_host');
	$mysql_port = get('bugzilla/mysql_port');
	$mysql_name = get('bugzilla/mysql_name');
	$mysql_user = get('bugzilla/mysql_user');
	$mysql_user_pwd  = get('bugzilla/mysql_user_pwd');
	$mysql_need_root = get('bugzilla/mysql_need_root');
	if ($mysql_need_root) {
		$mysql_root_name =  get('bugzilla/mysql_root_name');
		$mysql_root_pwd =  get('bugzilla/mysql_root_pwd');
	}
}

# Later maeans that there is no MySQL server ready for
# bugzilla now, user will come back later.
elsif ($install_way eq 'Later') {
	warn "You answer that there is no MySQL server "
	    ."suitable to support the bugzilla database. "
	    ."Use 'dpkg-reconfigure bugzilla' when you have"
	    ." a MySQL server.\n";
	exit 0;
}
	
# Well, that should never happend...
else {
	warn "Postinst error, unknown installation way : $install_way\n";
	exit 1;
}

# This is the minimal DB alteration : we just have to redo the 
# grant process as the password is generated by random.
# We'll also update the profiles in order to set a possible new
# Bugzilla admin (according to Debconf).
# Yes, the bugzilla user's password chnages whenever you install 
# the bugzilla package ;)
if ($skipped) {
	$dsn = "DBI:mysql:;$mysql_host;$mysql_port";
	dbi_connect($dsn, $mysql_root_name, $mysql_root_pwd) or 
		error "Can't connect as admin to the database ".
		      "($mysql_root_name\@$mysql_host:$mysql_port)\n".
		      "Use dpkg-reconfigure bugzilla to change the settings.", 0;

	# The privileges have to be set.
	debug "Updating the password of $mysql_user on $mysql_name.*";
	grant();
	dbi_disconnect();

	# Update the Bugzilla profiles.
	$dsn = "DBI:mysql:$mysql_name;$mysql_host;$mysql_port";
	dbi_connect($dsn, $mysql_user, $mysql_user_pwd) or
		error "Can't connect as user to the '$mysql_name' database ".
		      "($mysql_user\@$mysql_host:$mysql_port)\n".
		      "Use dpkg-reconfigure bugzilla to change the settings.", 0;
	populate_profiles();
	dbi_disconnect();

	# this is the time for Debconf cleanup.
	update_debconf_keys();
}

# Look that at this point, we have to create the database, either if
# the values came from user input or from the automatic detection.
else {
	if ($mysql_need_root eq 'true') {
		$dsn = "DBI:mysql:;$mysql_host;$mysql_port";

		# try to connect to the db ($dbh is global)
		dbi_connect($dsn, $mysql_root_name, $mysql_root_pwd) or
			error "Can't connect as admin to the database ".
			      "($mysql_root_name\@$mysql_host:$mysql_port)\n".
			      "Use dpkg-reconfigure bugzilla to change the settings.", 0;

		# this can happend when the package has been installed with an old version 
		# and then purged.
		# The db will be there but Debconf won't remember about it (as old default db name 
		# was 'bugs' and not 'bugzilla').
		if ($install_way eq 'Automatic' and exists_database()) {
			debug "The database $mysql_name already exists, fixing Debconf keys...";
			update_debconf_keys();
			warn "Database $mysql_name already exists, nothing to do.\n";
			exit 0;
		}
		else {
			
			# here it is, let's create the db.
			debug "Creating the $mysql_name database.";
			create_database();
		
			# reconnect (don't see why but I keep the maintainer choice).
			dbi_disconnect();
			dbi_connect($dsn, $mysql_root_name, $mysql_root_pwd) or 
				error "Can't connect as admin to the database ".
				      "($mysql_root_name\@$mysql_host:$mysql_port)\n".
				      "Use dpkg-reconfigure bugzilla to change the settings.", 0;
		
			# The privileges have to be set.
			debug "Granting access privileges to $mysql_user on $mysql_name.*";
			grant();
			dbi_disconnect();
		}
	}

	# Now we must be able to connect to the DB with the bugzilla user.
	$dsn = "DBI:mysql:$mysql_name;$mysql_host;$mysql_port";
	dbi_connect($dsn, $mysql_user, $mysql_user_pwd) or
		error "Can't connect as user to the '$mysql_name' database ".
		      "($mysql_user\@$mysql_host:$mysql_port)\n".
		      "Use dpkg-reconfigure bugzilla to change the settings.", 0;
	
	# some db population...
	create_profiles_tables();
	populate_profiles();
	dbi_disconnect();

	# this is the time for Debconf cleanup.
	update_debconf_keys();
}

debug "This is the time to update the localconfig file.";
alter_localconfig();

system('/usr/share/bugzilla/lib/checksetup.pl 1>&2') == 0
	or error "checksetup.pl failed";

my $temp="set -e\nset -- @ARGV\n" . << 'EOF';

EOF

system ($temp) / 256 == 0
	or error "Problem with debhelper scripts: $!";



exit 0;

sub alter_localconfig {
	
	# TODO: have localconfig rotate
	# TODO: don't change localconfig if there is no change on it

	debug "MySQL values : $mysql_host, $mysql_port, $mysql_name, $mysql_user, $mysql_user_pwd";
	unless ($mysql_host and $mysql_port and $mysql_name and
		$mysql_user and $mysql_user_pwd) {
		error "An error occured with the MySQL values !";
	}
	
	umask 0027; #there is password in localconfig
	rename ('/etc/bugzilla/localconfig','/etc/bugzilla/localconfig.dpkg.old') 
		or error "Can't rename /etc/bugzilla/localconfig : $!";
	open (CONFIG_OLD,"</etc/bugzilla/localconfig.dpkg.old")
		or error "Can't open /etc/bugzilla/localconfig.dpkg.old : $!";
	open (CONFIG_NEW,">/etc/bugzilla/localconfig")
		or error "Cant't open /etc/bugzilla/localconfig : $!";
	while (<CONFIG_OLD>) {
		s/(\$db_host\s*=\s*)"[^"]*"/$1"$mysql_host"/; 
		s/(\$db_port\s*=\s*)\d+/$1$mysql_port/; 
		s/(\$db_name\s*=\s*)"[^"]*"/$1"$mysql_name"/; 
		s/(\$db_user\s*=\s*)"[^"]*"/$1"$mysql_user"/; 
		s/(\$db_pass\s*=\s*)"[^"]*"/$1"\Q$mysql_user_pwd\E"/; 
		print CONFIG_NEW $_ or error "Can't write in /etc/bugzilla/localconfig : $!" ;
	}
	close CONFIG_OLD;
	close CONFIG_NEW;

	my @www_pwent = getpwnam("www-data") 
		or error "Can't find numeric uid/gid of www-data";
	chown ($www_pwent[2], $www_pwent[3], '/etc/bugzilla/localconfig') 
		or error "Can't change the owner of /etc/bugzilla/localconfig"; 
}

sub exists_database
{
	my @list = $dbh->func('_ListDBs');
	return grep /^$mysql_name$/, @list;
}

sub create_database {
	my @databases = $dbh->func('_ListDBs');
	unless (grep /^$mysql_name$/, @databases) {
		$dbh->func('createdb', $mysql_name, "$mysql_host:$mysql_port",
				       $mysql_root_name, $mysql_root_pwd, 'admin')
            	or error "Can't create the $mysql_name";
	}
}

sub grant {
	my $fqdn;
	
	if ( $mysql_host eq "localhost" ) {
		$fqdn='localhost';
	} else {
		$fqdn=`hostname -f`;
	}
	my $sql = "grant all on $mysql_name.* to '$mysql_user'\@$fqdn identified by '$mysql_user_pwd'";
	$dbh->do($sql)
		or error "Can't grant or create $mysql_user user";
}

sub create_profiles_tables {
	debug "create_profiles_tables";
	my $sth = $dbh->table_info(undef, undef, undef, "TABLE");
	my @tables = @{$dbh->selectcol_arrayref($sth, { Columns => [3] })};
	unless (grep /^profiles$/, @tables) {
		debug "creating table profiles";
		$dbh->do('create table profiles(
			  userid mediumint not null auto_increment primary key,
	    		  login_name varchar(255) not null,
    			  cryptpassword varchar(64),
	    		  realname varchar(255),
    			  groupset bigint not null default 0,
  	  		  disabledtext mediumtext,
    			  mybugslink tinyint not null default 1,
    			  blessgroupset bigint not null default 0,
    			  emailflags mediumtext,
			  unique(login_name))')
		or error "Can't create profiles table";
	}
}

sub populate_profiles {
	debug "populate_profiles";
	my $bugzilla_admin_name = get('bugzilla/bugzilla_admin_name');
	my $bugzilla_admin_real_name = get('bugzilla/bugzilla_admin_real_name');
	my $bugzilla_admin_pwd = get('bugzilla/bugzilla_admin_pwd');


	my $sth = $dbh->prepare(q{select userid 
				    from profiles
				   where login_name=?})
			or error "Can't prepare login selection";
	$sth->execute($bugzilla_admin_name) 
		or error "Can't execute login selection";

	(my $userid) = $sth->fetchrow_array;
	
	$sth->finish;
	
	if ( defined $userid ) {
		$dbh->do(q{update profiles
			      set realname= ?,
				   cryptpassword= ?,
				   groupset=0x7fffffffffffffff
			     where userid= ? },
			 undef,
			 $bugzilla_admin_real_name,Crypt($bugzilla_admin_pwd),$userid )
			or error "Can't update bugzilla admin profile";
	} else {
		$dbh->do(q{insert into profiles
				( login_name,
				     realname,
					cryptpassword,
					   groupset)
			  values( ?,
			  	     ?,
					?,
					   0x7fffffffffffffff)},
			 undef,
			 	  $bugzilla_admin_name,
				     $bugzilla_admin_real_name,
				        Crypt($bugzilla_admin_pwd)) 
			or error "Can't create the bugzilla admin profile";
	}
}

sub Crypt {
    my ($password) = @_;
    my @saltchars = (0..9, 'A'..'Z', 'a'..'z', '.', '/');
    my $salt = '';
    for ( my $i=0 ; $i < 8 ; ++$i ) {
        $salt .= $saltchars[rand(64)];
    }

    my $cryptedpassword = crypt($password, $salt);

    return $cryptedpassword;
}

# only here for debugging purpose.
# Will print on STDERR messages if 
# DEBIAN_BUGZILLA_DEBUG is here.
sub debug($)
{
	my $msg = shift;
	$msg = "" unless defined $msg;
	chomp $msg;
	warn "DEBUG\t- ".$msg."\n" if defined $ENV{DEBIAN_BUGZILLA_DEBUG};
}

# this sub will handle every error message
# that can happend.
#
# It will take a message and optionnally an 
# exit code.
#
# It will print on STDERR the message, and 
# will exit with the error code 
#
# If no error code is given, 2 will be used.
sub error($;$)
{
	my ($msg, $err) = @_;
	$msg = "No message ($!)" unless defined $msg;
	$err = 2 unless defined $err;
	
	chomp($msg);
	warn $msg."\n";
	exit $err;
}

# This will provide a way of connecting to the database 
# _without_ getting on STDERR ugly DBI messages.
# We know what to say to the user and we don't want 
# DBI to be verbose.
# DBI, shut up, I say ! :p
# If the conenction succeed returns 1, 0 else.
#
# If the script fails to connect, will return -1
sub dbi_connect($$$)
{
	my ($dsn, $name, $password) = @_;
	return 0 unless 
		defined $dsn and 
		defined $name and 
		defined $password;
	
	debug "Opening a MySQL connection on $dsn with user $name...\n";

	# Note that RaiseError => 1 tells to DBI to use die() when an error occurs
	# instead of printing it.
	# The fact that we use eval here, allow us to catch the message without dying.
	eval '$dbh = DBI->connect($dsn, $name, $password, {RaiseError => 1});';

	# yes, in case of errors, $@ will be true.
	return 0 if $@;
	return 1;
}

sub dbi_disconnect()
{
	debug "Closing the MySQL connection.";
	$dbh->disconnect;
}

# This will use the debian-sys-maint MySQL user to connect
# to the DB in order to look for an existing Bugzilla DB.
# Note that if the Debconf key 'bugzilla/mysql_name' is not 
# defined, we won't connect to the database.
sub exists_bugzilla_db()
{
	$mysql_name = get('bugzilla/mysql_name');
	return 0 unless $mysql_name;
	
	debug "Debconf key 'bugzilla/mysql_name' is defined : $mysql_name";

	my $connected = 0;
	$dsn = "DBI:mysql:;$DEBIAN_DBHOST;$DEBIAN_DBPORT";
	$connected = dbi_connect($dsn, $DEBIAN_DBUSER, $DEBIAN_DBPASS);

	if ($connected) {
		if (exists_database()) {
			debug "Found $mysql_name";
			dbi_disconnect();
			return 1;
		}
	}
	else {
		debug "Failed to connect !";
		return -1;
	}
	dbi_disconnect();
	return 0;
}

# Just read the default debian MySQL file to catch the 
# MySQL access values for debian-sys-maint.
sub init_default_vars()
{
	$DEBIAN_DBPORT = '3306';

	# parsing default.cnf file to get host, user and password.
	if (-f $default_file) {
		if (open(MYSQL_DEFAULT_FILE, $default_file)) {
			while (<MYSQL_DEFAULT_FILE>) {
				chomp;
				next if /^#/ or /^\[\w*\]$/;
				if (/(\S+)\s*=\s*(\S+)/) {
					my ($key, $val) = ($1, $2);
					if ($key eq 'host') {
						$DEBIAN_DBHOST = $val;
					}
					elsif($key eq 'user') {
						$DEBIAN_DBUSER = $val;
					}
					elsif($key eq 'password') {
						$DEBIAN_DBPASS = $val;
					}
				}
			}
			close MYSQL_DEFAULT_FILE;
		}
		else {
			warn "Cannot read $default_file\n";
			exit 1;
		}
	}
	else {
		warn "The file $default_file has disapeared.\n";
		exit 1;
	}
}

sub update_debconf_keys()
{
	reset('bugzilla/mysql_root_pwd');
	set('bugzilla/mysql_need_root','false');
	set('bugzilla/mysql_host', $mysql_host);
	set('bugzilla/mysql_port', $mysql_port);
	set('bugzilla/mysql_name', $mysql_name);
	set('bugzilla/mysql_user', $mysql_user);
}
# this will return a random string of the lentgh given.
sub random_string ($)
{
        my ($length) = @_;
        unless (defined ($length)) {
                return undef;
        }

        my @chars = ('a' .. 'z', 'A' .. 'Z', 0 .. 9);
        return (join ("", @chars[map {rand scalar @chars} (1 .. $length)]));
}