]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/View.pm
view: add view module to be used for rendering HTML
[public-inbox.git] / lib / PublicInbox / View.pm
1 # Copyright (C) 2014, Eric Wong <normalperson@yhbt.net> and all contributors
2 # License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
3 package PublicInbox::View;
4 use strict;
5 use warnings;
6 use CGI qw/escapeHTML escape/;
7 use Encode qw/decode encode/;
8 use Encode::MIME::Header;
9
10 # only one public function:
11 sub as_html {
12         my ($class, $mime) = @_;
13
14         headers_to_html_header($mime) .
15                 multipart_text_as_html($mime) .
16                 "</pre>\n";
17 }
18
19 # only private functions below.
20
21 sub multipart_text_as_html {
22         my ($mime) = @_;
23         my $rv = "";
24         my $part_nr = 0;
25
26         # scan through all parts, looking for displayable text
27         $mime->walk_parts(sub {
28                 my ($part) = @_;
29                 return if $part->subparts; # walk_parts already recurses
30
31                 my $part_type = $part->content_type;
32                 if ($part_type =~ m!\btext/[a-z0-9\+\._-]+\b!i) {
33                         my $fn = $part->filename;
34
35                         if ($part_nr > 0) {
36                                 defined($fn) or $fn = "part #$part_nr";
37                                 $rv .= add_filename_line($fn);
38                         }
39
40                         # n.b. $part->body should already be decoded if text
41                         $rv .= escapeHTML($part->body);
42                         $rv .= "\n" unless $rv =~ /\n\z/s;
43                 } else {
44                         $rv .= "-- part #$part_nr ";
45                         $rv .= escapeHTML($part_type);
46                         $rv .= " skipped\n";
47                 }
48                 ++$part_nr;
49         });
50         $rv;
51 }
52
53 sub add_filename_line {
54         my ($fn) = @_;
55         my $len = 72;
56         my $pad = "-";
57
58         $len -= length($fn);
59         $pad x= ($len/2) if ($len > 0);
60         "$pad " . escapeHTML($fn) . " $pad\n";
61 }
62
63 sub headers_to_html_header {
64         my ($simple) = @_;
65
66         my $rv = "";
67         my @title;
68         foreach my $h (qw(From To Cc Subject Date)) {
69                 my $v = $simple->header($h);
70                 defined $v or next;
71                 $v = decode("MIME-Header", $v);
72                 $v = encode("utf8", $v);
73                 $v = escapeHTML($v);
74                 $v =~ tr/\n/ /;
75                 $rv .= "$h: $v\n";
76
77                 if ($h eq "From" || $h eq "Subject") {
78                         push @title, $v;
79                 }
80         }
81
82         foreach my $h (qw(Message-ID In-Reply-To)) {
83                 my $v = $simple->header($h);
84                 defined $v or next;
85                 $v =~ tr/<>//d;
86                 my $html = escapeHTML($v);
87                 my $href = escapeHTML(escape($v));
88                 $rv .= "$h: <a href=\"$href\">$html</a>\n";
89         }
90
91         $rv .= "\n";
92
93         return ("<html><head><title>".
94                 join(' - ', @title) .
95                 '</title></head><body><pre style="white-space:pre-wrap">' .
96                 $rv);
97 }
98
99 1;