From cd040238114c91f4942e0847448a84830fac4f7c Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 23 Jul 2015 21:15:01 +0200 Subject: Allow custom database path. --- imapsync | 34 ++++++++++++++++++++++++--------- lib/Net/IMAP/Sync.pm | 54 +++++++++++++++++++++++++++++----------------------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/imapsync b/imapsync index a18e4cd..4ad95f3 100755 --- a/imapsync +++ b/imapsync @@ -47,10 +47,29 @@ usage(1) unless GetOptions(\%CONFIG, qw/debug help|h config=s quiet|q oneshot|1/ usage(0) if $CONFIG{help}; -my $CONFFILE = delete $CONFIG{config} // 'imapsync'; -my $CACHEDIR = './imapsync.cache'; # XXX use a config option -my $DBFILE = "$CACHEDIR/imap.guilhem.org.db"; -my $LOCKFILE = "$CACHEDIR/.imap.guilhem.org.lck"; +my $CONF = read_config( delete $CONFIG{config} // $NAME + , [qw/_ local remote/] + , database => qr/\A(\P{Control}+)\z/ ); +my ($DBFILE, $LOCKFILE); + +{ + $DBFILE = $CONF->{_}->{database} if defined $CONF->{_}; + $DBFILE //= $CONF->{remote}->{host}.'.db' if defined $CONF->{remote}; + $DBFILE //= $CONF->{local}->{host}. '.db' if defined $CONF->{local}; + die "Missing option database" unless defined $DBFILE; + + unless ($DBFILE =~ /\A\//) { + my $dir = ($ENV{XDG_DATA_HOME} // "$ENV{HOME}/.local/share") .'/'. $NAME; + $dir =~ /\A(\/\p{Print}+)\z/ or die "Insecure $dir"; + $dir = $1; + $DBFILE = $dir .'/'. $DBFILE; + unless (-d $dir) { + mkdir $dir, 0700 or die "Cannot mkdir $dir: $!\n"; + } + } + + $LOCKFILE = $DBFILE =~ s/([^\/]+)\z/.$1.lck/r; +} my ($DBH, $IMAP); @@ -67,10 +86,7 @@ $SIG{$_} = sub { clean(); die "$!\n"; } foreach qw/INT TERM/; ############################################################################# # Lock the database { - if (!-d $CACHEDIR) { - mkdir $CACHEDIR, 0700 or die "Cannot mkdir $CACHEDIR: $!\n"; - } - elsif (-f $LOCKFILE) { + if (-f $LOCKFILE) { open my $lock, '<', $LOCKFILE or die "Cannot open $LOCKFILE: $!\n"; my $pid = <$lock>; close $lock; @@ -164,7 +180,7 @@ sub msg($@) { # Connect to the local and remote IMAP servers foreach my $name (qw/local remote/) { - my %config = Net::IMAP::Sync::read_config($CONFFILE, $name); + my %config = %{$CONF->{$name}}; $config{$_} = $CONFIG{$_} foreach keys %CONFIG; $config{enable} = 'QRESYNC'; $config{name} = $name; diff --git a/lib/Net/IMAP/Sync.pm b/lib/Net/IMAP/Sync.pm index 2c2a434..2aff76c 100644 --- a/lib/Net/IMAP/Sync.pm +++ b/lib/Net/IMAP/Sync.pm @@ -57,13 +57,13 @@ my %OPTIONS = ( ############################################################################# # Utilities -# read_config($conffile, $section, %opts) -# Read $conffile's default section, then $section (which takes -# precedence). %opts extends %OPTIONS and maps each option to a -# regexp validating its values. +# read_config($conffile, $sections, %opts) +# Read $conffile's default section, then each section in the array +# reference $section (which takes precedence). %opts extends %OPTIONS +# and maps each option to a regexp validating its values. sub read_config($$%) { my $conffile = shift; - my $section = shift; + my $sections = shift; my %opts = (%OPTIONS, @_); $conffile = ($ENV{XDG_CONFIG_HOME} // "$ENV{HOME}/.config") .'/'. $conffile @@ -73,26 +73,32 @@ sub read_config($$%) { unless defined $conffile and -f $conffile and -r $conffile; my $h = Config::Tiny::->read($conffile); - die "No such section $section\n" unless defined $h->{$section}; - - my $conf = $h->{_}; # default section - $conf->{$_} = $h->{$section}->{$_} foreach keys %{$h->{$section}}; - - # default values - $conf->{type} //= 'imaps'; - $conf->{host} //= 'localhost'; - $conf->{port} //= $conf->{type} eq 'imaps' ? 993 : $conf->{type} eq 'imap' ? 143 : undef; - $conf->{auth} //= 'PLAIN LOGIN'; - $conf->{STARTTLS} //= 'TRUE'; - - # untaint and validate the config - foreach my $k (keys %$conf) { - die "Invalid option $k\n" unless defined $opts{$k}; - next unless defined $conf->{$k}; - die "Invalid option $k = $conf->{$k}\n" unless $conf->{$k} =~ $opts{$k}; - $conf->{$k} = $1; + + my %configs; + foreach my $section (@$sections) { + my $conf = { %{$h->{_}} }; # default section + $configs{$section} = $conf; + next unless defined $section and $section ne '_'; + + die "No such section $section\n" unless defined $h->{$section}; + $conf->{$_} = $h->{$section}->{$_} foreach keys %{$h->{$section}}; + + # default values + $conf->{type} //= 'imaps'; + $conf->{host} //= 'localhost'; + $conf->{port} //= $conf->{type} eq 'imaps' ? 993 : $conf->{type} eq 'imap' ? 143 : undef; + $conf->{auth} //= 'PLAIN LOGIN'; + $conf->{STARTTLS} //= 'TRUE'; + + # untaint and validate the config + foreach my $k (keys %$conf) { + die "Invalid option $k\n" unless defined $opts{$k}; + next unless defined $conf->{$k}; + die "Invalid option $k = $conf->{$k}\n" unless $conf->{$k} =~ $opts{$k}; + $conf->{$k} = $1; + } } - return %$conf; + return \%configs; } -- cgit v1.2.3