1=head1 usermin-lib.pl 2 3Functions for configuring Usermin running on this system. Example usage : 4 5 foreign_require("usermin", "usermin-lib.pl"); 6 @usermods = usermin::list_usermin_usermods(); 7 push(@usermods, [ 'joe', '', 'mailbox changepass' ]); 8 usermin::save_usermin_usermods(\@usermods); 9 10=cut 11 12BEGIN { push(@INC, ".."); }; 13use WebminCore; 14&init_config(); 15%access = &get_module_acl(); 16$access{'upgrade'} = 0 if (&is_readonly_mode()); # too hard to fake 17&foreign_require("webmin"); 18&foreign_require("acl"); 19%text = ( %webmin::text, %text ); 20 21$usermin_miniserv_config = "$config{'usermin_dir'}/miniserv.conf"; 22$usermin_config = "$config{'usermin_dir'}/config"; 23 24$update_host = "www.webmin.com"; 25$update_port = 80; 26$update_page = "/uupdates/uupdates.txt"; 27 28$standard_usermin_dir = "/etc/usermin"; 29$latest_rpm = "http://www.webmin.com/download/usermin-latest.noarch.rpm"; 30$latest_tgz = "http://www.webmin.com/download/usermin-latest.tar.gz"; 31 32$default_key_size = 2048; 33 34$cron_cmd = "$module_config_directory/update.pl"; 35 36=head2 get_usermin_miniserv_config(&hash) 37 38Similar to the standard get_miniserv_config function, but this one fills in 39the given hash ref with the contents of the /etc/usermin/miniserv.conf file. 40 41=cut 42sub get_usermin_miniserv_config 43{ 44&read_file($usermin_miniserv_config, \%usermin_miniserv_config_cache) 45 if (!%usermin_miniserv_config_cache); 46%{$_[0]} = %usermin_miniserv_config_cache; 47} 48 49=head2 put_usermin_miniserv_config(&hash) 50 51Writes out the Usermin miniserv configuration, based on the given hash ref. 52 53=cut 54sub put_usermin_miniserv_config 55{ 56%usermin_miniserv_config_cache = %{$_[0]}; 57&write_file($usermin_miniserv_config, \%usermin_miniserv_config_cache); 58} 59 60=head2 get_usermin_version 61 62Returns the version number of Usermin on this system. 63 64=cut 65sub get_usermin_version 66{ 67my ($ui_format_dev) = @_; 68local %miniserv; 69&get_usermin_miniserv_config(\%miniserv); 70open(VERSION, "<$miniserv{'root'}/version"); 71local $version = <VERSION>; 72close(VERSION); 73$version =~ s/\r|\n//g; 74# Format dev version nicely 75if ($ui_format_dev && length($version) == 13) { 76 return substr($version, 0, 5) . "." . substr($version, 5, 5 - 1) . "." . substr($version, 5 * 2 - 1); 77 } 78else { 79 return $version; 80 } 81} 82 83=head2 restart_usermin_miniserv 84 85Send a HUP signal to Usermin's miniserv, telling it to restart and re-read 86all configuration files. 87 88=cut 89sub restart_usermin_miniserv 90{ 91return undef if (&is_readonly_mode()); 92local($pid, %miniserv, $addr, $i); 93&get_usermin_miniserv_config(\%miniserv) || return; 94$miniserv{'inetd'} && return; 95open(PID, "<".$miniserv{'pidfile'}) || &error("Failed to open PID file"); 96chop($pid = <PID>); 97close(PID); 98if (!$pid) { &error("Invalid PID file"); } 99return &kill_logged('HUP', $pid); 100} 101 102=head2 reload_usermin_miniserv 103 104Sends a USR1 signal to the miniserv process, telling it to re-read most 105configuration files. 106 107=cut 108sub reload_usermin_miniserv 109{ 110return undef if (&is_readonly_mode()); 111local %miniserv; 112&get_usermin_miniserv_config(\%miniserv) || return; 113$miniserv{'inetd'} && return; 114 115local($pid, $addr, $i); 116open(PID, "<".$miniserv{'pidfile'}) || &error("Failed to open PID file"); 117chop($pid = <PID>); 118close(PID); 119if (!$pid) { &error("Invalid PID file"); } 120return &kill_logged('USR1', $pid); 121} 122 123=head2 get_usermin_config(&hash) 124 125Fills in the given hash ref with the contents of the global Usermin 126configuration file, typically at /etc/usermin/config. 127 128=cut 129sub get_usermin_config 130{ 131&read_file($usermin_config, \%usermin_config_cache) 132 if (!%usermin_config_cache); 133%{$_[0]} = %usermin_config_cache; 134} 135 136=head2 put_usermin_config(&hash) 137 138Writes the given hash ref to the global Usermin configuration file. 139 140=cut 141sub put_usermin_config 142{ 143%usermin_config_cache = %{$_[0]}; 144&write_file($usermin_config, \%usermin_config_cache); 145} 146 147=head2 get_usermin_root_directory 148 149Returns the Usermin install root directory. 150 151=cut 152sub get_usermin_root_directory 153{ 154my %miniserv; 155&get_usermin_miniserv_config(\%miniserv); 156return $miniserv{'root'}; 157} 158 159=head2 list_themes 160 161Returns an array of all usermin themes. The format is the same as the 162webmin::list_themes function. 163 164=cut 165sub list_themes 166{ 167local @rv; 168local %miniserv; 169&get_usermin_miniserv_config(\%miniserv); 170opendir(DIR, $miniserv{'root'}); 171foreach $m (readdir(DIR)) { 172 next if ($m =~ /^\./); 173 local %tinfo = &get_usermin_theme_info($m); 174 next if (!%tinfo); 175 next if (!&check_usermin_os_support(\%tinfo)); 176 push(@rv, \%tinfo); 177 } 178closedir(DIR); 179return @rv; 180} 181 182=head2 list_visible_themes([current-theme]) 183 184Lists all themes the user should be able to use, possibly including their 185current theme if one is set. 186 187=cut 188sub list_visible_themes 189{ 190my ($curr) = @_; 191my @rv; 192my %done; 193foreach my $theme (&list_themes()) { 194 my $iscurr = $curr && $theme->{'dir'} eq $curr; 195 next if (-l $root_directory."/".$theme->{'dir'} && 196 !$iscurr); 197 next if ($done{$theme->{'desc'}}++ && !$iscurr); 198 push(@rv, $theme); 199 } 200return @rv; 201} 202 203=head2 list_modules 204 205Returns a list of all usermin modules installed and supported on this system. 206Each is a hash ref in the same format as returned by Webmin's get_module_info 207function. 208 209=cut 210sub list_modules 211{ 212local (@mlist, $m, %miniserv); 213&get_usermin_miniserv_config(\%miniserv); 214local %cats; 215&read_file_cached("$config{'usermin_dir'}/webmin.cats", \%cats); 216opendir(DIR, $miniserv{'root'}); 217foreach $m (readdir(DIR)) { 218 local %minfo; 219 if ((%minfo = &get_usermin_module_info($m)) && 220 &check_usermin_os_support(\%minfo)) { 221 $minfo{'realcategory'} = $minfo{'category'}; 222 $minfo{'category'} = $cats{$m} if (defined($cats{$m})); 223 push(@mlist, \%minfo); 224 } 225 } 226closedir(DIR); 227@mlist = sort { $a->{'desc'} cmp $b->{'desc'} } @mlist; 228return @mlist; 229} 230 231=head2 get_usermin_module_info(module, [noclone]) 232 233Returns a hash contain details of a module, in the same format as 234Webmin's get_module_info function. Useful keys include : 235 236=item dir - The module's relative directory. 237 238=item desc - The human-readable title. 239 240=item category - Category the module is in, like login or apps. 241 242=item depends - Space-separated list of dependent modules. 243 244=item os_support - List of supported operating systems and versions. 245 246=cut 247sub get_usermin_module_info 248{ 249return () if ($_[0] =~ /^\./); 250local (%rv, $clone, %miniserv, $o); 251&get_usermin_miniserv_config(\%miniserv); 252&read_file("$miniserv{'root'}/$_[0]/module.info", \%rv) || return (); 253$clone = -l "$miniserv{'root'}/$_[0]"; 254foreach $o (@lang_order_list) { 255 $rv{"desc"} = $rv{"desc_$o"} if ($rv{"desc_$o"}); 256 } 257if ($clone && !$_[1] && $config_directory) { 258 $rv{'clone'} = $rv{'desc'}; 259 &read_file("$config{'usermin_dir'}/$_[0]/clone", \%rv); 260 } 261$rv{'dir'} = $_[0]; 262$rv{'realcategory'} = $rv{'category'}; 263 264# Apply description overrides 265$rv{'realdesc'} = $rv{'desc'}; 266local %descs; 267&read_file_cached("$config{'usermin_dir'}/webmin.descs", \%descs); 268if ($descs{$_[0]." ".$current_lang}) { 269 $rv{'desc'} = $descs{$_[0]." ".$current_lang}; 270 } 271elsif ($descs{$_[0]}) { 272 $rv{'desc'} = $descs{$_[0]}; 273 } 274 275return %rv; 276} 277 278=head2 get_usermin_theme_info(theme) 279 280Like get_usermin_module_info, but returns the details of a theme instead. 281This is basically the contents of its theme.info file. 282 283=cut 284sub get_usermin_theme_info 285{ 286local (%tinfo, $o); 287local %miniserv; 288&get_usermin_miniserv_config(\%miniserv); 289&read_file("$miniserv{'root'}/$_[0]/theme.info", \%tinfo) || return (); 290foreach $o (@lang_order_list) { 291 $tinfo{"desc"} = $rv{"desc_$o"} if ($tinfo{"desc_$o"}); 292 } 293$tinfo{'dir'} = $_[0]; 294return %tinfo; 295} 296 297=head2 check_usermin_os_support(&minfo) 298 299Given a Usermin module information hash ref (as returned by 300get_usermin_module_info), checks if it is supported on this OS. Returns 1 if 301yes, 0 if no. 302 303=cut 304sub check_usermin_os_support 305{ 306local $oss = $_[0]->{'os_support'}; 307return 1 if (!$oss || $oss eq '*'); 308local %uconfig; 309&get_usermin_config(\%uconfig); 310while(1) { 311 local ($os, $ver, $codes); 312 if ($oss =~ /^([^\/\s]+)\/([^\{\s]+)\{([^\}]*)\}\s*(.*)$/) { 313 $os = $1; $ver = $2; $codes = $3; $oss = $4; 314 } 315 elsif ($oss =~ /^([^\/\s]+)\/([^\/\s]+)\s*(.*)$/) { 316 $os = $1; $ver = $2; $oss = $3; 317 } 318 elsif ($oss =~ /^([^\{\s]+)\{([^\}]*)\}\s*(.*)$/) { 319 $os = $1; $codes = $2; $oss = $3; 320 } 321 elsif ($oss =~ /^\{([^\}]*)\}\s*(.*)$/) { 322 $codes = $1; $oss = $2; 323 } 324 elsif ($oss =~ /^(\S+)\s*(.*)$/) { 325 $os = $1; $oss = $2; 326 } 327 else { last; } 328 next if ($os && !($os eq $uconfig{'os_type'} || 329 $uconfig{'os_type'} =~ /^(\S+)-(\S+)$/ && $os eq "*-$2")); 330 next if ($ver && $ver ne $uconfig{'os_version'}); 331 next if ($codes && !eval $codes); 332 return 1; 333 } 334return 0; 335} 336 337=head2 read_usermin_acl(&array, &array) 338 339Reads the acl file into the given hashes. The first maps user,module to 3401 where granted, which the second maps a user to an array ref of module dirs. 341 342=cut 343sub read_usermin_acl 344{ 345local($user, $_, @mods); 346if (!%usermin_acl_hash_cache) { 347 open(ACL, "<".&usermin_acl_filename()); 348 while(<ACL>) { 349 if (/^(\S+):\s*(.*)/) { 350 local(@mods); 351 $user = $1; 352 @mods = split(/\s+/, $2); 353 foreach $m (@mods) { 354 $usermin_acl_hash_cache{$user,$m}++; 355 } 356 $usermin_acl_array_cache{$user} = \@mods; 357 } 358 } 359 close(ACL); 360 } 361if ($_[0]) { %{$_[0]} = %usermin_acl_hash_cache; } 362if ($_[1]) { %{$_[1]} = %usermin_acl_array_cache; } 363} 364 365=head2 usermin_acl_filename 366 367Returns the file containing the webmin ACL. 368 369=cut 370sub usermin_acl_filename 371{ 372return "$config{'usermin_dir'}/webmin.acl"; 373} 374 375=head2 save_usermin_acl(user, &modules) 376 377Updates the list of available modules in Usermin. 378 379=cut 380sub save_usermin_acl 381{ 382&open_tempfile(ACL, ">".&usermin_acl_filename()); 383&print_tempfile(ACL, $_[0],": ",join(" ", @{$_[1]}),"\n"); 384&close_tempfile(ACL); 385} 386 387=head2 install_usermin_module(file, unlink, nodeps) 388 389Installs a usermin module or theme, and returns either an error message 390or references to three arrays for descriptions, directories and sizes. 391On success or failure, the file is deleted if the unlink parameter is set. 392 393=cut 394sub install_usermin_module 395{ 396local ($file, $need_unlink, $nodeps) = @_; 397local (@mdescs, @mdirs, @msizes); 398if (&is_readonly_mode()) { 399 return "Module installs are not allowed in readonly mode"; 400 } 401 402# Uncompress the module file if needed 403open(MFILE, "<".$file); 404read(MFILE, $two, 2); 405close(MFILE); 406if ($two eq "\037\235") { 407 if (!&has_command("uncompress")) { 408 unlink($file) if ($need_unlink); 409 return &text('install_ecomp', "<tt>uncompress</tt>"); 410 } 411 local $temp = $file =~ /\/([^\/]+)\.Z/i ? &transname("$1") 412 : &transname(); 413 local $out = `uncompress -c "$file" 2>&1 >$temp`; 414 unlink($file) if ($need_unlink); 415 if ($?) { 416 unlink($temp); 417 return &text('install_ecomp2', $out); 418 } 419 $file = $temp; 420 $need_unlink = 1; 421 } 422elsif ($two eq "\037\213") { 423 if (!&has_command("gunzip")) { 424 unlink($file) if ($need_unlink); 425 return &text('install_egzip', "<tt>gunzip</tt>"); 426 } 427 local $temp = $file =~ /\/([^\/]+)\.gz/i ? &transname("$1") 428 : &transname(); 429 local $out = `gunzip -c "$file" 2>&1 >$temp`; 430 unlink($file) if ($need_unlink); 431 if ($?) { 432 unlink($temp); 433 return &text('install_egzip2', $out); 434 } 435 $file = $temp; 436 $need_unlink = 1; 437 } 438 439local %miniserv; 440&get_usermin_miniserv_config(\%miniserv); 441 442# Check if this is an RPM usermin module or theme 443local ($type, $redirect_to); 444open(TYPE, "<../install-type"); 445chop($type = <TYPE>); 446close(TYPE); 447if ($type eq 'rpm' && $file =~ /\.rpm$/i && 448 ($out = `rpm -qp $file 2>/dev/null`)) { 449 # Looks like an RPM of some kind, hopefully an RPM usermin module 450 # or theme 451 local ($out, %minfo, %tinfo); 452 if ($out !~ /^(wbm|wbt)-([^\s\-]+)/) { 453 unlink($file) if ($need_unlink); 454 return $text{'install_erpm'}; 455 } 456 $redirect_to = $name = $2; 457 $out = &backquote_logged("rpm -U \"$file\" 2>&1"); 458 if ($?) { 459 unlink($file) if ($need_unlink); 460 return &text('install_eirpm', "<tt>$out</tt>"); 461 } 462 463 $mdirs[0] = "$miniserv{'root'}/$name"; 464 if (%minfo = &get_usermin_module_info($name)) { 465 # Get the new module info 466 $mdescs[0] = $minfo{'desc'}; 467 $msizes[0] = &disk_usage_kb($mdirs[0]); 468 469 # Update the ACL for the usermin user 470 local %acl; 471 &read_usermin_acl(undef, \%acl); 472 &open_tempfile(ACL, "> ".&usermin_acl_filename()); 473 foreach $u (keys %acl) { 474 local @mods = @{$acl{$u}}; 475 if ($u eq 'user') { 476 push(@mods, $name); 477 @mods = &unique(@mods); 478 } 479 &print_tempfile(ACL, "$u: ",join(' ', @mods),"\n"); 480 } 481 &close_tempfile(ACL); 482 &webmin_log("install", undef, $name, 483 { 'desc' => $mdescs[0] }); 484 } 485 elsif (%tinfo = &get_usermin_theme_info($name)) { 486 # Get the theme info 487 $mdescs[0] = $tinfo{'desc'}; 488 $msizes[0] = &disk_usage_kb($mdirs[0]); 489 &webmin_log("tinstall", undef, $name, 490 { 'desc' => $mdescs[0] }); 491 } 492 else { 493 unlink($file) if ($need_unlink); 494 return $text{'install_eneither'}; 495 } 496 } 497else { 498 # Check if this is a valid module (a tar file of multiple module or 499 # theme directories) 500 local (%mods, %hasfile); 501 local $tar = `tar tf "$file" 2>&1`; 502 if ($?) { 503 unlink($file) if ($need_unlink); 504 return &text('install_etar', $tar); 505 } 506 foreach $f (split(/\n/, $tar)) { 507 if ($f =~ /^\.\/([^\/]+)\/(.*)$/ || $f =~ /^([^\/]+)\/(.*)$/) { 508 $redirect_to = $1 if (!$redirect_to); 509 $mods{$1}++; 510 $hasfile{$1,$2}++; 511 } 512 } 513 foreach $m (keys %mods) { 514 if (!$hasfile{$m,"module.info"} && !$hasfile{$m,"theme.info"}) { 515 unlink($file) if ($need_unlink); 516 return &text('install_einfo', "<tt>$m</tt>"); 517 } 518 } 519 if (!%mods) { 520 unlink($file) if ($need_unlink); 521 return $text{'install_enone'}; 522 } 523 524 # Get the module.info files to check dependencies 525 local $ver = &get_usermin_version(); 526 local $tmpdir = &transname(); 527 mkdir($tmpdir, 0700); 528 local $err; 529 local @realmods; 530 foreach $m (keys %mods) { 531 next if (!$hasfile{$m,"module.info"}); 532 push(@realmods, $m); 533 local %minfo; 534 system("cd $tmpdir ; tar xf \"$file\" $m/module.info ./$m/module.info >/dev/null 2>&1"); 535 if (!&read_file("$tmpdir/$m/module.info", \%minfo)) { 536 $err = &text('install_einfo', "<tt>$m</tt>"); 537 } 538 elsif (!&check_usermin_os_support(\%minfo)) { 539 $err = &text('install_eos', "<tt>$m</tt>", 540 $gconfig{'real_os_type'}, 541 $gconfig{'real_os_version'}); 542 } 543 elsif (!$minfo{'usermin'}) { 544 $err = &text('install_eusermin', "<tt>$m</tt>"); 545 } 546 elsif (!$nodeps) { 547 local $deps = $minfo{'usermin_depends'} || 548 $minfo{'depends'}; 549 foreach $dep (split(/\s+/, $minfo{'depends'})) { 550 if ($dep =~ /^[0-9\.]+$/) { 551 if ($dep > $ver) { 552 $err = &text('install_ever', 553 "<tt>$m</tt>", 554 "<tt>$dep</tt>"); 555 } 556 } 557 elsif (!-r "$miniserv{'root'}/$dep/module.info" 558 && !$mods{$dep}) { 559 $err = &text('install_edep', 560 "<tt>$m</tt>", "<tt>$dep</tt>"); 561 } 562 } 563 foreach $dep (split(/\s+/, $minfo{'perldepends'})) { 564 eval "use $dep"; 565 if ($@) { 566 $err = &text('install_eperldep', 567 "<tt>$m</tt>", "<tt>$dep</tt>", 568 "/cpan/download.cgi?source=3&cpan=$dep"); 569 } 570 } 571 } 572 last if ($err); 573 } 574 system("rm -rf $tmpdir >/dev/null 2>&1"); 575 if ($err) { 576 unlink($file) if ($need_unlink); 577 return $err; 578 } 579 580 # Delete modules or themes being replaced 581 foreach $m (@realmods) { 582 system("rm -rf '$miniserv{'root'}/$m' 2>&1 >/dev/null") if ($m ne 'webmin'); 583 } 584 585 # Extract all the modules and update perl path and ownership 586 local $out = `cd $miniserv{'root'} ; tar xf "$file" 2>&1 >/dev/null`; 587 if ($?) { 588 unlink($file) if ($need_unlink); 589 return &text('install_eextract', $out); 590 } 591 if ($need_unlink) { unlink($file); } 592 local $perl; 593 open(PERL, "<$miniserv{'root'}/miniserv.pl"); 594 <PERL> =~ /^#!(\S+)/; $perl = $1; 595 close(PERL); 596 local @st = stat($0); 597 foreach $moddir (keys %mods) { 598 local $pwd = "$miniserv{'root'}/$moddir"; 599 if ($hasfile{$moddir,"module.info"}) { 600 local %minfo = &get_usermin_module_info($moddir); 601 push(@mdescs, $minfo{'desc'}); 602 push(@mdirs, $pwd); 603 push(@msizes, &disk_usage_kb($pwd)); 604 &webmin_log("install", undef, $moddir, 605 { 'desc' => $minfo{'desc'} }); 606 } 607 else { 608 local %tinfo = &get_usermin_theme_info($moddir); 609 &read_file("theme.info", \%tinfo); 610 push(@mdescs, $tinfo{'desc'}); 611 push(@mdirs, $pwd); 612 push(@msizes, &disk_usage_kb($pwd)); 613 &webmin_log("tinstall", undef, $moddir, 614 { 'desc' => $tinfo{'desc'} }); 615 } 616 system("(find $pwd -name '*.cgi' ; find $pwd -name '*.pl') 2>/dev/null | $perl $miniserv{'root'}/perlpath.pl $perl -"); 617 system("chown -R $st[4]:$st[5] $pwd"); 618 } 619 620 # Copy appropriate config file from modules to /etc/webmin 621 local %ugconfig; 622 &get_usermin_config(\%ugconfig); 623 system("$perl $miniserv{'root'}/copyconfig.pl '$ugconfig{'os_type'}/$ugconfig{'real_os_type'}' '$ugconfig{'os_version'}/$ugconfig{'real_os_version'}' $miniserv{'root'} $config{'usermin_dir'} ".join(' ', @realmods)); 624 625 # Update ACL for this user so they can access the new modules 626 local %acl; 627 &read_usermin_acl(undef, \%acl); 628 &open_tempfile(ACL, "> ".&usermin_acl_filename()); 629 foreach $u (keys %acl) { 630 local @mods = @{$acl{$u}}; 631 if ($u eq 'user') { 632 push(@mods, @realmods); 633 @mods = &unique(@mods); 634 } 635 &print_tempfile(ACL, "$u: ",join(' ', @mods),"\n"); 636 } 637 &close_tempfile(ACL); 638 } 639&flush_modules_cache(); 640 641return [ \@mdescs, \@mdirs, \@msizes ]; 642} 643 644=head2 list_usermin_usermods 645 646Returns the list of additional module restrictions for usermin. 647This is a list of array refs, each element of which contains a username, 648a flag and an array ref of module names. The flag can be one of : 649 650=item + - Add the modules to the list available to this user. 651 652=item - - Take the modules away from this user. 653 654=item blank - Assign the modules to the list for this user. 655 656=cut 657sub list_usermin_usermods 658{ 659local @rv; 660open(USERMODS, "<$config{'usermin_dir'}/usermin.mods"); 661while(<USERMODS>) { 662 if (/^([^:]+):(\+|-|):(.*)/) { 663 push(@rv, [ $1, $2, [ split(/\s+/, $3) ] ]); 664 } 665 } 666close(USERMODS); 667return @rv; 668} 669 670=head2 save_usermin_usermods(&usermods) 671 672Saves the list of additional module restrictions. This must be an array ref 673in the same format as returned by list_usermin_usermods. 674 675=cut 676sub save_usermin_usermods 677{ 678&open_tempfile(USERMODS, ">$config{'usermin_dir'}/usermin.mods"); 679foreach $u (@{$_[0]}) { 680 &print_tempfile(USERMODS, 681 join(":", $u->[0], $u->[1], join(" ", @{$u->[2]})),"\n"); 682 } 683&close_tempfile(USERMODS); 684} 685 686=head2 get_usermin_miniserv_users 687 688Returns a list of Usermin users from miniserv.users. In normal use, there 689is only one, as all authentication is done using Unix users. 690 691=cut 692sub get_usermin_miniserv_users 693{ 694local %miniserv; 695&get_usermin_miniserv_config(\%miniserv); 696local @rv; 697open(USERS, "<".$miniserv{'userfile'}); 698while(<USERS>) { 699 s/\r|\n//g; 700 local @u = split(/:/, $_); 701 push(@rv, { 'name' => $u[0], 702 'pass' => $u[1], 703 'sync' => $u[2], 704 'cert' => $u[3], 705 'allow' => $u[4] }); 706 } 707close(USERS); 708return @rv; 709} 710 711=head2 save_usermin_miniserv_users(&user, ...) 712 713Updats the list of Usermin miniserv users, each of which is a hash ref 714in the format returned by get_usermin_miniserv_users. 715 716=cut 717sub save_usermin_miniserv_users 718{ 719local %miniserv; 720&get_usermin_miniserv_config(\%miniserv); 721&open_tempfile(USERS, ">$miniserv{'userfile'}"); 722local $u; 723foreach $u (@_) { 724 &print_tempfile(USERS, 725 join(":", $u->{'name'}, $u->{'pass'}, $u->{'sync'}, 726 $u->{'cert'}, $u->{'allow'}),"\n"); 727 } 728&close_tempfile(USERS); 729} 730 731=head2 can_use_module(module) 732 733Returns 1 if the current Webmin user can use some function of this module. 734 735=cut 736sub can_use_module 737{ 738return 1 if ($access{'mods'} eq '*'); 739local @mods = split(/\s+/, $access{'mods'}); 740return &indexof($_[0], @mods) >= 0; 741} 742 743=head2 get_usermin_base_version 744 745Gets the usermin version, rounded to the nearest .01 746 747=cut 748sub get_usermin_base_version 749{ 750return &base_version(&get_usermin_version()); 751} 752 753=head2 base_version 754 755Rounds a version number to the nearest .01 756 757=cut 758sub base_version 759{ 760return sprintf("%.2f0", $_[0]); 761} 762 763=head2 find_cron_job(\@jobs) 764 765Finds the cron job for Usermin updates, given an array ref of cron jobs 766as returned by cron::list_cron_jobs. 767 768=cut 769sub find_cron_job 770{ 771local ($job) = grep { $_->{'user'} eq 'root' && 772 $_->{'command'} eq $cron_cmd } @{$_[0]}; 773return $job; 774} 775 776=head2 delete_usermin_module(module, [delete-acls]) 777 778Deletes some usermin module, clone or theme, and return a description of 779the thing deleted. 780 781=cut 782sub delete_usermin_module 783{ 784local $m = $_[0]; 785return undef if (!$m); 786local %minfo = &get_usermin_module_info($m); 787%minfo = &get_usermin_theme_info($m) if (!%minfo); 788return undef if (!%minfo); 789local ($mdesc, @aclrm); 790@aclrm = ( $m ) if ($_[1]); 791local %miniserv; 792&get_usermin_miniserv_config(\%miniserv); 793local %ugconfig; 794&get_usermin_config(\%uconfig); 795local $mdir = "$miniserv{'root'}/$m"; 796local $cdir = "$config{'usermin_dir'}/$m"; 797if ($minfo{'clone'}) { 798 # Deleting a clone 799 local %cinfo; 800 &read_file("$config{'usermin_dir'}/$m/clone", \%cinfo); 801 &unlink_logged($mdir); 802 &system_logged("rm -rf ".quotemeta($cdir)); 803 if ($ugconfig{'theme'}) { 804 &unlink_logged("$miniserv{'root'}/$ugconfig{'theme'}/$m"); 805 } 806 $mdesc = &text('delete_desc1', $minfo{'desc'}, $minfo{'clone'}); 807 } 808else { 809 # Delete any clones of this module 810 local @clones; 811 local @mst = stat($mdir); 812 opendir(DIR, $miniserv{'root'}); 813 local $l; 814 foreach $l (readdir(DIR)) { 815 @lst = stat("$miniserv{'root'}/$l"); 816 if (-l "$miniserv{'root'}/$l" && $lst[1] == $mst[1]) { 817 &unlink_logged("$miniserv{'root'}/$l"); 818 &system_logged("rm -rf $config{'usermin_dir'}/$l"); 819 push(@clones, $l); 820 } 821 } 822 closedir(DIR); 823 824 open(TYPE, "<$mdir/install-type"); 825 chop($type = <TYPE>); 826 close(TYPE); 827 828 # Deleting the real module 829 local $size = &disk_usage_kb($mdir); 830 $mdesc = &text('delete_desc2', "<b>$minfo{'desc'}</b>", 831 "<tt>$mdir</tt>", $size); 832 if ($type eq 'rpm') { 833 # This module was installed from an RPM .. rpm -e it 834 &system_logged("rpm -e ubm-$m"); 835 } 836 else { 837 # Module was installed from a .wbm file .. just rm it 838 &system_logged("rm -rf ".quotemeta($mdir)); 839 } 840 } 841 842&webmin_log("delete", undef, $m, { 'desc' => $minfo{'desc'} }); 843return $mdesc; 844} 845 846=head2 flush_modules_cache 847 848Forces a rebuild of the Usermin module cache. 849 850=cut 851sub flush_modules_cache 852{ 853&unlink_file("$config{'usermin_dir'}/module.infos.cache"); 854} 855 856=head2 stop_usermin 857 858Kills the running Usermin server process, returning undef on success or an 859error message on failure. 860 861=cut 862sub stop_usermin 863{ 864local %miniserv; 865&get_usermin_miniserv_config(\%miniserv); 866local $pid; 867if (open(PID, "<".$miniserv{'pidfile'}) && ($pid = int(<PID>))) { 868 &kill_logged('TERM', $pid) || return &text('stop_ekill', $!); 869 close(PID); 870 } 871else { 872 return $text{'stop_efile'}; 873 } 874return undef; 875} 876 877=head2 start_usermin 878 879Starts the Usermin server process. Return value is always undef. 880 881=cut 882sub start_usermin 883{ 884&system_logged("$config{'usermin_dir'}/start >/dev/null 2>&1 </dev/null"); 885return undef; 886} 887 888=head2 get_install_type 889 890Returns the package type Usermin was installed form (rpm, deb, solaris-pkg 891or undef for tar.gz). 892 893=cut 894sub get_install_type 895{ 896local (%miniserv, $mode); 897&get_usermin_miniserv_config(\%miniserv); 898if (open(MODE, "<$miniserv{'root'}/install-type")) { 899 chop($mode = <MODE>); 900 close(MODE); 901 } 902else { 903 if ($miniserv{'root'} eq "/usr/libexec/usermin") { 904 $mode = "rpm"; 905 } 906 elsif ($miniserv{'root'} eq "/usr/share/usermin") { 907 $mode = "deb"; 908 } 909 else { 910 $mode = undef; 911 } 912 } 913return $mode; 914} 915 916=head2 switch_to_usermin_user(username) 917 918Returns a set-cookie header and redirect URL for auto-logging into Usermin 919as some user. 920 921=cut 922sub switch_to_usermin_user 923{ 924my ($user) = @_; 925 926# Stop Usermin first, so that the DBM can be safely written 927my %miniserv; 928&get_usermin_miniserv_config(\%miniserv); 929my $stopped; 930if (&check_pid_file($miniserv{'pidfile'})) { 931 &stop_usermin(); 932 $stopped = 1; 933 } 934 935# Generate a session ID and set it in the DB 936&acl::open_session_db(\%miniserv); 937&seed_random(); 938my $now = time(); 939my $sid = int(rand()*$now); 940$acl::sessiondb{$sid} = "$user $now $ENV{'REMOTE_ADDR'}"; 941dbmclose(%acl::sessiondb); 942if ($stopped) { 943 &start_usermin(); 944 } 945&reload_usermin_miniserv(); 946eval "use Net::SSLeay"; 947if ($@) { 948 $miniserv{'ssl'} = 0; 949 } 950my $ssl = $miniserv{'ssl'} || $miniserv{'inetd_ssl'}; 951my $sec = $ssl ? "; secure" : ""; 952my $sidname = $miniserv{'sidname'} || 'sid'; 953my $cookie = "$sidname=$sid; path=/$sec"; 954 955# Work out redirect host 956my @sockets = &webmin::get_miniserv_sockets(\%miniserv); 957my ($host, $port); 958if ($config{'host'}) { 959 # Specific hostname set 960 $host = $config{'host'}; 961 } 962else { 963 if ($sockets[0]->[0] ne "*") { 964 # Listening on special IP 965 $host = $sockets[0]->[0]; 966 $port = $sockets[0]->[1] if ($sockets[0]->[1] ne '*'); 967 } 968 else { 969 # Use same hostname as this server 970 $host = $ENV{'HTTP_HOST'}; 971 $host =~ s/:.*//; 972 } 973 } 974$port ||= $config{'port'} || $miniserv{'port'}; 975 976return ($cookie, ($ssl ? "https://" : "http://").$host.":".$port."/"); 977} 978 979=head2 get_usermin_email_url([module], [cgi], [force-default], [force-host]) 980 981Returns the base URL for accessing Usermin on this system, for use in emails. 982 983=cut 984sub get_usermin_email_url 985{ 986my ($mod, $cgi, $def, $forcehost) = @_; 987 988# Work out the base URL 989my $url; 990if (!$def && $config{'usermin_email_url'}) { 991 $url = $config{'usermin_email_url'}; 992 } 993else { 994 my %miniserv; 995 &get_usermin_miniserv_config(\%miniserv); 996 my $proto = $miniserv{'ssl'} ? 'https' : 'http'; 997 my $port = $miniserv{'port'}; 998 my $host = $forcehost || &get_system_hostname(); 999 my $defport = $proto eq 'https' ? 443 : 80; 1000 $url = $proto."://".$host.($port == $defport ? "" : ":".$port); 1001 } 1002 1003# Append module if needed 1004$url =~ s/\/$//; 1005if ($mod && $cgi) { 1006 $url .= "/".$mod."/".$cgi; 1007 } 1008elsif ($mod) { 1009 $url .= "/".$mod."/"; 1010 } 1011elsif ($cgi) { 1012 $url .= "/".$cgi; 1013 } 1014return $url; 1015} 1016 1017=head2 create_cron_wrapper(wrapper-path, module, script) 1018 1019Creates a wrapper script which calls a script in some module's directory 1020with the proper usermin environment variables set. 1021 1022The parameters are : 1023 1024=item wrapper-path - Full path to the wrapper to create, like /etc/usermin/yourmodule/foo.pl 1025 1026=item module - Module containing the real script to call. 1027 1028=item script - Program within that module for the wrapper to run. 1029 1030=cut 1031sub create_cron_wrapper 1032{ 1033my ($wrapper, $mod, $script) = @_; 1034my $perl_path = &get_perl_path(); 1035&open_tempfile(CMD, ">$wrapper"); 1036&print_tempfile(CMD, <<EOF 1037#!$perl_path 1038open(CONF, "<$usermin_miniserv_config") || die "Failed to open $usermin_miniserv_config : \$!"; 1039while(<CONF>) { 1040 \$root = \$1 if (/^root=(.*)/); 1041 } 1042close(CONF); 1043\$root || die "No root= line found in $usermin_miniserv_config"; 1044\$ENV{'PERLLIB'} = "\$root"; 1045\$ENV{'WEBMIN_CONFIG'} = "$config{'usermin_dir'}"; 1046\$ENV{'WEBMIN_VAR'} = "$ENV{'WEBMIN_VAR'}"; 1047delete(\$ENV{'MINISERV_CONFIG'}); 1048EOF 1049 ); 1050if ($mod) { 1051 &print_tempfile(CMD, "chdir(\"\$root/$mod\");\n"); 1052 &print_tempfile(CMD, "exec(\"\$root/$mod/$script\", \@ARGV) || die \"Failed to run \$root/$mod/$script : \$!\";\n"); 1053 } 1054else { 1055 &print_tempfile(CMD, "chdir(\"\$root\");\n"); 1056 &print_tempfile(CMD, "exec(\"\$root/$script\", \@ARGV) || die \"Failed to run \$root/$script : \$!\";\n"); 1057 } 1058&close_tempfile(CMD); 1059chmod(0755, $wrapper); 1060} 1061 10621; 1063 1064