wikicopy 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #!/usr/bin/perl -w
  2. #
  3. # Copyright (C) 2004, 2005, 2006 Alex Schroeder <alex@emacswiki.org>
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. require LWP;
  18. use Getopt::Std;
  19. our ($opt_v, $opt_w);
  20. # We make our own specialization of LWP::UserAgent that asks for
  21. # user/password if document is protected.
  22. {
  23. package RequestAgent;
  24. @ISA = qw(LWP::UserAgent);
  25. sub new {
  26. my $self = LWP::UserAgent::new(@_);
  27. $self;
  28. }
  29. sub get_basic_credentials {
  30. my($self, $realm, $uri) = @_;
  31. return split(':', $main::opt_w, 2);
  32. }
  33. }
  34. my $usage = qq{$0 [-i URL] [-d STRING] [-t SECONDS]
  35. \t[-u USERNAME] [-p PASSWORD] [-w USERNAME:PASSWORD]
  36. \t[-q QUESTION] [-a ANSWER] [-z SECRET]
  37. \t[SOURCE] TARGET
  38. SOURCE and TARGET are the base URLs for the two wikis. Visiting these
  39. two URLs should show you the respective homepages.
  40. Provide the page names to copy on STDIN or use -i to point to a page.
  41. You can use the index action with the raw parameter from the source
  42. wiki to copy all the pages. See example below.
  43. The list of page names should use the MIME type text/plain.
  44. By default, wikicopy will copy a page every five seconds. Use -t to
  45. override this. SECONDS is the number of seconds to wait between
  46. requests.
  47. If you use -d instead of providing a SOURCE, all the pages will be
  48. replaced with STRING. This is useful when replacing the page content
  49. with "DeletedPage", for example.
  50. -d Delete target pages instead of providing SOURCE (default: none)
  51. -s The summary for RecentChanges (default: none)
  52. -u The username for RecentChanges (default: none)
  53. -p The password to use for locked pages (default: none)
  54. -w The username:password combo for basic authentication (default:none)
  55. -q The question number to answer (default: 0, ie. the first question)
  56. -a The answer to the question (default: none)
  57. -z Alternatively, the secret key (default: question)
  58. -v Verbose output for debugging (default: none)
  59. Examples:
  60. wikicopy -i 'http://www.emacswiki.org/cgi-bin/alex?action=index;raw=1' \\
  61. http://www.emacswiki.org/cgi-bin/alex \\
  62. http://localhost/cgi-bin/wiki.pl
  63. wikicopy -d DeletedPage http://localhost/cgi-bin/wiki.pl < list.txt
  64. wikicopy -v -u 'ElGordo' -w 'simple:mind' \\
  65. -i 'http://www.communitywiki.org/odd/LosAngelesEcoVillage?action=index;raw=1' \\
  66. 'http://www.communitywiki.org/odd/LosAngelesEcoVillage' \\
  67. 'http://www.tentacle.net/~eeio/cgi/wiki.cgi'
  68. };
  69. sub UrlEncode {
  70. my $str = shift;
  71. return '' unless $str;
  72. my @letters = split(//, $str);
  73. my @safe = ('a' .. 'z', 'A' .. 'Z', '0' .. '9', '-', '_', '.', '!', '~', '*', "'", '(', ')', '#');
  74. foreach my $letter (@letters) {
  75. my $pattern = quotemeta($letter);
  76. if (not grep(/$pattern/, @safe)) {
  77. $letter = sprintf("%%%02x", ord($letter));
  78. }
  79. }
  80. return join('', @letters);
  81. }
  82. sub GetRaw {
  83. my ($uri) = @_;
  84. my $ua = RequestAgent->new;
  85. my $response = $ua->get($uri);
  86. print "no response\n" unless $response->code;
  87. print "GET ", $response->code, " ", $response->message, "\n" if $opt_v;
  88. return $response->content if $response->is_success;
  89. }
  90. sub post {
  91. my ($uri, $id, $data, $minor, $summary, $username, $password,
  92. $question, $answer, $secret) = @_;
  93. my $ua = RequestAgent->new;
  94. my %params = (title=>$id, text=>$data, raw=>1,
  95. username=>$username, pwd=>$password,
  96. summary=>$summary, question_num=>$question,
  97. answer=>$answer, $secret=>1,
  98. recent_edit=>$minor);
  99. if ($opt_v) {
  100. foreach my $key (keys %params) {
  101. my $value = $params{$key} || '(none)';
  102. $value = substr($value,0,50) . '...'
  103. if $key eq 'text' and length($value) > 53;
  104. warn "$key: $value\n";
  105. }
  106. }
  107. my $response = $ua->post($uri, \%params);
  108. my $status = $response->code . ' ' . $response->message;
  109. warn "POST $id failed: $status.\n" unless $response->is_success;
  110. }
  111. sub copy {
  112. my ($source, $replacement, $target, $interval, $minor, $summary,
  113. $username, $password, $question, $answer, $secret,
  114. @pages) = @_;
  115. foreach my $id (@pages) {
  116. print "$id\n";
  117. my $page = UrlEncode ($id);
  118. # fix URL for other wikis
  119. my $data = $replacement || GetRaw("$source?action=browse;id=$page;raw=1");
  120. next unless $data;
  121. post($target, $id, $data, $minor, $summary, $username, $password,
  122. $question, $answer, $secret);
  123. sleep($interval);
  124. }
  125. }
  126. sub main {
  127. our($opt_m, $opt_i, $opt_t, $opt_d, $opt_s, $opt_u, $opt_p,
  128. $opt_q, $opt_a, $opt_z);
  129. getopts('mi:t:d:s:u:p:q:a:z:w:v');
  130. my $interval = $opt_t ? $opt_t : 5;
  131. my $replacement = $opt_d;
  132. my ($source, $target);
  133. $source = shift(@ARGV) unless $replacement;
  134. $target = shift(@ARGV);
  135. die $usage if not $target or @ARGV; # not enough or too many
  136. my @pages = ();
  137. if ($opt_i) {
  138. my $data = GetRaw($opt_i);
  139. @pages = split(/\n/, $data);
  140. } else {
  141. print "List of pages:\n";
  142. while (<STDIN>) {
  143. chomp;
  144. push(@pages, $_);
  145. }
  146. }
  147. die "The list of pages is missing. Did you use -i?\n" unless @pages;
  148. copy($source, $replacement, $target, $interval, $opt_m ? 'on' : '', $opt_s,
  149. $opt_u, $opt_p, $opt_q, $opt_a, $opt_z||'question',
  150. @pages);
  151. }
  152. main();