1#!--PERL-- 2# -*- indent-tabs-mode: nil; -*- 3# vim:ft=perl:et:sw=4 4# $Id$ 5 6# Sympa - SYsteme de Multi-Postage Automatique 7# 8# Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel 9# Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 10# 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites 11# Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017 GIP RENATER 12# Copyright 2018 The Sympa Community. See the AUTHORS.md file at the 13# top-level directory of this distribution and at 14# <https://github.com/sympa-community/sympa.git>. 15# 16# This program is free software; you can redistribute it and/or modify 17# it under the terms of the GNU General Public License as published by 18# the Free Software Foundation; either version 2 of the License, or 19# (at your option) any later version. 20# 21# This program is distributed in the hope that it will be useful, 22# but WITHOUT ANY WARRANTY; without even the implied warranty of 23# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24# GNU General Public License for more details. 25# 26# You should have received a copy of the GNU General Public License 27# along with this program. If not, see <http://www.gnu.org/licenses/>. 28 29use lib '--modulesdir--'; 30use strict; 31use warnings; 32use Encode qw(); 33use English qw(-no_match_vars); 34use Getopt::Long; 35use Pod::Usage; 36use Sys::Hostname qw(); 37 38use Sympa::ConfDef; 39use Sympa::Constants; 40 41my $with_CPAN; # check if module "CPAN" installed. 42my $modfail; # any of required modules are not installed. 43 44BEGIN { 45 $with_CPAN = eval { require CPAN; }; 46 $modfail = !eval { 47 require Conf; 48 require Sympa::Language; 49 require Sympa::Tools::Text; 50 }; 51} 52 53# Detect console encoding. 54if (-t) { 55 no warnings; 56 57 eval { require Encode::Locale; }; 58 unless ($EVAL_ERROR 59 or Encode::resolve_alias($Encode::Locale::ENCODING_CONSOLE_IN) eq 60 'ascii' 61 or Encode::resolve_alias($Encode::Locale::ENCODING_CONSOLE_OUT) eq 62 'ascii') { 63 binmode(STDIN, ':encoding(console_in):bytes'); 64 binmode(STDOUT, ':encoding(console_out):bytes'); 65 binmode(STDERR, ':encoding(console_out):bytes'); 66 } 67} 68 69# Set language context if possible. 70if ($modfail) { 71 no warnings; 72 73 *gettext = sub { $_[1] ? sprintf('%*s', $_[1], $_[0]) : $_[0] }; 74 75 eval { require Text::Wrap; }; 76 if ($EVAL_ERROR) { 77 *Sympa::Tools::Text::wrap_text = sub {"$_[1]$_[0]\n"}; 78 } else { 79 $Text::Wrap::columns = 78; 80 *Sympa::Tools::Text::wrap_text = 81 sub { Text::Wrap::wrap($_[1], $_[2], $_[0]) . "\n"; }; 82 } 83} else { 84 no warnings; 85 86 my $language = Sympa::Language->instance; 87 *gettext = sub { 88 $_[1] 89 ? Sympa::Tools::Text::pad($language->gettext($_[0]), $_[1]) 90 : $language->gettext($_[0]); 91 }; 92 93 my $lang = $ENV{'LANGUAGE'} || $ENV{'LC_ALL'} || $ENV{'LANG'}; 94 $lang =~ s/\..*// if $lang; 95 $language->set_lang($lang, 'en-US', 'en'); 96} 97 98## sympa configuration file 99my $sympa_conf = Sympa::Constants::CONFIG; 100 101my %options; 102GetOptions( 103 \%options, 104 'target=s', 105 'create:s', # parameter is optional and only "sympa.conf" is allowed. 106 'batch', 107 'display', 108 'check', 109 'help|h', 110 'version|v', 111); 112 113if ($options{help}) { 114 pod2usage(); 115} elsif ($options{'version'}) { 116 printf "Sympa %s\n", Sympa::Constants::VERSION; 117 exit 0; 118} elsif (defined $options{create}) { 119 create_configuration(); 120} elsif ($options{display}) { 121 display_configuration(); 122} elsif ($options{check}) { 123 check_cpan(); 124} else { 125 my %user_param; 126 foreach my $arg (@ARGV) { 127 # check for key/values settings 128 if ($arg =~ /\A(\w+)=(.+)/) { 129 $user_param{$1} = $2; 130 } else { 131 die "$0: Invalid commandline argument: $arg\n"; 132 } 133 } 134 edit_configuration(%user_param); 135} 136 137exit 0; 138 139sub create_configuration { 140 my $conf; 141 if ($options{create} eq '' or $options{create} eq 'sympa.conf') { 142 $conf = $options{target} ? $options{target} : $sympa_conf; 143 } else { 144 pod2usage("$options{create} is not a valid argument"); 145 exit 1; 146 } 147 148 if (-f $conf) { 149 print STDERR "$conf file already exists, exiting\n"; 150 exit 1; 151 } 152 153 my $umask = umask 037; 154 unless (open NEWF, '>', $conf) { 155 umask $umask; 156 die "$0: " 157 . sprintf(gettext("Unable to open %s : %s"), $conf, $ERRNO) 158 . "\n"; 159 } 160 umask $umask; 161 162 if ($options{create} eq 'sympa.conf') { 163# print NEWF <<EOF 164## Configuration file for Sympa 165## many parameters are optional 166## refer to the documentation for a detailed list of parameters 167 168#EOF 169 } 170 171 my $title; 172 foreach my $param (@Sympa::ConfDef::params) { 173 next if $param->{obsolete}; 174 175 unless ($param->{'name'}) { 176 $title = gettext($param->{'gettext_id'}) 177 if $param->{'gettext_id'}; 178 next; 179 } 180 181 next unless $param->{'file'}; 182 ##next unless defined $param->{'default'} or defined $param->{'sample'}; 183 184 if ($title) { 185 printf NEWF "###\\\\\\\\ %s ////###\n\n", $title; 186 undef $title; 187 } 188 189 printf NEWF "## %s\n", $param->{'name'}; 190 191 if ($param->{'gettext_id'}) { 192 print NEWF Sympa::Tools::Text::wrap_text( 193 gettext($param->{'gettext_id'}), 194 '## ', '## '); 195 } 196 197 print NEWF Sympa::Tools::Text::wrap_text( 198 gettext($param->{'gettext_comment'}), 199 '## ', '## ') 200 if $param->{'gettext_comment'}; 201 202 if (defined $param->{'sample'}) { 203 printf NEWF '## ' . gettext('Example: ') . "%s\t%s\n", 204 $param->{'name'}, $param->{'sample'}; 205 } 206 207 if (defined $param->{'default'}) { 208 printf NEWF "#%s\t%s\n", $param->{'name'}, $param->{'default'}; 209 } elsif ($param->{'optional'}) { 210 printf NEWF "#%s\t\n", $param->{'name'}; 211 } else { 212 printf NEWF "#%s\t%s\n", $param->{'name'}, 213 gettext("(You must define this parameter)"); 214 } 215 print NEWF "\n"; 216 } 217 218 close NEWF; 219 print STDERR "$conf file has been created\n"; 220} 221 222sub display_configuration { 223 die "$0: You must run as superuser.\n" 224 if $UID; 225 die "$0: Installation of Sympa has not been completed.\n" 226 . "Run sympa_wizard.pl --check\n" 227 if $modfail; 228 229 # Load sympa config (but not using database) 230 unless (defined Conf::load($sympa_conf, 1)) { 231 printf STDERR 232 "$0: Unable to load sympa configuration, file %s or one of the virtual host robot.conf files contain errors. Exiting.\n", 233 $sympa_conf; 234 exit 1; 235 } 236 237 my ($var, $disp); 238 239 print "[SYMPA]\n"; 240 foreach my $key (sort keys %Conf::Conf) { 241 next 242 if grep { $key eq $_ } 243 qw(auth_services blocklist cas_number crawlers_detection 244 generic_sso_number ldap ldap_number listmasters 245 locale2charset nrcpt_by_domain robot_by_http_host request 246 robot_name robots source_file sympa trusted_applications 247 use_passwd); 248 249 $var = $Conf::Conf{$key}; 250 251 if ($key eq 'automatic_list_families') { 252 $disp = join ';', map { 253 my $name = $_; 254 join ':', map { sprintf '%s=%s', $_, $var->{$name}{$_} } 255 grep { !/\Aescaped_/ } 256 sort keys %{$var->{$name} || {}}; 257 } sort keys %{$var || {}}; 258 } elsif (ref $var eq 'ARRAY') { 259 $disp = join(',', map { defined $_ ? $_ : '' } @$var); 260 } else { 261 $disp = defined $var ? $var : ''; 262 } 263 264 printf "%s=\"%s\"\n", $key, $disp; 265 } 266} 267 268sub edit_configuration { 269 my %user_param = @_; 270 271 die "$0: You must run as superuser.\n" 272 if $UID; 273 die "$0: Installation of Sympa has not been completed.\n" 274 . "Run sympa_wizard.pl --check\n" 275 if $modfail; 276 277 # complement required fields. 278 foreach my $param (@Sympa::ConfDef::params) { 279 next if $param->{obsolete}; 280 next unless $param->{'name'}; 281 if ($param->{'name'} eq 'domain') { 282 $param->{'default'} = Sys::Hostname::hostname(); 283 } elsif ($param->{'name'} eq 'wwsympa_url') { 284 $param->{'default'} = sprintf 'http://%s/sympa', 285 Sys::Hostname::hostname(); 286 } elsif ($param->{'name'} eq 'listmaster') { 287 $param->{'default'} = sprintf 'your_email_address@%s', 288 Sys::Hostname::hostname(); 289 } 290 } 291 292 ## Load sympa config (but not using database) 293 unless (defined Conf::load($sympa_conf, 1)) { 294 printf STDERR 295 "$0: Unable to load sympa configuration, file %s or one of the virtual host robot.conf files contain errors. Exiting.\n", 296 $sympa_conf; 297 exit 1; 298 } 299 300 my $somechange = 0; 301 302 my @new_sympa_conf; 303 my $title = undef; 304 305 # dynamic defaults 306 my $domain = Sys::Hostname::hostname(); 307 my $http_host = "http://$domain"; 308 309 ## Edition mode 310 foreach my $param (@Sympa::ConfDef::params) { 311 next if $param->{obsolete}; 312 313 unless ($param->{'name'}) { 314 $title = gettext($param->{'gettext_id'}) 315 if $param->{'gettext_id'}; 316 next; 317 } 318 319 my $file = $param->{'file'}; 320 my $name = $param->{'name'}; 321 my $query = $param->{'gettext_id'} || ''; 322 $query = gettext($query) if $query; 323 my $advice = $param->{'gettext_comment'}; 324 $advice = gettext($advice) if $advice; 325 my $sample = $param->{'sample'}; 326 my $current_value; 327 328 next unless $file; 329 if ($file eq 'sympa.conf' or $file eq 'wwsympa.conf') { 330 $current_value = $Conf::Conf{$name}; 331 $current_value = '' unless defined $current_value; 332 } else { 333 next; 334 } 335 336 if ($title) { 337 ## write to conf file 338 push @new_sympa_conf, 339 sprintf "###\\\\\\\\ %s ////###\n\n", $title; 340 } 341 342 my $new_value = ''; 343 if ($options{batch}) { 344 if (exists $user_param{$name}) { 345 $new_value = $user_param{$name}; 346 } 347 } elsif ($param->{'edit'} and $param->{'edit'} eq '1') { 348 print "\n\n** $title **\n" if $title; 349 350 print "\n"; 351 print Sympa::Tools::Text::wrap_text($query || '', '* ', ' '); 352 print Sympa::Tools::Text::wrap_text($advice, ' ... ', ' ') 353 if $advice; 354 printf(gettext('%s [%s] : '), $name, $current_value); 355 $new_value = <STDIN>; 356 chomp $new_value; 357 } 358 if ($new_value eq '') { 359 $new_value = $current_value; 360 } 361 362 undef $title; 363 364 ## Skip empty parameters 365 next if $new_value eq '' and !$sample; 366 367 ## param is an ARRAY 368 if (ref($new_value) eq 'ARRAY') { 369 $new_value = join ',', @{$new_value}; 370 } 371 372 unless ($file eq 'sympa.conf' or $file eq 'wwsympa.conf') { 373 printf STDERR gettext("Incorrect parameter definition: %s\n"), 374 $file; 375 } 376 377 if ($new_value eq '') { 378 next unless $sample; 379 380 push @new_sympa_conf, 381 Sympa::Tools::Text::wrap_text($query, '## ', '## '); 382 383 if (defined $advice and length $advice) { 384 push @new_sympa_conf, 385 Sympa::Tools::Text::wrap_text($advice, '## ', '## '); 386 } 387 388 push @new_sympa_conf, "# $name\t$sample\n\n"; 389 } else { 390 push @new_sympa_conf, 391 Sympa::Tools::Text::wrap_text($query, '## ', '## '); 392 if (defined $advice and length $advice) { 393 push @new_sympa_conf, 394 Sympa::Tools::Text::wrap_text($advice, '## ', '## '); 395 } 396 397 if ($current_value ne $new_value) { 398 push @new_sympa_conf, "# was $name $current_value\n"; 399 $somechange = 1; 400 } 401 402 push @new_sympa_conf, "$name\t$new_value\n\n"; 403 } 404 } 405 406 if ($somechange) { 407 my @time = localtime time; 408 my $date = sprintf '%d%02d%02d%02d%02d%02d', 409 $time[5] + 1900, $time[4] + 1, @time[3, 2, 1, 0]; 410 411 ## Keep old config file 412 unless (rename $sympa_conf, $sympa_conf . '.' . $date) { 413 warn sprintf(gettext("Unable to rename %s : %s"), 414 $sympa_conf, $ERRNO); 415 } 416 417 ## Write new config file 418 my $umask = umask 037; 419 unless (open(SYMPA, "> $sympa_conf")) { 420 umask $umask; 421 die "$0: " 422 . sprintf(gettext("Unable to open %s : %s"), 423 $sympa_conf, $ERRNO) 424 . "\n"; 425 } 426 umask $umask; 427 chown [getpwnam(Sympa::Constants::USER)]->[2], 428 [getgrnam(Sympa::Constants::GROUP)]->[2], $sympa_conf; 429 430 print SYMPA @new_sympa_conf; 431 close SYMPA; 432 433 printf gettext( 434 "%s have been updated.\nPrevious versions have been saved as %s.\n" 435 ), $sympa_conf, "$sympa_conf.$date"; 436 } 437} 438 439sub check_cpan { 440 my %cpan_modules = CPANFile::load() or die; 441 442 print gettext( 443 "############################################################################## 444# This process will help you install all Perl (CPAN) modules required by Sympa 445# software. 446# Sympa requires from 50 to 65 additional Perl modules to run properly. 447# The whole installation process should take around 15 minutes. 448# You'll first have to configure the CPAN shell itself and select your 449# favourite CPAN server. 450# Note that you might prefer to install the required Perl modules using your 451# favourite DEB/RPM mechanism. 452# Feel free to interrupt the process if needed ; you can restart it safely 453# afterward. 454############################################################################## 455Press the Enter key to continue..." 456 ) . "\n"; 457 my $rep = <STDIN>; 458 459 print "\n"; 460 461 # Choose default DBD module if it has not been defined. 462 my $db_type; 463 if (open my $fh, '<', $sympa_conf) { 464 foreach my $line (<$fh>) { 465 if ($line =~ /\Adb_type\s+(\S*)/) { 466 $db_type = $1; 467 } 468 } 469 close $fh; 470 } 471 if ($db_type 472 and exists $cpan_modules{'DBD::' . $db_type}) { 473 $cpan_modules{'DBD::' . $db_type}->{mandatory} = 1; 474 } else { 475 my @dbd = ( 476 'MySQL/MariaDB' => 'DBD::mysql', 477 'PostgreSQL' => 'DBD::Pg', 478 'SQLite' => 'DBD::SQLite', 479 'Oracle' => 'DBD::Oracle', 480 ); 481 my $selected; 482 while (1) { 483 print "\n" 484 . gettext('Which RDBMS will you use for core database:') 485 . "\n"; 486 for (my $i = 0; $i < scalar @dbd; $i += 2) { 487 printf "%d: %s\n", $i / 2 + 1, $dbd[$i]; 488 } 489 printf gettext('-> Select RDBMS [1-%d] '), scalar @dbd / 2; 490 $selected = <STDIN>; 491 chomp $selected; 492 last 493 if $selected =~ /\A\d+\z/ 494 and 0 < $selected 495 and $selected * 2 <= scalar @dbd; 496 } 497 $cpan_modules{$dbd[$selected * 2 - 1]}->{mandatory} = 1; 498 } 499 500 ### REQ perl version 501 print "\n" . gettext('Checking for PERL version:') . "\n\n"; 502 # Compat. for perl < 5.10: $^V is not an object but a vector of integers. 503 my $rpv = eval 'v' . $cpan_modules{"perl"}{'required_version'} or die $@; 504 if ($^V ge $rpv) { 505 printf gettext('Your version of perl is OK (%s >= %s)') . "\n", $], 506 $cpan_modules{"perl"}{'required_version'}; 507 } else { 508 printf gettext( 509 "Your version of perl is TOO OLD (%s < %s)\nPlease INSTALL a new one !" 510 ) . "\n", $], $cpan_modules{"perl"}{'required_version'}; 511 } 512 513 print "\n" . gettext('Checking for REQUIRED modules:') . "\n\n"; 514 check_modules('y', \%cpan_modules, 'mandatory'); 515 print "\n" . gettext('Checking for OPTIONAL modules:') . "\n\n"; 516 check_modules('n', \%cpan_modules, 'optional'); 517 518 print gettext( 519 "******* NOTE ******* 520You can retrieve all theses modules from any CPAN server 521(for example ftp://ftp.pasteur.fr/pub/computing/CPAN/CPAN.html)" 522 ) . "\n"; 523###-------------------------- 524# reports modules status 525# $cpan_modules is the cpan_modules structure 526# $type is the type of modules (mandatory | optional) that should be installed 527###-------------------------- 528} 529 530sub check_modules { 531# my($default, $todo, $versions, $opt_features) = @_; 532 my ($default, $cpan_modules, $type) = @_; 533 534 printf "%s%s\n", gettext('perl module', -32), gettext('STATUS'); 535 printf "%-32s%s\n", gettext('-----------'), gettext('------'); 536 537 foreach my $mod (sort keys %$cpan_modules) { 538 539 ## Only check modules of the expected type 540 if ($type eq 'mandatory') { 541 next unless ($cpan_modules->{$mod}{mandatory}); 542 } elsif ($type eq 'optional') { 543 next if ($cpan_modules->{$mod}{mandatory}); 544 } 545 546 ## Skip perl itself to prevent a huge upgrade 547 next if ($mod eq 'perl'); 548 549 printf "%-32s", $mod; 550 551 eval "require $mod"; 552 if ($EVAL_ERROR) { 553 ### not installed 554 print gettext('was not found on this system.') . "\n"; 555 install_module($mod, {'default' => $default}, $cpan_modules); 556 } else { 557 my ($vs, $v); 558 $vs = "$mod" . "::VERSION"; 559 { 560 no strict 'refs'; 561 $v = $$vs; 562 } 563 564 my $rv = $cpan_modules->{$mod}{required_version} || "1.0"; 565 ### OK: check version 566 if ($v ge $rv) { 567 printf gettext('OK (%-6s >= %s)') . "\n", $v, $rv; 568 next; 569 } else { 570 printf gettext('version is too old (%s < %s)') . "\n", $v, 571 $rv; 572 printf gettext( 573 '>>>>>>> You must update "%s" to version "%s" <<<<<<.') 574 . "\n", $mod, 575 $cpan_modules->{$mod}{required_version}; 576 install_module($mod, {'default' => $default}, $cpan_modules); 577 } 578 } 579 } 580} 581 582##---------------------- 583# Install a CPAN module 584##---------------------- 585sub install_module { 586 return unless $with_CPAN; 587 588 my ($module, $options, $cpan_modules) = @_; 589 590 my $default = $options->{'default'}; 591 592 unless ($ENV{'FTP_PASSIVE'} and $ENV{'FTP_PASSIVE'} eq 1) { 593 $ENV{'FTP_PASSIVE'} = 1; 594 print "Setting FTP Passive mode\n"; 595 } 596 597 # This is required on RedHat 9 for DBD::mysql installation 598 my $lang; 599 if ($ENV{'LANG'} and $ENV{'LANG'} =~ /UTF-8/) { 600 $lang = $ENV{'LANG'}; 601 $ENV{'LANG'} = 'C'; 602 } 603 604 unless ($EUID == 0) { 605 printf gettext('## You need root privileges to install %s module. ##') 606 . "\n", $module; 607 print gettext( 608 '## Press the Enter key to continue checking modules. ##') 609 . "\n"; 610 my $t = <STDIN>; 611 return undef; 612 } 613 614 unless ($options->{'force'}) { 615 print Sympa::Tools::Text::wrap_text( 616 sprintf( 617 gettext('-> Usage of this module: %s') . "\n", 618 gettext($cpan_modules->{$module}{'gettext_id'}) 619 ), 620 '', ' ' 621 ) if ($cpan_modules->{$module}{'gettext_id'}); 622 print Sympa::Tools::Text::wrap_text( 623 sprintf( 624 gettext('-> Prerequisites: %s') . "\n", 625 gettext($cpan_modules->{$module}{'gettext_comment'}) 626 ), 627 '', ' ' 628 ) if ($cpan_modules->{$module}{'gettext_comment'}); 629 printf gettext('-> Install module %s ? [%s] '), $module, $default; 630 my $answer = <STDIN>; 631 chomp $answer; 632 $answer ||= $default; 633 return unless ($answer =~ /^y$/i); 634 } 635 636 $CPAN::Config->{'inactivity_timeout'} = 637 0; ## disable timeout to prevent timeout during modules installation 638 $CPAN::Config->{'colorize_output'} = 1; 639 $CPAN::Config->{'build_requires_install_policy'} = 640 'yes'; ## automatically installed prerequisites without asking 641 $CPAN::Config->{'prerequisites_policy'} = 642 'follow'; ## build prerequisites automatically 643 $CPAN::Config->{'load_module_verbosity'} = 644 'none'; ## minimum verbosity during module loading 645 $CPAN::Config->{'tar_verbosity'} = 646 'none'; ## minimum verbosity with tar command 647 648 # CPAN::Shell->clean($module) if ($options->{'force'}); 649 650 # CPAN::Shell->make($module); 651 # if ($options->{'force'}) { 652 # CPAN::Shell->force('test', $module); 653 # } else { 654 # CPAN::Shell->test($module); 655 # } 656 # # Could use CPAN::Shell->force('install') if make test failed 657 CPAN::Shell->install($module); 658 659 ## Check if module has been successfuly installed 660 unless (eval "require $module") { 661 662 ## Prevent recusive calls if already in force mode 663 if ($options->{'force'}) { 664 printf gettext( 665 "Installation of %s still FAILED. You should download the tar.gz from http://search.cpan.org and install it manually." 666 ), $module; 667 my $answer = <STDIN>; 668 } else { 669 printf gettext( 670 'Installation of %s FAILED. Do you want to force the installation of this module? (y/N) ' 671 ), $module; 672 my $answer = <STDIN>; 673 chomp $answer; 674 if ($answer =~ /^y/i) { 675 install_module($module, {'force' => 1}, $cpan_modules); 676 } 677 } 678 } 679 680 # Restore lang 681 $ENV{'LANG'} = $lang if $lang; 682} 683 684package CPANFile; 685 686use lib '--modulesdir--'; 687use strict; 688use warnings; 689 690our $description; 691our $is_optional; 692my %cpan_modules; 693 694sub feature { 695 shift; 696 local $description = shift; 697 local $is_optional = 1; 698 shift->(); 699} 700 701sub on { 702 return unless shift eq 'runtime'; 703 shift->(); 704} 705 706sub recommends { 707 local $is_optional = 1; 708 _depends(@_); 709} 710 711sub requires { 712 _depends(@_); 713} 714 715sub load { 716 do 'cpanfile'; 717 %cpan_modules; 718} 719 720sub _depends { 721 my $module = shift; 722 my $verreq = shift || '0'; 723 $verreq = [grep { !/[!<]/ } split /\s*,\s*/, $verreq]->[0]; 724 $verreq =~ s/\A[\s=>]+//; 725 $cpan_modules{$module} = { 726 required_version => $verreq, 727 ($is_optional ? () : (mandatory => 1)), 728 ($description ? (gettext_id => $description) : ()), 729 }; 730} 731 7321; 733__END__ 734 735=encoding utf-8 736 737=head1 NAME 738 739sympa_wizard, sympa_wizard.pl - Help Performing Sympa Initial Setup 740 741=head1 SYNOPSIS 742 743C<sympa_wizard.pl> 744S<[ C<--batch> [ I<key>=I<value> ... ] ]> 745S<[ C<--check> ]> 746S<[ C<--create> [ C<--target=>I<file> ] ]> 747S<[ C<--display> ]> 748S<[ C<-h, --help> ]> 749S<[ C<-v, --version> ]> 750 751=head1 OPTIONS 752 753=over 4 754 755=item C<sympa_wizard.pl> 756 757Edit current Sympa configuration. 758 759=item C<sympa_wizard.pl> C<--batch> I<key>=I<value> ... 760 761Edit in batch mode. 762Arguments would include pairs of parameter name and value. 763 764=item C<sympa_wizard.pl> C<--check> 765 766Check CPAN modules needed for running Sympa. 767 768=item C<sympa_wizard.pl> C<--create> [ C<--target> I<file> ] 769 770Creates a new F<sympa.conf> configuration file. 771 772=item C<sympa_wizard.pl> C<--display> 773 774Outputs all configuration parameters. 775 776=item C<sympa_wizard.pl> C<--help> 777 778Display usage instructions. 779 780=item C<sympa_wizard.pl> C<--version> 781 782Print version number. 783 784=back 785 786=head1 HISTORY 787 788This program was originally written by: 789 790=over 4 791 792=item Serge Aumont <sa@cru.fr> 793 794=item Olivier SalaE<252>n <os@cru.fr> 795 796=back 797 798C<--batch> and C<--display> options are added on Sympa 6.1.25 and 6.2.15. 799 800=cut 801