summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore15
-rw-r--r--lib/Config/ClawsMail.pm35
-rw-r--r--lib/Config/ClawsMail/Account.pm80
-rw-r--r--lib/Config/ClawsMail/Password.pm110
-rw-r--r--lib/Config/ClawsMail/Server.pm29
-rw-r--r--t/send.t29
6 files changed, 298 insertions, 0 deletions
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 <memory.h>
+
+#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;
+