#!perl -w
use strict;

# this script takes pairs of filename, description as parameters, and
# uploads them to the wiki.
#
#
# example: ( with descriptions )
# perl wikiupload.pl -d image.jpg "first image" image2.jpg "second image"

# example: ( just the files )
# perl wikiupload.pl *.pdf
#
# you need to create a file ~/.wikiuploadrc
# containing these lines:
# url=http://server.url
# user=USERNAME
# pass=PASSWORD
# special=Speciaal
#
#  - the special line is only nescesary for wikis in other languages than english

#####################################
#   some included support nodules
package WebServer;
use strict;
use warnings;
use HTTP::Request::Common qw(POST GET);
use LWP::UserAgent;
use HTTP::Cookies;

use List::Util qw(first);

sub new {
    my ($class, $baseurl)= @_;

    my $ua= LWP::UserAgent->new(agent=>'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040501');
    $ua->cookie_jar(HTTP::Cookies->new(hide_cookie2=>1));
    $ua->env_proxy();

    return bless {
        ua=>$ua,
        baseurl=>$baseurl,
    }, $class;
}
sub clearcookies {
    my $self= shift;

    $self->{ua}->cookie_jar(HTTP::Cookies->new(hide_cookie2=>1));
}
# almost interface compatible with httpost
#   - optional hashref with parameters is merged with parameters.
#
# httpget("/some.cgi", key1=>123, key2=>455);
# httpget("/some.cgi", { urlkey1=>999 }, key1=>123, key2=>455);
#
sub httpget {
    my $self= shift;
    my $path= shift;

    my $query;
    if (@_) {
        $query= shift;
        if (ref $query ne "HASH") {
            unshift @_, $query;
            $query=undef;
        }
    }
    my %params= @_;

    my $uri= URI->new($self->{baseurl});
    $uri->path($path);
    $uri->query_form($query?%$query:(), %params);
    my $rq= GET $uri;

    # todo: get rid of 'TE' header, and 'Connection'-TE flag. and 'Cookie2' header
    $rq->header(
        'User-Agent'=> 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040501',
        'Accept'=> ($path =~ /\.aspx|\.htm/ 
            ? 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'
            : $path =~ /\.css/
            ? 'text/css,*/*;q=0.1'
            : '*/*') ,
        'Accept-Language'=> 'en-us,en;q=0.5',
        #'Accept-Encoding'=> 'gzip,deflate',
        'Accept-Charset'=> 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
    );
    #print "request:\n", $rq->as_string, "\n";
    #warn "network access disabled\n";
    #return;
    my $rp= $self->{ua}->request($rq) or die "httperror: $@\n";

    return $rp->content;
}

# can be called in several ways:
# httppost("/some.cgi", key1=>123, key2=>455);
#   -> just form values
# httppost("/some.cgi", { urlkey1=>999 }, key1=>123, key2=>455);
#   -> both url and form params
# httppost("/some.cgi", key1=>123, key2=>455, file1=>["filename"]);
#   -> form-data file upload
sub httppost {
    my $self= shift;
    my $path= shift;

    my $query;
    if (@_) {
        $query= shift;
        if (ref $query ne "HASH") {
            unshift @_, $query;
            $query=undef;
        }
    }
    my %params= @_;

    my $useformdata= grep { defined ref $_ && ref $_ eq "ARRAY" } values %params;

    my $uri= URI->new($self->{baseurl});
    $uri->path($path);
    $uri->query_form(%$query) if ($query);
    my $rq;
    if ( $useformdata ) {
        $rq = POST $uri, Content_Type=>"form-data", Content=>[ %params ];
    }
    else {
        $rq = POST $uri, [ %params ];
    }

    # -- for http uploads : 
    # ( Content_Type=>"form-data", Content=>[ %params ]);
    $rq->header(
        'User-Agent'=> 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7) Gecko/20040501',
        'Accept'=> 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
        'Accept-Language'=> 'en-us,en;q=0.5',
        #'Accept-Encoding'=> 'gzip,deflate',
        'Accept-Charset'=> 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
    );
    #print "request:\n", $rq->as_string, "\n";
    #warn "network access disabled\n";
    #return;
    my $rp= $self->{ua}->request($rq) or die "httperror: $@\n";

    #print $rp->status_line, "\n";
    #print $rp->headers->as_string();

    return $rp->content;
}

sub httprequest {
    my ($self, $method, @params)= @_;
    if (lc($method) eq "get") {
        return $self->httpget(@params);
    }
    elsif (lc($method) eq "post") {
        return $self->httppost(@params);
    }
    else {
        die "invalid http request method '$method'\n";
    }
}



package MediaWiki;
use strict;
use warnings;
use HTML::TreeBuilder;

sub new {
    my ($class, $server, %properties)= @_;

    my $self= bless {
        server=> WebServer->new($server),
        url=> "/index.php",
        special=>"Special",
        %properties,
    }, $class;

    return $self;
}
sub post {
    my ($self, @params)= @_;
    $self->{server}->httppost($self->{url}, @params);
}
sub get {
    my ($self, @params)= @_;
    if (!$self->{loggedin}) {
        $self->login();
    }
    $self->{server}->httpget($self->{url}, @params);
}
sub cachedget {
    my ($self, @params)= @_;

    my $filename= encodeurlasfile(@params);

    if (-e $filename) {
        return readfile($filename);
    }

    my $data= $self->get(@params);
    savefile($filename, $data);
    return $data;
}
sub DESTROY {
    my $self= shift;
}
########################################################################


sub getwikisource {
    my ($self, $page)= @_;
    my $xml= $self->post(
        title=>"$self->{special}:Export", 
        action=>'submit',
        pages=>$page,
        curonly=>'true',
    );
    if ($xml =~ /<text[^>]*>(.*?)<\/text>/s) {
        return $1;
    }
    die "could not find <text> xml tag in\n$xml\n";
}
sub getcategoryitems {
    my ($self, $page)= @_;
    my $html= $self->get(
        title=>$page,
    );
    my $tree = HTML::TreeBuilder->new();
    $tree->parse($html);
    $tree->eof();

    my ($table)= $tree->look_down(
        "_tag"=>"table",
    ) or die "could not find category table for $page\n";

    my @items;
    for $a ($table->look_down("_tag", "a")) {
        push @items, $a->as_text();
    }
    return @items;
}
sub uploadfile {
    my ($self, $imgname, $imgdesc)= @_;
    $imgname =~ s/\\/\//g;
    ( my $imgdestname= $imgname ) =~ s/.*\///;

    my $answer= $self->post(
        { title=>"$self->{special}:Upload", },
        wpUploadFile=>[$imgname],
        wpDestFile=>$imgdestname,
        wpUploadDescription=>$imgdesc,
        wpUpload=>"Upload file",
        wpIgnoreWarning=>'true',
    );
#   if ($answer) {
#       print $answer->content;
#   }
}
sub logout {
    my $self= shift;
    my $answer= $self->get(title=>"$self->{special}:Userlogout");
    $self->{loggedin}= 0;
    $self->{server}->clearcookies();
}
sub login {
    my ($self, $username, $password)= @_;

    my $answer= $self->post(
        { action=>'submitlogin', title=>"$self->{special}:Userlogin" },
        wpName => $username,
        wpPassword => $password,
        wpLoginattempt => 'Log in',
    );
    $self->{loggedin}= 1;
}

# title=>'Template:UpcomingTable'
# action=>'submit'

# text   wpSummary
# flag   wpMinoredit 1
# flag   wpWatchthis
# button wpSave      Save page
# button wpPreview   Show preview
# button wpDiff      Show changes
# hidden wpSection
# hidden wpEdittime  20050730124636
# hidden wpEditToken cd44d6f6003e41d1d44b9a79266a846f
# text   wpTextbox1 

sub geteditform {
    my ($self, $page, $section)= @_;
    my $answer= $self->get(
        action=>'edit', 
        title=>$page,
        defined $section ? ( section=>$section ) : (),
    );
    my $tree = HTML::TreeBuilder->new();
    $tree->parse($answer);
    $tree->eof();

    my ($formtag)= $tree->look_down(
        "_tag"=>"form",
        "name"=>"editform",
    );
    my @inputelements= $formtag->look_down(
        "_tag"=>"input",
        sub { $_[0]->attr('type') ne 'submit' && $_[0]->attr('type') ne 'radio' }
    );
    my @textelements= $formtag->look_down(
        "_tag"=>"textarea",
    );


    my %form;
    # not handling radio buttons yet.
    for my $field (@inputelements) {
        $form{$field->attr('name')}= $field->attr('value')
    }
    for my $field (@textelements) {
        $form{$field->attr('name')}= $field->as_text;
    }
    return \%form;
}
sub saveeditform {
    my ($self, $page, $form)= @_;
    my $answer= $self->post(
        { action=>'submit', title=>$page, },
        wpSave=>"Save page",
        %$form,
    );
}

sub createpage {
    my ($self, $page, $content)= @_;

    my $f= $self->geteditform($page);
    if ($f->{wpTextbox1}) {
        print "----$page\n$f->{wpTextbox1}\n\n";
    }
    $f->{wpTextbox1}= $content;
    print map { sprintf("%-20s= %s\n", $_, defined $f->{$_} ? "'$f->{$_}'":"<undef>") } keys %$f;
    $self->saveeditform($page, $f);
}

package main;

use strict;
use warnings;
use IO::File;
use Getopt::Long;
$|=1;

sub usage {
    return <<__EOF__
Usage: perl wikiimageupload.pl [filelist]
or     perl wikiimageupload.pl -d file description file description ...

a config file needs to exist with the following contents:

url=http://www.mywikiserver.nl
user=mylogin
pass=mypassword
special=Speciaal


the special tag depends on the language of your wiki installation
__EOF__
}
my $with_description;
GetOptions(
    "d"=>\$with_description,
) or die usage();
if ($with_description && (@ARGV%2)) {
    die "expected an even nr of params after -d option\n";
}
my $config= readconfig();
my $m= MediaWiki->new($config->{url}, map { ($_=>$config->{$_}) } grep { $_ ne "url" && $_ ne "user" && $_ ne "pass" } keys %$config);
$m->login($config->{user}, $config->{pass});

if ($with_description) {
    for (my $i=0 ; $i<@ARGV ; $i+=2) {
        if (!-f $ARGV[$i]) {
            warn "file $ARGV[$i] not found\n";
        }
        else {
            $m->uploadfile($ARGV[$i], $ARGV[$i+1]);
        }
    }
}
else {
    for (my $i=0 ; $i<@ARGV ; $i++) {
        if (!-f $ARGV[$i]) {
            warn "file $ARGV[$i] not found\n";
        }
        else {
            $m->uploadfile($ARGV[$i], "");
        }
    }
}
sub readconfig {
    my %params;
    my $fh= IO::File->new("c:/local/etc/wiki.cfg", "r") or die "wiki.cfg: $!";
    while (<$fh>) {
        s/\s+$//;
        if (/(\w+)\s*=\s*(.*)/) {
            my ($k, $v)= ($1, $2);
            $params{$k}= $v;
        }
    }
    $fh->close();
    return \%params;
}
