sympa.pl.in 66.2 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, 2016, 2017 GIP RENATER
12
13
# Copyright 2017, 2018, 2019, 2020 The Sympa Community. See the AUTHORS.md
# file at the top-level directory of this distribution and at
14
# <https://github.com/sympa-community/sympa.git>.
15
16
17
18
19
20
21
22
23
24
25
26
#
# 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
27
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
28

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

IKEDA Soji's avatar
IKEDA Soji committed
42
use Sympa;
43
use Sympa::Aliases;
root's avatar
root committed
44
use Conf;
45
use Sympa::Constants;
sikeda's avatar
sikeda committed
46
use Sympa::DatabaseManager;
47
use Sympa::Family;
48
use Sympa::Language;
49
use Sympa::List;
50
use Sympa::Log;
51
use Sympa::Mailer;
52
use Sympa::Message;
53
use Sympa::Spindle::ProcessDigest;
54
use Sympa::Spindle::ProcessRequest;
55
use Sympa::Spool::Archive;
IKEDA Soji's avatar
IKEDA Soji committed
56
use Sympa::Template;
57
use Sympa::Tools::Data;
sikeda's avatar
sikeda committed
58
use Sympa::Upgrade;
59

60
## Init random engine
61
srand(time());
62

63
# Check options.
64
my %options;
65
66
unless (
    GetOptions(
Luc Didry's avatar
Luc Didry committed
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
        \%main::options,                'dump=s',
        'debug|d',                      'log_level=s',
        'config|f=s',                   'lang|l=s',
        'mail|m',                       'help|h',
        'version|v',                    'import=s',
        'make_alias_file',              'lowercase',
        'sync_list_db',                 'md5_encode_password',
        'close_list=s',                 'rename_list=s',
        'copy_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', 'conf_2_db',
        'export_list',                  'health_check',
        'send_digest',                  'keep_digest',
        'upgrade_config_location',      'role=s',
        'dump_users',                   'restore_users',
92
        'open_list=s',                  'show_pending_lists=s',
93
        'notify',                       'rebuildarc=s'
94
    )
Luc Didry's avatar
Luc Didry committed
95
) {
96
    pod2usage(-exitval => 1, -output => \*STDERR);
97
}
98
99
100
101
102
103
if ($main::options{'help'}) {
    pod2usage(0);
} elsif ($main::options{'version'}) {
    printf "Sympa %s\n", Sympa::Constants::VERSION;
    exit 0;
}
104
$Conf::sympa_config = $main::options{config};
105

106
if ($main::options{'debug'}) {
107
    $main::options{'log_level'} = 2 unless $main::options{'log_level'};
108
}
109

110
111
112
my $log = Sympa::Log->instance;
$log->{log_to_stderr} = 'notice,err'
    if $main::options{'upgrade'}
113
114
115
    || $main::options{'reload_list_config'}
    || $main::options{'test_database_message_buffer'}
    || $main::options{'conf_2_db'};
116

117
if ($main::options{'upgrade_config_location'}) {
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
    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,
135
136
                owner => Sympa::Constants::USER(),
                group => Sympa::Constants::GROUP(),
137
138
139
140
141
142
143
144
145
146
147
148
149
                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];
150
151
152
153
    if ($user ne Sympa::Constants::USER()) {
        die sprintf
            "Config dir %s exists but is not owned by %s (owned by %s).\n",
            $dir, Sympa::Constants::USER(), $user;
154
155
156
    }

    # Check permissions on config folder
157
    if (($stat[2] & Fcntl::S_IRWXU()) != Fcntl::S_IRWXU()) {
158
159
160
161
162
163
164
165
166
167
168
        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")) {
169
170
            die sprintf 'Could not move %s/%s to %s/%s: %s', $old_dir, $file,
                $dir, $file, $ERRNO;
171
172
173
        }
    }

174
    printf "Sympa configuration moved to %s\n", $dir;
175
    exit 0;
176
177
178
179
180
181
182
} 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
183
        die sprintf
184
185
186
187
188
            "Configuration file %s has errors.\n",
            Conf::get_sympa_conf();
    }

    ## Open the syslog and say we're read out stuff.
189
    $log->openlog(
190
191
192
193
        $Conf::Conf{'syslog'},
        $Conf::Conf{'log_socket_type'},
        service => 'sympa/health_check'
    );
194
195
196

    ## Setting log_level using conf unless it is set by calling option
    if ($main::options{'log_level'}) {
197
198
        $log->{level} = $main::options{'log_level'};
        $log->syslog(
199
200
201
202
203
            'info',
            'Configuration file read, log level set using options: %s',
            $main::options{'log_level'}
        );
    } else {
204
205
        $log->{level} = $Conf::Conf{'log_level'};
        $log->syslog(
206
207
208
209
210
211
            'info',
            'Configuration file read, default log level %s',
            $Conf::Conf{'log_level'}
        );
    }

212
    if (Conf::cookie_changed()) {
213
214
215
216
217
        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'};
    }

218
219
220
221
222
223
224
    ## Check if db_type is not the boilerplate one
    if ($Conf::Conf{'db_type'} eq '(You must define this parameter)') {
        die sprintf
            "Database type \"%s\" defined in sympa.conf is the boilerplate one and obviously incorrect. Verify db_xxx parameters in sympa.conf\n",
            $Conf::Conf{'db_type'};
    }

225
226
227
228
229
230
231
    ## Preliminary check of db_type
    unless ($Conf::Conf{'db_type'} and $Conf::Conf{'db_type'} =~ /\A\w+\z/) {
        die sprintf
            "Database type \"%s\" defined in sympa.conf seems incorrect. Verify db_xxx parameters in sympa.conf\n",
            $Conf::Conf{'db_type'};
    }

232
    ## Check database connectivity and probe database
sikeda's avatar
sikeda committed
233
    unless (Sympa::DatabaseManager::probe_db()) {
234
        die sprintf
235
            "Database %s defined in sympa.conf has not the right structure or is unreachable. Verify db_xxx parameters in sympa.conf\n",
236
237
238
239
240
            $Conf::Conf{'db_name'};
    }

    ## Now trying to load full config (including database)
    unless (Conf::load()) {    #FIXME: load Site, then robot cache
241
        die sprintf
242
243
244
245
246
247
            "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'})) {
248
        printf STDERR "Can't chdir to %s: %s\n", $Conf::Conf{'home'}, $ERRNO;
249
250
251
252
253
254
255
256
257
258
        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
259
    unless (Conf::data_structure_uptodate()) {
260
        printf STDOUT
261
262
263
            "Data structure was not updated; you should run sympa.pl --upgrade to run the upgrade process.\n";
    }

264
265
266
    exit 0;
}

267
my $default_lang;
serge.aumont's avatar
   
serge.aumont committed
268

269
my $language = Sympa::Language->instance;
270
my $mailer   = Sympa::Mailer->instance;
271

sikeda's avatar
sikeda committed
272
273
_load();

274
$log->openlog($Conf::Conf{'syslog'}, $Conf::Conf{'log_socket_type'});
sikeda's avatar
sikeda committed
275
276
277
278
279
280
281
282
283
284
285
286
287

# 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])) {
288
289
    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
290
}
291

sikeda's avatar
sikeda committed
292
293
# Sets the UMASK
umask(oct($Conf::Conf{'umask'}));
294

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

sikeda's avatar
sikeda committed
298
299
# Check for several files.
#FIXME: This would be done in --health_check mode.
300
301
302
unless (Conf::checkfiles()) {
    die "Missing files.\n";
    ## No return.
sikeda's avatar
sikeda committed
303
}
root's avatar
root committed
304

305
# Daemon called for dumping subscribers list
306
if ($main::options{'dump'} or $main::options{'dump_users'}) {
307
308
309
    my $all_lists;

    # Compat. for old style "--dump=LIST".
310
    my $list_id = $main::options{'dump'} || $main::options{'list'};
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329

    if (defined $list_id and $list_id eq 'ALL') {
        $all_lists =
            Sympa::List::get_lists('*', filter => [status => 'open']);
    } elsif (defined $list_id and length $list_id) {
        # The parameter is list ID and list have to be open.
        unless (0 < index $list_id, '@') {
            $log->syslog('err', 'Incorrect list address %s', $list_id);
            exit 1;
        }
        my $list = Sympa::List->new($list_id);
        unless (defined $list) {
            $log->syslog('err', 'Unknown list %s', $list_id);
            exit 1;
        }
        unless ($list->{'admin'}{'status'} eq 'open') {
            $log->syslog('err', 'List is not open: %s', $list);
            exit 1;
        }
330

331
        $all_lists = [$list];
332
    } else {
333
334
335
        $log->syslog('err', 'No lists specified');
        exit 1;
    }
336

337
338
339
340
341
342
343
344
345
346
    my @roles = qw(member);
    if ($main::options{'role'}) {
        my %roles = map { ($_ => 1) }
            ($main::options{'role'} =~ /\b(member|owner|editor)\b/g);
        @roles = sort keys %roles;
        unless (@roles) {
            $log->syslog('err', 'Unknown role %s', $main::options{'role'});
            exit 1;
        }
    }
347

348
349
    foreach my $list (@$all_lists) {
        foreach my $role (@roles) {
350
            unless ($list->dump_users($role)) {
351
352
353
354
355
356
                printf STDERR "%s: Could not dump list users (%s)\n",
                    $list->get_id, $role;
            } else {
                printf STDERR "%s: Dumped list users (%s)\n",
                    $list->get_id, $role;
            }
357
        }
358
    }
359

360
    exit 0;
361
} elsif ($main::options{'restore_users'}) {
362
363
364
365
366
367
368
369
370
371
372
373
374
375
    my $all_lists;

    my $list_id = $main::options{'list'};

    if (defined $list_id and $list_id eq 'ALL') {
        $all_lists =
            Sympa::List::get_lists('*', filter => [status => 'open']);
    } elsif (defined $list_id and length $list_id) {
        # The parameter is list ID and list have to be open.
        unless (0 < index $list_id, '@') {
            $log->syslog('err', 'Incorrect list address %s', $list_id);
            exit 1;
        }
        my $list = Sympa::List->new($list_id);
376
        unless (defined $list) {
377
378
379
380
381
382
383
384
385
386
387
388
389
            $log->syslog('err', 'Unknown list %s', $list_id);
            exit 1;
        }
        unless ($list->{'admin'}{'status'} eq 'open') {
            $log->syslog('err', 'List is not open: %s', $list);
            exit 1;
        }

        $all_lists = [$list];
    } else {
        $log->syslog('err', 'No lists specified');
        exit 1;
    }
390

391
392
393
394
395
396
397
398
    my @roles = qw(member);
    if ($main::options{'role'}) {
        my %roles = map { ($_ => 1) }
            ($main::options{'role'} =~ /\b(member|owner|editor)\b/g);
        @roles = sort keys %roles;
        unless (@roles) {
            $log->syslog('err', 'Unknown role %s', $main::options{'role'});
            exit 1;
399
        }
400
    }
401

402
    foreach my $list (@$all_lists) {
403
        foreach my $role (@roles) {
404
405
            unless ($list->restore_users($role)) {
                printf STDERR "%s: Could not restore list users (%s)\n",
406
407
                    $list->get_id, $role;
            } else {
408
                printf STDERR "%s: Restored list users (%s)\n",
409
410
                    $list->get_id, $role;
            }
411
        }
412
    }
413

414
415
416
417
418
419
420
    exit 0;
} elsif ($main::options{'make_alias_file'}) {
    my $robots = $main::options{'robot'} || '*';
    my @robots;
    if ($robots eq '*') {
        @robots = Sympa::List::get_robots();
    } else {
Sympa authors's avatar
Sympa authors committed
421
        for my $name (split /[\s,]+/, $robots) {
422
423
424
425
426
427
428
            next unless length($name);
            if (Conf::valid_robot($name)) {
                push @robots, $name;
            } else {
                printf STDERR "Invalid robot %s\n", $name;
            }
        }
429
430
431
432
433
434
435
436
437
438
    }
    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');
439
440
        next if $file eq 'none';

441
442
443
444
445
446
447
448
449
450
451
452
        $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;
    }
453

454
455
456
457
458
459
    # 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;
460
        }
461
462
        close $fh;
    }
463

464
465
466
467
    # Write files.
    foreach my $robot (sort @robots) {
        my $alias_manager = Conf::get_robot_conf($robot, 'alias_manager');
        my $sympa_aliases = $sympa_aliases{$robot};
468

469
470
471
472
473
        my $aliases =
            Sympa::Aliases->new($alias_manager, file => $sympa_aliases);
        next
            unless $aliases and $aliases->isa('Sympa::Aliases::Template');

474
475
476
477
        my $fh;
        unless (open $fh, '>>', $sympa_aliases) {    # append
            printf STDERR "Unable to create %s: %s\n", $sympa_aliases, $ERRNO;
            exit 1;
478
        }
479
480
481
        printf $fh "#\n#\tAliases for all Sympa lists open on %s\n#\n",
            $robot;
        close $fh;
482
483

        my $all_lists = Sympa::List::get_lists($robot);
484
485
        foreach my $list (@{$all_lists || []}) {
            next unless $list->{'admin'}{'status'} eq 'open';
486

487
            $aliases->add($list);
488
        }
489
    }
salaun's avatar
salaun committed
490

491
492
493
494
495
496
497
498
499
500
501
502
    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'});
503
    printf "md5 digest : %s \n", $md5;
504

505
506
    exit 0;
} elsif ($main::options{'import'}) {
507
    #FIXME The parameter should be a list address.
508
    unless ($main::options{'import'} =~ /\@/) {
509
510
        printf STDERR "Incorrect list address %s\n", $main::options{'import'};
        exit 1;
511
    }
512
    my $list;
513
    unless ($list = Sympa::List->new($main::options{'import'})) {
514
        printf STDERR "Unknown list name %s\n", $main::options{'import'};
515
516
        exit 1;
    }
517
518
519
520
521
522
523
524
525
    my $dump = do { local $RS; <STDIN> };

    my $spindle = Sympa::Spindle::ProcessRequest->new(
        context          => $list,
        action           => 'import',
        dump             => $dump,
        force            => 1,
        sender           => Sympa::get_address($list, 'listmaster'),
        scenario_context => {skip => 1},
526
        quiet            => $main::options{quiet},
527
528
529
530
    );
    unless ($spindle and $spindle->spin) {
        printf STDERR "Failed to add email addresses to %s\n", $list;
        exit 1;
531
    }
532
533
534
    my $status = _report($spindle);
    printf STDERR "Total imported subscribers: %d\n",
        scalar(grep { $_->[1] eq 'notice' and $_->[2] eq 'now_subscriber' }
Luc Didry's avatar
Luc Didry committed
535
            @{$spindle->{stash} || []});
536
    exit($status ? 0 : 1);
serge.aumont's avatar
   
serge.aumont committed
537

538
} elsif ($main::options{'md5_encode_password'}) {
539
    print STDERR "Obsoleted.  Use upgrade_sympa_password.pl.\n";
540

541
542
543
    exit 0;
} elsif ($main::options{'lowercase'}) {
    print STDERR "Working on user_table...\n";
544
    my $total = _lowercase_field('user_table', 'email_user');
545

546
547
    if (defined $total) {
        print STDERR "Working on subscriber_table...\n";
548
549
        my $total_sub =
            _lowercase_field('subscriber_table', 'user_subscriber');
550
551
        if (defined $total_sub) {
            $total += $total_sub;
552
        }
553
    }
554

555
556
557
558
    unless (defined $total) {
        print STDERR "Could not work on dabatase.\n";
        exit 1;
    }
559

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

562
563
    exit 0;
} elsif ($main::options{'close_list'}) {
564
565
566
567
568
    my ($listname, $robot_id) = split /\@/, $main::options{'close_list'}, 2;
    my $current_list = Sympa::List->new($listname, $robot_id);
    unless ($current_list) {
        printf STDERR "Incorrect list name %s.\n",
            $main::options{'close_list'};
569
570
        exit 1;
    }
sympa-authors's avatar
sympa-authors committed
571

572
573
574
575
576
577
578
579
580
581
    my $spindle = Sympa::Spindle::ProcessRequest->new(
        context          => $robot_id,
        action           => 'close_list',
        current_list     => $current_list,
        sender           => Sympa::get_address($robot_id, 'listmaster'),
        scenario_context => {skip => 1},
    );
    unless ($spindle and $spindle->spin and _report($spindle)) {
        printf STDERR "Could not close list %s\n", $current_list->get_id;
        exit 1;
582
583
    }
    exit 0;
584

585
} elsif ($main::options{'change_user_email'}) {
IKEDA Soji's avatar
IKEDA Soji committed
586
    unless ($main::options{'current_email'} and $main::options{'new_email'}) {
587
588
589
        print STDERR "Missing current_email or new_email parameter\n";
        exit 1;
    }
590

IKEDA Soji's avatar
IKEDA Soji committed
591
592
593
594
595
596
597
598
599
600
601
602
    my $spindle = Sympa::Spindle::ProcessRequest->new(
        context          => [Sympa::List::get_robots()],
        action           => 'move_user',
        current_email    => $main::options{'current_email'},
        email            => $main::options{'new_email'},
        sender           => Sympa::get_address('*', 'listmaster'),
        scenario_context => {skip => 1},
    );
    unless ($spindle and $spindle->spin and _report($spindle)) {
        printf STDERR "Failed to change user email address %s to %s\n",
            $main::options{'current_email'}, $main::options{'new_email'};
        exit 1;
603
604
    }
    exit 0;
IKEDA Soji's avatar
IKEDA Soji committed
605

606
} elsif ($main::options{'purge_list'}) {
607
608
609
    my ($listname, $robot_id) = split /\@/, $main::options{'purge_list'}, 2;
    my $current_list = Sympa::List->new($listname, $robot_id);
    unless ($current_list) {
Luc Didry's avatar
Luc Didry committed
610
611
        printf STDERR "Incorrect list name %s\n",
            $main::options{'purge_list'};
612
613
        exit 1;
    }
614

615
616
617
618
619
620
621
622
623
    my $spindle = Sympa::Spindle::ProcessRequest->new(
        context          => $robot_id,
        action           => 'close_list',
        current_list     => $current_list,
        mode             => 'purge',
        scenario_context => {skip => 1},
    );
    unless ($spindle and $spindle->spin and _report($spindle)) {
        printf STDERR "Could not purge list %s\n", $current_list->get_id;
624
        exit 1;
625
626
    }
    exit 0;
627

628
} elsif ($main::options{'rename_list'}) {
629
630
631
632
633
634
    my $current_list =
        Sympa::List->new(split(/\@/, $main::options{'rename_list'}, 2),
        {just_try => 1});
    unless ($current_list) {
        printf STDERR "Incorrect list name %s\n",
            $main::options{'rename_list'};
635
        exit 1;
636
    }
637

638
639
    my $listname = $main::options{'new_listname'};
    unless (defined $listname and length $listname) {
640
        print STDERR "Missing parameter new_listname\n";
641
        exit 1;
642
    }
643

644
645
646
647
648
    my $robot_id = $main::options{'new_listrobot'};
    unless (defined $robot_id) {
        $robot_id = $current_list->{'domain'};
    } else {
        unless (length $robot_id and Conf::valid_robot($robot_id)) {
649
            printf STDERR "Unknown robot \"%s\"\n", $robot_id;
650
651
            exit 1;
        }
652
    }
653

654
655
656
657
658
659
660
    my $spindle = Sympa::Spindle::ProcessRequest->new(
        context          => $robot_id,
        action           => 'move_list',
        current_list     => $current_list,
        listname         => $listname,
        sender           => Sympa::get_address($robot_id, 'listmaster'),
        scenario_context => {skip => 1},
661
    );
662
    unless ($spindle and $spindle->spin and _report($spindle)) {
663
        printf STDERR "Could not rename list %s to %s\@%s\n",
664
            $current_list->get_id, $listname, $robot_id;
665
666
667
        exit 1;
    }
    exit 0;
Luc Didry's avatar
Luc Didry committed
668

669
670
671
672
673
} elsif ($main::options{'copy_list'}) {
    my $current_list =
        Sympa::List->new(split(/\@/, $main::options{'copy_list'}, 2),
        {just_try => 1});
    unless ($current_list) {
Luc Didry's avatar
Luc Didry committed
674
        printf STDERR "Incorrect list name %s\n", $main::options{'copy_list'};
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
        exit 1;
    }

    my $listname = $main::options{'new_listname'};
    unless (defined $listname and length $listname) {
        print STDERR "Missing parameter new_listname\n";
        exit 1;
    }

    my $robot_id = $main::options{'new_listrobot'};
    unless (defined $robot_id) {
        $robot_id = $current_list->{'domain'};
    } else {
        unless (length $robot_id and Conf::valid_robot($robot_id)) {
            printf STDERR "Unknown robot \"%s\"\n", $robot_id;
            exit 1;
        }
    }

    my $spindle = Sympa::Spindle::ProcessRequest->new(
        context          => $robot_id,
        action           => 'move_list',
        current_list     => $current_list,
        listname         => $listname,
Luc Didry's avatar
Luc Didry committed
699
        mode             => 'copy',
700
701
702
703
704
705
706
707
708
        sender           => Sympa::get_address($robot_id, 'listmaster'),
        scenario_context => {skip => 1},
    );
    unless ($spindle and $spindle->spin and _report($spindle)) {
        printf STDERR "Could not copy list %s to %s\@%s\n",
            $current_list->get_id, $listname, $robot_id;
        exit 1;
    }
    exit 0;
709

710
} elsif ($main::options{'test_database_message_buffer'}) {
711
    print
712
713
714
715
716
717
718
719
720
721
722
723
724
        "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;
725

726
} elsif ($main::options{'create_list'}) {
727
    my $robot = $main::options{'robot'} || $Conf::Conf{'domain'};
728

729
730
731
732
    unless ($main::options{'input_file'}) {
        print STDERR "Error : missing 'input_file' parameter\n";
        exit 1;
    }
733

734
    my $spindle = Sympa::Spindle::ProcessRequest->new(
735
736
737
738
        context          => $robot,
        action           => 'create_list',
        parameters       => {file => $main::options{'input_file'}},
        sender           => Sympa::get_address($robot, 'listmaster'),
739
740
741
        scenario_context => {skip => 1}
    );
    unless ($spindle and $spindle->spin and _report($spindle)) {
742
        print STDERR "Could not create list\n";
743
744
        exit 1;
    }
745
    exit 0;
746

747
} elsif ($main::options{'instantiate_family'}) {
748
    my $robot = $main::options{'robot'} || $Conf::Conf{'domain'};
749

750
751
752
753
754
755
756
    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)) {
757
758
759
        printf STDERR
            "The family %s does not exist, impossible instantiation\n",
            $family_name;
760
761
        exit 1;
    }
762

763
764
765
766
    unless ($main::options{'input_file'}) {
        print STDERR "Error : missing input_file parameter\n";
        exit 1;
    }
767

768
    unless (-r $main::options{'input_file'}) {
Luc Didry's avatar
Luc Didry committed
769
770
        printf STDERR "Unable to read %s file\n",
            $main::options{'input_file'};
771
772
        exit 1;
    }
773

774
    unless (
775
776
        instantiate(
            $family,
777
            $main::options{'input_file'},
778
779
            close_unknown => $main::options{'close_unknown'},
            quiet         => $main::options{quiet},
780
        )
Luc Didry's avatar
Luc Didry committed
781
    ) {
782
783
784
        print STDERR "\nImpossible family instantiation : action stopped \n";
        exit 1;
    }
785

786
    my %result;
787
    my $err = get_instantiation_results($family, \%result);
788

789
790
791
792
    unless ($main::options{'quiet'}) {
        print STDOUT "@{$result{'info'}}";
        print STDOUT "@{$result{'warn'}}";
    }
793
    if ($err >= 0) {
794
        print STDERR "@{$result{'errors'}}";
IKEDA Soji's avatar
tidyall    
IKEDA Soji committed
795
        exit 1;
796
    }
797

798
799
    exit 0;
} elsif ($main::options{'add_list'}) {
800
    my $robot = $main::options{'robot'} || $Conf::Conf{'domain'};
801

802
803
804
805
806
    my $family_name;
    unless ($family_name = $main::options{'add_list'}) {
        print STDERR "Error : missing family parameter\n";
        exit 1;
    }
807

808
809
    my $family;
    unless ($family = Sympa::Family->new($family_name, $robot)) {
810
        printf STDERR
811
812
            "The family %s does not exist, impossible to add a list\n",
            $family_name;
813
814
        exit 1;
    }
815

816
817
818
819
    unless ($main::options{'input_file'}) {
        print STDERR "Error : missing 'input_file' parameter\n";
        exit 1;
    }
820

821
    my $spindle = Sympa::Spindle::ProcessRequest->new(
Luc Didry's avatar
Luc Didry committed
822
823
        context          => $family,
        action           => 'create_automatic_list',
824
        parameters       => {file => $main::options{'input_file'}},
Luc Didry's avatar
Luc Didry committed
825
        sender           => Sympa::get_address($family, 'listmaster'),
826
827
828
        scenario_context => {skip => 1},
    );
    unless ($spindle and $spindle->spin and _report($spindle)) {
829
830
        printf STDERR "Impossible to add a list to the family %s\n",
            $family_name;
831
832
        exit 1;
    }
833

834
    exit 0;
835

836
837
} elsif ($main::options{'sync_include'}) {
    my $list = Sympa::List->new($main::options{'sync_include'});
IKEDA Soji's avatar
IKEDA Soji committed
838
    my $role = $main::options{'role'} || 'member';    # Compat. <= 6.2.54
839

840
    unless (defined $list) {
Luc Didry's avatar
Luc Didry committed
841
842
        printf STDERR "Incorrect list name %s\n",
            $main::options{'sync_include'};
843
844
        exit 1;
    }
IKEDA Soji's avatar
IKEDA Soji committed
845
    unless (grep { $role eq $_ } qw(member owner editor)) {
846
847
848
        printf STDERR "Unknown role %s\n", $role;
        exit 1;
    }
849

IKEDA Soji's avatar
IKEDA Soji committed
850
851
852
853
854
855
856
857
858
859
    my $spindle = Sympa::Spindle::ProcessRequest->new(
        context          => $list,
        action           => 'include',
        role             => $role,
        sender           => Sympa::get_address($list, 'listmaster'),
        scenario_context => {skip => 1},
    );
    unless ($spindle and $spindle->spin and _report($spindle)) {
        printf STDERR "Could not sync role %s of list %s with data sources\n",
            $role, $list->get_id;
860
861
862
863
864
        exit 1;
    }
    exit 0;
## Migration from one version to another
} elsif ($main::options{'upgrade'}) {
865

866
    $log->syslog('notice', "Upgrade process...");
867

868
869
    $main::options{'from'} ||= Sympa::Upgrade::get_previous_version();
    $main::options{'to'}   ||= Sympa::Constants::VERSION;
870

871
    if ($main::options{'from'} eq $main::options{'to'}) {
872
        $log->syslog('notice', 'Current version: %s; no upgrade is required',
873
874
875
            $main::options{'to'});
        exit 0;
    } else {
876
        $log->syslog('notice', "Upgrading from %s to %s...",
877
878
            $main::options{'from'}, $main::options{'to'});
    }
879

880
881
882
    unless (
        Sympa::Upgrade::upgrade($main::options{'from'}, $main::options{'to'}))
    {
883
        $log->syslog('err', "Migration from %s to %s failed",
884
885
886
            $main::options{'from'}, $main::options{'to'});
        exit 1;
    }
887

888
    $log->syslog('notice', 'Upgrade process finished');
889
    Sympa::Upgrade::update_version();
890

891
    exit 0;
892

893
} elsif ($main::options{'upgrade_shared'}) {
894
    print STDERR "Obsoleted.  Use upgrade_shared_repository.pl.\n";
895

896
897
898
    exit 0;
} elsif ($main::options{'reload_list_config'}) {
    if ($main::options{'list'}) {
899
        $log->syslog('notice', 'Loading list %s...', $main::options{'list'});
900
901
        my $list =
            Sympa::List->new($main::options{'list'}, '',
902
            {reload_config => 1});
903
        unless (defined $list) {
Luc Didry's avatar
Luc Didry committed
904
905
            printf STDERR "Error : incorrect list name '%s'\n",
                $main::options{'list'};
906
907
908
            exit 1;
        }
    } else {
909
        $log->syslog('notice', "Loading ALL lists...");
910
        my $all_lists = Sympa::List::get_lists('*', reload_config => 1);
911
    }
912
    $log->syslog('notice', '...Done.');
913

914
915
    exit 0;
}
916

917
918
##########################################
elsif ($main::options{'modify_list'}) {
919
    my $robot = $main::options{'robot'} || $Conf::Conf{'domain'};
920

921
922
    my $family;
    unless ($main::options{'modify_list'}) {
923
924
925
        print STDERR "Error : missing family parameter\n";
        exit 1;
    }
926
927
    unless ($family =
        Sympa::Family->new($main::options{'modify_list'}, $robot)) {
928
929
        printf STDERR
            "The family %s does not exist, impossible to modify the list.\n",
930
            $main::options{'modify_list'};
931
932
933
934
935
936
        exit 1;
    }
    unless ($main::options{'input_file'}) {
        print STDERR "Error : missing input_file parameter\n";
        exit 1;
    }
937

938
939
940
941
942
943
944
945
946
947
    # list config family updating
    my $spindle = Sympa::Spindle::ProcessRequest->new(
        context          => $family,
        action           => 'update_automatic_list',
        parameters       => {file => $main::options{'input_file'}},
        sender           => Sympa::get_address($family, 'listmaster'),
        scenario_context => {skip => 1},
    );
    unless ($spindle and $spindle->spin and _report($spindle)) {
        print STDERR "No object list resulting from updating\n";
948
949
        exit 1;
    }
950

951
952
    exit 0;
}
953

954
955
##########################################
elsif ($main::options{'close_family'}) {
956
    my $robot = $main::options{'robot'} || $Conf::Conf{'domain'};
957

958
959
960
961
962
963
    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)) {
964
965
966
        printf STDERR
            "The family %s does not exist, impossible family closure\n",
            $family_name;
967
968
        exit 1;
    }
969

970
971
972
973
974
975
976
977
    my $lists = Sympa::List::get_lists($family);
    my @impossible_close;
    my @close_ok;

    foreach my $list (@{$lists || []}) {
        my $listname <