#!/usr/bin/perl -w
#
# M2WP - 1.00
#
# This is M2WP - Mail 2 WordPress, an SMTP mail robot, which can
# be used to post blog entries to a wordpress blog.
#
# By  accessing  this  software,  M2WP, you  are  duly  informed
# of and agree to be  bound  by the  conditions  described below
# in this notice:
#
# This  software  product,  M2WP,  is developed by Thomas Linden
# and  copyrighted  (C)  2005-2006  by  Thomas Linden,  with all
# rights reserved.
#
# There  is  no charge for M2WP software.  You  can redistribute
# it and/or modify it under the terms of the GNU  General Public
# License, which is incorporated by reference herein.
#
# M2WP is distributed  WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS,
# OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE or that
# the use of it will not infringe on any third party's intellec-
# tual property rights.
#
# You  should  have received a copy of the  GNU  General  Public
# License  along  with M2WP.   Copies can also be obtained from:
#
#   http://www.gnu.org/licenses/gpl.txt
#
# or by writing to:
#
#   Free Software Foundation, Inc.
#   59 Temple Place, Suite 330
#   Boston, MA 02111-1307
#   USA
#
# Or contact:
#
#  "Thomas Linden" <tom@daemon.de>
#
# Find more details about M2WP on its website:
#
#  http://www.daemon.de/M2wp
#

use Data::Dumper;
use MIME::Parser;
use WWW::Mechanize;
use Config::General;
use Getopt::Long;
use strict;

my $me = "m2wp";
my $VERSION = 1.00;

#
# Default config
my $ConfigFile = "/etc/m2wp.conf";

#
# commandline options
my($o_h, $o_c, $o_v);
Getopt::Long::Configure( qw(no_ignore_case));
if (! GetOptions (
		  "config|c=s" => \$o_c,
		  "version|v"  => \$o_v,
                  "help|h"     => \$o_h,
		 )    ) {
  &usage;
}

if ($o_h) {
  &usage;
}

if ($o_v) {
  &finish("m2wp version $VERSION\n", 0);
}

my %conf = ParseConfig(-ConfigFile => $o_c ? $o_c : $ConfigFile);
my %category = %{$conf{Categories}};

mkdir($conf{ParserDirectory}) or die "Could not create $conf{ParserDirectory}: $!\n";
chdir($conf{ParserDirectory}) or die "Could not chdir to $conf{ParserDirectory}: $!\n";


#
# end of user customizable part
#
my $parser = new MIME::Parser;
my $entity = $parser->parse(\*STDIN);
$parser->decode_headers(1);
$parser->extract_uuencode(1);
$parser->output_to_core(1);
my @mail_header = split('\n',$entity->head->as_string);
my %head;

foreach my $entry (@mail_header) {
   if ($entry =~ /(From|Subject|Message-ID): \s*(.*)$/) {
       my $field = $1;
       my $value = $2;
       $head{$field} = $value;
   }
}

my (undef, $mail) = split /\s\s*/, $head{From};
if (! $mail) {
    # no name part
    $mail = $head{From};
}
$head{From} = $mail;
$head{From} =~ s/[<>]//g;

# check email from for permission
my @allowed = split /\s*,\s*/, $conf{AllowedEmail};
if (! grep { $head{From} =~ /$_/i } @allowed ) {
    &finish("Permission denied!\n", 1);
}

my ($subject, $cat) = split /\s*\-\s*/, $head{Subject};
$head{Subject} = $subject;
if (! $cat) {
    # we will not post a category in this case, see below
    $head{Category} = undef;
}
else {
    $head{Category} = $category{$cat};
}


my ($body, $data, @images);
if ($body = $entity->bodyhandle) {
     # simple mail, no multipart mime-types here
     $data = $body->as_string;
}
else {
    # multipart mail
    my @parts = $entity->parts();
    foreach my $part (@parts) {
	if ( $part->effective_type() eq "text/html" ) {
            # prefer HTML
            $data = $part->stringify();
            $data =~ s/<(font|body|meta|html)[^>]*>//isg;
	    $data =~ s/<\!doctype[^>]*>//isg;
	    $data =~ s/<\/(font|meta|body|html)>//isg;
	    $data =~ s/<head[^>]*>.+<\/head>//isg;
	    last;
	}
	elsif ( $part->effective_type() eq "text/plain" ) {
	    # store it anyway, will be overwritten if html is available
	    $data = $part->stringify();
	}
	else {
            # images are stored automatically, just fetch the names
            # FIXME post the image (BELOW!)
	    if ( $part->effective_type() =~ /image/ ) {
                my $data = $part->stringify();
		if ($data =~ /Content-Disposition: attachment; filename="(.+)"/i) {
		    push @images, $1;
		}
	    }
	}
    }
}

$data =~ s/content-(type|disposition): .+//ig;

my $mech = WWW::Mechanize->new();

# fetch login page and login
$mech->get($conf{BlogAdminURI});
$mech->field('log', $conf{LoginUser});
$mech->field('pwd', $conf{LoginPasswd});
$mech->submit(); # to get login cookie

# fetch blog posting page and form
$mech->get("$conf{BlogAdminURI}/post.php");
$mech->form('post');

# fetch the user_ID value
my $user_ID;
for my $obj (@{$mech->{form}->{inputs}}) {
    if ($obj->{name} eq "user_ID") {
         $user_ID = $obj->{value};
    }
}
if (! $user_ID ) {
    &finish("Unable to fetch user_ID, could not login?\n", 1);
}

# fill in all required fields
if ( $head{Category} ) {
   # if not specified, don't post it, thus use WP's default 
   $mech->tick("post_category[]", "$head{Category}");
   $mech->tick("post_category[]", "$category{$conf{DefaultCategory}}", undef);
}

$mech->field("post_title", $head{Subject});
$mech->field("content", $data);
$mech->field("publish", "Ver&ouml;ffentlichen");
$mech->field("comment_status", "open");
$mech->field("post_status", "publish");

# FIXME technorati must be fetched off the incoming mail somehow
#$mech->field("metakeyselect", "ttaglist");
#$mech->field("metakeyinput", "");
#$mech->field("metavalue", "testing curious meta blah");

# post the crab
my $result = $mech->submit();

if ($result->{_content} !~ /<div class="updated">/is) {
  # FIXME fetch error message from $result, if any (?)
  &finish("Failed to post to blog!\n", 1);
}

sub usage {
  &finish("${me} [-vh] [-c <config>]\n", 0);
}

sub finish {
  my ($message, $exit) = @_;
  system("rm -f $conf{ParserDirectory}/msg-$$.*");
  print STDERR $message;
  exit $exit || 0;
}


1;
