Conf.pm 85.8 KB
Newer Older
1
2
3
4
# -*- indent-tabs-mode: nil; -*-
# vim:ft=perl:et:sw=4
# $Id$

5
# Sympa - SYsteme de Multi-Postage Automatique
6
7
8
9
#
# 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
10
# Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017 GIP RENATER
11
12
13
# Copyright 2017 The Sympa Community. See the AUTHORS.md file at the top-level
# directory of this distribution and at
# <https://github.com/sympa-community/sympa.git>.
14
15
16
17
18
19
20
21
22
23
24
25
#
# 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
26
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
27

root's avatar
root committed
28
29
30
31
## This module handles the configuration file for Sympa.

package Conf;

32
33
use strict;
use warnings;
34
use English qw(-no_match_vars);
35
use Storable;
36

37
use Sympa;
38
use Sympa::ConfDef;
39
use Sympa::Constants;
40
use Sympa::DatabaseManager;
41
use Sympa::Language;
42
use Sympa::LockedFile;
43
use Sympa::Log;
44
use Sympa::Regexps;
45
46
use Sympa::Tools::Data;
use Sympa::Tools::File;
47
use Sympa::Tools::Text;
root's avatar
root committed
48

49
50
my $log = Sympa::Log->instance;

51
=encoding utf-8
52

53
54
55
#=head1 NAME
#
#Conf - Sympa configuration
56
57
58
59
60
61
62

=head1 DESCRIPTION

=head2 CONSTANTS AND EXPORTED VARIABLES

=cut

63
## Database and SQL statement handlers
64
my $sth;
65
# parameters hash, keyed by parameter name
66
our %params =
67
    map { $_->{name} => $_ }
68
    grep { $_->{name} } @Sympa::ConfDef::params;
69
70
71

# valid virtual host parameters, keyed by parameter name
my %valid_robot_key_words;
72
my %db_storable_parameters;
73
my %optional_key_words;
74
foreach my $hash (@Sympa::ConfDef::params) {
75
76
77
78
    $valid_robot_key_words{$hash->{'name'}} = 1 if ($hash->{'vhost'});
    $db_storable_parameters{$hash->{'name'}} = 1
        if (defined($hash->{'db'}) and $hash->{'db'} ne 'none');
    $optional_key_words{$hash->{'name'}} = 1 if ($hash->{'optional'});
79
80
}

sikeda's avatar
sikeda committed
81
our $params_by_categories = _get_parameters_names_by_category();
82

83
my %old_params = (
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
    trusted_ca_options               => 'capath,cafile',
    'msgcat'                         => '',
    queueexpire                      => '',
    clean_delay_queueother           => '',
    web_recode_to                    => 'filesystem_encoding', # ??? - 5.2
    'localedir'                      => '',
    'ldap_export_connection_timeout' => '',                    # 3.3b3 - 4.1?
    'ldap_export_dnmanager'          => '',                    # ,,
    'ldap_export_host'               => '',                    # ,,
    'ldap_export_name'               => '',                    # ,,
    'ldap_export_password'           => '',                    # ,,
    'ldap_export_suffix'             => '',                    # ,,
    'tri'                            => 'sort',                # ??? - 1.3.4-1
    'sort'                           => '',                    # 1.4.0 - ???
    'pidfile'                        => '',                    # ??? - 6.1.17
    'pidfile_distribute'             => '',                    # ,,
    'pidfile_creation'               => '',                    # ,,
    'pidfile_bulk'                   => '',                    # ,,
    'archived_pidfile'               => '',                    # ,,
    'bounced_pidfile'                => '',                    # ,,
    'task_manager_pidfile'           => '',                    # ,,
105
106
107
108
109
110
    'email_gecos'       => 'gecos',              # 6.2a.?? - 6.2a.33
    'lock_method'       => '',                   # 5.3b.3 - 6.2a.33
    'html_editor_file'  => 'html_editor_url',    # 6.2a
    'openssl'           => '',                   # ?? - 6.2a.40
    'distribution_mode' => '',                   # 5.0a.1 - 6.2a.40
    'queuedistribute'   => '',                   # ,,
111
112
113
114

    # These are not yet implemented
    'crl_dir'          => '',
    'dkim_header_list' => '',
root's avatar
root committed
115
);
116

117
118
## These parameters now have a hard-coded value
## Customized value can be accessed though as %Ignored_Conf
olivier.salaun's avatar
olivier.salaun committed
119
my %Ignored_Conf;
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
my %hardcoded_params = (filesystem_encoding => 'utf8');

my %trusted_applications = (
    'trusted_application' => {
        'occurrence' => '0-n',
        'format'     => {
            'name' => {
                'format'     => '\S*',
                'occurrence' => '1',
                'case'       => 'insensitive',
            },
            'ip' => {
                'format'     => '\d+\.\d+\.\d+\.\d+',
                'occurrence' => '0-1'
            },
            'md5password' => {
                'format'     => '.*',
                'occurrence' => '0-1'
            },
            'proxy_for_variables' => {
                'format'     => '.*',
                'occurrence' => '0-n',
                'split_char' => ','
143
144
145
146
147
148
149
150
151
152
153
            },
            'set_variables' => {
                'format'     => '\S+=.*',
                'occurrence' => '0-n',
                'split_char' => ',',
            },
            'allow_commands' => {
                'format'     => '\S+',
                'occurrence' => '0-n',
                'split_char' => ',',
            },
154
155
        }
    }
156
);
157
my $binary_file_extension = ".bin";
158

159
our $wwsconf;
olivier.salaun's avatar
olivier.salaun committed
160
our %Conf = ();
root's avatar
root committed
161

162
163
164
165
166
167
168
169
170
171
172
=head2 FUNCTIONS

=over 4

=item load ( [ CONFIG_FILE ], [ NO_DB ], [ RETURN_RESULT ] )

Loads and parses the configuration file.  Reports errors if any.

do not try to load database values if NO_DB is set;
do not change gloval hash %Conf if RETURN_RESULT is set;

173
174
## we known that's dirty, this proc should be rewritten without this global
## var %Conf
175
176
177
178
179

=back

=cut

root's avatar
root committed
180
sub load {
181
182
    my $config_file   = shift || get_sympa_conf();
    my $no_db         = shift;
183
    my $return_result = shift;
184
    my $force_reload;
185

root's avatar
root committed
186
    my $config_err = 0;
187
188
    my %line_numbered_config;

189
    if (_source_has_not_changed($config_file) and !$return_result) {
190
191
192
193
        if (my $tmp_conf = _load_binary_cache(
                {'config_file' => $config_file . $binary_file_extension}
            )
            ) {
194
            %Conf = %{$tmp_conf};
195
196
197
            # Will force the robot.conf reloading, as sympa.conf is the
            # default.
            $force_reload = 1;
198
        }
199
    } else {
200
        $log->syslog('debug3',
201
202
            'File %s has changed since the last cache. Loading file',
            $config_file);
203
204
        # Will force the robot.conf reloading, as sympa.conf is the default.
        $force_reload = 1;
205
        ## Loading the Sympa main config file.
206
207
208
209
210
211
212
        if (my $config_loading_result = _load_config_file_to_hash(
                {'path_to_config_file' => $config_file}
            )
            ) {
            %line_numbered_config =
                %{$config_loading_result->{'numbered_config'}};
            %Conf       = %{$config_loading_result->{'config'}};
213
            $config_err = $config_loading_result->{'errors'};
214
        } else {
215
216
217
218
219
            return undef;
        }
        # Returning the config file content if this is what has been asked.
        return (\%line_numbered_config) if ($return_result);

220
221
        # Users may define parameters with a typo or other errors. Check that
        # the parameters
222
        # we found in the config file are all well defined Sympa parameters.
sikeda's avatar
sikeda committed
223
        $config_err += _detect_unknown_parameters_in_config(
224
225
226
227
228
229
            {   'config_hash' => \%Conf,
                'config_file_line_numbering_reference' =>
                    \%line_numbered_config,
            }
        );

230
231
        # Some parameter values are hardcoded. In that case, ignore what was
        #  set in the config file and simply use the hardcoded value.
232
233
        %Ignored_Conf =
            %{_set_hardcoded_parameter_values({'config_hash' => \%Conf,})};
root's avatar
root committed
234

sikeda's avatar
sikeda committed
235
        _set_listmasters_entry({'config_hash' => \%Conf, 'main_config' => 1});
236
237
238
239
240

        ## Some parameters must have a value specifically defined in the
        ## config. If not, it is an error.
        $config_err += _detect_missing_mandatory_parameters(
            {'config_hash' => \%Conf, 'file_to_check' => $config_file});
241

242
        # Some parameters need special treatments to get their final values.
sikeda's avatar
sikeda committed
243
        _infer_server_specific_parameter_values({'config_hash' => \%Conf,});
244

sikeda's avatar
sikeda committed
245
        _infer_robot_parameter_values({'config_hash' => \%Conf});
root's avatar
root committed
246

247
        if ($config_err) {
248
            $log->syslog('err', 'Errors while parsing main config file %s',
sikeda's avatar
sikeda committed
249
                $config_file);
250
251
            return undef;
        }
252

253
254
        _store_source_file_name(
            {'config_hash' => \%Conf, 'config_file' => $config_file});
sikeda's avatar
sikeda committed
255
        _save_config_hash_to_binary({'config_hash' => \%Conf,});
256
    }
root's avatar
root committed
257

258
259
    if (my $missing_modules_count =
        _check_cpan_modules_required_by_config({'config_hash' => \%Conf,})) {
260
        $log->syslog('err', 'Warning: %d required modules are missing',
sikeda's avatar
sikeda committed
261
            $missing_modules_count);
262
263
    }

264
265
    _replace_file_value_by_db_value({'config_hash' => \%Conf})
        unless ($no_db);
sikeda's avatar
sikeda committed
266
267
    _load_server_specific_secondary_config_files({'config_hash' => \%Conf,});
    _load_robot_secondary_config_files({'config_hash' => \%Conf});
268

269
    ## Load robot.conf files
270
271
272
273
274
275
276
277
    unless (
        load_robots(
            {   'config_hash'  => \%Conf,
                'no_db'        => $no_db,
                'force_reload' => $force_reload
            }
        )
        ) {
278
279
        return undef;
    }
sikeda's avatar
sikeda committed
280
    ##_create_robot_like_config_for_main_robot();
root's avatar
root committed
281
    return 1;
282
}
283

salaun's avatar
salaun committed
284
285
## load each virtual robots configuration files
sub load_robots {
286
    my $param = shift;
287
288
    my @robots;

sikeda's avatar
sikeda committed
289
    my $robots_list_ref = get_robots_list();
290
    unless (defined $robots_list_ref) {
291
        $log->syslog('err', 'Robots config loading failed');
292
        return undef;
293
    } else {
294
295
296
297
298
        @robots = @{$robots_list_ref};
    }
    unless ($#robots > -1) {
        return 1;
    }
299
    my $exiting = 0;
300
    foreach my $robot (@robots) {
301
        my $robot_config_file = "$Conf{'etc'}/$robot/robot.conf";
302
303
304
305
306
307
308
309
310
        my $robot_conf        = undef;
        unless (
            $robot_conf = _load_single_robot_config(
                {   'robot'        => $robot,
                    'no_db'        => $param->{'no_db'},
                    'force_reload' => $param->{'force_reload'}
                }
            )
            ) {
311
            $log->syslog(
sikeda's avatar
sikeda committed
312
313
314
315
                'err',
                'The config for robot %s contain errors: it could not be correctly loaded',
                $robot
            );
316
            $exiting = 1;
317
        } else {
318
319
            $param->{'config_hash'}{'robots'}{$robot} = $robot_conf;
        }
320
321
        #_check_double_url_usage(
        #    {'config_hash' => $param->{'config_hash'}{'robots'}{$robot}});
322
    }
323
    return undef if ($exiting);
324
    return 1;
325
326
327
328
329
330
331
}

## returns a robot conf parameter
sub get_robot_conf {
    my ($robot, $param) = @_;

    if (defined $robot && $robot ne '*') {
332
333
        if (   defined $Conf{'robots'}{$robot}
            && defined $Conf{'robots'}{$robot}{$param}) {
334
335
            return $Conf{'robots'}{$robot}{$param};
        }
336
    }
337
    ## default
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
    return $Conf{$param};
}

=over 4

=item get_sympa_conf

Gets path name of main config file.
Path name is taken from:

=over 4

=item 1

C<--config> command line option

=item 2

C<SYMPA_CONFIG> environment variable

=item 3

built-in default

=back

=back

=cut

368
369
our $sympa_config;

370
sub get_sympa_conf {
371
    return $sympa_config || $ENV{'SYMPA_CONFIG'} || Sympa::Constants::CONFIG;
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
}

=over 4

=item get_wwsympa_conf

Gets path name of wwsympa.conf file.
Path name is taken from:

=over 4

=item 1

C<SYMPA_WWSCONFIG> environment variable

=item 2

built-in default

=back

=back

=cut

sub get_wwsympa_conf {
    return $ENV{'SYMPA_WWSCONFIG'} || Sympa::Constants::WWSCONFIG;
salaun's avatar
salaun committed
399
400
}

401
402
# deletes all the *.conf.bin files.
sub delete_binaries {
403
    $log->syslog('debug2', '');
404
    my @files = (get_sympa_conf(), get_wwsympa_conf());
sikeda's avatar
sikeda committed
405
    foreach my $robot (@{get_robots_list()}) {
406
407
408
        push @files, "$Conf{'etc'}/$robot/robot.conf";
    }
    foreach my $c_file (@files) {
409
410
        my $binary_file = $c_file . ".bin";
        if (-f $binary_file) {
411
412
            if (-w $binary_file) {
                unlink $binary_file;
413
            } else {
414
                $log->syslog(
415
                    'err',
416
                    'Could not remove file %s. You should remove it manually to ensure the configuration used is valid',
417
418
                    $binary_file
                );
419
420
421
422
423
            }
        }
    }
}

424
425
# Return a reference to an array containing the names of the robots on the
# server.
426
sub get_robots_list {
427
    $log->syslog('debug2', "Retrieving the list of robots on the server");
428
    my @robots_list;
429
    unless (opendir DIR, $Conf{'etc'}) {
430
        $log->syslog('err',
sikeda's avatar
sikeda committed
431
432
            'Unable to open directory %s for virtual robots config',
            $Conf{'etc'});
433
434
435
436
437
438
439
440
441
442
443
444
        return undef;
    }
    foreach my $robot (readdir DIR) {
        my $robot_config_file = "$Conf{'etc'}/$robot/robot.conf";
        next unless (-d "$Conf{'etc'}/$robot");
        next unless (-f $robot_config_file);
        push @robots_list, $robot;
    }
    closedir(DIR);
    return \@robots_list;
}

445
## Returns a hash containing the values of all the parameters of the group
446
447
## (as defined in Sympa::ConfDef) whose name is given as argument, in the
## context of the robot given as argument.
448
449
sub get_parameters_group {
    my ($robot, $group) = @_;
450
    $log->syslog('debug3', 'Getting parameters for group "%s"', $group);
451
452
    my $param_hash;
    foreach my $param_name (keys %{$params_by_categories->{$group}}) {
453
        $param_hash->{$param_name} = get_robot_conf($robot, $param_name);
454
455
456
    }
    return $param_hash;
}
457
458

## fetch the value from parameter $label of robot $robot from conf_table
459
sub get_db_conf {
460
461
462
    my $robot = shift;
    my $label = shift;

463
464
465
466
467
    # if the value is related to a robot that is not explicitly defined, apply
    # it to the default robot.
    $robot = '*' unless (-f $Conf{'etc'} . '/' . $robot . '/robot.conf');
    unless ($robot) { $robot = '*' }

468
    my $sdm = Sympa::DatabaseManager->instance;
469
    unless (
470
471
472
473
474
475
        $sdm
        and $sth = $sdm->do_prepared_query(
            q{SELECT value_conf AS value
              FROM conf_table
              WHERE robot_conf = ? AND label_conf = ?},
            $robot, $label
476
477
        )
        ) {
478
        $log->syslog(
479
480
481
482
483
            'err',
            'Unable retrieve value of parameter %s for robot %s from the database',
            $label,
            $robot
        );
484
        return undef;
485
    }
486

487
    my $value = $sth->fetchrow;
488

489
    $sth->finish();
490
    return $value;
491
492
493
}

## store the value from parameter $label of robot $robot from conf_table
494
sub set_robot_conf {
495
496
497
498
    my $robot = shift;
    my $label = shift;
    my $value = shift;

499
    $log->syslog('info', 'Set config for robot %s, %s="%s"',
500
        $robot, $label, $value);
501

502
503
504
505
506
507
508
509
    # set the current config before to update database.
    if (-f "$Conf{'etc'}/$robot/robot.conf") {
        $Conf{'robots'}{$robot}{$label} = $value;
    } else {
        $Conf{$label} = $value;
        $robot = '*';
    }

510
    my $sdm = Sympa::DatabaseManager->instance;
511
    unless (
512
513
514
515
516
517
        $sdm
        and $sth = $sdm->do_prepared_query(
            q{SELECT COUNT(*)
              FROM conf_table
              WHERE robot_conf = ? AND label_conf = ?},
            $robot, $label
518
519
        )
        ) {
520
        $log->syslog(
521
522
523
524
525
            'err',
            'Unable to check presence of parameter %s for robot %s in database',
            $label,
            $robot
        );
526
        return undef;
527
    }
528

529
530
    my $count = $sth->fetchrow;
    $sth->finish();
531

532
    if ($count == 0) {
533
        unless (
534
535
536
537
538
            $sth = $sdm->do_prepared_query(
                q{INSERT INTO conf_table
                  (robot_conf, label_conf, value_conf)
                  VALUES (?, ?, ?)},
                $robot, $label, $value
539
540
            )
            ) {
541
            $log->syslog(
542
543
544
545
546
547
                'err',
                'Unable add value %s for parameter %s in the robot %s DB conf',
                $value,
                $label,
                $robot
            );
548
549
            return undef;
        }
550
551
    } else {
        unless (
552
553
554
555
556
557
            $sth = $sdm->do_prepared_query(
                q{UPDATE conf_table
                  SET robot_conf = ?, label_conf = ?, value_conf = ?
                  WHERE robot_conf = ? AND label_conf = ?},
                $robot, $label, $value,
                $robot, $label
558
559
            )
            ) {
560
            $log->syslog(
561
562
563
564
565
566
                'err',
                'Unable set parameter %s value to %s in the robot %s DB conf',
                $label,
                $value,
                $robot
            );
567
            return undef;
568
        }
569
570
571
    }
}

572
573
# Store configs to database
sub conf_2_db {
574
    $log->syslog('debug2', '(%s)', @_);
575

576
    my @conf_parameters = @Sympa::ConfDef::params;
577
578

    # store in database robots parameters.
579
580
581
    # load only parameters that are in a robot.conf file (do not apply
    # defaults).
    my $robots_conf = load_robots();
582

583
    unless (opendir DIR, $Conf{'etc'}) {
584
        $log->syslog('err',
sikeda's avatar
sikeda committed
585
586
            'Unable to open directory %s for virtual robots config',
            $Conf{'etc'});
587
588
589
590
591
592
        return undef;
    }

    foreach my $robot (readdir(DIR)) {
        next unless (-d "$Conf{'etc'}/$robot");
        next unless (-f "$Conf{'etc'}/$robot/robot.conf");
593

594
        my $config;
595
        if (my $result_of_config_loading = _load_config_file_to_hash(
596
                {         'path_to_config_file' => $Conf{'etc'} . '/'
597
598
599
600
601
                        . $robot
                        . '/robot.conf'
                }
            )
            ) {
602
603
            $config = $result_of_config_loading->{'config'};
        }
sikeda's avatar
sikeda committed
604
        _remove_unvalid_robot_entry($config);
605
606
607
608
609
610
611
612
613
614
615
616
617
618

        for my $i (0 .. $#conf_parameters) {
            if ($conf_parameters[$i]->{'name'}) {
                # skip separators in conf_parameters structure
                if (($conf_parameters[$i]->{'vhost'} eq '1')
                    && #skip parameters that can't be define by robot so not to be loaded in db at that stage
                    ($config->{$conf_parameters[$i]->{'name'}})
                    ) {
                    Conf::set_robot_conf(
                        $robot,
                        $conf_parameters[$i]->{'name'},
                        $config->{$conf_parameters[$i]->{'name'}}
                    );
                }
619
620
621
            }
        }
    }
622
    closedir(DIR);
623
624

    # store in database sympa;conf and wwsympa.conf
625

626
627
    ## Load configuration file. Ignoring database config and get result
    my $global_conf;
628
629
    unless ($global_conf =
        Conf::load(Conf::get_sympa_conf(), 1, 'return_result')) {
630
        $log->syslog('err', 'Configuration file %s has errors',
631
632
            Conf::get_sympa_conf());
        return undef;
633
    }
634
635
636
637
638
639
640
641
642
643

    for my $i (0 .. $#conf_parameters) {
        if (($conf_parameters[$i]->{'edit'} eq '1')
            && $global_conf->{$conf_parameters[$i]->{'name'}}) {
            Conf::set_robot_conf(
                "*",
                $conf_parameters[$i]->{'name'},
                $global_conf->{$conf_parameters[$i]->{'name'}}[0]
            );
        }
644
645
646
    }
}

647
648
649
## Check required files and create them if required
sub checkfiles_as_root {

650
    my $config_err = 0;
sympa-authors's avatar
   
sympa-authors committed
651

652
    ## Check aliases file
653
654
655
    unless (-f $Conf{'sendmail_aliases'}
        || ($Conf{'sendmail_aliases'} =~ /^none$/i)) {
        unless (open ALIASES, ">$Conf{'sendmail_aliases'}") {
656
            $log->syslog(
657
658
659
660
661
662
                'err',
                "Failed to create aliases file %s",
                $Conf{'sendmail_aliases'}
            );
            return undef;
        }
663

664
665
666
667
668
        print ALIASES
            "## This aliases file is dedicated to Sympa Mailing List Manager\n";
        print ALIASES
            "## You should edit your sendmail.mc or sendmail.cf file to declare it\n";
        close ALIASES;
669
        $log->syslog(
670
671
672
673
674
            'notice',
            "Created missing file %s",
            $Conf{'sendmail_aliases'}
        );
        unless (
675
            Sympa::Tools::File::set_file_rights(
676
677
678
679
680
681
                file  => $Conf{'sendmail_aliases'},
                user  => Sympa::Constants::USER,
                group => Sympa::Constants::GROUP,
                mode  => 0644,
            )
            ) {
682
            $log->syslog('err', 'Unable to set rights on %s',
683
684
685
                $Conf{'db_name'});
            return undef;
        }
sympa-authors's avatar
   
sympa-authors committed
686
687
    }

688
    foreach my $robot (keys %{$Conf{'robots'}}) {
sympa-authors's avatar
sympa-authors committed
689

690
691
692
693
        # create static content directory
        my $dir = get_robot_conf($robot, 'static_content_path');
        if ($dir ne '' && !-d $dir) {
            unless (mkdir($dir, 0775)) {
694
695
                $log->syslog('err', 'Unable to create directory %s: %m',
                    $dir);
696
697
                $config_err++;
            }
698

699
            unless (
700
                Sympa::Tools::File::set_file_rights(
701
702
703
704
705
                    file  => $dir,
                    user  => Sympa::Constants::USER,
                    group => Sympa::Constants::GROUP,
                )
                ) {
706
                $log->syslog('err', 'Unable to set rights on %s',
707
708
709
                    $Conf{'db_name'});
                return undef;
            }
710
        }
711
712
    }

713
    return 1;
714
715
}

sikeda's avatar
sikeda committed
716
717
718
719
720
721
722
723
724
725
## Check if data structures are uptodate
## If not, no operation should be performed before the upgrade process is run
sub data_structure_uptodate {
    my $version_file =
        Conf::get_robot_conf('*', 'etc') . '/data_structure.version';
    my $data_structure_version;

    if (-f $version_file) {
        my $fh;
        unless (open $fh, '<', $version_file) {
726
            $log->syslog('err', 'Unable to open %s: %m', $version_file);
sikeda's avatar
sikeda committed
727
728
729
730
731
732
733
734
735
736
737
738
739
740
            return undef;
        }
        while (<$fh>) {
            next if /^\s*$/;
            next if /^\s*\#/;
            chomp;
            $data_structure_version = $_;
            last;
        }
        close $fh;
    }

    if (defined $data_structure_version
        and $data_structure_version ne Sympa::Constants::VERSION) {
741
        $log->syslog('err',
sikeda's avatar
sikeda committed
742
743
744
745
746
747
748
749
            "Data structure (%s) is not uptodate for current release (%s)",
            $data_structure_version, Sympa::Constants::VERSION);
        return 0;
    }

    return 1;
}

750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
# Check if cookie parameter was changed.
# Old name: tools::cookie_changed().
sub cookie_changed {
    my $current = $Conf::Conf{'cookie'};
    $current = '' unless defined $current;

    my $changed = 1;
    if (-f "$Conf::Conf{'etc'}/cookies.history") {
        my $fh;
        unless (open $fh, "$Conf::Conf{'etc'}/cookies.history") {
            $log->syslog('err', 'Unable to read %s/cookies.history',
                $Conf::Conf{'etc'});
            return undef;
        }
        my $oldcook = <$fh>;
        close $fh;
        ($oldcook) = reverse split /\s+/, $oldcook;
        $oldcook = '' unless defined $oldcook;

        if ($oldcook eq $current) {
            $log->syslog('debug2', 'Cookie is stable');
            $changed = 0;
        }
        return $changed;
    } else {
        my $umask = umask 037;
        unless (open COOK, ">$Conf::Conf{'etc'}/cookies.history") {
            umask $umask;
            $log->syslog('err', 'Unable to create %s/cookies.history',
                $Conf::Conf{'etc'});
            return undef;
        }
        umask $umask;
        chown [getpwnam(Sympa::Constants::USER)]->[2],
            [getgrnam(Sympa::Constants::GROUP)]->[2],
            "$Conf::Conf{'etc'}/cookies.history";
        print COOK "$current ";
        close COOK;
        return (0);
    }
}

root's avatar
root committed
792
793
794
## Check a few files
sub checkfiles {
    my $config_err = 0;
795

796
    foreach my $p (qw(sendmail antivirus_path)) {
797
798
799
        next unless $Conf{$p};

        unless (-x $Conf{$p}) {
800
            $log->syslog('err', "File %s does not exist or is not executable",
801
802
                $Conf{$p});
            $config_err++;
803
        }
804
805
    }

806
    foreach my $qdir (qw(spool queuetask tmpdir)) {
807
        unless (-d $Conf{$qdir}) {
808
            $log->syslog('info', 'Creating spool %s', $Conf{$qdir});
809
            unless (mkdir($Conf{$qdir}, 0775)) {
810
811
                $log->syslog('err', 'Unable to create spool %s',
                    $Conf{$qdir});
812
813
814
                $config_err++;
            }
            unless (
815
                Sympa::Tools::File::set_file_rights(
816
817
818
                    file  => $Conf{$qdir},
                    user  => Sympa::Constants::USER,
                    group => Sympa::Constants::GROUP,
819
820
                )
                ) {
821
                $log->syslog('err', 'Unable to set rights on %s',
822
823
                    $Conf{$qdir});
                $config_err++;
824
            }
825
        }
root's avatar
root committed
826
    }
827

828
829
830
831
832
833
834
    # Check if directory parameters point to the same directory.
    my @keys = qw(bounce_path etc home
        queue queueauth queuebounce queuebulk queuedigest
        queuemod queueoutgoing queuesubscribe queuetask
        queuetopic spool tmpdir viewmail_dir);
    push @keys, 'queueautomatic'
        if $Conf::Conf{'automatic_list_feature'} eq 'on';
835
    my %dirs = (Sympa::Constants::PIDDIR() => 'PID directory');
836

837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
    foreach my $key (@keys) {
        my $val = $Conf::Conf{$key};
        next unless $val;

        if ($dirs{$val}) {
            $log->syslog(
                'err',
                'Error in config: %s and %s parameters pointing to the same directory (%s)',
                $dirs{$val},
                $key,
                $val
            );
            $config_err++;
        } else {
            $dirs{$val} = $key;
        }
853
854
    }

855
856
857
858
859
860
861
862
863
    # Create pictures directory. FIXME: Would be created on demand.
    my $pictures_dir = $Conf::Conf{'pictures_path'};
    unless (-d $pictures_dir) {
        unless (mkdir $pictures_dir, 0775) {
            $log->syslog('err', 'Unable to create directory %s',
                $pictures_dir);
            $config_err++;
        } else {
            chmod 0775, $pictures_dir;  # set masked bits.
864

865
866
867
868
869
870
871
872
873
874
            my $index_path = $pictures_dir . '/index.html';
            my $fh;
            unless (open $fh, '>', $index_path) {
                $log->syslog(
                    'err',
                    'Unable to create %s as an empty file to protect directory',
                    $index_path
                );
            } else {
                close $fh;
875
876
877
            }
        }
    }
878

879
880
    #update_css();

root's avatar
root committed
881
882
883
884
    return undef if ($config_err);
    return 1;
}

885
## return 1 if the parameter is a known robot
886
## Valid options :
887
##    'just_try' : prevent error logs if robot is not valid
888
sub valid_robot {
889
    my $robot   = shift;
890
    my $options = shift;
891

892
893
    ## Main host
    return 1 if ($robot eq $Conf{'domain'});
894

895
    ## Missing etc directory
896
    unless (-d $Conf{'etc'} . '/' . $robot) {
897
        $log->syslog(
898
            'err',  'Robot %s undefined; no %s directory',
899
900
901
            $robot, $Conf{'etc'} . '/' . $robot
        ) unless ($options->{'just_try'});
        return undef;
902
    }
903

904
    ## Missing expl directory
905
    unless (-d $Conf{'home'} . '/' . $robot) {
906
        $log->syslog(
907
            'err',  'Robot %s undefined; no %s directory',
908
909
910
            $robot, $Conf{'home'} . '/' . $robot
        ) unless ($options->{'just_try'});
        return undef;
911
    }
912

913
914
    ## Robot not loaded
    unless (defined $Conf{'robots'}{$robot}) {
915
        $log->syslog('err', 'Robot %s was not loaded by this Sympa process',
916
917
918
            $robot)
            unless ($options->{'just_try'});
        return undef;
919
    }
920

921
922
923
924
925
926
927
928
929
    return 1;
}

## Returns the SSO record correponding to the provided sso_id
## return undef if none was found
sub get_sso_by_id {
    my %param = @_;

    unless (defined $param{'service_id'} && defined $param{'robot'}) {
930
        return undef;
931
932
933
    }

    foreach my $sso (@{$Conf{'auth_services'}{$param{'robot'}}}) {
934
        $log->syslog('notice', 'SSO: %s', $sso->{'service_id'});
935
        next unless ($sso->{'service_id'} eq $param{'service_id'});
936

937
        return $sso;
938
    }
939

940
941
942
943
944
    return undef;
}

##########################################
## Low level subs. Not supposed to be called from other modules.
945
946
947
##########################################

sub _load_auth {
948
949

    my $robot         = shift;
950
951
    my $is_main_robot = shift;
    # find appropriate auth.conf file
952
953
    my $config_file =
        _get_config_file_name({'robot' => $robot, 'file' => "auth.conf"});
954
    $log->syslog('debug', '(%s)', $config_file);
955

956
    $robot ||= $Conf{'domain'};
957
    my $line_num   = 0;
958
959
    my $config_err = 0;
    my @paragraphs;
960
    my %result;
961
962
963
964
965
966
967
968
969
970
971
972
973
    my $current_paragraph;

    my %valid_keywords = (
        'ldap' => {
            'regexp'          => '.*',
            'negative_regexp' => '.*',
            'host'            => '[\w\.\-]+(:\d+)?(\s*,\s*[\w\.\-]+(:\d+)?)*',
            'timeout'         => '\d+',
            'suffix'          => '.+',
            'bind_dn'         => '.+',
            'bind_password'   => '.+',
            'get_dn_by_uid_filter'        => '.+',
            'get_dn_by_email_filter'      => '.+',
974
975
976
977
            'email_attribute'             => Sympa::Regexps::ldap_attrdesc(),
            'alternative_email_attribute' => Sympa::Regexps::ldap_attrdesc()
                . '(\s*,\s*'
                . Sympa::Regexps::ldap_attrdesc() . ')*',
978
979
            'scope'                       => 'base|one|sub',
            'authentication_info_url'     => 'http(s)?:/.*',
980
            'use_tls'                     => 'starttls|ldaps|none',
sikeda's avatar
sikeda committed
981
982
            'use_ssl'                     => '1',                  # Obsoleted
            'use_start_tls'               => '1',                  # Obsoleted
983
            'ssl_version' => 'sslv2/3|sslv2|sslv3|tlsv1|tlsv1_1|tlsv1_2',
984
985
986
987
988
989
            'ssl_ciphers' => '[\w:]+',
            'ssl_cert'    => '.+',
            'ssl_key'     => '.+',
            'ca_verify'   => '\w+',
            'ca_path'     => '.+',
            'ca_file'     => '.+',
990
991
992
993
994
995
996
997
998
999
1000
        },

        'user_table' => {
            'regexp'          => '.*',
            'negative_regexp' => '.*'
        },

        'cas' => {
            'base_url'                   => 'http(s)?:/.*',
            'non_blocking_redirection'   => 'on|off',
            'login_path'                 => '.*',
For faster browsing, not all history is shown. View entire blame