From f25286a60b8ad26994c5c88c60b7c0ec998ae803 Mon Sep 17 00:00:00 2001 From: dakkar Date: Tue, 26 Jan 2016 18:12:37 +0000 Subject: first commit, it works already --- .gitignore | 15 ++++++ lib/Config/ClawsMail.pm | 35 +++++++++++++ lib/Config/ClawsMail/Account.pm | 80 ++++++++++++++++++++++++++++ lib/Config/ClawsMail/Password.pm | 110 +++++++++++++++++++++++++++++++++++++++ lib/Config/ClawsMail/Server.pm | 29 +++++++++++ t/send.t | 29 +++++++++++ 6 files changed, 298 insertions(+) create mode 100644 .gitignore create mode 100644 lib/Config/ClawsMail.pm create mode 100644 lib/Config/ClawsMail/Account.pm create mode 100644 lib/Config/ClawsMail/Password.pm create mode 100644 lib/Config/ClawsMail/Server.pm create mode 100644 t/send.t diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8bedcf2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +blib +pm_to_blib +*.sw? +Makefile +Makefile.old +MANIFEST.bak +*.tar.gz +/inc/ +/META.* +/MYMETA.* +.prove +*~ +/.build/ +/_Inline/ + diff --git a/lib/Config/ClawsMail.pm b/lib/Config/ClawsMail.pm new file mode 100644 index 0000000..4543a18 --- /dev/null +++ b/lib/Config/ClawsMail.pm @@ -0,0 +1,35 @@ +package Config::ClawsMail; +use Moo; +use Config::INI::Reader; +use Config::ClawsMail::Account; +use Types::Standard qw(HashRef InstanceOf); +use Path::Tiny; +use namespace::clean; + +has accounts => ( + is => 'ro', + isa => HashRef[InstanceOf['Config::ClawsMail::Account']], + default => sub { +{}; }, +); + +sub BUILDARGS { + my ($class,@etc) = @_; + + my $args = $class->next::method(@etc); + return $args if $args->{accounts}; + + my $config_file = delete $args->{config_file} + || path($ENV{HOME},'.claws-mail','accountrc'); + my $config_hash = Config::INI::Reader->read_file( + $config_file, + ); + + for my $account_conf (values %{$config_hash}) { + my $account = Config::ClawsMail::Account->new_from_config($account_conf); + $args->{accounts}{$account->account_name} = $account; + } + + return $args; +} + +1; diff --git a/lib/Config/ClawsMail/Account.pm b/lib/Config/ClawsMail/Account.pm new file mode 100644 index 0000000..c00bad4 --- /dev/null +++ b/lib/Config/ClawsMail/Account.pm @@ -0,0 +1,80 @@ +package Config::ClawsMail::Account; +use Moo; +use 5.020; +use Types::Standard qw(Str InstanceOf); +use Config::ClawsMail::Server; +use namespace::clean; + +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,$config) = @_; + die "unhandled protocol" unless $config->{protocol} eq '3'; + + 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)}, + }); + + 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 => ( + $config->{smtp_password} eq '!' + ? $config->{password} + : $config->{smtp_password} + ), + ) : () ), + }); + + 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->cleartext_password, + ) : () ) + ); +} + +1; diff --git a/lib/Config/ClawsMail/Password.pm b/lib/Config/ClawsMail/Password.pm new file mode 100644 index 0000000..c3e1b19 --- /dev/null +++ b/lib/Config/ClawsMail/Password.pm @@ -0,0 +1,110 @@ +package Config::ClawsMail::Password; +use strict; +use warnings; +use Inline 'C'; +use MIME::Base64; +use namespace::clean -except => [qw(decrypt_password)]; + +sub cleartext_password { + my ($password) = @_; + return decrypt_password(decode_base64($password)); +} + +1; + +__DATA__ +__C__ +#include + +#define PASSCRYPT_KEY "passkey0" +unsigned char crypt_cfb_iv[64]; +int crypt_cfb_blocksize = 8; /* 8 for DES */ + +static void crypt_unpack(unsigned char *a) { + int i, j; + + for (i = 7; i >= 0; --i) + for (j = 7; j >= 0; --j) + a[(i << 3) + j] = (a[i] & (0x80 >> j)) != 0; +} + +static void crypt_cfb_xor( + unsigned char *to, + const unsigned char *from, + unsigned len) { + unsigned i; + unsigned j; + unsigned char c; + + for (i = 0; i < len; i++) { + c = 0; + for (j = 0; j < 8; j++) + c = (c << 1) | *from++; + *to++ ^= c; + } +} + +static void crypt_cfb_shift( + unsigned char *to, + const unsigned char *from, + unsigned len) { + unsigned i; + unsigned j; + unsigned k; + + if (len < crypt_cfb_blocksize) { + i = len * 8; + j = crypt_cfb_blocksize * 8; + for (k = i; k < j; k++) { + to[0] = to[i]; + ++to; + } + } + + for (i = 0; i < len; i++) { + j = *from++; + for (k = 0x80; k; k >>= 1) + *to++ = ((j & k) != 0); + } +} + +static void crypt_cfb_buf( + const char key[8], + unsigned char *buf, + unsigned len, + unsigned chunksize, + int decrypt) { + unsigned char temp[64]; + + memcpy(temp, key, 8); + crypt_unpack(temp); + setkey((const char *) temp); + memset(temp, 0, sizeof(temp)); + + memset(crypt_cfb_iv, 0, sizeof(crypt_cfb_iv)); + + if (chunksize > crypt_cfb_blocksize) + chunksize = crypt_cfb_blocksize; + + while (len) { + memcpy(temp, crypt_cfb_iv, sizeof(temp)); + encrypt((char *) temp, 0); + if (chunksize > len) + chunksize = len; + if (decrypt) + crypt_cfb_shift(crypt_cfb_iv, buf, chunksize); + crypt_cfb_xor((unsigned char *) buf, temp, chunksize); + if (!decrypt) + crypt_cfb_shift(crypt_cfb_iv, buf, chunksize); + len -= chunksize; + buf += chunksize; + } +} + +SV* decrypt_password(SV* password) { + size_t len = sv_len(password); + char *tmp = (char*)malloc(len); + memcpy(tmp,SvPVbyte(password,len),len); + crypt_cfb_buf(PASSCRYPT_KEY, tmp, len, 1, 1 ); + return newSVpvn(tmp,len); +} diff --git a/lib/Config/ClawsMail/Server.pm b/lib/Config/ClawsMail/Server.pm new file mode 100644 index 0000000..e3eefab --- /dev/null +++ b/lib/Config/ClawsMail/Server.pm @@ -0,0 +1,29 @@ +package Config::ClawsMail::Server; +use Moo; +use Types::Standard qw(Str Enum); +use Config::ClawsMail::Password; +use namespace::clean; + +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' }, +); + +sub cleartext_password { + my ($self) = @_; + return Config::ClawsMail::Password::cleartext_password($self->password); +} + +1; diff --git a/t/send.t b/t/send.t new file mode 100644 index 0000000..69d21a8 --- /dev/null +++ b/t/send.t @@ -0,0 +1,29 @@ +#!perl +use strict; +use warnings; +use Test::More; +use Config::ClawsMail; +use Email::Sender::Simple qw(sendmail); +use Email::Simple; +use Email::Simple::Creator; + +my $claws = Config::ClawsMail->new(); +my $account = $claws->accounts->{BB}; +my $address = sprintf q{%s <%s>}, + $account->name, $account->address; + +my $email = Email::Simple->create( + header => [ + To => $address, + From => $address, + Subject => 'config::claws-mail test', + ], + body => "test for Config::ClawsMail\n", +); +ok( + sendmail($email, {transport => $account->email_transport}), + 'sending should work', +); + +done_testing; + -- cgit v1.2.3