#!/usr/bin/perl 
#   pmwe  (pmwikiedit)
#   Copyright 2002-2004 Jonathan Scott Duff 
#   duff@pobox.com
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#   HISTORY
#   April  5, 2004	Added improvements suggested by Tom Hoover
#   April 10, 2004	Added some documentation and other improvements
#   May 8, 2004		Fixed a couple of small bugs

=pod

=head1 NAME

	pmwe - PmWiki Edit - Edit wiki pages using your favorite editor

=head1 SYNPOSIS

	pmwe [options] [Abbr:][WikiGroup/]WikiPage		-or-
	pmwe [options] URL	
	
=head1 DESCRIPTION

Edit wiki pages using your favorite editor. Currently only works
with pmwiki.

=head2 OPTIONS

	-a AUTHOR	Specify the author of the wiki page

        -c FILENAME	Name of cookie file to use. Defaults to
        		$HOME/.pmwe.cookies

        -e EDITOR	Full path to the editor you wish to use. If
                        unspecified, uses the value in the EDITOR
                        environment variable otherwise uses "vi" 

        -g		Grabs the passwords as they are entered and
                        writes them to the filename specified by the -p
                        option. This option has no effect if -p is not
                        specified.

	-m FILENAME	A file for mapping Intermap-like abbreviations to URLs
                        The format for this file is "abbreviation URL"
                        where the space can be any number of whitespace
                        characters. The URL must be such that adding
                        "Group/WikiPage" to the end will access the
                        wiki page.

        -p FILENAME     File for keeping the passwords for your realms.  
                        The format of the file is "realm:user:password".
                        When used in conjunction with the -g option,
                        realms and passwords will be written to the
                        password file as used.

	-u URL		Specify a URL for accessing the wiki. The URL
                        must be such that adding "Group/WikiPage" to the
                        end will access the wikipage.

=head1 AUTHOR

Jonathan Scott Duff duff@pobox.com

=cut

use strict;
use warnings;

use Getopt::Std;
my %opt; getopts('m:a:e:u:c:p:gr', \%opt);

my %passwords;
if ($opt{'p'} && open(P,$opt{'p'})) {
   while (<P>) { 
      chomp; my($realm,$user,$pass) = split /:/,$_,2; 
      $realm = 'pm' unless $realm;
      $passwords{$realm}= [ $user, $pass ]; 
   }
   close P;
}

package PmWikiAgent;

use base qw(LWP::UserAgent);
use Term::ReadKey;

our $Author;
sub set_author { shift; $Author = shift; }

sub new {
   my $self = LWP::UserAgent::new(@_);
   $self->agent("pmwikiedit");
   return $self;
}

our $Password;
sub get_basic_credentials
{
   my($self, $realm) = @_;
   return ($passwords{$realm}[0],$passwords{$realm}[1]) if $passwords{$realm};
   print "Enter username/password for $realm\n";
   print "Username [$Author]: "; chomp(my $author = <STDIN>);
   $author = $Author if $author eq '';
   if ($Password) { print "Press ENTER to use the same password as before\n"; }
   print "Password: ";
   ReadMode('noecho'); chomp(my $password = <STDIN>); ReadMode('restore');
   print "\n";  # because we disabled echo
   if ($password eq '') { $password = $Password; }
   else { $Password = $password; }
   savepass($realm,$author,$password) if $opt{'g'};
   return ($author, $password);
}

use Fcntl ':flock';
sub savepass {
   return unless $opt{'p'};
   my ($realm,$user,$pass) = @_;
   $passwords{$realm} = [ $user, $pass ];
   return unless open(P,">",$opt{'p'});
   flock(P,LOCK_EX);
   for (sort keys %passwords) { print P join(":",$_,@{$passwords{$_}}),"\n"; }
   close P;
}

package main;

use HTTP::Cookies;
use File::Temp qw(tempfile);

my $DefaultWikiUrl = 'http://www.pmwiki.org/wiki/';
my $DefaultWikiPage = 'Main/WikiSandbox';
my $DefaultWikiEditor = 'vi';
my $DefaultWikiAuthor = 'Pm';

my %urlmap;
my $mapfile = $opt{'m'} || "$ENV{'HOME'}/.pmwe.map";
if (open(MAP, $mapfile)) {
   while (<MAP>) { my ($n,$url) = split; $urlmap{$n} = $url; }
   close MAP;
}

my $cookiefile	= $opt{'c'} || "$ENV{'HOME'}/.pmwe.cookies";
my $jar = HTTP::Cookies->new(file => $cookiefile, autosave => 1);
$jar->scan(sub { $DefaultWikiAuthor = $_[2] if $_[1] eq 'author'; });

my $wikipage	= shift || $DefaultWikiPage;
my $editor	= $opt{'e'} || $ENV{'EDITOR'} || $DefaultWikiEditor;
my $author	= $opt{'a'} || $ENV{'WikiAuthor'} || $DefaultWikiAuthor;
my $wikiurl	= $opt{'u'} || shift || $DefaultWikiUrl;

my $pageurl;
if ($wikipage =~ /^http/) { 
   $pageurl = $wikipage;
   if ($wikipage =~ /pagename/) 
      { ($wikipage) = $pageurl =~ /pagename=([^&]+)/; }
   else { ($wikipage) = $pageurl =~ m!(\w+/\w+)$!; }
} else {
   $wikiurl = $urlmap{$1} if $wikipage=~s/^(\w+):// && $urlmap{$1};
   if (not exists $urlmap{$1}) { die "There exists no map for $1\n"; }

   $wikipage =~ tr!/!.!;
   $wikipage .= ".$wikipage" unless $wikipage =~ /\./;

   $wikiurl ||= $DefaultWikiUrl;
   $wikiurl .= '/' unless $wikiurl =~ m!/$!;
   $pageurl = "$wikiurl$wikipage";
}

while (1) { 
  my $sep = ($pageurl =~ /\?/) ? '&' : '?';
  my $ua = PmWikiAgent->new; $ua->set_author($author); $ua->cookie_jar($jar);
  my $req = HTTP::Request->new('GET', "$pageurl${sep}action=source");
  my $outfile = (tempfile(UNLINK => 1))[1];
  my $res = $ua->request($req,$outfile);
  
  my $mtime0 = (stat($outfile))[9];
  system("$editor $outfile");
  my $mtime1 = (stat($outfile))[9];
  $mtime0 == $mtime1 && die "content unchanged.\n"; 
  
  open(OUTFILE, $outfile) or die;
  $res = $ua->post($pageurl, [
     action	=> 'edit',
     post       => 1,
     pagename	=> $wikipage,
     text	=> do { local $/; <OUTFILE> },
     author	=> 'Pm',
     authpw     => '!secret',
     code1      => 6472,
  ]);
  close OUTFILE;
  $res->is_error && die "Unable to save content!\n".$res->as_string."\n";
  exit(0) unless $opt{'r'};
}