2 # based on Ikiwiki skeleton plugin.
4 # Copyright (c) 2008 David Bremner <bremner@unb.ca>
5 # This file is distributed under the Artistic License/GPL2+
8 use Email::MIME::Modifier;
9 package Email::MIMEFolder;
10 use base 'Email::Folder';
11 sub bless_message { return Email::MIME->new($_[1]) };
14 package IkiWiki::Plugin::mailbox;
16 use Email::FolderType qw(folder_type);
20 use File::Temp qw/tempfile/;
21 use File::MimeInfo::Magic;
29 hook(type => "preprocess", id => "mailbox", call => \&preprocess);
30 hook(type => "scan", id => "mailbox", call => \&scan);
31 hook(type => "pagetemplate", id=>"mailbox", call => \&pagetemplate);
32 hook(type => "htmlize",id=>"mbox",call => \&mbox_htmlize);
33 IkiWiki::loadplugin("filecheck");
38 my $page=$params{page};
40 my $linktext=$config{url}.'/mailbox.css';
42 push @{$metaheaders{$page}},
43 '<link rel="stylesheet" href="'.$linktext.'" type="text/css"/>'
46 sub preprocess (@) { #{{{
49 my $page=$params{page};
50 my $type=$params{type} || 'Maildir';
52 my $path=$params{path} || error gettext("missing parameter") . " path";
54 # hmm, this should probably only be inserted once per page.
56 my $dir=bestpath($page,$params{path}) ||
57 error("could not find ".$params{path});
59 $params{path} = $config{srcdir} ."/" . $dir;
60 $params{type} = $type;
62 return format_mailbox(%params);
69 my $path=$config{srcdir} . '/' . $params{page}.".mbox";
70 return format_mailbox(path=>$path,type=>'Mbox',destpage=>$params{page});
73 ### The guts of the plugin
75 sub format_mailbox(@){
77 my $path=$params{path} || error gettext("missing parameter "). 'path';
78 my $type=$params{type} || error gettext("missing paramater ")."type";
81 my $folder=Email::MIMEFolder->new($path,reader=>'Email::Folder::'.$type) || error("mailbox could not be opened");
82 my $threader=new Email::Thread($folder->messages);
86 my @roots= sort { str2time($a->header('Date')) <=>
87 str2time($b->header('Date'))} ($threader->rootset);
89 return join "\n", map { format_thread(%params,thread=>$_) } @roots;
96 my $thread=$params{thread} || error gettext("missing parameter") . "thread";
100 if ($thread->message) {
101 $output .= format_message(%params,message=>$thread->message);
103 $output .= sprintf gettext("Message %s not available"), $thread->messageid;
107 $output .= '<div class="emailthreadindent">' .
108 format_thread(%params,thread=>$thread->child).
113 $output .= format_thread(%params,thread=>$thread->next);
118 sub sanitize_address($$){
121 my $strategy= $config{mailbox_obfuscation_strategy} || "delete";
123 return $val if ($strategy eq "none");
125 if ($hdrname =~ qr/From|To|Reply-To|CC/){
126 my @addrs=Email::Address->parse($val);
127 foreach my $addr (@addrs){
128 if ($strategy eq "rot13"){
129 my $orig=$addr->address;
130 $orig =~ y/A-Za-z/N-ZA-Mn-za-m/;
131 $addr->address($orig);
133 $addr->address(gettext("address deleted"));
136 $val=join(",",map {$_->format;} @addrs);
144 my $val=$message->header($name);
146 $val = sanitize_address($name,$val);
148 $val = escapeHTML($val);
150 my $hash={'HEADERNAME'=>$name,'VAL'=>$val};
153 sub format_message(@){
156 my $message=$params{message} ||
157 error gettext("missing parameter"). "message";
160 my $dest=$params{destpage} ||
161 error gettext("missing parameter"). "destpage";
163 my $keep_headers=$params{headers} || qr/^(subject|from|date)[:]?$/i;
166 template("email.tmpl") || error gettext("missing template");
170 my @names = grep {m/$keep_headers/;} ($message->header_names);
172 my @headers=map { make_pair($message,$_) } @names;
174 $template->param(HEADERS=>[@headers]);
176 my $allowed_attachments=$params{allowed_attachments} ||
177 "maxsize(100kb) and (mimetype(text/plain) or mimetype(text/html))";
179 my @rawparts=$message->parts;
182 if ($message->content_type =~ m|^multipart/alternative|){
183 #according to RFC 1521, the last part is the most 'faithful'
184 while (scalar(@rawparts) && !scalar(@parts)){
185 my $part=pop(@rawparts);
186 if (check_part($part,$dest,$allowed_attachments)){
193 my $allowed=check_part($_,$dest,$allowed_attachments );
195 $_->content_type_set('text/plain');
196 $_->body_set("[ omitting part $partcount: $allowed ]");
204 my $body= join("\n", map { format_part($_->content_type, $_->body) }
207 $template->param(body=>format_body($body));
209 $output .= $template->output();
216 my $allowed_attachments=shift;
219 #this sucks. But someone would need to modify filecheck to
220 #accept a blob of content. Or maybe hacking with IO::Scalar
222 my $tmpfile=File::Temp->new();
223 binmode $tmpfile,':utf8';
224 print $tmpfile $part->body();
226 return pagespec_match($dest, $allowed_attachments, file=>$tmpfile);
236 # $rval .= "[ $mime_type ]\n";
238 if ($mime_type =~ "^text/html"){
241 $rval .= "<pre>".escapeHTML($body)."</pre>";
253 # based on bestdir From Arpit Jain
254 # http://ikiwiki.info/todo/Bestdir_along_with_bestlink_in_IkiWiki.pm/
255 # need to clarify license
256 sub bestpath ($$) { #{{{
261 if ($link=~s/^\/+//) {
267 $l.="/" if length $l;
269 if (-d "$config{srcdir}/$l" || -f "$config{srcdir}/$l") {
272 } while $cwd=~s!/?[^/]+$!!;
274 if (length $config{userdir}) {
275 my $l = "$config{userdir}/".lc($link);
277 if (-d $l || -f $l) {
285 sub pagetemplate (@) { #{{{
287 my $page=$params{page};
288 my $destpage=$params{destpage};
289 my $template=$params{template};
292 if ($page =~ /.*comments/ && defined($config{mailbox_copyright})){
293 $template->param(COPYRIGHT=>$config{mailbox_copyright});
296 if (exists $metaheaders{$page} && $template->query(name => "meta")) {
297 # avoid duplicate meta lines
299 $template->param(meta => join("\n", grep { (! $seen{$_}) && ($seen{$_}=1) } @{$metaheaders{$page}}));
305 package Email::FolderType::MH;
309 return 0 if (! -d $folder);
310 opendir DIR,$folder || error("opendir failed");
313 return 0 if (!m|\.| && !m|\.\.| && !m|\d+|);