]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - wiki_stdlib.php3
fixed raw HTML exploit
[SourceForge/phpwiki.git] / wiki_stdlib.php3
1 <!-- $Id: wiki_stdlib.php3,v 1.16 2000-06-26 20:05:22 ahollosi Exp $ -->
2 <?
3    /*
4       Standard functions for Wiki functionality
5          GeneratePage($template, $content, $name, $hash)
6          LinkExistingWikiWord($wikiword) 
7          LinkUnknownWikiWord($wikiword) 
8          LinkURL($url)
9          RenderQuickSearch() 
10          RenderFullSearch() 
11          CookSpaces($pagearray) 
12          class Stack
13          SetHTMLOutputMode($newmode, $depth)
14          UpdateRecentChanges($dbi, $pagename, $isnewpage) 
15          SaveCopyToArchive($pagename, $pagehash) 
16          ParseAndLink($bracketlink)
17    */
18
19
20    function GeneratePage($template, $content, $name, $hash)
21    {
22       global $ScriptUrl, $AllowedProtocols, $templates;
23       global $datetimeformat;
24
25       if (!is_array($hash))
26          unset($hash);
27
28       $page = join('', file($templates[$template]));
29       $page = str_replace('###', "#$FieldSeparator#", $page);
30
31       // valid for all pagetypes
32       $page = str_replace("#$FieldSeparator#SCRIPTURL#$FieldSeparator#",
33                         $ScriptUrl, $page);
34       $page = str_replace("#$FieldSeparator#PAGE#$FieldSeparator#",
35                         htmlspecialchars($name), $page);
36       $page = str_replace("#$FieldSeparator#ALLOWEDPROTOCOLS#$FieldSeparator#",
37                         $AllowedProtocols, $page);
38
39       // invalid for messages (search results, error messages)
40       if ($template != 'MESSAGE') {
41          $page = str_replace("#$FieldSeparator#PAGEURL#$FieldSeparator#",
42                         rawurlencode($name), $page);
43          $page = str_replace("#$FieldSeparator#LASTMODIFIED#$FieldSeparator#",
44                         date($datetimeformat, $hash['lastmodified']), $page);
45          $page = str_replace("#$FieldSeparator#LASTAUTHOR#$FieldSeparator#",
46                         $hash['author'], $page);
47          $page = str_replace("#$FieldSeparator#VERSION#$FieldSeparator#",
48                         $hash['version'], $page);
49       }
50
51       // valid only for EditLinks
52       if ($template == 'EDITLINKS') {
53          for ($i = 1; $i <= NUM_LINKS; $i++)
54             $page = str_replace("#$FieldSeparator#R$i#$FieldSeparator#",
55                         $hash['refs'][$i], $page);
56       }
57
58       if ($hash['copy']) {
59          $page = str_replace("#$FieldSeparator#IFCOPY#$FieldSeparator#",
60                         '', $page);
61       } else {
62          $page = ereg_replace("#$FieldSeparator#IFCOPY#$FieldSeparator#[^\n]*",
63                         '', $page);
64       }
65
66       $page = str_replace("#$FieldSeparator#CONTENT#$FieldSeparator#",
67                         $content, $page);
68       print $page;
69    }
70
71
72    function LinkExistingWikiWord($wikiword) {
73       global $ScriptUrl;
74       $enc_word = rawurlencode($wikiword);
75       $wikiword = htmlspecialchars($wikiword);
76       return "<a href=\"$ScriptUrl?$enc_word\">$wikiword</a>";
77    }
78
79    function LinkUnknownWikiWord($wikiword) {
80       global $ScriptUrl;
81       $enc_word = rawurlencode($wikiword);
82       $wikiword = htmlspecialchars($wikiword);
83       return "<u>$wikiword</u><a href=\"$ScriptUrl?edit=$enc_word\">?</a>";
84    }
85
86    function LinkURL($url) {
87       global $ScriptUrl;
88       if(ereg("[<>\"]", $url)) {
89          return "<b><u>BAD URL -- remove all of &lt;, &gt;, &quot;</u></b>";
90       }
91       $enc_url = htmlspecialchars($url);
92       return "<a href=\"$url\">$enc_url</a>";
93    }
94
95    
96    function RenderQuickSearch() {
97       global $value, $ScriptUrl;
98       $formtext = "<form action='$ScriptUrl'>\n<input type='text' size='40' name='search' value='$value'>\n</form>\n";
99       return $formtext;
100    }
101
102    function RenderFullSearch() {
103       global $value, $ScriptUrl;
104       $formtext = "<form action='$ScriptUrl'>\n<input type='text' size='40' name='full' value='$value'>\n</form>\n";
105       return $formtext;
106    }
107
108    // converts spaces to tabs
109    function CookSpaces($pagearray) {
110       return preg_replace("/ {3,8}/", "\t", $pagearray);
111    }
112
113
114    class Stack {
115       var $items;
116       var $size = 0;
117
118       function push($item) {
119          $this->items[$this->size] = $item;
120          $this->size++;
121          return true;
122       }  
123    
124       function pop() {
125          if ($this->size == 0) {
126             return false; // stack is empty
127          }  
128          $this->size--;
129          return $this->items[$this->size];
130       }  
131    
132       function cnt() {
133          return $this->size;
134       }  
135
136       function top() {
137          return $this->items[$this->size - 1];
138       }  
139
140    }  
141    // end class definition
142
143
144    // I couldn't move this to wiki_config.php3 because it 
145    // wasn't declared yet.
146    $stack = new Stack;
147
148    /* 
149       Wiki HTML output can, at any given time, be in only one mode.
150       It will be something like Unordered List, Preformatted Text,
151       plain text etc. When we change modes we have to issue close tags
152       for one mode and start tags for another.
153    */
154
155    function SetHTMLOutputMode($tag, $tagdepth, $tabcount) {
156       global $stack;
157       $retvar = "";
158    
159       if ($tagdepth == SINGLE_DEPTH) {
160          if ($tabcount < $stack->cnt()) {
161             // there are fewer tabs than stack,
162             // reduce stack to that tab count
163             while ($stack->cnt() > $tabcount) {
164                $closetag = $stack->pop();
165                if ($closetag == false) {
166                   //echo "bounds error in tag stack";
167                   break;
168                }
169                $retvar .= "</$closetag>\n";
170             }
171
172             // if list type isn't the same,
173             // back up one more and push new tag
174             if ($tag != $stack->top()) {
175                $closetag = $stack->pop();
176                $retvar .= "</$closetag><$tag>\n";
177                $stack->push($tag);
178             }
179    
180          } elseif ($tabcount > $stack->cnt()) {
181             // we add the diff to the stack
182             // stack might be zero
183             while ($stack->cnt() < $tabcount) {
184                #echo "<$tag>\n";
185                $retvar .= "<$tag>\n";
186                $stack->push($tag);
187                if ($stack->cnt() > 10) {
188                   // arbitrarily limit tag nesting
189                   echo "Stack bounds exceeded in SetHTMLOutputMode\n";
190                   exit();
191                }
192             }
193    
194          } else {
195             if ($tag == $stack->top()) {
196                return;
197             } else {
198                $closetag = $stack->pop();
199                #echo "</$closetag>\n";
200                #echo "<$tag>\n";
201                $retvar .= "</$closetag>\n";
202                $retvar .= "<$tag>\n";
203                $stack->push($tag);
204             }
205          }
206    
207       } elseif ($tagdepth == ZERO_DEPTH) {
208          // empty the stack for $depth == 0;
209          // what if the stack is empty?
210          if ($tag == $stack->top()) {
211             return;
212          }
213          while ($stack->cnt() > 0) {
214             $closetag = $stack->pop();
215             #echo "</$closetag>\n";
216             $retvar .= "</$closetag>\n";
217          }
218    
219          if ($tag) {
220             #echo "<$tag>\n";
221             $retvar .= "<$tag>\n";
222             $stack->push($tag);
223          }
224    
225       } else {
226          // error
227          echo "Passed bad tag depth value in SetHTMLOutputMode\n";
228          exit();
229       }
230
231       return $retvar;
232
233    }
234    // end SetHTMLOutputMode
235
236
237
238    // The Recent Changes file is solely handled here
239    function UpdateRecentChanges($dbi, $pagename, $isnewpage) {
240
241       global $remoteuser; // this is set in the config
242       global $dateformst;
243
244       $recentchanges = RetrievePage($dbi, "RecentChanges");
245
246       // this shouldn't be necessary, since PhpWiki loads 
247       // default pages if this is a new baby Wiki
248       if ($recentchanges == -1) {
249          $recentchanges = array(); 
250       }
251
252       $now = time();
253       $today = date($dateformat, $now);
254
255       if (date($dateformat, $recentchanges["lastmodified"]) != $today) {
256          $isNewDay = TRUE;
257          $recentchanges["lastmodified"] = $now;
258       } else {
259          $isNewDay = FALSE;
260       }
261
262       $numlines = sizeof($recentchanges["content"]);
263       $newpage = array();
264       $k = 0;
265
266       // scroll through the page to the first date and break
267       // dates are marked with "____" at the beginning of the line
268       for ($i = 0; $i < ($numlines + 1); $i++) {
269          if (preg_match("/^____/",
270                         $recentchanges["content"][$i])) {
271             break;
272          } else {
273             $newpage[$k++] = $recentchanges["content"][$i];
274          }
275       }
276
277       // if it's a new date, insert it, else add the updated page's
278       // name to the array
279
280       if ($isNewDay) {
281          $newpage[$k++] = "____$today\r";
282       } else {
283          $newpage[$k++] = $recentchanges["content"][$i++];
284       }
285       if($isnewpage) {
286          $newpage[$k++] = "\t* [$pagename] (new) ..... $remoteuser\r";
287       } else {
288          $newpage[$k++] = "\t* [$pagename] ..... $remoteuser\r";
289       }
290
291       // copy the rest of the page into the new array
292       $pagename = preg_quote($pagename);
293       for (; $i < ($numlines + 1); $i++) {
294          // skip previous entry for $pagename
295          if (preg_match("/\[$pagename\]/", $recentchanges["content"][$i])) {
296             continue;
297          } else {
298             $newpage[$k++] = $recentchanges["content"][$i];
299          }
300       }
301
302       $recentchanges["content"] = $newpage;
303
304       InsertPage($dbi, "RecentChanges", $recentchanges);
305    }
306
307
308    // for archiving pages to a seperate dbm
309    function SaveCopyToArchive($pagename, $pagehash) {
310       global $ArchiveDataBase;
311
312       $adbi = OpenDataBase($ArchiveDataBase);
313       $newpagename = $pagename;
314       InsertPage($adbi, $newpagename, $pagehash);
315    }
316
317
318    function ParseAndLink($bracketlink) {
319       global $dbi, $AllowedProtocols;
320
321       // $bracketlink will start and end with brackets; in between
322       // will be either a page name, a URL or both seperated by a pipe.
323
324       // strip brackets and leading space
325       preg_match("/(\[\s*)(.+?)(\s*\])/", $bracketlink, $match);
326       $linkdata = $match[2];
327
328       // send back links that are only numbers (they are references)
329       if (preg_match("/^\d+$/", $linkdata)) {
330          return $bracketlink;
331       }
332
333       // send back escaped ([[) bracket sets
334       if (preg_match("/^\[/", $linkdata)) {
335          return htmlspecialchars(substr($bracketlink, 1));
336       }
337
338       // match the contents 
339       preg_match("/([^|]+)(\|)?([^|]+)?/", $linkdata, $matches);
340
341       if (isset($matches[3])) {
342          $URL = trim($matches[3]);
343          $linkname = htmlspecialchars(trim($matches[1]));
344          // assert proper URL's
345          if (preg_match("#^($AllowedProtocols):#", $URL)) {
346             return "<a href=\"$URL\">$linkname</a>";
347          } else {
348             return "<b><u>BAD URL -- links have to start with one of " .                                                "$AllowedProtocols followed by ':'</u></b>";
349          }
350       }
351
352       if (isset($matches[1])) {
353          $linkname = trim($matches[1]);
354          if (IsWikiPage($dbi, $linkname)) {
355             return LinkExistingWikiWord($linkname);
356          } elseif (preg_match("#^($AllowedProtocols):#", $linkname)) {
357             return LinkURL($linkname);
358          } elseif ($linkname == 'Search') {
359             return RenderQuickSearch();
360          } elseif ($linkname == 'Fullsearch') {
361             return RenderFullSearch();
362          } else {
363             return LinkUnknownWikiWord($linkname);
364          }
365       }
366
367       return $bracketlink;
368
369    }
370
371 ?>