#----------------------------------------------------------------------
# A minimal IMAP4 client for QRESYNC-capable servers
# Copyright © 2015 Guilhem Moulin <guilhem@fripost.org>
#
# 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 3 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
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#----------------------------------------------------------------------
package Net::IMAP::InterIMAP v0.0.2;
use warnings;
use strict;
use Compress::Raw::Zlib qw/Z_OK Z_FULL_FLUSH Z_SYNC_FLUSH MAX_WBITS/;
use Config::Tiny ();
use IO::Select ();
use Net::SSLeay ();
use List::Util qw/all first/;
use POSIX ':signal_h';
use Socket qw/SOCK_STREAM IPPROTO_TCP AF_INET AF_INET6 SOCK_RAW :addrinfo/;
use Exporter 'import';
BEGIN {
Net::SSLeay::load_error_strings();
Net::SSLeay::SSLeay_add_ssl_algorithms();
Net::SSLeay::randomize();
our @EXPORT_OK = qw/read_config compact_set $IMAP_text $IMAP_cond/;
}
# Regexes for RFC 3501's 'ATOM-CHAR', 'ASTRING-CHAR' and 'TEXT-CHAR'.
my $RE_ATOM_CHAR = qr/[\x21\x23\x24\x26\x27\x2B-\x5B\x5E-\x7A\x7C-\x7E]/;
my $RE_ASTRING_CHAR = qr/[\x21\x23\x24\x26\x27\x2B-\x5B\x5D-\x7A\x7C-\x7E]/;
my $RE_TEXT_CHAR = qr/[\x01-\x09\x0B\x0C\x0E-\x7F]/;
# Map each option to a regexp validating its values.
my %OPTIONS = (
host => qr/\A(\P{Control}+)\z/,
port => qr/\A(\P{Control}+)\z/,
proxy => qr/\A(\P{Control}+)\z/,
type => qr/\A(imaps?|tunnel)\z/,
STARTTLS => qr/\A(YES|NO)\z/i,
username => qr/\A([\x01-\x7F]+)\z/,
password => qr/\A([\x01-\x7F]+)\z/,
auth => qr/\A($RE_ATOM_CHAR+(?: $RE_ATOM_CHAR+)*)\z/,
command => qr/\A(\P{Control}+)\z/,
'null-stderr' => qr/\A(YES|NO)\z/i,
compress => qr/\A($RE_ATOM_CHAR+(?: $RE_ATOM_CHAR+)*)\z/,
SSL_fingerprint => qr/\A((?:[A-Za-z0-9]+\$)?\p{AHex}+)\z/,
SSL_cipherlist => qr/\A(\P{Control}+)\z/,
SSL_verify => qr/\A(YES|NO)\z/i,
SSL_CApath => qr/\A(\P{Control}+)\z/,
SSL_CAfile => qr/\A(\P{Control}+)\z/,
);
# Use the same buffer size as Net::SSLeay::read(), to ensure there is
# never any pending data left in the current TLS record
my $BUFSIZE = 32768;
my $CRLF = "\x0D\x0A";
#############################################################################
# Utilities
# 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 $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;
my $h = Config::Tiny::->read($conffile);
my %configs;
foreach my $section (@$sections) {
my $conf = defined $h->{_} ? { %{$h->{_}} } : {}; # default section
$configs{$section} = $conf;
if ($section ne '_') {
die "No such section $section\n" unless defined $h->{$section};
$conf->{$_} = $h->{$section}->{$_} foreach keys %{$h->{$section
|