diff options
Diffstat (limited to 'lib/Config/ClawsMail')
-rw-r--r-- | lib/Config/ClawsMail/Account.pm | 86 | ||||
-rw-r--r-- | lib/Config/ClawsMail/MainConfigParser.pm | 15 | ||||
-rw-r--r-- | lib/Config/ClawsMail/PasswordStore.pm | 59 | ||||
-rw-r--r-- | lib/Config/ClawsMail/PasswordStoreParser.pm | 21 | ||||
-rw-r--r-- | lib/Config/ClawsMail/Server.pm | 28 |
5 files changed, 209 insertions, 0 deletions
diff --git a/lib/Config/ClawsMail/Account.pm b/lib/Config/ClawsMail/Account.pm new file mode 100644 index 0000000..67fc707 --- /dev/null +++ b/lib/Config/ClawsMail/Account.pm @@ -0,0 +1,86 @@ +package Config::ClawsMail::Account; +use v5.26; +use Moo; +# VERSION +use Types::Standard qw(Str InstanceOf); +use Config::ClawsMail::Server; +use namespace::clean; + +# ABSTRACT: Claws-Mail account + +has [qw(account_name name address)] => ( + is => 'ro', + required => 1, + isa => Str, +); + +has [qw(imap smtp)] => ( + is => 'ro', + isa => InstanceOf['Config::ClawsMail::Server'], +); + +my @ssl_string=qw(no ssl starttls); +sub new_from_config { + my ($class,$args) = @_; + + my $config = $args->{account_config}; + return unless $config->{protocol} eq '1'; + + my $section = $args->{account_section}; + + my $password_store = $args->{password_store}; + + my $imap_server = Config::ClawsMail::Server->new({ + host => $config->{receive_server}, + port => ( + $config->{set_imapport} + ? $config->{imap_port} + : $config->{ssl_imap} == 1 + ? scalar getservbyname('imaps','tcp') + : scalar getservbyname('imap','tcp') + ), + ssl => $ssl_string[$config->{ssl_imap}], + %{$config}{qw(user_id)}, + password => $password_store->password_for($section,'recv'), + }); + + my $smtp_server = Config::ClawsMail::Server->new({ + host => $config->{smtp_server}||$config->{receive_server}, + port => ( + $config->{set_smtpport} + ? $config->{smtp_port} + : $config->{ssl_smtp} == 1 + ? scalar getservbyname('smtps','tcp') + : scalar getservbyname('smtp','tcp') + ), + ssl => $ssl_string[$config->{ssl_smtp}], + ( $config->{use_smtp_auth} ? ( + user_id => $config->{smtp_user_id} || $config->{user_id}, + password => $password_store->password_for($section,'send') || $password_store->password_for($section,'recv') + ) : () ), + }); + + return $class->new({ + %{$config}{qw(account_name name address)}, + imap => $imap_server, + smtp => $smtp_server, + }); +} + +sub email_transport { + my ($self) = @_; + + require Email::Sender::Transport::SMTPS; + my $smtp = $self->smtp; + return Email::Sender::Transport::SMTPS->new( + host => $smtp->host, + port => $smtp->port, + ssl => $smtp->ssl, + ( $smtp->user_id ? ( + sasl_username => $smtp->user_id, + sasl_password => $smtp->password, + ) : () ) + ); +} + +1; diff --git a/lib/Config/ClawsMail/MainConfigParser.pm b/lib/Config/ClawsMail/MainConfigParser.pm new file mode 100644 index 0000000..4283e05 --- /dev/null +++ b/lib/Config/ClawsMail/MainConfigParser.pm @@ -0,0 +1,15 @@ +package Config::ClawsMail::MainConfigParser; +use v5.26; +use strict; +use warnings; +# VERSION +use parent 'Config::INI::Reader'; +# ABSTRACT: Config::INI::Reader tweaked for clawsrc + +sub handle_unparsed_line { + my ($self, $line, $handle) = @_; + return if $line =~ m{\.so$}; # plugin name + return $self->next::method($line,$handle); +} + +1; diff --git a/lib/Config/ClawsMail/PasswordStore.pm b/lib/Config/ClawsMail/PasswordStore.pm new file mode 100644 index 0000000..52a0a67 --- /dev/null +++ b/lib/Config/ClawsMail/PasswordStore.pm @@ -0,0 +1,59 @@ +package Config::ClawsMail::PasswordStore; +use v5.26; +use Moo; +# VERSION +use experimental 'signatures'; +use Types::Standard qw(HashRef); +use Crypt::Misc qw(decode_b64); +use Crypt::KeyDerivation qw(pbkdf2); +use Crypt::Cipher::AES; +use Crypt::Mode::CBC; +use namespace::clean; + +# ABSTRACT: decrypt Claws-Mail password store + +sub PASSCRYPT_KEY() { 'passkey0' } + +has raw_data => ( + is => 'ro', + required => 1, + isa => HashRef, +); + +has master_password => ( is => 'ro' ); + +has master_salt_bs64 => ( is => 'ro', required => 1 ); +has master_salt => ( is => 'lazy' ); +sub _build_master_salt($self) { decode_b64($self->master_salt_bs64) } + +sub decrypt($self,$input) { + return $input unless $input && $input =~ m{\A \{ ([a-z0-9-]+),(\d+) \} (.+) \z}smxi; + my ($algo,$rounds,$ciphertext) = ($1,$2,$3); + + die 'unknown algo' unless $algo eq 'AES-256-CBC'; + + my $key = pbkdf2( + $self->master_password || PASSCRYPT_KEY(), + $self->master_salt, + $rounds, 'SHA1', 32, # 32 bytes = 256 bits,for AES-256 + ); + + my $cipher = Crypt::Mode::CBC->new('AES', 0); # 0 = no padding + + $ciphertext = decode_b64($ciphertext); + + # claws sets up 16 random bytes as IV?? + my $iv = '0123456789abcdef'; + + my $cleartext = $cipher->decrypt($ciphertext, $key, $iv); + + # the first 16 bytes are generated from the IV, we don't care + # about them + return substr($cleartext,16); +} + +sub password_for($self,$section,$key) { + return $self->decrypt($self->raw_data->{$section}{$key}); +} + +1; diff --git a/lib/Config/ClawsMail/PasswordStoreParser.pm b/lib/Config/ClawsMail/PasswordStoreParser.pm new file mode 100644 index 0000000..fe76b1e --- /dev/null +++ b/lib/Config/ClawsMail/PasswordStoreParser.pm @@ -0,0 +1,21 @@ +package Config::ClawsMail::PasswordStoreParser; +use v5.26; +use strict; +use warnings; +# VERSION +use parent 'Config::INI::Reader'; +# ABSTRACT: Config::INI::Reader tweaked for passwordstorerc + +sub parse_section_header { + my ($head) = $_[1] =~ /^\s*\[\s*(.+?)\s*\]\s*$/ + or return; + $head =~ s{account:}{Account: }; + return $head; +} + +sub parse_value_assignment { + return ($1, $2) if $_[1] =~ /^\s*([^\s\pC]+?)\s+(.*?)\s*$/; + return; +} + +1; diff --git a/lib/Config/ClawsMail/Server.pm b/lib/Config/ClawsMail/Server.pm new file mode 100644 index 0000000..e7c64bb --- /dev/null +++ b/lib/Config/ClawsMail/Server.pm @@ -0,0 +1,28 @@ +package Config::ClawsMail::Server; +use v5.26; +use Moo; +# VERSION +use Types::Standard qw(Str Enum); +use Config::ClawsMail::Password; +use namespace::clean; + +# ABSTRACT: Claws-Mail send/receive server + +has [qw(host port)] => ( + is => 'ro', + required => 1, + isa => Str, +); + +has [qw(user_id password)] => ( + is => 'ro', + isa => Str, +); + +has ssl => ( + is => 'ro', + isa => Enum[qw(no ssl starttls)], + default => sub { 'no' }, +); + +1; |