submit.pl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. #!/usr/bin/perl
  2. # Copyright (C) 2010, 2012 Alex Schroeder <alex@gnu.org>
  3. # This program is free software: you can redistribute it and/or modify it under
  4. # the terms of the GNU General Public License as published by the Free Software
  5. # Foundation, either version 3 of the License, or (at your option) any later
  6. # version.
  7. #
  8. # This program is distributed in the hope that it will be useful, but WITHOUT
  9. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  10. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  11. #
  12. # You should have received a copy of the GNU General Public License along with
  13. # this program. If not, see <http://www.gnu.org/licenses/>.
  14. package OddMuse;
  15. use URI;
  16. use LWP::UserAgent;
  17. use utf8;
  18. # load Oddmuse core
  19. $RunCGI = 0;
  20. do "wiki.pl";
  21. # globals
  22. my $page = "Feeds";
  23. my $site = "http://campaignwiki.org/wiki/Planet";
  24. my $src = "$site/raw/$page";
  25. my $target = "$site/$page";
  26. $FullUrl = "http://campaignwiki.org/submit";
  27. my %valid_content_type = ('application/atom+xml' => 1,
  28. 'application/rss+xml' => 1,
  29. 'application/xml' => 1,
  30. 'text/xml' => 1);
  31. main();
  32. sub default {
  33. print $q->p("Submit a blog to the "
  34. . $q->a({-href=>'http://campaignwiki.org/planet'},
  35. 'Old School RPG Planet') . ".");
  36. print GetFormStart();
  37. print $q->p($q->label({-for=>'url', -style=>'display: inline-block; width:30ex'},
  38. T('URL:')) . ' '
  39. . $q->textfield(-style=>'display: inline-block; width:60ex',
  40. -name=>'url', -id=>'url', -size=>50)
  41. . $q->br()
  42. . $q->label({-for=>'username', -style=>'display: inline-block; width:30ex'},
  43. T('Your name for the log file:')) . ' '
  44. . $q->textfield(-style=>'display: inline-block; width:60ex',
  45. -name=>'username', -id=>'username', -size=>50));
  46. print $q->submit('go', 'Go!');
  47. print $q->end_form();
  48. print $q->p("Drag this bookmarklet to your bookmarks bar for easy access:",
  49. $q->a({-href=>q{javascript:location='http://campaignwiki.org/submit?url='+encodeURIComponent(window.location.href)}}, 'Submit OSR Blog') . ".");
  50. }
  51. my %cached_blogs;
  52. sub parse_blogs {
  53. return %cached_blogs if %cached_blogs;
  54. my @data = split(/\n/, GetRaw($src));
  55. my $url;
  56. my $paramref;
  57. foreach $_ (@data) {
  58. if (/^\[(.+)\]/) {
  59. $url = $1;
  60. $paramref = {};
  61. } elsif (/^([a-z_]+) *= *(.+)/) {
  62. $paramref->{$1} = $2;
  63. }
  64. if ($url && $paramref->{name}) {
  65. $cached_blogs{$url} = $paramref;
  66. }
  67. }
  68. return %cached_blogs;
  69. }
  70. sub host_exists {
  71. my ($host, %blogs) = @_;
  72. foreach my $candidate (keys %blogs) {
  73. my $u = URI->new($candidate);
  74. return $candidate if $host eq $u->host;
  75. }
  76. }
  77. sub debug_url {
  78. my $url = $q->url(-path_info=>1) . "?debug=1;";
  79. $url .= join(";", map { $_ . "=" . GetParam($_) }
  80. qw(username confirmed candidate url));
  81. return $url;
  82. }
  83. sub check_url {
  84. my $url = shift;
  85. print $q->p("Debug: url=$url") if GetParam("debug");
  86. my $frown = $q->img({-src=>"http://emacswiki.org/pics/smiles/sad.png",
  87. -alt=>":("});
  88. my $smile = $q->img({-src=>"http://emacswiki.org/pics/smiles/smile.png",
  89. -alt=>":)"});
  90. my $u = URI->new($url);
  91. eval {$u->host };
  92. if ($@) {
  93. $url = 'http://' . $url;
  94. $u = URI->new($url);
  95. eval {$u->host };
  96. }
  97. # - not an url
  98. # - it's campaign wiki site
  99. # - no username
  100. # or read Feeds page and
  101. # - it's a duplicate
  102. # - it's a partial match: continue with confirmed=1
  103. # or read the list of alternatives from the url
  104. # - one of the feeds listed is known: continue with confirmed=2
  105. # - no feeds were listed: url is a feed or report it
  106. # - one feed was listed: try it
  107. # - some feeds were listed: pick one
  108. if ($@) {
  109. # the prefixing of http:// above should make it really hard to reach this code
  110. print $q->p($q->a({-href=>$url}, $url) . qq{
  111. seems to be <strong>invalid</strong>. $frown Make sure you use something
  112. like the following: <tt>http://grognardia.blogspot.com/</tt>});
  113. } elsif ($url =~ /campaignwiki\.org/i) {
  114. print $q->p(qq{
  115. This looks <strong>familiar</strong>!
  116. I do not think that adding any of the wikis on this site is the right
  117. thing to do, though.});
  118. print $q->p(qq{Thanks for testing it. }
  119. . $q->img({-src=>"http://www.emacswiki.org/pics/grin.png"}));
  120. } elsif (not GetParam('username', '')) {
  121. print $q->p(qq{As an anti-spam measure I'd really like you to
  122. <strong>provide a name</strong> for the log file. Sorry about that. $frown});
  123. } else {
  124. my %blogs = parse_blogs();
  125. my $duplicate = host_exists($u->host, %blogs);
  126. if ($blogs{$url}) {
  127. print $q->p("We already list ",
  128. $q->a({-href=>$duplicate}, $duplicate));
  129. } elsif ($duplicate && !GetParam('confirmed')) {
  130. print $q->p("We have a partial match: ",
  131. $q->a({-href=>$duplicate}, $duplicate));
  132. print GetFormStart();
  133. print $q->hidden('confirmed', 1);
  134. print $q->hidden('url', $url);
  135. print $q->submit('go', 'Proceed anyway!');
  136. print $q->end_form();
  137. } else {
  138. my ($status, @alternatives) = get_feeds($url, %blogs);
  139. if ($status eq 'known' && GetParam('confirmed') < 2) {
  140. print $q->p($q->a({-href=>$url},
  141. "The page you submitted")
  142. . " lists "
  143. . $q->a({-href=>$alternatives[0]},
  144. "a known feed") . ".");
  145. print GetFormStart();
  146. print $q->hidden('confirmed', 2);
  147. print $q->hidden('url', $url);
  148. print $q->submit('go', 'Proceed anyway!');
  149. print $q->end_form();
  150. } elsif ($#alternatives < 0) {
  151. if (is_feed($url)) {
  152. post_addition($url);
  153. } else {
  154. print $q->p("Apparently " . $q->a({-href=>$url}, QuoteHtml($url))
  155. . " is not a feed and doesn't link to any feed. "
  156. . "There is nothing for me to add. " . $frown);
  157. print $q->p("If you feel like it, you could try to "
  158. . $q->a({-href=>debug_url()}, "debug")
  159. . " this.");
  160. }
  161. } elsif ($#alternatives == 0) {
  162. print $q->p($q->a({-href=>$url}, "The page you submitted")
  163. . " lists "
  164. . $q->a({-href=>$alternatives[0]},
  165. "one new feed")
  166. . ".");
  167. print GetFormStart();
  168. print $q->hidden('url', $alternatives[0]);
  169. print $q->submit('go', 'Take it!');
  170. print $q->end_form();
  171. print $q->p("If you feel like it, you could try to "
  172. . $q->a({-href=>debug_url()}, "debug")
  173. . " this.");
  174. } else {
  175. print GetFormStart();
  176. print $q->p("You need to pick one of the candidates:");
  177. print $q->p(join($q->br(), map {
  178. $q->input({-type=>"radio", -name=>"url", -value=>$_},
  179. $q->a({-href=>$_}, QuoteHtml($_))) } @alternatives));
  180. print $q->submit('go', 'Submit');
  181. print $q->end_form();
  182. }
  183. }
  184. }
  185. }
  186. sub is_feed {
  187. my $url = shift;
  188. my $ua = LWP::UserAgent->new;
  189. my $response = $ua->get($url);
  190. return unless $response->is_success;
  191. return $valid_content_type{$response->content_type};
  192. }
  193. sub get_feeds {
  194. my $url = shift;
  195. my %others = @_;
  196. my $html = GetRaw($url);
  197. my @links = $html =~ /<link\b *(.*?)>/g;
  198. print $q->p("Debug: " . scalar(@links) . " links found") if GetParam("debug");
  199. print $q->pre($html) unless scalar(@links);
  200. print $q->p("Debug: no content returned") if GetParam("debug") and not $html;
  201. my @feeds;
  202. foreach my $link (@links) {
  203. print $q->p("Debug: $link")
  204. if GetParam("debug");
  205. if ($link !~ /\brel=(['"])alternate\1/i) {
  206. print $q->p("Debug: missing rel='alternate'")
  207. if GetParam("debug");
  208. next;
  209. }
  210. $link =~ /\btype=(['"])(.*?)\1/i;
  211. my $type = $2;
  212. if (not $valid_content_type{$type}) {
  213. print $q->p("Debug: type parameter is invalid ($type)")
  214. if GetParam("debug");
  215. next;
  216. }
  217. $link =~ /\bhref=(['"])(.*?)\1/i;
  218. my $href = $2;
  219. # clean up blogspot urls and prefer atom format
  220. $href =~ s/\?alt=rss$//i if $href =~ /blogspot/i;
  221. if (not $href) {
  222. print $q->p("Debug: href missing")
  223. if GetParam("debug");
  224. next;
  225. }
  226. if ($others{$href}) {
  227. print $q->p("Debug: feed already known ($href)")
  228. if GetParam("debug");
  229. if ($q->param('confirmed') >= 2) {
  230. next;
  231. } else {
  232. # don't look for other alternatives!
  233. return 'known', $href;
  234. }
  235. }
  236. push(@feeds, $href);
  237. }
  238. print $q->p("Debug: returning " . scalar(@feeds) . " links found")
  239. if GetParam("debug");
  240. return 'ok', @feeds;
  241. }
  242. sub config {
  243. my %blogs = @_;
  244. my $result = qq{#! config file for the RPG Planet
  245. # format:
  246. # Feed URL in square brackets, followed by name = and the name of the feed
  247. };
  248. foreach my $url (sort {lc($blogs{$a}->{name}) cmp lc($blogs{$b}->{name})} keys %blogs) {
  249. $result .= "[$url]\n";
  250. $paramref = $blogs{$url};
  251. foreach my $key (sort keys %{$paramref}) {
  252. $result .= $key . " = " . $paramref->{$key} . "\n";
  253. }
  254. }
  255. return $result;
  256. }
  257. sub post_addition {
  258. my $url = shift;
  259. print $q->p("Missing URL?") unless $url;
  260. my ($title, $final_url) = get_title($url);
  261. my %blogs = parse_blogs();
  262. if ($blogs{$final_url}) {
  263. print $q->p("The URL you ",
  264. $q->a({-href=>$url}, 'picked'),
  265. " is redirected to an URL we already list: ",
  266. $q->a({-href=>$final_url}, $blogs{$final_url}),
  267. ".");
  268. } else {
  269. $title = $final_url unless $title;
  270. print $q->p("Adding ",
  271. $q->a({-href=>$final_url}, $title));
  272. my %param = (name => $title);
  273. $blogs{$url} = \%param;
  274. my $result = config(%blogs);
  275. my $ua = LWP::UserAgent->new;
  276. my %params = (text => $result,
  277. title => $page,
  278. summary => $title,
  279. username => GetParam('username'),
  280. pwd => GetParam('pwd'));
  281. # spam fighting modules
  282. $params{$QuestionaskerSecretKey} = 1 if $QuestionaskerSecretKey;
  283. $params{$HoneyPotOk} = GetParam($HoneyPotOk, time) if $HoneyPotOk;
  284. my $response = $ua->post($site, \%params);
  285. if ($response->is_error) {
  286. print $q->p("The submission failed!");
  287. print $q->pre($response->status_line . "\n"
  288. . $response->content);
  289. } else {
  290. print $q->p("See for yourself: ",
  291. $q->a({-href=>$target}, $page));
  292. }
  293. }
  294. }
  295. sub get_title {
  296. my $uri = shift;
  297. my $ua = LWP::UserAgent->new;
  298. my $response = $ua->get($uri);
  299. return unless $response->is_success;
  300. my $title;
  301. $title = $1 if $response->content =~ m!<title.*?>(.*?)</title>!;
  302. return $title, $response->request->uri;
  303. }
  304. sub main {
  305. Init();
  306. if ($q->path_info eq '/source') {
  307. seek DATA, 0, 0;
  308. print "Content-type: text/plain; charset=UTF-8\r\n\r\n", <DATA>;
  309. } elsif ($q->path_info eq '/test') {
  310. print "Content-type: text/plain; charset=UTF-8\r\n\r\n";
  311. print config(parse_blogs());
  312. } else {
  313. $UserGotoBar .= $q->a({-href=>$q->url . '/source'}, 'Source');
  314. print GetHeader('', 'Submit a new blog');
  315. print $q->start_div({-class=>'content index'});
  316. if (not GetParam('url')) {
  317. print $q->p("Debug: no url parameter provided.") if GetParam("debug");
  318. default();
  319. } else {
  320. SetParam('title', 'Feeds'); # required to trigger HoneyPotInspection()
  321. check_url(GetParam('url'));
  322. }
  323. print $q->p('Questions? Send mail to Alex Schröder <'
  324. . $q->a({-href=>'mailto:kensanata@gmail.com'},
  325. 'kensanata@gmail.com') . '>');
  326. print $q->end_div();
  327. PrintFooter();
  328. }
  329. }
  330. __DATA__