]> git.vanrenterghem.biz Git - git.ikiwiki.info.git/blobdiff - IkiWiki/Plugin/mailbox.pm
special case for comments pages
[git.ikiwiki.info.git] / IkiWiki / Plugin / mailbox.pm
index 838a6378e43d4647abcf1b5edb6e8fcb5ee9ee6b..92ddf99f32f65f141f471b0590b61ba4d443d91c 100644 (file)
 # Copyright (c) 2008 David Bremner <bremner@unb.ca>
 # This file is distributed under the Artistic License/GPL2+
 
+use Email::MIME;
+use Email::MIME::Modifier;
+package Email::MIMEFolder;
+use base 'Email::Folder';
+sub bless_message { return  Email::MIME->new($_[1]) };
+
+
 package IkiWiki::Plugin::mailbox;
 
-use warnings;
-use strict;
+use Email::FolderType qw(folder_type);
 use IkiWiki 2.00;
-use Email::Folder;
+use Email::Thread;
+use CGI 'escapeHTML';
+use File::Temp qw/tempfile/;
+use File::MimeInfo::Magic;
+use Date::Parse;
+use Email::Address;
+
+my %metaheaders;
+
 
 sub import { #{{{
-       hook(type => "getopt", id => "mailbox",  call => \&getopt);
-       hook(type => "checkconfig", id => "mailbox", call => \&checkconfig);
-       hook(type => "needsbuild", id => "mailbox", call => \&needsbuild);
        hook(type => "preprocess", id => "mailbox", call => \&preprocess);
-       hook(type => "filter", id => "mailbox", call => \&filter);
-       hook(type => "linkify", id => "mailbox", call => \&linkify);
        hook(type => "scan", id => "mailbox", call => \&scan);
-       hook(type => "htmlize", id => "mailbox", call => \&htmlize);
-       hook(type => "sanitize", id => "mailbox", call => \&sanitize);
-       hook(type => "postscan", id => "mailbox", call => \&postscan);
-       hook(type => "format", id => "mailbox", call => \&format);
-       hook(type => "pagetemplate", id => "mailbox", call => \&pagetemplate);
-       hook(type => "templatefile", id => "mailbox", call => \&templatefile);
-       hook(type => "delete", id => "mailbox", call => \&delete);
-       hook(type => "change", id => "mailbox", call => \&change);
-       hook(type => "cgi", id => "mailbox", call => \&cgi);
-       hook(type => "auth", id => "mailbox", call => \&auth);
-       hook(type => "sessioncgi", id => "mailbox", call => \&sessioncgi);
-       hook(type => "canedit", id => "mailbox", call => \&canedit);
-       hook(type => "editcontent", id => "mailbox", call => \&editcontent);
-       hook(type => "formbuilder_setup", id => "mailbox", call => \&formbuilder_setup);
-       hook(type => "formbuilder", id => "mailbox", call => \&formbuilder);
-       hook(type => "savestate", id => "mailbox", call => \&savestate);
+       hook(type => "pagetemplate", id=>"mailbox", call => \&pagetemplate);
+       hook(type => "htmlize",id=>"mbox",call => \&mbox_htmlize);
+       IkiWiki::loadplugin("filecheck");
 } # }}}
 
-sub getopt () { #{{{
-       debug("mailbox plugin getopt");
-} #}}}
-
-sub checkconfig () { #{{{
-       debug("mailbox plugin checkconfig");
-} #}}}
+sub scan(@){
+       my %params=@_;
+       my $page=$params{page};
+       
+       my $linktext=$config{url}.'/mailbox.css';
 
-sub needsbuild () { #{{{
-       debug("mailbox plugin needsbuild");
-} #}}}
+       push @{$metaheaders{$page}}, 
+              '<link rel="stylesheet" href="'.$linktext.'" type="text/css"/>'
+}
 
 sub preprocess (@) { #{{{
        my %params=@_;
        
        my $page=$params{page};
-       my $type=$params{type} || 'maildir';
-
-       print STDERR join("\n",%params);
+       my $type=$params{type} || 'Maildir';
 
-       error("path is mandatory") if (!defined($params{path}));
-
-       # note, mbox is not a directory
-       my $dir=bestdir($page,$params{path}) || 
-           error("could not find ".$params{path});
-
-       $dir = $config{srcdir} ."/" . $dir;
-
-       return  format_mailbox(path=>$dir);
-
-} # }}}
-
-sub filter (@) { #{{{
-       my %params=@_;
-       
+       my $path=$params{path} ||  error gettext("missing parameter") . " path";
        
-#      debug("mailbox plugin: path=".$params{path});
+       # hmm, this should probably only be inserted once per page.
 
-       return $params{content};
-} # }}}
+       my $dir=bestpath($page,$params{path}) || 
+           error("could not find ".$params{path});
 
-sub linkify (@) { #{{{
-       my %params=@_;
+       $params{path} = $config{srcdir} ."/" . $dir;
+       $params{type} = $type;
        
-       debug("mailbox plugin running as linkify");
+       return  format_mailbox(%params);
 
-       return $params{content};
 } # }}}
 
-sub scan (@) { #{{{a
-       my %params=@_;
-
-       debug("mailbox plugin running as scan");
-} # }}}
-
-sub htmlize (@) { #{{{
-       my %params=@_;
-
-       debug("mailbox plugin running as htmlize");
-
-       return $params{content};
-} # }}}
+sub mbox_htmlize(@){
+    my %params=@_;
+    
+    my $path=$config{srcdir} . '/' . $params{page}.".mbox";
+    return format_mailbox(path=>$path,type=>'Mbox',destpage=>$params{page});
+}
 
-sub sanitize (@) { #{{{
-       my %params=@_;
-       
-       debug("mailbox plugin running as a sanitizer");
+### The guts of the plugin
+### parameters 
+sub format_mailbox(@){
+    my %params=@_;
+    my $path=$params{path} || error gettext("missing parameter "). 'path';
+    my $type=$params{type} || error gettext("missing paramater ")."type";
 
-       return $params{content};
-} # }}}
+    debug('type='.$type);
+    my $folder=Email::MIMEFolder->new($path,reader=>'Email::Folder::'.$type) || error("mailbox could not be opened");
+    my $threader=new Email::Thread($folder->messages);
 
-sub postscan (@) { #{{{
-       my %params=@_;
-       
-       debug("mailbox plugin running as postscan");
-} # }}}
+    $threader->thread();
 
-sub format (@) { #{{{
-       my %params=@_;
-       
-       debug("mailbox plugin running as a formatter");
+    my @roots= sort  { str2time($a->header('Date'))  <=> 
+                          str2time($b->header('Date'))}  ($threader->rootset);
 
-       return $params{content};
-} # }}}
+    return join "\n", map { format_thread(%params,thread=>$_) } @roots; 
 
-sub pagetemplate (@) { #{{{
-       my %params=@_;
-       my $page=$params{page};
-       my $template=$params{template};
-       
-       debug("mailbox plugin running as a pagetemplate hook");
-} # }}}
-
-sub templatefile (@) { #{{{
-       my %params=@_;
-       my $page=$params{page};
-       
-       debug("mailbox plugin running as a templatefile hook");
-} # }}}
-
-sub delete (@) { #{{{
-       my @files=@_;
+}
 
-       debug("mailbox plugin told that files were deleted: @files");
-} #}}}
+sub format_thread(@){
+    my %params=@_;
+    
+    my $thread=$params{thread} || error gettext("missing parameter") . "thread";
+
+    my $output="";
+
+    if ($thread->message) {
+       $output .= format_message(%params,message=>$thread->message);
+    } else {
+       $output .= sprintf gettext("Message %s not available"), $thread->messageid;
+    }
+
+    if ($thread->child){
+       $output .= '<div class="emailthreadindent">' .
+           format_thread(%params,thread=>$thread->child).
+           '</div>';
+    }
+
+    if ($thread->next){
+       $output .= format_thread(%params,thread=>$thread->next);
+    }
+    return $output;
+}
 
-sub change (@) { #{{{
-       my @files=@_;
+sub sanitize_address($$){
+    my $hdrname=shift;
+    my $val=shift;
+    my $strategy= $config{mailbox_obfuscation_strategy} || "delete";
+
+    return $val if ($strategy eq  "none");
+
+    if ($hdrname =~ qr/From|To|Reply-To|CC/){
+       my @addrs=Email::Address->parse($val);
+       foreach my $addr (@addrs){
+           if ($strategy eq "rot13"){
+               my $orig=$addr->address;
+               $orig =~ y/A-Za-z/N-ZA-Mn-za-m/;
+               $addr->address($orig); 
+           } else {
+               $addr->address(gettext("address deleted"));
+           }
+       }
+       $val=join(",",map {$_->format;} @addrs);
+    }
+    return $val;
+                    }
+
+sub make_pair($$){
+    my $message=shift;
+    my $name=shift;
+    my $val=$message->header($name);
+
+    $val = sanitize_address($name,$val);
+
+    $val = escapeHTML($val);
+
+    my $hash={'HEADERNAME'=>$name,'VAL'=>$val};
+    return $hash;
+}
+sub format_message(@){
+    my  %params=@_;
 
-       debug("mailbox plugin told that changed files were rendered: @files");
-} #}}}
+    my $message=$params{message} || 
+       error gettext("missing parameter"). "message";
 
-sub cgi ($) { #{{{
-       my $cgi=shift;
 
-       debug("mailbox plugin running in cgi");
-} #}}}
+    my $dest=$params{destpage} || 
+       error gettext("missing parameter"). "destpage";
 
-sub auth ($$) { #{{{
-       my $cgi=shift;
-       my $session=shift;
+    my $keep_headers=$params{headers} || qr/^(subject|from|date)[:]?$/i;
+    
+    my $template= 
+       template("email.tmpl") || error gettext("missing template");
 
-       debug("mailbox plugin running in auth");
-} #}}}
+    my $output="";
 
-sub sessionncgi ($$) { #{{{
-       my $cgi=shift;
-       my $session=shift;
+    my @names = grep  {m/$keep_headers/;}  ($message->header_names);
+    
+    my @headers=map { make_pair($message,$_) } @names;
+    
+    
 
-       debug("mailbox plugin running in sessioncgi");
-} #}}}
+    $template->param(HEADERS=>[@headers]);
 
-sub canedit ($$$) { #{{{
-       my $page=shift;
-       my $cgi=shift;
-       my $session=shift;
+    my $allowed_attachments=$params{allowed_attachments} || 
+       "maxsize(100kb) and mimetype(text/plain)";
 
-       debug("mailbox plugin running in canedit");
-} #}}}
+    my @parts=$message->parts;
 
-sub editcontent ($$$) { #{{{
-       my %params=@_;
+    my $partcount=1;
+    foreach(@parts){
+       #this sucks. But someone would need to modify filecheck to
+       #accept a blob of content. Or maybe hacking with IO::Scalar
+       my $tmpfile=File::Temp->new();
 
-       debug("mailbox plugin running in editcontent");
+       binmode $tmpfile,':utf8';
+       print $tmpfile $_->body();
 
-       return $params{content};
-} #}}}
+       my $allowed=pagespec_match($dest, $allowed_attachments, file=>$tmpfile);
 
-sub formbuilder_setup (@) { #{{{
-       my %params=@_;
-       
-       debug("mailbox plugin running in formbuilder_setup");
-} # }}}
+       if (!$allowed) {
+           debug("clobbering attachment $partcount");
+           $_->content_type_set('text/plain');
+           $_->body_set("[ omitting part $partcount: $allowed ]");
 
-sub formbuilder (@) { #{{{
-       my %params=@_;
-       
-       debug("mailbox plugin running in formbuilder");
-} # }}}
+       }
+       $partcount++;
+    }
+    my $body= join("\n", map { $_->body }  @parts);
 
-sub savestate () { #{{{
-       debug("mailbox plugin running in savestate");
-} #}}}
+    $template->param(body=>format_body($body));
 
-### The guts of the plugin
-### parameters 
-sub format_mailbox(@){
-    my %params=@_;
-    my $path=$params{path} || error("path parameter mandatory");
-    my $header_list=$params{headers} || "subject,from";
+    $output .= $template->output();
+    return $output;
+}
 
-    my $folder=Email::Folder->new($path) || error("mailbox could not be opened");
-    return join "\n", map { format(message=>$_) } $folder->messages;
+sub format_body($){
+    my $body=shift;
 
+    # it is not completely clear to me the right way to go here.  
+    # passing things straight to markdown is not working all that
+    # well.
+    return "<pre>".escapeHTML($body)."</pre>";
 }
-
 ### Utilities
 
-# From Arpit Jain
+# based on bestdir From Arpit Jain
 # http://ikiwiki.info/todo/Bestdir_along_with_bestlink_in_IkiWiki.pm/
 # need to clarify license
-sub bestdir ($$) { #{{{
+sub bestpath ($$) { #{{{
     my $page=shift;
        my $link=shift;
        my $cwd=$page;
@@ -231,7 +233,7 @@ sub bestdir ($$) { #{{{
                my $l=$cwd;
                $l.="/" if length $l;
                $l.=$link;
-               if (-d "$config{srcdir}/$l") {
+               if (-d "$config{srcdir}/$l" || -f "$config{srcdir}/$l") {
                        return $l;
                }
        } while $cwd=~s!/?[^/]+$!!;
@@ -239,7 +241,7 @@ sub bestdir ($$) { #{{{
        if (length $config{userdir}) {
                my $l = "$config{userdir}/".lc($link);
 
-               if (-d $l) {
+               if (-d $l || -f $l) {
                        return $l;
                }
        }
@@ -247,6 +249,39 @@ sub bestdir ($$) { #{{{
        return "";
 } #}}}
 
+sub pagetemplate (@) { #{{{
+       my %params=@_;
+        my $page=$params{page};
+        my $destpage=$params{destpage};
+        my $template=$params{template};
+
+
+       if ($page =~ /.*comments/  && defined($config{mailbox_copyright})){
+           $template->param(COPYRIGHT=>$config{mailbox_copyright});
+       }
+
+       if (exists $metaheaders{$page} && $template->query(name => "meta")) {
+               # avoid duplicate meta lines
+               my %seen;
+               $template->param(meta => join("\n", grep { (! $seen{$_}) && ($seen{$_}=1) } @{$metaheaders{$page}}));
+       }
+}
+
+
+
+package Email::FolderType::MH;
+
+sub match {
+    my $folder = shift;
+    return 0 if (! -d $folder);
+    opendir DIR,$folder || error("opendir failed");
+
+    while (<DIR>){
+      return 0 if (!m|\.| && !m|\.\.| && !m|\d+|);
+    }
+    return 1;
+}
+
 
 
-1
+1;