summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md55
-rw-r--r--lib/JWebmail/Controller/Webmail.pm8
-rw-r--r--lib/JWebmail/Model/Driver/MockJSON.pm5
-rwxr-xr-xlib/JWebmail/Model/Driver/QMailAuthuser/Extract.pm76
-rw-r--r--lib/JWebmail/Model/Driver/QMailAuthuser/schema.json54
-rw-r--r--templates/headers/_display_headers.html.ep6
-rw-r--r--templates/webmail/readmail.html.ep22
7 files changed, 126 insertions, 100 deletions
diff --git a/README.md b/README.md
index 8b7fdbd..9b2eac8 100644
--- a/README.md
+++ b/README.md
@@ -7,12 +7,12 @@ oMail has not seen much progress in the last two decades
so my <jannis@fehcom.de> goal is to bring it up to date.
OMail was tied to qmail. JWebmail is not so tightly bound.
-## This includes:
+This includes:
- Using a perl web framework and leave the deprecated CGI behind.
You can still use it in a cgi setup if you like but you now
have the option of plack/psgi and fcgi as well as the
build in server hypnotoad.
-- Set up a MVC architecture instead of spaghetti.
+- Set up a MVC architecture instead of a single file.
- Improve security by only running a small part of the
model with elevated privileges.
- Make sure it works well with sqmail and its authuser
@@ -21,6 +21,7 @@ OMail was tied to qmail. JWebmail is not so tightly bound.
Maybe I even add a POP or IMAP based backends instead
of reading them from disk.
+
## License
JWebmail is available under the GNU General Public License
version 3 or later.
@@ -39,6 +40,7 @@ an external web server.
Posts
-----
+
* Complain about IPC::Open2 ignoring 'open' pragma
* Complain about undef references causing errors, and non-fine granular switch no strict 'refs'
* Thank for perldoc.org
@@ -46,6 +48,7 @@ Posts
I18N patch url_for
------------------
+
I have taken the monkey patching approach that was taken by Mojolicious::Plugin::I18N.
I used `can` to get the old method for looser coupling and used the Mojo::Util::monkey_patch
instead of manually doing it. This is probably overkill.
@@ -62,6 +65,7 @@ One issue when taking the Match approach is that it needs knowledge of the stash
values which can cause cyclic references.
I thought of three approaches injecting the modified Match instance into the class:
+
1. Extending the Mojolicious::Controller and overriding the new method.
This has the issue that inheritance is static but one can use Roles that
are dynamically consumed.
@@ -79,27 +83,28 @@ I am now using a global route containing the language after the changes to
matching have been published. No need to monkey_patch and it works well enough.
-Concepts
---------
-- Router
-- Configuration
-- Middleware (auth)
-- Controller/Handler
-- Templates
-- Template helpers
-- i18n (url rewriting)
-- Sessions (server side)
-- Flash, maybe
-- Pagination
-- Validation
-- Logging
-- Debug printing
-- Development server
-- MIME handling
+## Concepts
+
+ Router Mojolicious build-in
+ Configuration INI via Config::Tiny
+ Middleware (auth) Mojo under
+ Controller/Handler Mojolicious::Controller
+ Templates Mojo format ep
+ Template helpers Mojolicious->helper
+ i18n (url rewriting) see 'I18N patch url_for'
+ Sessions (server side) self developed plug-in
+ Flash Mojo
+ Pagination self developed
+ Validation Mojo
+ Logging Mojo::Log
+ Debug printing Data::Dumper
+ Development server Mojo
+ MIME handling
Dependencies
------------
+
- M & V
- Mojolicious
- Config::Tiny
@@ -107,3 +112,15 @@ Dependencies
- C
- Mail::Box::Manager
- Email::MIME
+
+
+## Architecture
+
+ Webserver <--> Application
+ Server
+ |
+ Application <--> Extractor
+
+The Webserver acts as a proxy to the Application Server.
+
+The Extractor is a stateless process that reads mails from a source.
diff --git a/lib/JWebmail/Controller/Webmail.pm b/lib/JWebmail/Controller/Webmail.pm
index f0f2efa..cd7b5c7 100644
--- a/lib/JWebmail/Controller/Webmail.pm
+++ b/lib/JWebmail/Controller/Webmail.pm
@@ -298,11 +298,11 @@ sub raw {
}
if (my $type = $self->param('body')) {
- if ($mail->{content_type} =~ '^multipart/') {
- my ($content) = grep {$_->{type} =~ $type} @{ $mail->{body} };
- $self->render(text => $content->{val});
+ if ($mail->{head}{content_type} =~ '^multipart/') {
+ my ($content) = grep {$_->{head}{content_type} =~ $type} @{ $mail->{body} };
+ $self->render(text => $content->{body});
}
- elsif ($mail->{content_type} =~ $type) {
+ elsif ($mail->{head}{content_type} =~ $type) {
$self->render(text => $mail->{body}) ;
}
else {
diff --git a/lib/JWebmail/Model/Driver/MockJSON.pm b/lib/JWebmail/Model/Driver/MockJSON.pm
index aafc74d..258246d 100644
--- a/lib/JWebmail/Model/Driver/MockJSON.pm
+++ b/lib/JWebmail/Model/Driver/MockJSON.pm
@@ -62,9 +62,8 @@ sub communicate {
my $s = sub {
my $sort_by = $args{args}->[LIST_SORT];
my $rev = $sort_by !~ m/^![[:lower:]]+/ ? 1 : -1;
- $sort_by =~ s/!//;
- $sort_by = "date_received" if $sort_by eq "date";
- return ($a->{$sort_by} cmp $b->{$sort_by}) * $rev;
+ $sort_by =~ s/^!//;
+ return (($a->{$sort_by}||$a->{head}{$sort_by}) cmp ($b->{$sort_by}||$b->{head}{$sort_by})) * $rev;
};
return ([sort { &$s } @{ $self->list_reply }[$args{args}->[LIST_START]..$args{args}->[LIST_END]]], 0);
}
diff --git a/lib/JWebmail/Model/Driver/QMailAuthuser/Extract.pm b/lib/JWebmail/Model/Driver/QMailAuthuser/Extract.pm
index 6639ad9..a59e265 100755
--- a/lib/JWebmail/Model/Driver/QMailAuthuser/Extract.pm
+++ b/lib/JWebmail/Model/Driver/QMailAuthuser/Extract.pm
@@ -12,7 +12,7 @@ use Carp;
use List::Util 'min';
use Encode v2.88 'decode';
-use open IO => ':encoding(UTF-8)', ':std';
+#use open IO => ':encoding(UTF-8)', ':std';
no warnings 'experimental::smartmatch';
use Mail::Box::Manager;
@@ -22,9 +22,9 @@ use constant ROOT_MAILDIR => '.';
sub main {
my ($maildir) = shift(@ARGV) =~ m/(.*)/;
- my ($su) = shift(@ARGV) =~ m/(.*)/;
- my ($user) = shift(@ARGV) =~ m/([[:alpha:]]+)/;
- my $mode = shift @ARGV; _ok($mode =~ m/([[:alpha:]-]{1,20})/);
+ my ($su) = shift(@ARGV) =~ m/(.*)/;
+ my ($user) = shift(@ARGV) =~ m/([[:alpha:]]+)/;
+ my $mode = shift @ARGV; _ok($mode =~ m/([[:alpha:]-]{1,20})/);
my @args = @ARGV;
delete $ENV{PATH};
@@ -57,7 +57,7 @@ sub main {
};
$folder->close;
- print encode_json $reply;
+ print(encode_json $reply);
if (ref $reply eq 'HASH' && $reply->{error}) {
exit 3;
}
@@ -115,16 +115,18 @@ sub list {
for my $msg (@msgs) {
my $msg2 = {
- subject => decode('MIME-Header', $msg->subject),
- from => _addresses($msg->from),
- to => _addresses($msg->to),
- cc => _addresses($msg->cc),
- bcc => _addresses($msg->bcc),
- date_received => _iso8601_utc($msg->timestamp),
- size => $msg->size,
- content_type => ''. $msg->contentType,
- mid => $msg->messageId,
- new => $msg->label('seen'),
+ mid => $msg->messageId,
+ size => $msg->size,
+ new => $msg->label('seen'),
+ head => {
+ subject => decode('MIME-Header', $msg->subject),
+ from => _addresses($msg->from),
+ to => _addresses($msg->to),
+ cc => _addresses($msg->cc),
+ bcc => _addresses($msg->bcc),
+ date => _iso8601_utc($msg->timestamp),
+ content_type => ''.$msg->contentType,
+ },
};
push @msgs2, $msg2;
}
@@ -165,19 +167,21 @@ sub _addresses {
sub read_mail {
my ($folder, $mid) = @_;
-
+
my $msg = $folder->find($mid);
return {error => 'no such message', mid => $mid} unless $msg;
return {
- subject => decode('MIME-Header', $msg->subject),
- from => _addresses($msg->from),
- to => _addresses($msg->to),
- cc => _addresses($msg->cc),
- bcc => _addresses($msg->bcc),
- date_received => _iso8601_utc($msg->timestamp),
- size => $msg->size,
- content_type => ''. $msg->contentType,
- body => do {
+ size => $msg->size,
+ head => {
+ subject => decode('MIME-Header', $msg->subject),
+ from => _addresses($msg->from),
+ to => _addresses($msg->to),
+ cc => _addresses($msg->cc),
+ bcc => _addresses($msg->bcc),
+ date => _iso8601_utc($msg->timestamp),
+ content_type => ''. $msg->contentType,
+ },
+ body => do {
if ($msg->isMultipart) {
[map {{type => ''. $_->contentType, val => '' . $_->decoded}} $msg->body->parts]
}
@@ -200,21 +204,23 @@ sub search {
return scalar(grep { $_->decoded =~ /$search_pattern/ || (decode('MIME-Header', $_->subject)) =~ /$search_pattern/ } $m->body->parts)
if $m->isMultipart;
- $m->body->decoded =~ /$search_pattern/ ||(decode('MIME-Header', $m->subject)) =~ /$search_pattern/;
+ $m->body->decoded =~ /$search_pattern/ || (decode('MIME-Header', $m->subject)) =~ /$search_pattern/;
});
my @msgs2;
for my $msg (@msgs) {
my $msg2 = {
- subject => decode('MIME-Header', $msg->subject),
- from => _addresses($msg->from),
- to => _addresses($msg->to),
- cc => _addresses($msg->cc),
- bcc => _addresses($msg->bcc),
- date_received => _iso8601_utc($msg->timestamp),
- size => $msg->size,
- content_type => ''. $msg->contentType,
- mid => $msg->messageId,
+ size => $msg->size,
+ mid => $msg->messageId,
+ head => {
+ subject => decode('MIME-Header', $msg->subject),
+ from => _addresses($msg->from),
+ to => _addresses($msg->to),
+ cc => _addresses($msg->cc),
+ bcc => _addresses($msg->bcc),
+ date => _iso8601_utc($msg->timestamp),
+ content_type => ''. $msg->contentType,
+ },
};
push @msgs2, $msg2;
}
diff --git a/lib/JWebmail/Model/Driver/QMailAuthuser/schema.json b/lib/JWebmail/Model/Driver/QMailAuthuser/schema.json
index 5d5247a..b63a5eb 100644
--- a/lib/JWebmail/Model/Driver/QMailAuthuser/schema.json
+++ b/lib/JWebmail/Model/Driver/QMailAuthuser/schema.json
@@ -33,47 +33,51 @@
"mail_head": {
"type": "object",
"properties": {
- "new": {"type": "boolean"},
- "mid": {"type": "string"},
"content_type": {"type": "string"},
- "size": {"type": "integer", "minimum": 0},
- "date_send": {"type": "string"},
- "date_received": {"type": "string"},
+ "date": {"type": "string"},
"cc": {"$ref": "#/definitions/mail_addrs"},
"bcc": {"$ref": "#/definitions/mail_addrs"},
"to": {"$ref": "#/definitions/mail_addrs"},
"from": {"$ref": "#/definitions/mail_addrs"},
"subject": {"type": "string"}
},
- "required": ["mid"]
+ "required": ["date", "from"]
},
- "list": {
+ "head_list": {
"type": "array",
"items": {
"$ref": "#/definitions/mail_head"
}
},
- "mail": {
- "$ref": "#/definitions/mail_head",
- "properties": {
- "body": {
- "anyOf": [
- {"type": "string"},
- {
- "type": "array",
- "minItems": 1,
- "items": {
- "type": "object",
- "properties": {
- "val": {"type": "string"},
- "type": {"type": "string"}
- }
- }
+ "mail_body": {
+ "anyOf": [
+ {"type": "string"},
+ {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "object",
+ "properties": {
+ "head": {"$ref": "#/definitions/mail_head"},
+ "body": {"$ref": "#/definitions/mail_body"}
}
- ]
+ }
+ },
+ {
+ "ref": "#/definitions/mail"
}
+ ]
+ },
+ "mail": {
+ "type": "object",
+ "properties": {
+ "new": {"type": "boolean"},
+ "mid": {"type": "string"},
+ "size": {"type": "integer", "minimum": 0},
+ "head": {"$ref": "#/definitions/mail_head"},
+ "body": {"$ref": "#/definitions/mail_body"}
},
- "required": ["body"]
+ "required": ["mid"]
}
}
}
diff --git a/templates/headers/_display_headers.html.ep b/templates/headers/_display_headers.html.ep
index da04873..d3f9457 100644
--- a/templates/headers/_display_headers.html.ep
+++ b/templates/headers/_display_headers.html.ep
@@ -71,14 +71,14 @@
%= ucfirst($msg->{is_multipart} ? l('yes') : l('no'));
</td>
<td>
- % my $date = parse_iso_date $msg->{date_received};
+ % my $date = parse_iso_date $msg->{head}{date};
%= join('/', $date->{mday}, $date->{month}, $date->{year}) . " $date->{hour}:$date->{min}";
</td>
<td>
- %= $msg->{from}->[0]->{name} || $msg->{from}->[0]->{email};
+ %= $msg->{head}{from}[0]{name} || $msg->{head}{from}[0]{email};
</td>
<td>
- <a href="<%= url_for('read', id => $msg->{mid}) %>"> <%= $msg->{subject} || '_' %> </a>
+ <a href="<%= url_for('read', id => $msg->{mid}) %>"> <%= $msg->{head}{subject} || '_' %> </a>
</td>
<td>
%= print_sizes10 $msg->{size};
diff --git a/templates/webmail/readmail.html.ep b/templates/webmail/readmail.html.ep
index 8c2432a..0a9fde0 100644
--- a/templates/webmail/readmail.html.ep
+++ b/templates/webmail/readmail.html.ep
@@ -12,25 +12,25 @@
<dl id=show-head>
<dt> <%= uc l 'subject' %> </dt>
- <dd> <%= $msg->{subject} %> </dd>
-%= $mail_fmt->('from', $msg->{from});
-%= $mail_fmt->('to', $msg->{to});
-%= $mail_fmt->('cc', $msg->{cc}) if !ref $msg->{cc} || @{ $msg->{cc} };
-%= $mail_fmt->('bcc', $msg->{bcc}) if !ref $msg->{bcc} || @{ $msg->{cc} };
+ <dd> <%= $msg->{head}{subject} %> </dd>
+%= $mail_fmt->('from', $msg->{head}{from});
+%= $mail_fmt->('to', $msg->{head}{to});
+%= $mail_fmt->('cc', $msg->{head}{cc}) if !ref $msg->{head}{cc} || @{ $msg->{head}{cc} };
+%= $mail_fmt->('bcc', $msg->{head}{bcc}) if !ref $msg->{head}{bcc} || @{ $msg->{head}{bcc} };
<dt> <%= uc l 'date' %> </dt>
- <dd> <%= $msg->{date_received} %> </dd>
+ <dd> <%= $msg->{head}{date} %> </dd>
<dt> <%= uc l 'size' %> </dt>
<dd> <%= print_sizes10 $msg->{size} %> </dd>
<dt> <%= uc l 'content-type' %> </dt>
- <dd> <%= $msg->{content_type} %> </dd>
+ <dd> <%= $msg->{head}{content_type} %> </dd>
</dl>
% my $body = $msg->{body};
-% if ($msg->{content_type} eq 'multipart/alternative') {
+% if ($msg->{head}{content_type} eq 'multipart/alternative') {
% for (reverse @$body) {
<div class=show-body>
-% my $x = mime_render($_->{type}, $_->{val});
+% my $x = mime_render($_->{head}{content_type}, $_->{body});
%== $x;
</div>
% last if $x;
@@ -39,13 +39,13 @@
% elsif (ref $body eq 'HASH') {
% for (%$body) {
<div class=show-body>
-%== mime_render($_->{type}, $_->{val});
+%== mime_render($_->{head}{content_type}, $_->{body});
</div>
% }
% }
% else {
<div class=show-body>
-%== mime_render($msg->{content_type}, $body);
+%== mime_render($msg->{head}{content_type}, $body);
</div>
% }