#!/usr/bin/perl use strict; use warnings; use Getopt::Long; use Pod::Usage; use Path::Class qw(dir file); use meon::Web::Config; use meon::Web::env; use Email::MIME; use Email::Sender::Simple qw(sendmail); use XML::Chain qw(xc); use DateTime; use String::Ident; use URI::Escape qw(uri_escape); use Imager; use List::Util qw(min); use List::MoreUtils qw(uniq); use File::Basename qw(basename); use DateTime::Format::Mail; use Run::Env; use 5.010; exit main(); sub main { my $help; my $dst_domain; my $notify_email; my $author; GetOptions( 'help|h' => \$help, 'hostname=s' => \$dst_domain, 'notify=s' => \$notify_email, 'author=s' => \$author, ) or pod2usage; pod2usage if $help; pod2usage unless defined $dst_domain; my $email_txt = do {local $/; <>;}; my $email = Email::MIME->new($email_txt); my ($from) = Email::Address->parse($email->header_obj->header('From')); die 'no sender' unless $from; my ($to) = Email::Address->parse($email->header_obj->header('To')); my $reply_from_email = $to // meon::Web::Config->get->{main}->{'no-reply-email'}; my @to_notify = uniq map {defined($_) ? $_ : ()} ($notify_email, $from->address); my $subject = $email->header_obj->header('Subject'); die 'no subject' unless $subject; my $date_str = $email->header_obj->header('Date'); my $sent_dt; if ($date_str) { $sent_dt = eval {DateTime::Format::Mail->parse_datetime($date_str)}; } $sent_dt //= DateTime->now(); $sent_dt->set_time_zone('UTC'); my $ident = String::Ident->cleanup($subject); my $hostname_folder = meon::Web::Config->hostname_to_folder($dst_domain); my $dst_hostname_dir = dir(meon::Web::SPc->srvdir, 'www', 'meon-web', $hostname_folder, 'content',); die 'no such hostname ' . $dst_domain unless $hostname_folder; my ($year, $month) = ($sent_dt->year, $sent_dt->strftime('%m')); my $dst_dir = $dst_hostname_dir->subdir($year, $month); $dst_dir->mkpath() unless (-d $dst_dir); my $year_idx = $dst_dir->parent->file('index.xml'); my $month_idx = $dst_dir->file('index.xml'); $year_idx->spew(year_index()) unless (-f $year_idx); $month_idx->spew(month_index()) unless (-f $month_idx); my $assets_dir = $dst_dir->subdir($ident); my $dst_file = $dst_dir->file($ident . '.xml'); my $entry = xc(xml_template())->set_io_any($dst_file); $entry->find('//w:title')->empty->t($subject); $entry->find('//w:created')->empty->t($sent_dt); $entry->find('//w:author')->empty->t($author // $from->name // 'anonymous'); my $content_el = $entry->find('//xhtml:div[@id="main-content"]')->empty; my $main_img_el = $entry->find('//xhtml:img[@id="main-image"]'); my $img_thumb_el = $entry->find('//w:img-thumb')->empty; my @images; my $attach_ul_el = $entry->find('//xhtml:div[@id="attachments"]/xhtml:ul'); my $has_attachments = 0; my $img_div = $content_el->find('//xhtml:div[@id="images-list"]'); foreach my $part ($email->parts) { my $mime_type = $part->content_type; my $body = $part->body; my $filename = $part->filename; if ($mime_type =~ m{^text/plain}i) { $body =~ s/^\s+//; $body =~ s/\s+$//; $content_el->a(xc('p')->t($body))->t("\n") if (length($body)); } elsif ($mime_type =~ m{^text/html}i) { # skip for now } if ($filename) { $has_attachments = 1; $assets_dir->mkpath unless -d $assets_dir; my $file = $assets_dir->file($filename); $file->spew($body); my $href = $ident . '/' . uri_escape($filename); if ($mime_type =~ m{^image/}) { push( @images, { src => $href, file => $file, } ); } else { $attach_ul_el->c('li')->c('a', 'href' => $href)->t($mime_type); $attach_ul_el->t("\n"); } } } if (@images) { my $first_image_file = $images[0]->{file}; if (my $img = Imager->new(file => $first_image_file)) { my $thumb_file = $first_image_file; if ($first_image_file->basename =~ m/.([.][^\s]+)$/) { $thumb_file = substr($thumb_file, 0, length($thumb_file) - length($1)) . '-thumb' . $1; } else { $thumb_file = $thumb_file . '-t'; } my $min_size = min($img->getwidth, $img->getheight); $img = $img->crop(left => 0, top => 0, right => $min_size, bottom => $min_size) ->scale(xpixels => 250, qtype => 'mixing'); $img->write(file => $thumb_file); $img_thumb_el->t(uri_escape(basename($thumb_file))); } else { $img_thumb_el->rm; } if ($content_el->children->count) { my $img = shift(@images); $main_img_el->attr(src => $img->{src}); } else { $main_img_el->rm; } foreach my $img (@images) { $img_div->a('img', src => $img->{src})->t("\n"); } } else { $img_thumb_el->rm; $main_img_el->rm; $img_div->rm; } unless ($has_attachments) { $attach_ul_el->parent->rm; } $entry->store; foreach my $to_email (@to_notify) { my @attachments; my @email_headers = ( header_str => [ From => $reply_from_email, To => $to_email, Subject => 'new post on ' . $dst_domain, ], ); my @email_text = ( attributes => { content_type => "text/plain", charset => "UTF-8", encoding => "8bit", }, body_str => 'new post processed and available under http://' . $dst_domain . '/' . file($year, $month, $ident) . "\n", ); my $email = Email::MIME->create(@email_headers, @email_text,); if (Run::Env->prod) { sendmail($email, {to => $to_email, from => $reply_from_email}); } else { my $email_string = $email->as_string; my $i = 0; for (@attachments) { $i++; $email_string .= "\nAttachment $i: " . $_; } warn $email_string; } } return 0; } sub xml_template { return XML::LibXML->load_xml(string => <<'__XML_TEMPLATE__'); __FIXME__
__FIXME__ __FIXME__ __FIXME__ __FIXME__
__FIXME__
__XML_TEMPLATE__ } sub year_index { return << '__YEAR_INDEX__'; {$TIMELINE_NEWEST}/
__YEAR_INDEX__ } sub month_index { return << '__MONTH_INDEX__';
__MONTH_INDEX__ } =head1 NAME meon-web-mail2blog - parse email and create timeline entries out of it =head1 SYNOPSIS meon-web-mail2timeline-entry --hostname domain --hostname domain where to store timeline entries --notify email (opt.) send notification about new submissions --author name (opt.) set author, otherwise taken from email name =head1 DESCRIPTION =cut