From 8ee2d7149baa58ea225cb40e0f95030ee21f1081 Mon Sep 17 00:00:00 2001 From: "Jannis M. Hoffmann" Date: Mon, 13 Mar 2023 21:34:03 +0100 Subject: Split up Helper plugin and added Views instead --- lib/JWebmail/View/RenderMail.pm | 191 ++++++++++++++++++++++++++++++++++++++++ lib/JWebmail/View/Webmail.pm | 88 ++++++++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 lib/JWebmail/View/RenderMail.pm create mode 100644 lib/JWebmail/View/Webmail.pm (limited to 'lib/JWebmail/View') diff --git a/lib/JWebmail/View/RenderMail.pm b/lib/JWebmail/View/RenderMail.pm new file mode 100644 index 0000000..07f356c --- /dev/null +++ b/lib/JWebmail/View/RenderMail.pm @@ -0,0 +1,191 @@ +package JWebmail::View::RenderMail; + +use Mojo::Base -base; + +use Mojo::ByteStream; +use Mojo::Util 'xml_escape'; + + +has 'c'; + + +sub render_text_plain { + my ($_self, $_subtype, $content, $_path) = @_; + + $content = xml_escape($content); + $content =~ s/\n/
/g; + + return qq'
\n $content
\n'; +} + +sub render_text_html { + my ($self, $_subtype, $_content, $path) = @_; + + my $url = $self->c->url_for('raw', id => $self->c->stash('id')); + $url = $url->query(path => join('.', @$path)) if @$path; + + return qq'\n'; +} + +sub render_image { + my ($_self, $subtype, $content, $_path) = @_; + + return qq''; +} + +sub render_multipart_alternative { + my ($self, $_subtype, $content, $path) = @_; + + my $parts = $content->{parts}; + my $R = qq'
'; + my $i = 0; + my $end; + + for (reverse @$parts) { + if (!$end) { + my $x = $self->mime_render(to_mime_types($_->{head}), $_->{body}, [@$path, $#$parts-$i]); + if ($x) { + $R .= $x; + $end = 1; + } + } + else { + $R .= '
'; + $R .= ''; + $R .= to_mime_type($_->{head}); + $R .= "\n"; + $R .= $self->mime_render(to_mime_types($_->{head}), $_->{body}, [@$path, $#$parts-$i]); + $R .= "
\n"; + } + ++$i; + } + return $R . "
\n"; +} + +sub render_multipart { + my ($self, $_subtype, $content, $path) = @_; + + my $parts = $content->{parts}; + my $R = qq'
'; + my $i = 0; + + for (@$parts) { + if ( !$_->{head}{content_disposition} + || lc $_->{head}{content_disposition} eq 'none' + || lc $_->{head}{content_disposition} eq 'inline') { + + $R .= $self->mime_render(to_mime_types($_->{head}), $_->{body}, [@$path, $i]); + } + elsif (lc $_->{head}{content_disposition} eq 'attachment') { + $R .= '

'; + $R .= $self->c->link_to($self->c->url_for(raw => id => $self->c->stash('id'))->query(path => join('.', @$path, $i)), (download => $_->{head}{filename}) => sub { + 'Attachment ' . xml_escape($_->{head}{filename}) . ' of type ' . to_mime_type($_->{head}); + }); + $R .= "

\n"; + } + else { + warn "unknown Content-Disposition '$_->{head}{content_disposition}'"; + $R .= "

unknown Content-Disposition '$_->{head}{content_disposition}'

\n"; + } + ++$i; + } + return $R . "
\n"; +} + +sub _format_header { + my ($self, $category, $value) = @_; + + my $R = ''; + + if (ref $value eq 'ARRAY' && $value->@*) { + $R .= '
' . xml_escape(uc $self->c->l($category)) . "
\n"; + for ($value->@*) { + $R .= '
'; + $R .= xml_escape($_->{display_name} ? qq("$_->{display_name}" <$_->{address}>) : "$_->{address}"); + $R .= "
\n"; + } + } + return $R; +} + +sub render_message { + my ($self, $subtype, $msg, $path) = @_; + + warn "unkown mime-subtype $subtype" unless $subtype eq 'rfc822'; + + my $R .= '
'; + + $R .= '
'; + $R .= '
' . xml_escape(uc $self->c->l('subject')) . '
'; + $R .= '
' . xml_escape($msg->{head}{subject}) . "
\n"; + $R .= $self->_format_header(from => $msg->{head}{from}); + $R .= $self->_format_header(to => $msg->{head}{to}); + $R .= $self->_format_header(cc => $msg->{head}{cc}); + $R .= $self->_format_header(bcc => $msg->{head}{bcc}); + $R .= '
' . xml_escape(uc $self->c->l('date')) . '
'; + $R .= '
' . xml_escape($msg->{head}{date}) . "
\n"; + $R .= '
' . xml_escape(uc $self->c->l('content-type')) . '
'; + $R .= '
' . to_mime_type($msg->{head}{mime}) . "
\n"; + $R .= "
\n"; + + #my $content = ref $msg->{body} && exists $msg->{body}{parts} ? $msg->{body}{parts} : $msg->{body}; + + $R .= $self->mime_render(to_mime_types($msg->{head}{mime}), $msg->{body}, [@$path, 0]); + + return $R . "
\n"; +} + +our %MIME_Render_Subs = ( + 'text/plain' => \&render_text_plain, + 'text/html' => \&render_text_html, + 'multipart/alternative' => \&render_multipart_alternative, + 'multipart' => \&render_multipart, + 'message' => \&render_message, + 'image' => \&render_image, +); + +sub mime_render { + my ($self, $maintype, $subtype, $content, $path) = @_; + + my $renderer = $MIME_Render_Subs{"$maintype/$subtype"} || $MIME_Render_Subs{$maintype}; + + unless ($renderer) { + return "

Unsupported MIME type of $maintype/$subtype.

\n"; + } + + return $renderer->($self, $subtype, $content, $path); +} + + +sub to_mime_type { lc xml_escape("$_[0]->{content_maintype}/$_[0]->{content_subtype}") } +sub to_mime_types { return xml_escape($_[0]->{content_maintype}), xml_escape($_[0]->{content_subtype}) } + + +sub format_mail { + my ($self, $mail) = @_; + + return Mojo::ByteStream->new($self->mime_render('message', 'rfc822', $mail, [])); +} + +1 + +__END__ + +=encoding utf-8 + +=head1 NAME + +JWebmail::View::RenderMail - Does the heavy lifting of converting an E-Mail to HTML + +=head1 FUNCTIONS + +=head2 to_mime_type + +Combines the content_maintype and content_subtype attributes into the regular MIME description. +These attributes are found in a mail head mime section or as head for multipart messages. + +=head1 METHODS + +=head2 format_mail + +Renders a mail to html recursively. diff --git a/lib/JWebmail/View/Webmail.pm b/lib/JWebmail/View/Webmail.pm new file mode 100644 index 0000000..464c97e --- /dev/null +++ b/lib/JWebmail/View/Webmail.pm @@ -0,0 +1,88 @@ +package JWebmail::View::Webmail; + +use Mojo::Base -base; + +use POSIX qw(floor round log); + + +### template formatting functions + +sub print_sizes10 { + shift; + my $var = shift || return '0 Byte'; + + my $i = floor(((log($var)/log(10))+1e-5) / 3); + my $expo = $i * 3; + + my @PREFIX; + $PREFIX[0] = 'Byte'; + $PREFIX[1] = 'kByte'; + $PREFIX[2] = 'MByte'; + $PREFIX[3] = 'GByte'; + $PREFIX[4] = 'TByte'; + $PREFIX[5] = 'PByte'; + + return sprintf('%.0f %s', $var / (10**$expo), $PREFIX[$i]); +} + +sub print_sizes2 { + shift; + my $var = shift || return '0 Byte'; + + my $i = floor(((log($var)/log(2))+1e-5) / 10); + my $expo = $i * 10; + my %PREFIX = ( + 0 => 'Byte', + 1 => 'KiByte', + 2 => 'MiByte', + 3 => 'GiByte', + 4 => 'TiByte', + 5 => 'PiByte', + ); + my $pref = $PREFIX{$i}; + return round($var / (2**$expo)) . " $pref"; +} + +my sub dgt { "([[:digit:]]{$_[0]})" } + +sub parse_iso_date { + shift; + state $rx = do { my $re = dgt(4).'-'.dgt(2).'-'.dgt(2).'T'.dgt(2).':'.dgt(2).':'.dgt(2); qr/$re/a }; + my @d = shift =~ /$rx/; + if (@d != 6) { + # TODO + warn "issue when parsing date"; + } + return { + year => $d[0], + month => $d[1], + mday => $d[2], + hour => $d[3], + min => $d[4], + sec => $d[5], + }; +} + +1 + +__END__ + +=head1 VIEW METHODS + +=head2 print_sizes10 + +A helper for templates used to format byte sizes. + + $app->helper(print_sizes10 => sub { shift; JWebmail::Plugin::Helper::print_sizes10(@_) }); + + %= print_sizes10 12345 # => 12 kB + +=head2 print_sizes2 + +A helper for templates used to format byte sizes. + + %= print_sizes10 12345 # => 12 KiB + +This is not registered by default. + +=head2 parse_iso_date -- cgit v1.2.3