1<?php if (!defined('PmWiki')) exit();
2/*  Copyright 2004-2021 Patrick R. Michaud (pmichaud@pobox.com)
3    This file is part of PmWiki; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published
5    by the Free Software Foundation; either version 2 of the License, or
6    (at your option) any later version.  See pmwiki.php for full details.
7
8    This script provides a URL-approval capability.  To enable this
9    script, add the following line to a configuration file:
10
11        include_once('scripts/urlapprove.php');
12
13    The URL prefixes to be allowed are stored as patterns in
14    $WhiteUrlPatterns.  This array can be loaded from config.php, or
15    from the wiki pages given by the $ApprovedUrlPagesFmt[] array.
16    Any http: or https: URL that isn't in $WhiteUrlPatterns is rendered
17    using $UnapprovedLinkFmt.
18
19    The script also provides ?action=approveurls and ?action=approvesites,
20    which scan the current page for any new URLs to be automatically added
21    the first page of $UrlApprovalPagesFmt.
22
23    Finally, the script will block any post containing more than
24    $UnapprovedLinkCountMax unapproved urls in it.  By default this
25    is set to a very large number, leaving the posting of unapproved
26    urls wide open, but by setting $UnapprovedLinkCountMax to a smaller
27    number you can limit the number of unapproved urls that make it into
28    a page.  (Wikispammers seem to like to post long lists of urls, while
29    more "normal" authors tend to only post a few.)
30
31    Script maintained by Petko YOTOV www.pmwiki.org/petko
32*/
33
34$LinkFunctions['http:'] = 'LinkHTTP';
35$LinkFunctions['https:'] = 'LinkHTTP';
36SDV($ApprovedUrlPagesFmt, array('$SiteAdminGroup.ApprovedUrls'));
37SDV($UnapprovedLinkFmt,
38  "\$LinkText<a class='apprlink' href='{\$PageUrl}?action=approvesites&amp;\$TokenName=\$TokenValue'>$[(approve sites)]</a>");
39SDVA($HTMLStylesFmt, array('urlapprove' => '.apprlink { font-size:smaller; }'));
40SDV($ApproveUrlPattern,
41  "\\bhttps?:[^\\s$UrlExcludeChars]*[^\\s.,?!$UrlExcludeChars]");
42$WhiteUrlPatterns = (array)@$WhiteUrlPatterns;
43SDV($HandleActions['approveurls'], 'HandleApprove');
44SDV($HandleAuth['approveurls'], 'edit');
45SDV($HandleActions['approvesites'], 'HandleApprove');
46SDV($HandleAuth['approvesites'], 'edit');
47SDV($UnapprovedLinkCountMax, 1000000);
48array_splice($EditFunctions, array_search('PostPage', $EditFunctions),
49  0, 'BlockUnapprovedPosts');
50
51function LinkHTTP($pagename,$imap,$path,$alt,$txt,$fmt=NULL) {
52  global $EnableUrlApprovalRequired, $IMap, $WhiteUrlPatterns, $FmtV,
53    $UnapprovedLink, $UnapprovedLinkCount, $UnapprovedLinkFmt;
54  if (!IsEnabled($EnableUrlApprovalRequired,1))
55    return LinkIMap($pagename,$imap,$path,$alt,$txt,$fmt);
56  static $havereadpages;
57  if (!$havereadpages) { ReadApprovedUrls($pagename); $havereadpages=true; }
58  static $token;
59  if (!$token) $token = pmtoken();
60  $FmtV['$TokenValue'] = urlencode($token);
61
62  $p = str_replace(' ','%20',$path);
63  $url = str_replace('$1',$p,$IMap[$imap]);
64  if (!isset($UnapprovedLink)) $UnapprovedLink = array();
65  foreach((array)$WhiteUrlPatterns as $pat) {
66    if (preg_match("!^$pat(/|$)!i",$url))
67      return LinkIMap($pagename,$imap,$path,$alt,$txt,$fmt);
68  }
69  $FmtV['$LinkUrl'] = PUE(str_replace('$1',$path,$IMap[$imap]));
70  $FmtV['$LinkText'] = $txt;
71  $FmtV['$LinkAlt'] = str_replace(array('"',"'"),array('&#34;','&#39;'),$alt);
72  $UnapprovedLink[] = $url;
73  @$UnapprovedLinkCount++;
74  return FmtPageName($UnapprovedLinkFmt,$pagename);
75}
76
77function ReadApprovedUrls($pagename) {
78  global $ApprovedUrlPagesFmt,$ApproveUrlPattern,$WhiteUrlPatterns;
79  foreach((array)$ApprovedUrlPagesFmt as $p) {
80    $pn = FmtPageName($p, $pagename);
81    StopWatch("ReadApprovedUrls $pn begin");
82    $apage = ReadPage($pn, READPAGE_CURRENT);
83    preg_match_all("/$ApproveUrlPattern/",@$apage['text'],$match);
84    foreach($match[0] as $a)
85      $WhiteUrlPatterns[] = preg_quote($a,'!');
86    StopWatch("ReadApprovedUrls $pn end");
87  }
88}
89
90function HandleApprove($pagename, $auth='edit') {
91  global $ApproveUrlPattern,$WhiteUrlPatterns,$ApprovedUrlPagesFmt,$action;
92  Lock(2);
93  $page = ReadPage($pagename);
94  $text = preg_replace('/[()]/','',$page['text']);
95  preg_match_all("/$ApproveUrlPattern/",$text,$match);
96  ReadApprovedUrls($pagename);
97  $addpat = array();
98  foreach($match[0] as $a) {
99    if ($action=='approvesites')
100      $a=preg_replace("!^([^:]+://[^/]+).*$!",'$1',$a);
101    $addpat[] = $a;
102  }
103  if (count($addpat)>0) {
104    $aname = FmtPageName($ApprovedUrlPagesFmt[0],$pagename);
105    $apage = RetrieveAuthPage($aname, $auth);
106    if (!$apage) Abort("?cannot edit $aname");
107    if(! AutoCheckToken()) {
108      Abort('? $[Token invalid or missing.]');
109    }
110    $new = $apage;
111    if (substr($new['text'],-1,1)!="\n") $new['text'].="\n";
112    foreach($addpat as $a) {
113      foreach((array)$WhiteUrlPatterns as $pat)
114        if (preg_match("!^$pat(/|$)!i",$a)) continue 2;
115      $urlp = preg_quote($a,'!');
116      $WhiteUrlPatterns[] = $urlp;
117      $new['text'].="  $a\n";
118    }
119    $_POST['post'] = 'y';
120    PostPage($aname,$apage,$new);
121  }
122  Redirect($pagename);
123}
124
125function BlockUnapprovedPosts($pagename, &$page, &$new) {
126  global $EnableUrlApprovalRequired, $UnapprovedLinkCount,
127    $UnapprovedLinkCountMax, $EnablePost, $MessagesFmt, $BlockMessageFmt;
128  if (!IsEnabled($EnableUrlApprovalRequired, 1)) return;
129  if ($UnapprovedLinkCount <= $UnapprovedLinkCountMax) return;
130  if ($page['=auth']['admin']) return;
131  $EnablePost = 0;
132  $MessagesFmt[] = $BlockMessageFmt;
133  $MessagesFmt[] = XL('Too many unapproved external links.');
134}
135
136