sympa.pl.in 43 KB
Newer Older
1
#!--PERL--
2
3
4
5
# -*- indent-tabs-mode: nil; -*-
# vim:ft=perl:et:sw=4
# $Id$

6
# Sympa - SYsteme de Multi-Postage Automatique
7
8
9
10
#
# Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel
# Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
# 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites
11
# Copyright (c) 2011, 2012, 2013, 2014, 2015 GIP RENATER
12
13
14
15
16
17
18
19
20
21
22
23
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
24
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
25

26
use lib split(/:/, $ENV{SYMPALIB} || ''), '--modulesdir--';
27
28
use strict;
use warnings;
29
use Digest::MD5;
30
use English qw(-no_match_vars);
31
32
use Fcntl qw();
use File::Basename qw();
sikeda's avatar
sikeda committed
33
use File::Copy qw();
34
use File::Path qw();
35
use Getopt::Long;
36
use Pod::Usage;
sikeda's avatar
sikeda committed
37
use POSIX qw();
root's avatar
root committed
38

39
use Sympa::Admin;
root's avatar
root committed
40
use Conf;
41
use Sympa::Config_XML;
42
use Sympa::Constants;
sikeda's avatar
sikeda committed
43
use Sympa::DatabaseManager;
44
use Sympa::Family;
45
use Sympa::Language;
46
use Sympa::List;
47
use Sympa::Log;
48
use Sympa::Mailer;
49
use Sympa::Report;
50
use Sympa::Spool;
51
use tools;
52
use Sympa::Tools::Data;
53
use Sympa::Tools::Password;
sikeda's avatar
sikeda committed
54
use Sympa::Upgrade;
55

56
## Init random engine
57
srand(time());
58

59
# Check options.
60
my %options;
61
62
unless (
    GetOptions(
63
64
        \%main::options,        'dump=s',
        'debug|d',              'log_level=s',
65
66
        'config|f=s',           'lang|l=s',
        'mail|m',               'help|h',
67
        'version|v',            'import=s',
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
        'make_alias_file',      'lowercase',
        'sync_list_db',         'md5_encode_password',
        'close_list=s',         'rename_list=s',
        'new_listname=s',       'new_listrobot=s',
        'purge_list=s',         'create_list',
        'instantiate_family=s', 'robot=s',
        'add_list=s',           'modify_list=s',
        'close_family=s',       'md5_digest=s',
        'change_user_email',    'current_email=s',
        'new_email=s',          'input_file=s',
        'sync_include=s',       'upgrade',
        'upgrade_shared',       'from=s',
        'to=s',                 'reload_list_config',
        'list=s',               'quiet',
        'close_unknown',        'test_database_message_buffer',
83
        'conf_2_db',            'export_list',
84
85
        'health_check',         'send_digest',
        'keep_digest',          'upgrade_config_location',
86
87
    )
    ) {
88
    pod2usage(-exitval => 1, -output => \*STDERR);
89
}
90
91
92
93
94
95
if ($main::options{'help'}) {
    pod2usage(0);
} elsif ($main::options{'version'}) {
    printf "Sympa %s\n", Sympa::Constants::VERSION;
    exit 0;
}
96

97
if ($main::options{'debug'}) {
98
    $main::options{'log_level'} = 2 unless $main::options{'log_level'};
99
}
100

101
102
103
my $log = Sympa::Log->instance;
$log->{log_to_stderr} = 'notice,err'
    if $main::options{'upgrade'}
104
105
106
        || $main::options{'reload_list_config'}
        || $main::options{'test_database_message_buffer'}
        || $main::options{'conf_2_db'};
107

108
if ($main::options{'upgrade_config_location'}) {
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
    my $config_file = Conf::get_sympa_conf();

    if (-f $config_file) {
        printf "Sympa configuration already located at %s\n", $config_file;
        exit 0;
    }

    my ($file, $dir, $suffix) = File::Basename::fileparse($config_file);
    my $old_dir = $dir;
    $old_dir =~ s/sympa\///;

    # Try to create config path if it does not exist
    unless (-d $dir) {
        my $error;
        File::Path::make_path(
            $dir,
            {   mode  => 0755,
                owner => 'sympa',
                group => 'sympa',
                error => \$error
            }
        );
        if (@$error) {
            my $diag = pop @$error;
            my ($target, $error) = %$diag;
            die "Unable to create $target: $error";
        }
    }

    # Check ownership of config folder
    my @stat = stat($dir);
    my $user = (getpwuid $stat[4])[0];
    if ($user ne 'sympa') {
        die
            "Config dir $dir exists but is not owned by sympa (owned by $user)";
    }

    # Check permissions on config folder
147
    if (($stat[2] & Fcntl::S_IRWXU()) != Fcntl::S_IRWXU()) {
148
149
150
151
152
153
154
155
156
157
158
        die
            "Config dir $dir exists, but sympa does not have rwx permissions on it";
    }

    # Move files from old location to new one
    opendir(my $dh, $old_dir) or die("Could not open $dir for reading");
    my @files = grep(/^(ww)?sympa\.conf.*$/, readdir($dh));
    closedir($dh);

    foreach my $file (@files) {
        unless (File::Copy::move("$old_dir/$file", "$dir/$file")) {
159
160
            die sprintf 'Could not move %s/%s to %s/%s: %s', $old_dir, $file,
                $dir, $file, $ERRNO;
161
162
163
164
165
        }
    }

    printf "Sympa configuration moved to $dir\n";
    exit 0;
166
167
168
169
170
171
172
} elsif ($main::options{'health_check'}) {
    ## Health check

    ## Load configuration file. Ignoring database config for now: it avoids
    ## trying to load a database that could not exist yet.
    unless (Conf::load(Conf::get_sympa_conf(), 'no_db')) {
        #FIXME: force reload
173
        die sprintf
174
175
176
177
178
            "Configuration file %s has errors.\n",
            Conf::get_sympa_conf();
    }

    ## Open the syslog and say we're read out stuff.
179
    $log->openlog(
180
181
182
183
        $Conf::Conf{'syslog'},
        $Conf::Conf{'log_socket_type'},
        service => 'sympa/health_check'
    );
184
185
186

    ## Setting log_level using conf unless it is set by calling option
    if ($main::options{'log_level'}) {
187
188
        $log->{level} = $main::options{'log_level'};
        $log->syslog(
189
190
191
192
193
            'info',
            'Configuration file read, log level set using options: %s',
            $main::options{'log_level'}
        );
    } else {
194
195
        $log->{level} = $Conf::Conf{'log_level'};
        $log->syslog(
196
197
198
199
200
201
            'info',
            'Configuration file read, default log level %s',
            $Conf::Conf{'log_level'}
        );
    }

202
    if (Conf::cookie_changed()) {
203
204
205
206
207
        die sprintf
            'sympa.conf/cookie parameter has changed. You may have severe inconsitencies into password storage. Restore previous cookie or write some tool to re-encrypt password in database and check spools contents (look at %s/cookies.history file).',
            $Conf::Conf{'etc'};
    }

208
    ## Check database connectivity and probe database
sikeda's avatar
sikeda committed
209
    unless (Sympa::DatabaseManager::probe_db()) {
210
        die sprintf
211
212
213
214
215
216
            "Database %s defined in sympa.conf has not the right structure or is unreachable. verify db_xxx parameters in sympa.conf\n",
            $Conf::Conf{'db_name'};
    }

    ## Now trying to load full config (including database)
    unless (Conf::load()) {    #FIXME: load Site, then robot cache
217
        die sprintf
218
219
220
221
222
223
            "Unable to load Sympa configuration, file %s or any of the virtual host robot.conf files contain errors. Exiting.\n",
            Conf::get_sympa_conf();
    }

    ## Change working directory.
    if (!chdir($Conf::Conf{'home'})) {
224
        printf STDERR "Can't chdir to %s: %s\n", $Conf::Conf{'home'}, $ERRNO;
225
226
227
228
229
230
231
232
233
234
        exit 1;
    }

    ## Check for several files.
    unless (Conf::checkfiles_as_root()) {
        printf STDERR "Missing files.\n";
        exit 1;
    }

    ## Check that the data structure is uptodate
sikeda's avatar
sikeda committed
235
    unless (Conf::data_structure_uptodate()) {
236
237
238
239
240
        printf STDERR
            "Data structure was not updated; you should run sympa.pl --upgrade to run the upgrade process.\n";
        exit 1;
    }

241
242
243
    exit 0;
}

244
my $default_lang;
serge.aumont's avatar
   
serge.aumont committed
245

246
my $language = Sympa::Language->instance;
247
my $mailer   = Sympa::Mailer->instance;
248

sikeda's avatar
sikeda committed
249
250
_load();

251
$log->openlog($Conf::Conf{'syslog'}, $Conf::Conf{'log_socket_type'});
sikeda's avatar
sikeda committed
252
253
254
255
256
257
258
259
260
261
262
263
264

# Set the User ID & Group ID for the process
$GID = $EGID = (getgrnam(Sympa::Constants::GROUP))[2];
$UID = $EUID = (getpwnam(Sympa::Constants::USER))[2];

## Required on FreeBSD to change ALL IDs
## (effective UID + real UID + saved UID)
POSIX::setuid((getpwnam(Sympa::Constants::USER))[2]);
POSIX::setgid((getgrnam(Sympa::Constants::GROUP))[2]);

## Check if the UID has correctly been set (useful on OS X)
unless (($GID == (getgrnam(Sympa::Constants::GROUP))[2])
    && ($UID == (getpwnam(Sympa::Constants::USER))[2])) {
265
266
    die
        "Failed to change process user ID and group ID. Note that on some OS Perl scripts can't change their real UID. In such circumstances Sympa should be run via sudo.\n";
sikeda's avatar
sikeda committed
267
}
268

sikeda's avatar
sikeda committed
269
270
# Sets the UMASK
umask(oct($Conf::Conf{'umask'}));
271

sikeda's avatar
sikeda committed
272
## Most initializations have now been done.
273
$log->syslog('notice', 'Sympa %s Started', Sympa::Constants::VERSION());
root's avatar
root committed
274

sikeda's avatar
sikeda committed
275
276
# Check for several files.
#FIXME: This would be done in --health_check mode.
277
278
279
unless (Conf::checkfiles()) {
    die "Missing files.\n";
    ## No return.
sikeda's avatar
sikeda committed
280
}
root's avatar
root committed
281

282
283
# Daemon called for dumping subscribers list
if ($main::options{'dump'}) {
284

285
286
287
288
    my ($all_lists, $list);
    if ($main::options{'dump'} eq 'ALL') {
        $all_lists = Sympa::List::get_lists('*');
    } else {
289

290
291
        ## The parameter can be a list address
        unless ($main::options{'dump'} =~ /\@/) {
292
            $log->syslog('err', 'Incorrect list address %s',
293
                $main::options{'dump'});
294

295
296
            exit;
        }
297

298
299
        my $list = Sympa::List->new($main::options{'dump'});
        unless (defined $list) {
300
            $log->syslog('err', 'Unknown list %s', $main::options{'dump'});
301

302
            exit;
303
        }
304
305
        push @$all_lists, $list;
    }
306

307
308
309
    foreach my $list (@$all_lists) {
        unless ($list->dump()) {
            print STDERR "Could not dump list(s)\n";
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
    exit 0;
} elsif ($main::options{'make_alias_file'}) {
    my $robots = $main::options{'robot'} || '*';
    my @robots;
    if ($robots eq '*') {
        @robots = Sympa::List::get_robots();
    } else {
        @robots = grep { length $_ } split(/[\s,]+/, $robots);
    }
    exit 0 unless @robots;

    # There may be multiple aliases files.  Give each of them suffixed
    # name.
    my ($basename, %robots_of, %sympa_aliases);
    $basename = sprintf '%s/sympa_aliases.%s', $Conf::Conf{'tmpdir'}, $PID;

    foreach my $robot (@robots) {
        my $file = Conf::get_robot_conf($robot, 'sendmail_aliases');
        $robots_of{$file} ||= [];
        push @{$robots_of{$file}}, $robot;
    }
    if (1 < scalar(keys %robots_of)) {
        my $i = 0;
        %sympa_aliases = map {
            $i++;
            map { $_ => sprintf('%s.%03d', $basename, $i) } @{$robots_of{$_}}
        } sort keys %robots_of;
    } else {
        %sympa_aliases = map { $_ => $basename } @robots;
    }
343

344
345
346
347
348
349
    # Create files.
    foreach my $sympa_aliases (values %sympa_aliases) {
        my $fh;
        unless (open $fh, '>', $sympa_aliases) {    # truncate if exists
            printf STDERR "Unable to create %s: %s\n", $sympa_aliases, $ERRNO;
            exit 1;
350
        }
351
352
        close $fh;
    }
353

354
355
356
357
358
    # Write files.
    foreach my $robot (sort @robots) {
        my $all_lists     = Sympa::List::get_lists($robot);
        my $alias_manager = Conf::get_robot_conf($robot, 'alias_manager');
        my $sympa_aliases = $sympa_aliases{$robot};
359

360
361
362
363
        my $fh;
        unless (open $fh, '>>', $sympa_aliases) {    # append
            printf STDERR "Unable to create %s: %s\n", $sympa_aliases, $ERRNO;
            exit 1;
364
        }
365
366
367
368
369
        printf $fh "#\n#\tAliases for all Sympa lists open on %s\n#\n",
            $robot;
        close $fh;
        foreach my $list (@{$all_lists || []}) {
            next unless $list->{'admin'}{'status'} eq 'open';
370

371
372
            system($alias_manager, 'add', $list->{'name'}, $list->{'domain'},
                $sympa_aliases);
373
        }
374
    }
salaun's avatar
salaun committed
375

376
377
378
379
380
381
382
383
384
385
386
387
388
    if (1 < scalar(keys %robots_of)) {
        printf
            "Sympa aliases files %s.??? were made.  You probably need to install them in your SMTP engine.\n",
            $basename;
    } else {
        printf
            "Sympa aliases file %s was made.  You probably need to install it in your SMTP engine.\n",
            $basename;
    }
    exit 0;
} elsif ($main::options{'md5_digest'}) {
    my $md5 = Digest::MD5::md5_hex($main::options{'md5_digest'});
    printf "md5 digest : $md5 \n";
389

390
391
392
    exit 0;
} elsif ($main::options{'import'}) {
    my ($list, $total);
salaun's avatar
salaun committed
393

394
395
    ## The parameter should be a list address
    unless ($main::options{'import'} =~ /\@/) {
396
        $log->syslog(
397
398
399
400
401
402
            'err',
            'Incorrect list address %s',
            $main::options{'import'}
        );
        exit;
    }
salaun's avatar
salaun committed
403

404
    unless ($list = Sympa::List->new($main::options{'import'})) {
405
        $log->syslog('err', 'Unknown list name %s', $main::options{'import'});
406
407
        exit 1;
    }
salaun's avatar
salaun committed
408

409
410
411
412
    ## Read imported data from STDIN
    while (<STDIN>) {
        next if /^\s*$/;
        next if /^\s*\#/;
salaun's avatar
salaun committed
413

414
415
        unless (/^\s*((\S+|\".*\")@\S+)(\s*(\S.*))?\s*$/) {
            printf STDERR "Not an email address: %s\n", $_;
416
        }
salaun's avatar
salaun committed
417

418
419
420
421
422
423
424
        my $email = lc($1);
        my $gecos = $4;
        my $u;
        my $defaults = $list->get_default_user_options();
        %{$u} = %{$defaults};
        $u->{'email'} = $email;
        $u->{'gecos'} = $gecos;
serge.aumont's avatar
   
serge.aumont committed
425

426
427
428
429
430
        $list->add_list_member($u);
        if (defined $list->{'add_outcome'}{'errors'}) {
            printf STDERR "\nCould not add %s. %s\n", $email,
                $list->{'add_outcome'}{'errors'}{'error_message'};
            next;
431
        }
432
        print STDERR '+';
serge.aumont's avatar
   
serge.aumont committed
433

434
435
        $total++;
    }
serge.aumont's avatar
   
serge.aumont committed
436

437
    printf STDERR "Total imported subscribers: %d\n", $total;
438

439
440
441
    exit 0;
} elsif ($main::options{'md5_encode_password'}) {
    printf STDERR "Obsoleted.  Use upgrade_sympa_password.pl.\n";
442

443
444
445
446
    exit 0;
} elsif ($main::options{'lowercase'}) {
    print STDERR "Working on user_table...\n";
    my $total = Sympa::List::lowercase_field('user_table', 'email_user');
447

448
449
450
451
452
453
    if (defined $total) {
        print STDERR "Working on subscriber_table...\n";
        my $total_sub = Sympa::List::lowercase_field('subscriber_table',
            'user_subscriber');
        if (defined $total_sub) {
            $total += $total_sub;
454
        }
455
    }
456

457
458
459
460
    unless (defined $total) {
        print STDERR "Could not work on dabatase.\n";
        exit 1;
    }
461

462
    printf STDERR "Total lowercased rows: %d\n", $total;
sympa-authors's avatar
sympa-authors committed
463

464
465
    exit 0;
} elsif ($main::options{'close_list'}) {
sympa-authors's avatar
sympa-authors committed
466

467
468
    my ($listname, $robotname) = split /\@/, $main::options{'close_list'};
    my $list = Sympa::List->new($listname, $robotname);
sympa-authors's avatar
sympa-authors committed
469

470
471
472
473
    unless (defined $list) {
        print STDERR "Incorrect list name $main::options{'close_list'}\n";
        exit 1;
    }
sympa-authors's avatar
sympa-authors committed
474

475
476
477
478
479
480
481
482
483
484
485
486
    if ($list->{'admin'}{'family_name'}) {
        unless (
            $list->set_status_family_closed('close_list', $list->{'name'})) {
            print STDERR
                "Could not close list $main::options{'close_list'}\n";
            exit 1;
        }
    } else {
        unless ($list->close_list()) {
            print STDERR
                "Could not close list $main::options{'close_list'}\n";
            exit 1;
487
        }
488
    }
sympa-authors's avatar
sympa-authors committed
489

490
491
    printf STDOUT "List %s has been closed, aliases have been removed\n",
        $list->{'name'};
sympa-authors's avatar
sympa-authors committed
492

493
494
    exit 0;
} elsif ($main::options{'change_user_email'}) {
495

496
497
498
499
500
    unless ($main::options{'current_email'}
        && $main::options{'new_email'}) {
        print STDERR "Missing current_email or new_email parameter\n";
        exit 1;
    }
501

502
503
504
505
506
507
508
509
510
511
512
    foreach my $robot (Sympa::List::get_robots()) {
        printf STDOUT "Doing processing for virtual robot %s...\n", $robot;
        my ($status, $failed_for) = Sympa::Admin::change_user_email(
            current_email => $main::options{'current_email'},
            new_email     => $main::options{'new_email'},
            robot         => $robot
        );
        unless (defined $status) {
            printf STDERR
                "Failed to change user email address in virtual robot %s'}\n",
                $robot;
513
514
            exit 1;
        }
515

516
517
518
519
        foreach my $failed_list (@$failed_for) {
            printf STDERR
                "Failed to change user email address for list %s'}\n",
                $failed_list->{'name'};
520
        }
521
    }
522

523
524
    printf STDOUT "Email address %s has been changed to %s\n",
        $main::options{'current_email'}, $main::options{'new_email'};
525

526
527
    exit 0;
} elsif ($main::options{'purge_list'}) {
528

529
530
    my ($listname, $robotname) = split /\@/, $main::options{'purge_list'};
    my $list = Sympa::List->new($listname, $robotname);
531

532
533
534
535
    unless (defined $list) {
        print STDERR "Incorrect list name $main::options{'purge_list'}\n";
        exit 1;
    }
536

537
538
539
    if ($list->{'admin'}{'family_name'}) {
        unless (
            $list->set_status_family_closed('purge_list', $list->{'name'})) {
540
            print STDERR
541
                "Could not purge list $main::options{'purge_list'}\n";
542
543
            exit 1;
        }
544
545
546
547
    } else {
        unless ($list->purge()) {
            print STDERR
                "Could not purge list $main::options{'close_list'}\n";
548
549
            exit 1;
        }
550
    }
551

552
553
    printf STDOUT "List %s has been closed, aliases have been removed\n",
        $list->{'name'};
554

555
556
    exit 0;
} elsif ($main::options{'rename_list'}) {
557

558
    ## TODO A completer
559

560
561
    my ($listname, $robotname) = split /\@/, $main::options{'rename_list'};
    my $list = Sympa::List->new($listname, $robotname);
562

563
564
    unless (defined $list) {
        print STDERR "Incorrect list name $main::options{'rename_list'}\n";
565
        exit 1;
566
    }
567

568
569
    unless ($main::options{'new_listname'}) {
        print STDERR "Missing parameter new_listname\n";
570
        exit 1;
571
    }
572

573
574
575
576
    unless ($main::options{'new_listrobot'}) {
        print STDERR "Missing parameter new_listrobot\n";
        exit 1;
    }
577

578
579
    my ($new_listname, $new_robotname) =
        ($main::options{'new_listname'}, $main::options{'new_listrobot'});
580

581
582
583
584
585
586
587
    my $result = Sympa::Admin::rename_list(
        list         => $list,
        new_listname => $new_listname,
        new_robot    => $new_robotname,
        options      => {'skip_authz' => 1},
        user_email   => 'listmaster@' . $robotname,
    );
588

589
590
591
592
593
594
    unless ($result == 1) {
        printf STDERR "Could not rename list %s to %s: %s\@%s\n",
            $main::options{'rename_list'},   $main::options{'new_listname'},
            $main::options{'new_listrobot'}, $result;
        exit 1;
    }
595

596
597
598
    printf STDOUT "List %s has been renamed to %s\@%s\n",
        $main::options{'rename_list'}, $main::options{'new_listname'},
        $main::options{'new_listrobot'};
599

600
    exit 0;
601

602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
} elsif ($main::options{'test_database_message_buffer'}) {
    printf
        "Deprecated.  Size of messages no longer limited by database packet size.\n";
    exit 1;
} elsif ($main::options{'conf_2_db'}) {

    printf
        "Sympa is going to store %s in database conf_table. This operation do NOT remove original files\n",
        Conf::get_sympa_conf();
    if (Conf::conf_2_db()) {
        printf "Done";
    } else {
        printf "an error occur";
    }
    exit 1;
617

618
619
} elsif ($main::options{'create_list'}) {
    my $robot = $main::options{'robot'} || $Conf::Conf{'host'};
620

621
622
623
624
    unless ($main::options{'input_file'}) {
        print STDERR "Error : missing 'input_file' parameter\n";
        exit 1;
    }
625

626
627
628
629
    unless (open INFILE, $main::options{'input_file'}) {
        print STDERR "Unable to open $main::options{'input_file'}) file";
        exit 1;
    }
630

631
632
633
634
635
    my $config = Sympa::Config_XML->new(\*INFILE);
    unless (defined $config->createHash()) {
        print STDERR "Error in representation data with these xml data\n";
        exit 1;
    }
636

637
    my $hash = $config->getHash();
638

639
    close INFILE;
640

641
642
643
644
645
646
647
    # Check length.
    if ($hash->{'config'}{'listname'}
        and Sympa::Constants::LIST_LEN() <
        length($hash->{'config'}{'listname'})) {
        print STDERR "Too long list name\n";
        exit 1;
    }
648

649
650
651
652
653
654
655
    my $resul =
        Sympa::Admin::create_list_old($hash->{'config'}, $hash->{'type'},
        $robot, "command_line");
    unless (defined $resul) {
        print STDERR "Could not create list with these xml data\n";
        exit 1;
    }
656

657
658
659
660
661
662
    if (!defined($resul->{'aliases'}) || $resul->{'aliases'} == 1) {
        printf STDOUT "List has been created \n";
        exit 0;
    } else {
        printf STDOUT
            "List has been created, required aliases :\n $resul->{'aliases'} \n";
663
        exit 0;
664
665
666
    }
} elsif ($main::options{'instantiate_family'}) {
    my $robot = $main::options{'robot'} || $Conf::Conf{'host'};
667

668
669
670
671
672
673
674
675
676
677
678
    my $family_name;
    unless ($family_name = $main::options{'instantiate_family'}) {
        print STDERR "Error : missing family parameter\n";
        exit 1;
    }
    my $family;
    unless ($family = Sympa::Family->new($family_name, $robot)) {
        print STDERR
            "The family $family_name does not exist, impossible instantiation\n";
        exit 1;
    }
679

680
681
682
683
    unless ($main::options{'input_file'}) {
        print STDERR "Error : missing input_file parameter\n";
        exit 1;
    }
684

685
686
687
688
    unless (-r $main::options{'input_file'}) {
        print STDERR "Unable to read $main::options{'input_file'} file";
        exit 1;
    }
689

690
691
692
693
694
695
696
697
698
    unless (
        $family->instantiate(
            $main::options{'input_file'},
            $main::options{'close_unknown'}
        )
        ) {
        print STDERR "\nImpossible family instantiation : action stopped \n";
        exit 1;
    }
699

700
701
702
    my %result;
    my $err = $family->get_instantiation_results(\%result);
    close INFILE;
703

704
705
706
707
708
709
710
    unless ($main::options{'quiet'}) {
        print STDOUT "@{$result{'info'}}";
        print STDOUT "@{$result{'warn'}}";
    }
    if ($err) {
        print STDERR "@{$result{'errors'}}";
    }
711

712
713
    exit 0;
} elsif ($main::options{'add_list'}) {
714

715
    my $robot = $main::options{'robot'} || $Conf::Conf{'host'};
716

717
718
719
720
721
    my $family_name;
    unless ($family_name = $main::options{'add_list'}) {
        print STDERR "Error : missing family parameter\n";
        exit 1;
    }
722

723
724
    print STDOUT
        "\n************************************************************\n";
725

726
727
728
729
730
731
    my $family;
    unless ($family = Sympa::Family->new($family_name, $robot)) {
        print STDERR
            "The family $family_name does not exist, impossible to add a list\n";
        exit 1;
    }
732

733
734
735
736
    unless ($main::options{'input_file'}) {
        print STDERR "Error : missing 'input_file' parameter\n";
        exit 1;
    }
737

738
739
740
741
    unless (open INFILE, $main::options{'input_file'}) {
        print STDERR "\n Impossible to open input file  : $ERRNO \n";
        exit 1;
    }
742

743
744
745
746
747
748
    my $result;
    unless ($result = $family->add_list(\*INFILE)) {
        print STDERR
            "\nImpossible to add a list to the family : action stopped \n";
        exit 1;
    }
749

750
751
    print STDOUT
        "\n************************************************************\n";
752

753
754
755
756
757
758
    unless (defined $result->{'ok'}) {
        printf STDERR "\n%s\n", join("\n", @{$result->{'string_info'}});
        print STDERR "\n The action has been stopped because of error :\n";
        printf STDERR "\n%s\n", join("\n", @{$result->{'string_error'}});
        exit 1;
    }
759

760
    close INFILE;
761

762
763
764
    print STDOUT "\n%s\n", join("\n", @{$result->{'string_info'}});
    exit 0;
} elsif ($main::options{'sync_include'}) {
765

766
    my $list = Sympa::List->new($main::options{'sync_include'});
767

768
769
770
771
    unless (defined $list) {
        print STDERR "Incorrect list name $main::options{'sync_include'}\n";
        exit 1;
    }
772

773
774
775
776
    unless (defined $list->sync_include()) {
        print STDERR "Failed to synchronize list members\n";
        exit 1;
    }
777

778
779
780
781
782
    printf "Members of list %s have been successfully update.\n",
        $list->get_list_address();
    exit 0;
## Migration from one version to another
} elsif ($main::options{'upgrade'}) {
783

784
    $log->syslog('notice', "Upgrade process...");
785

786
787
    $main::options{'from'} ||= Sympa::Upgrade::get_previous_version();
    $main::options{'to'}   ||= Sympa::Constants::VERSION;
788

789
    if ($main::options{'from'} eq $main::options{'to'}) {
790
        $log->syslog('err', 'Current version: %s; no upgrade is required',
791
792
793
            $main::options{'to'});
        exit 0;
    } else {
794
        $log->syslog('notice', "Upgrading from %s to %s...",
795
796
            $main::options{'from'}, $main::options{'to'});
    }
797

798
799
800
    unless (
        Sympa::Upgrade::upgrade($main::options{'from'}, $main::options{'to'}))
    {
801
        $log->syslog('err', "Migration from %s to %s failed",
802
803
804
            $main::options{'from'}, $main::options{'to'});
        exit 1;
    }
805

806
    $log->syslog('notice', 'Upgrade process finished');
807
    Sympa::Upgrade::update_version();
808

809
    exit 0;
810

811
812
813
## rename file names that may be incorrectly encoded because of previous Sympa
## versions
} elsif ($main::options{'upgrade_shared'}) {
814

815
    $log->syslog('notice', "Upgrade shared process...");
816

817
    my $listname;
818
    my $robot;
819

820
    unless (($main::options{'list'}) || ($main::options{'robot'})) {
821
        $log->syslog('err',
822
823
            "listname and domain are required, use --list= --robot= options");
        exit 0;
824
    }
825
826
    $listname = $main::options{'list'};
    $robot    = $main::options{'robot'};
827

828
    $log->syslog('notice', "Upgrading share for list=%s robot=%s",
829
        $listname, $robot);
830

831
    my $list = Sympa::List->new($listname, $robot);
832

833
834
835
836
837
    unless (defined $list) {
        printf STDERR "Incorrect list or domain name : %s %s\n",
            $listname, $robot;
        exit 1;
    }
838

839
    if (-d $list->{'dir'} . '/shared') {
840
        $log->syslog(
841
842
843
844
            'notice',
            'Processing list %s...',
            $list->get_list_address()
        );
845

846
847
848
849
850
851
852
853
854
        ## Determine default lang for this list
        ## It should tell us what character encoding was used for
        ## filenames
        $language->set_lang(
            $list->{'admin'}{'lang'},
            Conf::get_robot_conf($robot, 'lang'),
            $default_lang
        );
        my $list_encoding = tools::lang2charset($language->get_lang);
855

856
857
        my $count = tools::qencode_hierarchy($list->{'dir'} . '/shared',
            $list_encoding);
858

859
        if ($count) {
860
            $log->syslog('notice', 'List %s: %d filenames has been changed',
861
                $list->{'name'}, $count);
862
        }
863
    }
864
    $log->syslog('notice', 'Upgrade_shared process finished');
865

866
867
868
869
    exit 0;

} elsif ($main::options{'reload_list_config'}) {
    if ($main::options{'list'}) {
870
        $log->syslog('notice', 'Loading list %s...', $main::options{'list'});
871
872
873
874
875
876
877
878
879
        my $list =
            Sympa::List->new($main::options{'list'}, '',
            {'reload_config' => 1});
        unless (defined $list) {
            print STDERR
                "Error : incorrect list name '$main::options{'list'}'\n";
            exit 1;
        }
    } else {
880
        $log->syslog('notice', "Loading ALL lists...");
881
        my $all_lists = Sympa::List::get_lists('*', 'reload_config' => 1);
882
    }
883
    $log->syslog('notice', '...Done.');
884

885
886
    exit 0;
}
887

888
889
##########################################
elsif ($main::options{'modify_list'}) {
890

891
    my $robot = $main::options{'robot'} || $Conf::Conf{'host'};
892

893
894
895
896
897
    my $family_name;
    unless ($family_name = $main::options{'modify_list'}) {
        print STDERR "Error : missing family parameter\n";
        exit 1;
    }
898

899
900
    print STDOUT
        "\n************************************************************\n";
901

902
903
904
905
906
907
    my $family;
    unless ($family = Sympa::Family->new($family_name, $robot)) {
        print STDERR
            "The family $family_name does not exist, impossible to modify the list.\n";
        exit 1;
    }
908

909
910
911
912
    unless ($main::options{'input_file'}) {
        print STDERR "Error : missing input_file parameter\n";
        exit 1;
    }
913

914
915
916
917
    unless (open INFILE, $main::options{'input_file'}) {
        print STDERR "Unable to open $main::options{'input_file'}) file";
        exit 1;
    }
918

919
920
921
922
923
924
    my $result;
    unless ($result = $family->modify_list(\*INFILE)) {
        print STDERR
            "\nImpossible to modify the family list : action stopped. \n";
        exit 1;
    }
925

926
927
    print STDOUT
        "\n************************************************************\n";
928

929
930
931
932
933
934
    unless (defined $result->{'ok'}) {
        printf STDERR "\n%s\n", join("\n", @{$result->{'string_info'}});
        print STDERR "\nThe action has been stopped because of error :\n";
        printf STDERR "\n%s\n", join("\n", @{$result->{'string_error'}});
        exit 1;
    }
935

936
    close INFILE;
937

938
939
940
    printf STDOUT "\n%s\n", join("\n", @{$result->{'string_info'}});
    exit 0;
}
941

942
943
##########################################
elsif ($main::options{'close_family'}) {
944

945
    my $robot = $main::options{'robot'} || $Conf::Conf{'host'};
946

947
948
949
950
951
952
953
954
955
956
    my $family_name;
    unless ($family_name = $main::options{'close_family'}) {
        pod2usage(-exitval => 1, -output => \*STDERR);
    }
    my $family;
    unless ($family = Sympa::Family->new($family_name, $robot)) {
        print STDERR
            "The family $family_name does not exist, impossible family closure\n";
        exit 1;
    }
957

958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
    my $string;
    unless ($string = $family->close_family()) {
        print STDERR "\nImpossible family closure : action stopped \n";
        exit 1;
    }

    print STDOUT $string;
    exit 0;
}
##########################################
elsif ($main::options{'sync_list_db'}) {
    unless ($Conf::Conf{'db_list_cache'} eq 'on') {
        print STDOUT "\nSympa not configured to use database list caching \n";
        exit 1;
    }