]> git.g-eek.se Git - interimap.git/commitdiff
pullimap, interimap: Use $XDG_CONFIG_HOME/$NAME/config as config file.
authorGuilhem Moulin <guilhem@fripost.org>
Sun, 20 Jan 2019 18:55:33 +0000 (19:55 +0100)
committerGuilhem Moulin <guilhem@fripost.org>
Sun, 20 Jan 2019 20:33:36 +0000 (21:33 +0100)
Changelog
README
interimap
interimap.md
lib/Net/IMAP/InterIMAP.pm
pullimap
pullimap.md

index 7939fa245ba793c570bf4c5938f9f0d2e0558405..7f033c8e5139766df7a38c541743e51c0d973745 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -3,6 +3,10 @@ interimap (0.4) UNRELEASED
   * pullimap: replace non RFC 5321-compliant envelope sender addresses
     (received by the IMAP FETCH ENVELOPE command) with the null address
     <>.
+  * pullimap, interimap: take configuration filename (default: "config")
+    relative to $XDG_CONFIG_HOME/$NAME (or ~/.config/$NAME), to comply
+    with the XDG specification.  Thus the previous default config file
+    $XDG_CONFIG_HOME/$NAME should become $XDG_CONFIG_HOME/$NAME/config.
   + Library: new API idle_start() and idle_stop().
   + Add support for untagged ESEARCH responses from RFC 4731.
   + pullimap: Use extended SEARCH commands (RFC 4731) if supported by
diff --git a/README b/README
index 6c3ae34fc0b649264c2c6d470418cedb1961fa0d..70bf1df5cc9d5c905aa911e3e0787828a4db05af 100644 (file)
--- a/README
+++ b/README
@@ -52,7 +52,7 @@ the AUTHENTICATE command.  For instance the following configuration
 snippet saves bandwidth and brings a significant speed gain compared to
 type=imaps.
 
-    local: $XDG_CONFIG_HOME/interimap:
+    local: $XDG_CONFIG_HOME/interimap/config:
       [remote]
       type = tunnel
       command = /usr/bin/ssh user@imap.example.net
index 049b564a198f6d47a3db310e2b2ac7b63f35458c..0f34bf9735972bea076a965cd93f26416eacf4a9 100755 (executable)
--- a/interimap
+++ b/interimap
@@ -30,7 +30,7 @@ use Fcntl qw/F_GETFD F_SETFD FD_CLOEXEC/;
 use List::Util 'first';
 
 use lib 'lib';
-use Net::IMAP::InterIMAP qw/read_config compact_set/;
+use Net::IMAP::InterIMAP qw/xdg_basedir read_config compact_set/;
 
 # Clean up PATH
 $ENV{PATH} = join ':', qw{/usr/bin /bin};
@@ -63,20 +63,24 @@ my $COMMAND = do {
 };
 usage(1) if defined $COMMAND and (($COMMAND eq 'delete' and !@ARGV) or ($COMMAND eq 'rename' and $#ARGV != 1));
 usage(1) if defined $COMMAND and (defined $CONFIG{watch} or defined $CONFIG{notify});
-usage(1) if $CONFIG{target} and !(defined $COMMAND and ($COMMAND eq 'delete'or $COMMAND eq 'rename'));
+usage(1) if $CONFIG{target} and !(defined $COMMAND and ($COMMAND eq 'delete' or $COMMAND eq 'rename'));
 $CONFIG{watch} = $CONFIG{notify} ? 900 : 60 if (defined $CONFIG{watch} or $CONFIG{notify}) and !$CONFIG{watch};
 @ARGV = map {uc $_ eq 'INBOX' ? 'INBOX' : $_ } @ARGV; # INBOX is case-insensitive
 die "Invalid mailbox name $_" foreach grep !/\A([\x01-\x7F]+)\z/, @ARGV;
 
 
-my $CONF = read_config( delete $CONFIG{config} // $NAME
-                      , [qw/_ local remote/]
-                      , database => qr/\A(\P{Control}+)\z/
-                      , logfile => qr/\A(\/\P{Control}+)\z/
-                      , 'list-mailbox' => qr/\A([\x01-\x09\x0B\x0C\x0E-\x7F]+)\z/
-                      , 'list-select-opts' => qr/\A([\x21\x23\x24\x26\x27\x2B-\x5B\x5E-\x7A\x7C-\x7E]+)\z/
-                      , 'ignore-mailbox' => qr/\A([\x01-\x09\x0B\x0C\x0E-\x7F]+)\z/
-                      );
+my $CONF = do {
+    my $conffile = delete($CONFIG{config}) // "config";
+    $conffile = xdg_basedir( XDG_CONFIG_HOME => ".config", $NAME, $conffile );
+    read_config( $conffile
+               , [qw/_ local remote/]
+               , database => qr/\A(\P{Control}+)\z/
+               , logfile => qr/\A(\/\P{Control}+)\z/
+               , 'list-mailbox' => qr/\A([\x01-\x09\x0B\x0C\x0E-\x7F]+)\z/
+               , 'list-select-opts' => qr/\A([\x21\x23\x24\x26\x27\x2B-\x5B\x5E-\x7A\x7C-\x7E]+)\z/
+               , 'ignore-mailbox' => qr/\A([\x01-\x09\x0B\x0C\x0E-\x7F]+)\z/
+               );
+};
 my ($DBFILE, $LOGGER_FD);
 
 {
@@ -84,16 +88,7 @@ my ($DBFILE, $LOGGER_FD);
     $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 "Can't mkdir $dir: $!\n";
-        }
-    }
+    $DBFILE = xdg_basedir( XDG_DATA_HOME => ".local/share", $NAME, $DBFILE );
 
     if (defined $CONF->{_} and defined $CONF->{_}->{logfile}) {
         require 'POSIX.pm';
index 4a321f12af8956915fa1bd97c1eae5050f1b3348..ca83dba69d7ef73f2343af4121d3ffd93f05bb42 100644 (file)
@@ -140,8 +140,8 @@ Options
 `--config=`*FILE*
 
 :   Specify an alternate [configuration file](#configuration-file).
-    Relative paths start from *$XDG_CONFIG_HOME*, or *~/.config* if the
-    `XDG_CONFIG_HOME` environment variable is unset.
+    Relative paths start from *$XDG_CONFIG_HOME/interimap*, or *~/.config/interimap*
+    if the `XDG_CONFIG_HOME` environment variable is unset.
 
 `--target={local,remote,database}`
 
@@ -192,9 +192,9 @@ Configuration file
 ==================
 
 Unless told otherwise by the `--config=FILE` command-line option,
-`interimap` reads its configuration from *$XDG_CONFIG_HOME/interimap*
-(or *~/.config/interimap* if the `XDG_CONFIG_HOME` environment variable
-is unset) as an [INI file].
+`interimap` reads its configuration from *$XDG_CONFIG_HOME/interimap/config*
+(or *~/.config/interimap/config* if the `XDG_CONFIG_HOME` environment
+variable is unset) as an [INI file].
 The syntax of the configuration file is a series of `OPTION=VALUE`
 lines organized under some `[SECTION]`; lines starting with a ‘#’ or
 ‘;’ character are ignored as comments.
index 67b3ce5a91b5c7bb27a93e2cd0f1a48e83601d54..7b0a2be76ae6cb34d1f578823427cdd94d0db898 100644 (file)
@@ -22,7 +22,7 @@ use strict;
 
 use Compress::Raw::Zlib qw/Z_OK Z_FULL_FLUSH Z_SYNC_FLUSH MAX_WBITS/;
 use Config::Tiny ();
-use Errno 'EINTR';
+use Errno qw/EEXIST EINTR/;
 use Fcntl qw/F_GETFD F_SETFD FD_CLOEXEC/;
 use Net::SSLeay ();
 use List::Util qw/all first/;
@@ -35,7 +35,7 @@ BEGIN {
     Net::SSLeay::SSLeay_add_ssl_algorithms();
     Net::SSLeay::randomize();
 
-    our @EXPORT_OK = qw/read_config compact_set $IMAP_text $IMAP_cond
+    our @EXPORT_OK = qw/xdg_basedir read_config compact_set $IMAP_text $IMAP_cond
                         slurp is_dirty has_new_mails/;
 }
 
@@ -76,6 +76,35 @@ my $CRLF = "\x0D\x0A";
 #############################################################################
 # Utilities
 
+# xdg_basedir($xdg_variable, $default, $subdir, $path)
+#   Return $path if $path is absolute.  Otherwise, return
+#   "$ENV{$xdg_variable}/$subdir/$path" (resp. "~/$default/$subdir/path"
+#   if the "$xdg_variable" environment variable is not set).
+#   An error is raised if "$ENV{$xdg_variable}" (resp. "~/$default") is
+#   not an existing absolute directory.
+#   If "$ENV{$xdg_variable}/$subdir" doesn't exist, it is created with
+#   mode 0700.
+sub xdg_basedir($$$$) {
+    my ($xdg_variable, $default, $subdir, $path) = @_;
+    $path =~ /\A(\p{Print}+)\z/ or die "Insecure $path";
+    $path = $1;
+    return $path if $path =~ /\A\//;
+
+    my $basedir = $ENV{$xdg_variable};
+    unless (defined $basedir) {
+        my @getent = getpwuid($>);
+        $basedir = $getent[7] ."/". $default;
+    }
+    die "No such directory: ", $basedir unless -d $basedir;
+    $basedir .= "/".$subdir;
+    $basedir =~ /\A(\/\p{Print}+)\z/ or die "Insecure $basedir";
+    $basedir = $1;
+    unless (mkdir ($basedir, 0700)) {
+        die "Couldn't create $basedir: $!\n" unless $! == EEXIST;
+    }
+    return $basedir ."/". $path;
+}
+
 # read_config($conffile, $sections, %opts)
 #   Read $conffile's default section, then each section in the array
 #   reference $section (which takes precedence).  %opts extends %OPTIONS
@@ -85,9 +114,6 @@ sub read_config($$%) {
     my $sections = shift;
     my %opts = (%OPTIONS, @_);
 
-    $conffile = ($ENV{XDG_CONFIG_HOME} // "$ENV{HOME}/.config") .'/'. $conffile
-        unless $conffile =~ /\A\//; # relative path
-
     die "No such config file $conffile\n"
         unless defined $conffile and -f $conffile and -r $conffile;
 
index 736bbff92710046ed21d291c7f4b77a7bff85284..e666114370dbdeeac119fe2c1203911266740afd 100755 (executable)
--- a/pullimap
+++ b/pullimap
@@ -31,7 +31,7 @@ use List::Util 'first';
 use Socket qw/PF_INET PF_INET6 SOCK_STREAM/;
 
 use lib 'lib';
-use Net::IMAP::InterIMAP qw/read_config compact_set/;
+use Net::IMAP::InterIMAP qw/xdg_basedir read_config compact_set/;
 
 # Clean up PATH
 $ENV{PATH} = join ':', qw{/usr/bin /bin};
@@ -59,15 +59,19 @@ usage(1) unless $#ARGV == 0 and $ARGV[0] ne '_';
 #######################################################################
 # Read and validate configuration
 #
-my $CONF = read_config( delete $CONFIG{config} // $NAME,
-                      , [$ARGV[0]]
-                      , statefile => qr/\A(\P{Control}+)\z/
-                      , mailbox => qr/\A([\x01-\x7F]+)\z/
-                      , 'deliver-method' => qr/\A([ls]mtp:\[.*\]:\d+)\z/
-                      , 'deliver-ehlo' => qr/\A(\P{Control}+)\z/
-                      , 'deliver-rcpt' => qr/\A(\P{Control}+)\z/
-                      , 'purge-after' => qr/\A(\d*)\z/
-                      )->{$ARGV[0]};
+my $CONF = do {
+    my $conffile = delete($CONFIG{config}) // "config";
+    $conffile = xdg_basedir( XDG_CONFIG_HOME => ".config", $NAME, $conffile );
+    read_config( $conffile
+               , [$ARGV[0]]
+               , statefile => qr/\A(\P{Control}+)\z/
+               , mailbox => qr/\A([\x01-\x7F]+)\z/
+               , 'deliver-method' => qr/\A([ls]mtp:\[.*\]:\d+)\z/
+               , 'deliver-ehlo' => qr/\A(\P{Control}+)\z/
+               , 'deliver-rcpt' => qr/\A(\P{Control}+)\z/
+               , 'purge-after' => qr/\A(\d*)\z/
+               )->{$ARGV[0]};
+};
 
 my ($MAILBOX, $STATE);
 do {
@@ -75,16 +79,7 @@ do {
 
     my $statefile = $CONF->{statefile} // $ARGV[0];
     die "Missing option statefile" unless defined $statefile;
-    $statefile = $statefile =~ /\A(\p{Print}+)\z/ ? $1 : die "Insecure $statefile";
-
-    unless ($statefile =~ /\A\//) {
-        my $dir = ($ENV{XDG_DATA_HOME} // "$ENV{HOME}/.local/share") .'/'. $NAME;
-        $dir = $dir =~ /\A(\/\p{Print}+)\z/ ? $1 : die "Insecure $dir";
-        $statefile = $dir .'/'. $statefile;
-        unless (-d $dir) {
-            mkdir $dir, 0700 or die "Can't mkdir $dir: $!\n";
-        }
-    }
+    $statefile = xdg_basedir( XDG_DATA_HOME => ".local/share", $NAME, $statefile );
 
     sysopen($STATE, $statefile, O_CREAT|O_RDWR|O_DSYNC, 0600) or die "Can't open $statefile: $!";
     # XXX we need to pack the struct flock manually: not portable!
index 06035ace6c5f49d6c12f8bca9f10093aa9b8299b..eac8efadde5b7483897529d3512c207d18b4def6 100644 (file)
@@ -32,8 +32,8 @@ Options
 `--config=`*FILE*
 
 :   Specify an alternate [configuration file](#configuration-file).
-    Relative paths start from *$XDG_CONFIG_HOME*, or *~/.config* if the
-    `XDG_CONFIG_HOME` environment variable is unset.
+    Relative paths start from *$XDG_CONFIG_HOME/pullimap*, or *~/.config/pullimap*
+    if the `XDG_CONFIG_HOME` environment variable is unset.
 
 `--idle`[`=`*seconds*]
 
@@ -74,9 +74,9 @@ Configuration file
 ==================
 
 Unless told otherwise by the `--config=FILE` command-line option,
-`pullimap` reads its configuration from *$XDG_CONFIG_HOME/pullimap* (or
-*~/.config/pullimap* if the `XDG_CONFIG_HOME` environment variable is
-unset) as an [INI file].
+`pullimap` reads its configuration from *$XDG_CONFIG_HOME/pullimap/config*
+(or *~/.config/pullimap/config* if the `XDG_CONFIG_HOME` environment variable
+is unset) as an [INI file].
 The syntax of the configuration file is a series of `OPTION=VALUE`
 lines organized under some `[SECTION]`; lines starting with a ‘#’ or
 ‘;’ character are ignored as comments.