f88d89855a7f1598fba9b6dd90822a4a178497d6
[atutor.git] / mods / wiki / ewiki.php
1 <?php @define("EWIKI_VERSION", "R1.02b");
2
3 /*
4   ErfurtWiki - a pretty flexible, fast and user-friendly wiki framework
5   ¯¯¯¯¯¯¯¯¯¯
6   Is PUBLIC DOMAIN (no license, no warranty); feel free to redistribute
7   under any other license, if you want. (c) 2003-2005 WhoEver wants to.
8
9   project+help:
10     http://erfurtwiki.sourceforge.net/
11     http://ewiki.berlios.de/
12   lead by:
13     Mario Salzer <mario*erphesfurt·de>
14     Andy Fundinger <andy*burgiss·com>
15
16   call it from within yoursite.php / layout script like this:
17     <?php
18        include("ewiki.php");
19        $CONTENT = ewiki_page();
20     ? >
21     <HTML>...<BODY>...
22     <?php
23        echo $CONTENT;
24     ? >
25     ...</HTML>
26 */
27
28 #-- for future backwards compatibility to R1.02b (temporary file dependencies)
29 if (!function_exists("ewiki_page_edit")) { include_once("plugins/edit.php"); }
30 if (!function_exists("ewiki_format")) { include_once("plugins/format.php"); }
31 if (!function_exists("ewiki_binary")) { include_once("plugins/feature/binary.php"); }
32 if (!function_exists("ewiki_author")) { include_once("plugins/misc.php"); }
33 if (!class_exists("ewiki_database_mysql")) { include_once("plugins/db/mysql.php"); }
34
35         #-------------------------------------------------------- config ---
36
37         #-- this disables most PHPs debugging (_NOTICE) messages
38         error_reporting(0x0000377 & error_reporting());
39 #       error_reporting(E_ALL^E_NOTICE);  // development
40
41         #-- the location of your ewiki-wrapper script
42         define("EWIKI_SCRIPT", "?id=");                 # relative to docroot
43 #       define("EWIKI_SCRIPT_URL", "http://../?id=");   # absolute URL
44
45         #-- change to your needs (site lang)
46         define("EWIKI_NAME", "UnnamedWiki");            # Wiki title
47         define("EWIKI_PAGE_INDEX", "ATutorWiki");       # default page
48         define("EWIKI_PAGE_PRETTY_URL", 'mods\/wiki\/index.php');               #pretty URL format of this page
49         define("EWIKI_PAGE_LIST", "PageIndex");
50         define("EWIKI_PAGE_SEARCH", "SearchPages");
51         define("EWIKI_PAGE_NEWEST", "NewestPages");
52         define("EWIKI_PAGE_HITS", "MostVisitedPages");
53         define("EWIKI_PAGE_VERSIONS", "MostOftenChangedPages");
54         define("EWIKI_PAGE_UPDATES", "UpdatedPages");   # like RecentChanges
55
56         #-- default settings are good settings - most often ;)
57         #- look & feel
58         define("EWIKI_PRINT_TITLE", 2);         # <h2>WikiPageName</h2> on top
59         define("EWIKI_SPLIT_TITLE", 0);         # <h2>Wiki Page Name</h2>
60         define("EWIKI_CONTROL_LINE", 1);        # EditThisPage-link at bottom
61         define("EWIKI_LIST_LIMIT", 20);         # listing limit
62         #- behaviour
63         define("EWIKI_AUTO_EDIT", 1);           # edit box for non-existent pages
64         define("EWIKI_EDIT_REDIRECT", 1);       # redirect after edit save
65         define("EWIKI_DEFAULT_ACTION", "view"); # (keep!)
66         define("EWIKI_CASE_INSENSITIVE", 1);    # wikilink case sensitivity
67         define("EWIKI_HIT_COUNTING", 1);
68         define("EWIKI_RESOLVE_DNS", 1);         # gethostbyaddr() when editing
69         define("UNIX_MILLENNIUM", 1000000000);
70         #- rendering
71         define("EWIKI_ALLOW_HTML", 0);          # often a very bad idea
72         define("EWIKI_HTML_CHARS", 1);          # allows for &#200;
73         define("EWIKI_ESCAPE_AT", 1);           # "@" -> "&#x40;"
74         #- http/urls
75         define("EWIKI_SUBPAGE_LONGTITLE", 0);
76         define("EWIKI_SUBPAGE_START", ".:/");   # set to "" to disable [.Sub] getting a link to [CurrentPage.Sub]
77 #        define("EWIKI_SUBPAGE_CHARS", ".:/-!");
78         define("EWIKI_HTTP_HEADERS", 1);        # most often a good thing
79         define("EWIKI_NO_CACHE", 1);            # browser+proxy shall not cache
80         define("EWIKI_URLENCODE", 1);           # disable when _USE_PATH_INFO
81         define("EWIKI_URLDECODE", 1);
82 #new!   define("EWIKI_URL_UTF8", 1);            # fix UTF-8 parameters
83         define("EWIKI_USE_PATH_INFO", 1);
84         define("EWIKI_USE_ACTION_PARAM", 1);    # 2 for alternative link style
85         define("EWIKI_ACTION_SEP_CHAR", "/");
86         define("EWIKI_ACTION_TAKE_ASIS", 1);
87         define("EWIKI_UP_PAGENUM", "n");        # _UP_ means "url parameter"
88         define("EWIKI_UP_PAGEEND", "e");
89         define("EWIKI_UP_BINARY", "binary");
90         define("EWIKI_UP_UPLOAD", "upload");
91         define("EWIKI_UP_PARENTID", "parent_page");
92         define("EWIKI_UP_LISTLIM", "limit");
93         #- other stuff
94         define("EWIKI_DEFAULT_LANG", "en");
95         define("EWIKI_CHARSET", "ISO-8859-1");  # nothing else supported
96         #- user permissions
97         define("EWIKI_PROTECTED_MODE", 0);      # disable funcs + require auth
98         define("EWIKI_PROTECTED_MODE_HIDING", 0);  # hides disallowed actions
99         define("EWIKI_AUTH_DEFAULT_RING", 3);   # 0=root 1=priv 2=user 3=view
100         define("EWIKI_AUTO_LOGIN", 1);          # [auth_query] on startup
101
102         #-- allowed WikiPageNameCharacters
103         define("EWIKI_CHARS_L", "a-z_µ¤ß-ÿ$");  # \337-\377
104         define("EWIKI_CHARS_U", "A-Z0-9À-Þ");   # \300-\336
105         define("EWIKI_CHARS", EWIKI_CHARS_L.EWIKI_CHARS_U);
106
107         #-- database
108         @define("EWIKI_DB_TABLE_NAME", "ewiki");        # MySQL / ADOdb
109         @define("EWIKI_DBFILES_DIRECTORY", "/tmp");     # see "db_flat_files.php"
110         define("EWIKI_DBA", "/tmp/ewiki.db3");          # see "db_dba.php"
111         define("EWIKI_DBQUERY_BUFFER", 512*1024);       # 512K
112         define("EWIKI_INIT_PAGES", "./init-pages");     # for initialization
113
114         define("EWIKI_DB_F_TEXT", 1<<0);
115         define("EWIKI_DB_F_BINARY", 1<<1);
116         define("EWIKI_DB_F_DISABLED", 1<<2);
117         define("EWIKI_DB_F_HTML", 1<<3);
118         define("EWIKI_DB_F_READONLY", 1<<4);
119         define("EWIKI_DB_F_WRITEABLE", 1<<5);
120         define("EWIKI_DB_F_APPENDONLY", 1<<6);
121         define("EWIKI_DB_F_SYSTEM", 1<<7);
122         define("EWIKI_DB_F_PART", 1<<8);
123         define("EWIKI_DB_F_MINOR", 1<<9);
124         define("EWIKI_DB_F_HIDDEN", 1<<10);
125         define("EWIKI_DB_F_ARCHIVE", 1<<11);
126         define("EWIKI_DB_F_EXEC", 1<<17);
127         define("EWIKI_DB_F_TYPE", EWIKI_DB_F_TEXT | EWIKI_DB_F_BINARY | EWIKI_DB_F_DISABLED | EWIKI_DB_F_SYSTEM | EWIKI_DB_F_PART);
128         define("EWIKI_DB_F_ACCESS", EWIKI_DB_F_READONLY | EWIKI_DB_F_WRITEABLE | EWIKI_DB_F_APPENDONLY);
129         define("EWIKI_DB_F_COPYMASK", EWIKI_DB_F_TYPE | EWIKI_DB_F_ACCESS | EWIKI_DB_F_HIDDEN | EWIKI_DB_F_HTML | EWIKI_DB_F_ARCHIVE);
130
131         define("EWIKI_DBFILES_NLR", '\\n');
132         define("EWIKI_DBFILES_ENCODE", 0 || (DIRECTORY_SEPARATOR != "/"));
133         define("EWIKI_DBFILES_GZLEVEL", "2");
134
135         #-- internal, auto-discovered
136         define("EWIKI_ADDPARAMDELIM", (strstr(EWIKI_SCRIPT,"?") ? "&" : "?"));
137         define("EWIKI_SERVER", ($_SERVER["HTTP_HOST"] ? $_SERVER["HTTP_HOST"] : $_SERVER["SERVER_NAME"]) . ( ($_SERVER["SERVER_PORT"] != "80") ? (":" . $_SERVER["SERVER_PORT"]) : ""));
138         define("EWIKI_BASE_URL", (@$_SERVER["HTTPS"] ? "https" : "http") . "://" . EWIKI_SERVER . substr(realpath(dirname(__FILE__)), strlen(realpath($_SERVER["DOCUMENT_ROOT"]))) . "/");      # URL to ewiki dir
139         define("EWIKI_BASE_DIR", dirname(__FILE__));
140
141         #-- binary content (images)
142         define("EWIKI_ENGAGE_BINARY", 1);
143         @define("EWIKI_SCRIPT_BINARY", /*"/binary.php?binary="*/  ltrim(strtok(" ".EWIKI_SCRIPT,"?"))."?".EWIKI_UP_BINARY."="  );
144         define("EWIKI_CACHE_IMAGES", 1  &&!headers_sent());
145         define("EWIKI_IMAGE_MAXSIZE", 64 *1024);
146         define("EWIKI_IMAGE_MAXWIDTH", 3072);
147         define("EWIKI_IMAGE_MAXHEIGHT", 2048);
148         define("EWIKI_IMAGE_MAXALLOC", 1<<19);
149         define("EWIKI_IMAGE_RESIZE", 1);
150         define("EWIKI_IMAGE_ACCEPT", "image/jpeg,image/png,image/gif,application/x-shockwave-flash");
151         define("EWIKI_IDF_INTERNAL", "internal://");
152         define("EWIKI_ACCEPT_BINARY", 0);   # for arbitrary binary data files
153
154         #-- misc
155         define("EWIKI_TMP", isset($_SERVER["TEMP"]) ? $_SERVER["TEMP"] : "/tmp");
156         define("EWIKI_VAR", "./var");           # should be world-writable
157         define("EWIKI_LOGLEVEL", -1);           # 0=error 1=warn 2=info 3=debug
158         define("EWIKI_LOGFILE", "/tmp/ewiki.log");
159
160         #-- plugins (tasks mapped to function names)
161         $ewiki_plugins["database"][] = "ewiki_database_mysql";
162         $ewiki_plugins["edit_preview"][] = "ewiki_page_edit_preview";
163         $ewiki_plugins["render"][] = "ewiki_format";
164         $ewiki_plugins["init"][-5] = "ewiki_localization";
165         if (EWIKI_ENGAGE_BINARY)
166         $ewiki_plugins["init"][-1] = "ewiki_binary";
167         $ewiki_plugins["handler"][-105] = "ewiki_eventually_initialize";
168         $ewiki_plugins["handler"][] = "ewiki_intermap_walking";
169         $ewiki_plugins["view_append"][-1] = "ewiki_control_links";
170         $ewiki_plugins["view_final"][-1] = "ewiki_add_title";
171         $ewiki_plugins["page_final"][] = "ewiki_http_headers";
172         $ewiki_plugins["page_final"][99115115] = "ewiki_page_css_container";
173         $ewiki_plugins["edit_form_final"][] = "ewiki_page_edit_form_final_imgupload";
174         $ewiki_plugins["format_block"]["pre"][] = "ewiki_format_pre";
175         $ewiki_plugins["format_block"]["code"][] = "ewiki_format_pre";
176         $ewiki_plugins["format_block"]["htm"][] = "ewiki_format_html";
177         $ewiki_plugins["format_block"]["html"][] = "ewiki_format_html";
178         $ewiki_plugins["format_block"]["comment"][] = "ewiki_format_comment";
179
180         #-- internal pages
181         $ewiki_plugins["page"][EWIKI_PAGE_LIST] = "ewiki_page_index";
182         $ewiki_plugins["page"][EWIKI_PAGE_NEWEST] = "ewiki_page_newest";
183         $ewiki_plugins["page"][EWIKI_PAGE_SEARCH] = "ewiki_page_search";
184         if (EWIKI_HIT_COUNTING) $ewiki_plugins["page"][EWIKI_PAGE_HITS] = "ewiki_page_hits";
185         $ewiki_plugins["page"][EWIKI_PAGE_VERSIONS] = "ewiki_page_versions";
186         $ewiki_plugins["page"][EWIKI_PAGE_UPDATES] = "ewiki_page_updates";
187
188         #-- page actions
189         $ewiki_plugins["action"]["edit"] = "ewiki_page_edit";
190         $ewiki_plugins["action_always"]["links"] = "ewiki_page_links";
191         $ewiki_plugins["action"]["info"] = "ewiki_page_info";
192         $ewiki_plugins["action"]["view"] = "ewiki_page_view";
193
194         #-- helper vars ---------------------------------------------------
195         $ewiki_config["idf"]["url"] = array("http://", "mailto:", EWIKI_IDF_INTERNAL, "ftp://", "https://", "data:", "irc://", "telnet://", "news://", "chrome://", "file://", "gopher://", "httpz://");
196         $ewiki_config["idf"]["img"] = array(".jpeg", ".png", ".jpg", ".gif", ".j2k");
197         $ewiki_config["idf"]["obj"] = array(".swf", ".svg");
198
199         #-- entitle actions
200         $ewiki_config["action_links"]["view"] = array(
201                 "edit" => "EDITTHISPAGE",       # ewiki_t() is called on these
202                 "links" => "BACKLINKS",
203                 "info" => "PAGEHISTORY",
204                 "like" => "LIKEPAGES",
205         ) + (array)@$ewiki_config["action_links"]["view"];
206         $ewiki_config["action_links"]["info"] = array(
207                 "view" => "browse",
208                 "edit" => "fetchback",
209         ) + (array)@$ewiki_config["action_links"]["info"];
210
211         #-- variable configuration settings (go into '$ewiki_config')
212         $ewiki_config_DEFAULTS_tmp = array(
213            "edit_thank_you" => 1,
214            "edit_box_size" => "77x17",
215            "print_title" => EWIKI_PRINT_TITLE,
216            "split_title" => EWIKI_SPLIT_TITLE,
217            "control_line" => EWIKI_CONTROL_LINE,
218            "list_limit" => EWIKI_LIST_LIMIT,
219            "script" => EWIKI_SCRIPT,
220            "script_url" => (defined("EWIKI_SCRIPT_URL")?EWIKI_SCRIPT_URL:NULL),
221            "script_binary" => EWIKI_SCRIPT_BINARY,
222            "qmark_links" => "0b?",
223         #-- heart of the wiki -- don't try to read this! ;)
224            "wiki_pre_scan_regex" =>     '/
225                 (?<![!~\\\\])
226                 ((?:(?:\w+:)*['.EWIKI_CHARS_U.']+['.EWIKI_CHARS_L.']+){2,}[\w\d]*(?<!_))
227                 |\^([-'.EWIKI_CHARS_L.EWIKI_CHARS_U.']{3,})
228                 |\[ (?:"[^\]\"]+" | \s+ | [^:\]#]+\|)*  ([^\|\"\[\]\#]+)  (?:\s+ | "[^\]\"]+")* [\]\#] 
229                 |(\w{3,9}:\/\/[^\s\[\]\'\"()<>]+[^\s\[\]\'\"()<>!,.\-:;?])
230                 /x',
231            "wiki_link_regex" => "\007 [!~\\\\]?(
232                 \#?\[[^<>\[\]\n]+\] |
233                 \w[-_.+\w]+@(\w[-_\w]+[.])+\w{2,} |
234                 \^[-".EWIKI_CHARS_U.EWIKI_CHARS_L."]{3,} |
235                 \b([\w]{3,}:)*([".EWIKI_CHARS_U."]+[".EWIKI_CHARS_L."]+){2,}\#?[\w\d]* |
236                 ([a-z]{2,9}://|mailto:|data:)[^\s\[\]\'\"()<>]+[^\s\[\]\'\"()<>,.!\-:;?]
237                 ) \007x",
238         #-- rendering ruleset
239            "wm_indent" => '<div style="margin-left:15px;" class="indent">',
240            "wm_table_defaults" => 'cellpadding="2" border="1" cellspacing="0"',
241            "wm_whole_line" => array("&gt;&gt;" => 'div align="right"'),
242            "wm_max_header"=>3,
243            "wm_publishing_headers"=>0,
244            "htmlentities" => array(
245                 "&" => "&amp;",
246                 ">" => "&gt;",
247                 "<" => "&lt;",
248            ),
249            "wm_source" => array(
250                 "%%%" => "<br />",
251                 "&lt;br&gt;" => "<br />",
252                 "\t" => "        ",
253                 "\n;:" => "\n      ",   # workaround, replaces the old ;:
254            ),
255            "wm_list" => array(
256                 "-" => array('ul type="square"', "", "li"),
257                 "*" => array('ul type="circle"', "", "li"),
258                 "#" => array("ol", "", "li"),
259                 ":" => array("dl", "dt", "dd"),
260         #<out># ";" => array("dl", "dt", "dd"),
261            ),
262            "wm_style" => array(
263                 "'''''" => array("<b><i>", "</i></b>"),
264                 "'''" => array("<b>", "</b>"),
265                 "''" => array("<em>", "</em>"),
266                 "__" => array("<strong>", "</strong>"),
267                 "^^" => array("<sup>", "</sup>"),
268                 "==" => array("<tt>", "</tt>"),
269         #<off># "___" => array("<i><b>", "</b></i>"),
270         #<off># "***" => array("<b><i>", "</i></b>"),
271         #<off># "###" => array("<big><b>", "</b></big>"),
272  #<broken+bug># "//" => array("<i>", "</i>"),   # conflicts with URLs, could only be done with regex
273                 "**" => array("<b>", "</b>"),
274                 "##" => array("<big>", "</big>"),
275                 "µµ" => array("<small>", "</small>"),
276            ),
277            "wm_start_end" => array(
278         #<off># array("[-", "-]", "<s>", "</s>"),
279         #<off># array("(*", "*)", "<!--", "-->"),
280            ),
281         #-- rendering plugins
282            "format_block" => array(
283                 "html" => array("&lt;html&gt;", "&lt;/html&gt;", "html", 0x0000),
284                 "htm" => array("&lt;htm&gt;", "&lt;/htm&gt;", "html", 0x0003),
285                 "code" => array("&lt;code&gt;", "&lt;/code&gt;", false, 0x0004),
286                 "pre" => array("&lt;pre&gt;", "&lt;/pre&gt;", false, 0x0027|4),
287                 "comment" => array("\n&lt;!--", "--&gt;", false, 0x0030),
288                 #<off>#  "verbatim" => array("&lt;verbatim&gt;", "&lt;/verbatim&gt;", false, 0x0030),
289            ),
290            "format_params" => array(
291                 "scan_links" => 1,
292                 "html" => EWIKI_ALLOW_HTML,
293                 "mpi" => 1,
294            ),
295         );
296         #-- copy above settings into real _config[] array
297         foreach ($ewiki_config_DEFAULTS_tmp as $set => $val) {
298            if (!isset($ewiki_config[$set])) {
299               $ewiki_config[$set] = $val;
300            }
301            elseif (is_array($val)) foreach ($val as $vali=>$valv) {
302               if (is_int($vali)) {
303                  $ewiki_config[$set][] = $valv;
304               }
305               elseif (!isset($ewiki_config[$set][$vali])) {
306                  $ewiki_config[$set][$vali] = $valv;
307               }
308            }
309         }
310         $ewiki_config_DEFAULTS_tmp = $valv = $vali = $val = NULL;
311         
312         #-- special pre-sets
313         $ewiki_config["ua"] = "ewiki/".EWIKI_VERSION
314            . " (".PHP_OS."; PHP/".PHP_VERSION.")" . @$ewiki_config["ua"];
315
316
317         #-- text  (never remove the "C" or "en" sections!)
318         #
319         $ewiki_t["C"] = (array)@$ewiki_t["C"] + array(
320            "DATE" => "%a, %d %b %G %T %Z",
321            "EDIT_TEXTAREA_RESIZE_JS" => '<a href="javascript:ewiki_enlarge()" style="text-decoration:none">+</a><script type="text/javascript"><!--'."\n".'function ewiki_enlarge() {var ta=document.getElementById("ewiki_content");ta.style.width=((ta.cols*=1.1)*10).toString()+"px";ta.style.height=((ta.rows*=1.1)*30).toString()+"px";}'."\n".'//--></script>',
322         );
323         #
324         $ewiki_t["en"] = (array)@$ewiki_t["en"] + array(
325            "EDITTHISPAGE" => "EditThisPage",
326            "APPENDTOPAGE" => "Add to",
327            "BACKLINKS" => "BackLinks",
328            "EDITCOMPLETE" => 'Your edit has been saved click <a href="$url">here</a> to see the edited page.',
329            "PAGESLINKINGTO" => "Pages linking to \$title",
330            "PAGEHISTORY" => "PageInfo",
331            "INFOABOUTPAGE" => "Information about page",
332            "LIKEPAGES" => "Pages like this",
333            "NEWESTPAGES" => "Newest Pages",
334            "LASTCHANGED" => "last changed on %c",
335            "DOESNOTEXIST" => "This page does not yet exist, please click on EditThisPage if you'd like to create it.",
336            "DISABLEDPAGE" => "This page is currently not available.",
337            "ERRVERSIONSAVE" => "Sorry, while you edited this page someone else
338                 did already save a changed version. Please go back to the
339                 previous screen and copy your changes to your computers
340                 clipboard to insert it again after you reload the edit
341                 screen.",
342            "ERRORSAVING" => "An error occoured while saving your changes. Please try again.",
343            "THANKSFORCONTRIBUTION" => "Thank you for your contribution!",
344            "CANNOTCHANGEPAGE" => "This page cannot be changed.",
345            "OLDVERCOMEBACK" => "Make this old version come back to replace the current one",
346            "PREVIEW" => "Preview",
347            "SAVE" => "Save",
348            "CANCEL_EDIT" => "CancelEditing",
349            "UPLOAD_PICTURE_BUTTON" => "upload picture &gt;&gt;&gt;",
350            "EDIT_FORM_1" => "It is <a href=\"".EWIKI_SCRIPT."GoodStyle\">GoodStyle</a>
351                 to just start writing. With <a href=\"".EWIKI_SCRIPT."WikiMarkup\">WikiMarkup</a>
352                 you can style your text later.<br />",
353            "EDIT_FORM_2" => "<br />Please do not write things, which may make other
354                 people angry. And please keep in mind that you are not all that
355                 anonymous in the internet (find out more about your computers
356                 '<a href=\"http://google.com/search?q=my+computers+IP+address\">IP address</a>' at Google).",
357            "BIN_IMGTOOLARGE" => "Image file is too large!",
358            "BIN_NOIMG" => "This is no image file (inacceptable file format)!",
359            "FORBIDDEN" => "You are not authorized to access this page.",
360         );
361         #
362         $ewiki_t["es"] = (array)@$ewiki_t["es"] + array(
363            "EDITTHISPAGE" => "EditarEstaPágina",
364            "BACKLINKS" => "EnlacesInversos",
365            "PAGESLINKINGTO" => "Páginas enlazando \$title",
366            "PAGEHISTORY" => "InfoPágina",
367            "INFOABOUTPAGE" => "Información sobre la página",
368            "LIKEPAGES" => "Páginas como esta",
369            "NEWESTPAGES" => "Páginas más nuevas",
370            "LASTCHANGED" => "última modificación %d/%m/%Y a las %H:%M",
371            "DOESNOTEXIST" => "Esta página aún no existe, por favor eliga EditarEstaPágina si desea crearla.",
372            "DISABLEDPAGE" => "Esta página no está disponible en este momento.",
373            "ERRVERSIONSAVE" => "Disculpe, mientras editaba esta página alguién más
374                 salvó una versión modificada. Por favor regrese a
375                 a la pantalla anterior y copie sus cambios a su computador
376                 para insertalos nuevamente después de que cargue
377                 la pantalla de edición.",
378            "ERRORSAVING" => "Ocurrió un error mientras se salvavan sus cambios. Por favor intente de nuevo.",
379            "THANKSFORCONTRIBUTION" => "Gracias por su contribución!",
380            "CANNOTCHANGEPAGE" => "Esta página no puede ser modificada.",
381            "OLDVERCOMEBACK" => "Hacer que esta versión antigua regrese a remplazar la actual",
382            "PREVIEW" => "Previsualizar",
383            "SAVE" => "Salvar",
384            "CANCEL_EDIT" => "CancelarEdición",
385            "UPLOAD_PICTURE_BUTTON" => "subir gráfica &gt;&gt;&gt;",
386            "EDIT_FORM_1" => "<a href=\"".EWIKI_SCRIPT."BuenEstilo\">BuenEstilo</a> es
387                 escribir lo que viene a su mente. No se preocupe mucho
388                 por la apariencia. También puede agregar <a href=\"".EWIKI_SCRIPT."ReglasDeMarcadoWiki\">ReglasDeMarcadoWiki</a>
389                 más adelante si piensa que es necesario.<br />",
390            "EDIT_FORM_2" => "<br />Por favor no escriba cosas, que puedan
391                 enfadar a otras personas. Y por favor tenga en mente que
392                 usted no es del todo anónimo en Internet 
393                 (encuentre más sobre 
394                 '<a href=\"http://google.com/search?q=my+computers+IP+address\">IP address</a>' de su computador con Google).",
395            "BIN_IMGTOOLARGE" => "¡La gráfica es demasiado grande!",
396            "BIN_NOIMG" => "¡No es un archivo con una gráfica (formato de archivo inaceptable)!",
397            "FORBIDDEN" => "No está autorizado para acceder a esta página.",
398         );
399         #
400         $ewiki_t["de"] = (array)@$ewiki_t["de"] + array(
401            "EDITTHISPAGE" => "DieseSeiteÄndern",
402            "APPENDTOPAGE" => "Ergänze",
403            "BACKLINKS" => "ZurückLinks",
404            "PAGESLINKINGTO" => "Verweise zur Seite \$title",
405            "PAGEHISTORY" => "SeitenInfo",
406            "INFOABOUTPAGE" => "Informationen über Seite",
407            "LIKEPAGES" => "Ähnliche Seiten",
408            "NEWESTPAGES" => "Neueste Seiten",
409            "LASTCHANGED" => "zuletzt geändert am %d.%m.%Y um %H:%M",
410            "DISABLEDPAGE" => "Diese Seite kann momentan nicht angezeigt werden.",
411            "ERRVERSIONSAVE" => "Entschuldige, aber während Du an der Seite
412                 gearbeitet hast, hat bereits jemand anders eine geänderte
413                 Fassung gespeichert. Damit nichts verloren geht, browse bitte
414                 zurück und speichere Deine Änderungen in der Zwischenablage
415                 (Bearbeiten->Kopieren) um sie dann wieder an der richtigen
416                 Stelle einzufügen, nachdem du die EditBoxSeite nocheinmal
417                 geladen hast.<br />
418                 Vielen Dank für Deine Mühe.",
419            "ERRORSAVING" => "Beim Abspeichern ist ein Fehler aufgetreten. Bitte versuche es erneut.",
420            "THANKSFORCONTRIBUTION" => "Vielen Dank für Deinen Beitrag!",
421            "CANNOTCHANGEPAGE" => "Diese Seite kann nicht geändert werden.",
422            "OLDVERCOMEBACK" => "Diese alte Version der Seite wieder zur Aktuellen machen",
423            "PREVIEW" => "Vorschau",
424            "SAVE" => "Speichern",
425            "CANCEL_EDIT" => "ÄnderungenVerwerfen",
426            "UPLOAD_PICTURE_BUTTON" => "Bild hochladen &gt;&gt;&gt;",
427            "EDIT_FORM_1" => "Es ist <a href=\"".EWIKI_SCRIPT."GuterStil\">GuterStil</a>,
428                 einfach drauf los zu tippen. Mit den <a href=\"".EWIKI_SCRIPT."FormatierungsRegeln\">FormatierungsRegeln</a>
429                 kannst du den Text später noch umgestalten.<br />",
430            "EDIT_FORM_2" => "<br />Bitte schreib keine Dinge, die andere Leute
431                 verärgern könnten. Und bedenke auch, daß es schnell auf
432                 dich zurückfallen kann wenn du verschiedene andere Dinge sagst (mehr Informationen zur
433                 '<a href=\"http://google.de/search?q=computer+IP+adresse\">IP Adresse</a>'
434                 deines Computers findest du bei Google).",
435         );
436         $ewiki_t["nl"] = (array)@$ewiki_t["nl"] + array(
437            "EDITTHISPAGE" => "BewerkPagina",
438         );
439
440         #-- InterWiki:Links
441         $ewiki_config["interwiki"] = (array)@$ewiki_config["interwiki"] +
442         array(
443            "javascript" => "",  # this actually protects from javascript: links
444            "url" => "",
445            "jump" => "",        # fallback; if jump plugin isn't loaded
446 #          "self" => "this",
447            "this" => defined("EWIKI_SCRIPT_URL")?EWIKI_SCRIPT_URL:EWIKI_SCRIPT,
448            // real entries:
449            "ErfurtWiki" => "http://erfurtwiki.sourceforge.net/",
450            "InterWiki" => "MetaWiki",
451            "MetaWiki" => "http://sunir.org/apps/meta.pl?",
452            "Wiki" => "WardsWiki",
453            "WardsWiki" => "http://www.c2.com/cgi/wiki?",
454            "WikiFind" => "http://c2.com/cgi/wiki?FindPage&value=",
455            "WikiPedia" => "http://www.wikipedia.com/wiki.cgi?",
456            "MeatBall" => "MeatballWiki",
457            "MeatballWiki" => "http://www.usemod.com/cgi-bin/mb.pl?",
458            "UseMod"       => "http://www.usemod.com/cgi-bin/wiki.pl?",
459            "CommunityWiki" => "http://www.emacswiki.org/cgi-bin/community/",
460            "WikiFeatures" => "http://wikifeatures.wiki.taoriver.net/moin.cgi/",
461            "PhpWiki" => "http://phpwiki.sourceforge.net/phpwiki/index.php3?",
462            "LinuxWiki" => "http://linuxwiki.de/",
463            "OpenWiki" => "http://openwiki.com/?",
464            "Tavi" => "http://andstuff.org/tavi/",
465            "TWiki" => "http://twiki.sourceforge.net/cgi-bin/view/",
466            "MoinMoin" => "http://www.purl.net/wiki/moin/",
467            "Google" => "http://google.com/search?q=",
468            "ISBN" => "http://www.amazon.com/exec/obidos/ISBN=",
469            "icq" => "http://www.icq.com/",
470         );
471         
472 // end of config
473
474
475
476
477 #-------------------------------------------------------------------- init ---
478
479
480 #-- bring up database backend
481 if (!isset($ewiki_db) && ($pf = $ewiki_plugins["database"][0])) {
482    if (class_exists($pf)) {
483       $ewiki_db = new $pf;
484    }
485    elseif (function_exists($pf)) {
486       include("plugins/db/oldapi.php"); // eeeyk! temporary workaround!
487    }
488 }
489
490 #-- init stuff, autostarted parts (done a 2nd time inside ewiki_page)
491 if ($pf_a = $ewiki_plugins["init"]) {
492    ksort($pf_a);
493    foreach ($pf_a as $pf) {
494       $pf($GLOBALS);
495    }
496    unset($ewiki_plugins["init"]);
497 }
498
499
500
501 #-------------------------------------------------------------------- main ---
502
503 /*  This is the main function, which you should preferrably call to
504     integrate the ewiki into your web site; it chains to most other
505     parts and plugins (including the edit box).
506     If you do not supply the requested pages "$id" we will fetch it
507     from the pre-defined possible URL parameters.
508 */
509 function ewiki_page($id=false) {
510
511    global $ewiki_links, $ewiki_plugins, $ewiki_ring, $ewiki_t,
512       $ewiki_errmsg, $ewiki_data, $ewiki_title, $ewiki_id,
513       $ewiki_action, $ewiki_config;
514
515    #-- output str
516    $o = "";
517
518    #-- selected page
519    if (!strlen($id)) {
520       $id = ewiki_id();
521    }
522
523    #-- page action
524    $action = EWIKI_DEFAULT_ACTION;
525    if ($delim = strpos($id, EWIKI_ACTION_SEP_CHAR)) {
526       $a = substr($id, 0, $delim);
527       if (EWIKI_ACTION_TAKE_ASIS || in_array($a, $ewiki_plugins["action"]) || in_array($a, $ewiki_plugins["action_always"])) {
528          $action = rawurlencode($a);
529          $id = substr($id, $delim + 1);
530       }
531    }
532
533    if (EWIKI_USE_ACTION_PARAM && isset($_REQUEST["action"])) {
534       $action = rawurlencode($_REQUEST["action"]);
535    }
536    $ewiki_data = array();
537    $ewiki_id = $id;
538    $ewiki_title = ewiki_split_title($id);
539    $ewiki_action = $action;
540
541    #-- more initialization
542    if ($pf_a = @$ewiki_plugins["init"]) {
543       ksort($pf_a);
544       foreach ($pf_a as $pf) {
545          $o .= $pf();
546       }
547       unset($ewiki_plugins["init"]);
548    }
549    #-- micro-gettext stub (for upcoming/current transition off of ewiki_t)
550    if (!function_exists("_")) {
551       function _($text) { return($text); }
552       function gettext($text) { return($text); }
553    }
554
555    #-- fetch from db
556    $version = false;
557    if (!isset($_REQUEST["content"]) && ($version = 0 + @$_REQUEST["version"])) {
558       $ewiki_config["forced_version"] = $version;
559    }
560    $ewiki_data = ewiki_db::GET($id, $version);
561    $data = &$ewiki_data;
562
563    #-- pre-check if actions exist
564    $pf_page = ewiki_array($ewiki_plugins["page"], $id);
565
566    #-- edit <form> or info/ page for non-existent and empty pages
567    if (($action==EWIKI_DEFAULT_ACTION) && empty($data["content"]) && empty($pf_page)) {
568       if ($data["version"] >= 2) {
569          $action = "info";
570       }
571       elseif (EWIKI_AUTO_EDIT) {
572          $action = "edit";
573       }
574       else {
575          $data["content"] = ewiki_t("DOESNOTEXIST");
576       }
577    }
578
579    #-- internal "create" action / used for authentication requests
580    if (($action == "edit")&&(($data["version"]==0) && !isset($pf_page))) {
581       $ewiki_config["create"] = $id;
582    }
583
584    #-- require auth
585    if (EWIKI_PROTECTED_MODE) {
586       if (!ewiki_auth($id, $data, $action, $ring=false, $force=EWIKI_AUTO_LOGIN)) {
587          return($o.=$ewiki_errmsg);
588       }
589    }
590
591    #-- handlers
592    $handler_o = "";
593    if ($pf_a = @$ewiki_plugins["handler"]) {
594       ksort($pf_a);
595       foreach ($pf_a as $pf_i=>$pf) {
596          if ($handler_o = $pf($id, $data, $action, $pf_i)) { break; }
597    }  }
598
599    #-- stop here if page is not marked as _TEXT,
600    #   perform authentication then, and let only administrators proceed
601    if (!$handler_o) {
602       if (!empty($data["flags"]) && (($data["flags"] & EWIKI_DB_F_TYPE) != EWIKI_DB_F_TEXT)) {
603          if (($data["flags"] & EWIKI_DB_F_BINARY) && ($pf = $ewiki_plugins["handler_binary"][0])) {
604             return($pf($id, $data, $action)); //_BINARY entries handled separately
605          }
606       elseif ((!EWIKI_PROTECTED_MODE || !ewiki_auth($id, $data, $action, 0, 1)) && ($ewiki_ring!=0)) {
607             return(ewiki_t("DISABLEDPAGE"));
608          }
609       }
610    }
611
612    #-- finished by handler
613    if ($handler_o) {
614       $o .= $handler_o;
615    }
616    #-- actions that also work for static and internal pages
617    elseif (($pf = @$ewiki_plugins["action_always"][$action]) && function_exists($pf)) {
618       $o .= $pf($id, $data, $action);
619    }
620    #-- internal pages
621    elseif ($pf_page && function_exists($pf_page)) {
622       $o .= $pf_page($id, $data, $action);
623    }
624    #-- page actions
625    else {
626       $pf = @$ewiki_plugins["action"][$action];
627
628       #-- fallback to "view" action
629       if (empty($pf) || !function_exists($pf)) {
630
631          $pf = "ewiki_page_view";
632          $action = "view";     // we could also allow different (this is a
633          // catch-all) view variants, but this would lead to some problems
634       }
635
636       $o .= $pf($id, $data, $action);
637    }
638
639    #-- error instead of page?
640    if (empty($o) && $ewiki_errmsg) {
641       $o = $ewiki_errmsg;
642    }
643
644    #-- html post processing
645    if ($pf_a = $ewiki_plugins["page_final"]) {
646       ksort($pf_a);
647       foreach ($pf_a as $pf) {
648          $pf($o, $id, $data, $action);
649       }
650    }
651
652    if (EWIKI_ESCAPE_AT && !isset($ewiki_config["@"])) {
653       $o = str_replace("@", "&#x40;", $o);
654    }
655
656    $ewiki_data = &$data;
657    unset($ewiki_data["content"]);
658    return($o);
659 }
660
661
662
663 #-- HTTP meta headers
664 function ewiki_http_headers(&$o, $id, &$data, $action, $saveasfilename=1) {
665    global $ewiki_t, $ewiki_config;
666    if (EWIKI_HTTP_HEADERS && !headers_sent()) {
667       if (!empty($data)) {
668          if (($uu = @$data["id"]) && $saveasfilename) @header('Content-Disposition: inline; filename="' . urlencode($uu) . '.html"');
669          if ($uu = @$data["version"]) @header('Content-Version: ' . $uu);
670          if ($uu = @$data["lastmodified"]) @header('Last-Modified: ' . gmstrftime($ewiki_t["C"]["DATE"], $uu));
671       }
672       if (EWIKI_NO_CACHE) {
673          header('Expires: ' . gmstrftime($ewiki_t["C"]["DATE"], UNIX_MILLENNIUM));
674          header('Pragma: no-cache');
675          header('Cache-Control: no-cache, must-revalidate' . (($ewiki_author||EWIKI_PROTECTED_MODE)?", private":"") );
676          # ", private" flag only for authentified users / _PROT_MODE
677       }
678       #-- ETag
679       if ($data["version"] && ($etag=ewiki_etag($data)) || ($etag=md5($o))) {
680          $weak = "W/" . urlencode($id) . "." . $data["version"];
681          header("ETag: \"$etag\"");     ###, \"$weak\"");
682          header("X-Server: $ewiki_config[ua]");
683       }
684    }
685 }
686 function ewiki_etag(&$data) {
687    return(  urlencode($data["id"]) . ":" . dechex($data["version"]) . ":ewiki:" .
688             dechex(crc32($data["content"]) & 0x7FFFBFFF)  );
689 }
690
691
692
693 #-- encloses whole page output with a descriptive <div>
694 function ewiki_page_css_container(&$o, &$id, &$data, &$action) {
695    $sterilized_id = trim(preg_replace('/[^\w\d]+/', "-", $id), "-");
696    $sterilized_id = preg_replace('/^(\d)/', 'page$1', $sterilized_id);
697    $o = "<div class=\"wiki $action $sterilized_id\">\n" . $o . "\n</div>\n";
698 }
699
700
701
702 function ewiki_split_title ($id='', $split=-1, $entities=1) {
703    if ($split==-1) {
704       $split = $GLOBALS["ewiki_config"]["split_title"];
705    }
706    strlen($id) or ($id = $GLOBALS["ewiki_id"]);
707    if ($split) {
708       $id = preg_replace("/([".EWIKI_CHARS_L."])([".EWIKI_CHARS_U."]+)/", "$1 $2", $id);
709    }
710    return($entities ? htmlentities($id) : $id);
711 }
712
713
714
715 function ewiki_add_title(&$html, $id, &$data, $action, $go_action="links") {
716    if (EWIKI_PRINT_TITLE)
717       $html = "<div class=\"text-head\">\n"
718          . ewiki_make_title($id, '', 1, $action, $go_action)
719          . "\n</div>\n" . $html;
720 }
721
722
723 function ewiki_make_title($id='', $title='', $class=3, $action="view", $go_action="links", $may_split=1) {
724
725    global $ewiki_config, $ewiki_plugins, $ewiki_title, $ewiki_id;
726
727    #-- advanced handler
728    if ($pf = @$ewiki_plugins["make_title"][0]) {
729       return($pf($title, $class, $action, $go_action, $may_split));
730    }
731    #-- disabled
732    elseif (!$ewiki_config["print_title"]) {
733       return("");
734    }
735
736    #-- get id
737    if (empty($id)) {
738       $id = $ewiki_id;
739    }
740
741    #-- get title
742    if (!strlen($title)) {
743       $title = $ewiki_title;  // already in &html; format
744    }
745    elseif ($ewiki_config["split_title"] && $may_split) {
746       $title = ewiki_split_title($title, $ewiki_config["split_title"], 0&($title!=$ewiki_title));
747    }
748    else {
749       $title = htmlentities($title);
750    }
751
752    #-- title mangling
753    if ($pf_a = @$ewiki_plugins["title_transform"]) {
754       foreach ($pf_a as $pf) { $pf($id, $title, $go_action); }
755    }
756
757    #-- clickable link or simple headline
758    if ($class <= $ewiki_config["print_title"]) {
759       if ($uu = @$ewiki_config["link_title_action"][$action]) {
760          $go_action = $uu;
761       }
762       if ($uu = @$ewiki_config["link_title_url"]) {
763          $href = $uu;
764          unset($ewiki_config["link_title_url"]);
765       }
766       else {
767          $href = ewiki_script($go_action, $id);
768       }
769       $o = '<a href="' . $href . '">' . ($title) . '</a>';
770    }
771    else {
772       $o = $title;
773    }
774
775    // h2.page.title is obsolete; h2.text-title recommended
776    return('<h2 class="text-title page title">' . $o . '</h2>');
777 }
778
779
780
781
782 function ewiki_page_view($id, &$data, $action, $all=1) {
783
784    global $ewiki_plugins, $ewiki_config;
785    $o = "";
786
787    #-- render requested wiki page  <-- goal !!!
788    $render_args = array(
789       "scan_links" => 1,
790       "html" => (EWIKI_ALLOW_HTML||(@$data["flags"]&EWIKI_DB_F_HTML)),
791    );
792    $o .= '<div class="text-body">' . "\n"
793       . $ewiki_plugins["render"][0] ($data["content"], $render_args)
794       . "</div>\n";
795    if (!$all) {
796       return($o);
797    }
798
799    #-- control line + other per-page info stuff
800    if ($pf_a = $ewiki_plugins["view_append"]) {
801       ksort($pf_a);
802       $o .= "<div class=\"wiki-plugins\">\n";
803       foreach ($pf_a as $n => $pf) { $o .= $pf($id, $data, $action); }
804       $o .= "</div>\n";
805    }
806    if ($pf_a = $ewiki_plugins["view_final"]) {
807       ksort($pf_a);
808       foreach ($pf_a as $n => $pf) { $pf($o, $id, $data, $action); }
809    }
810
811    if (!empty($_REQUEST["thankyou"]) && $ewiki_config["edit_thank_you"]) {
812       $o = '<div class="text-prefix system-message">'
813          . ewiki_t("THANKSFORCONTRIBUTION") . "</div>\n" . $o;
814    }
815
816    if (EWIKI_HIT_COUNTING) {
817       ewiki_db::HIT($id);
818    }
819
820    return($o);
821 }
822
823
824
825
826 #-------------------------------------------------------------------- util ---
827
828
829 /*  retrieves "$id/$action" string from URL / QueryString / PathInfo,
830     change this in conjunction with ewiki_script() to customize your URLs
831     further whenever desired
832 */
833 function ewiki_id() {
834    ($id = @$_REQUEST["id"]) or
835    ($id = @$_REQUEST["name"]) or
836    ($id = @$_REQUEST["page"]) or
837    ($id = @$_REQUEST["file"]) or
838    (EWIKI_USE_PATH_INFO)
839       and isset($_SERVER["PATH_INFO"])
840       and ($_SERVER["PATH_INFO"] != $_SERVER["SCRIPT_NAME"])  // Apache+PHP workaround
841           and (preg_replace('/\/\d+\/'.EWIKI_PAGE_PRETTY_URL.'/', '', $_SERVER['PATH_INFO']) != '') 
842       and ($id = ltrim($_SERVER["PATH_INFO"], "/")) or
843    (!isset($_REQUEST["id"])) and ($id = trim(strtok($_SERVER["QUERY_STRING"], "&?;")));
844    if (!strlen($id) || ($id=="id=")) {
845       $id = EWIKI_PAGE_INDEX;
846    }
847    (EWIKI_URLDECODE) && ($id = urldecode($id));
848    return($id);
849 }
850
851
852
853
854 /*  replaces EWIKI_SCRIPT, works more sophisticated, and
855     bypasses various design flaws
856     - if only the first parameter is used (old style), it can contain
857       a complete "action/WikiPage" - but this is ambigutious
858     - else $asid is the action, and $id contains the WikiPageName
859     - $ewiki_config["script"] will now be used in favour of the constant
860     - needs more work on _BINARY, should be a separate function
861 */
862 function ewiki_script($asid, $id=false, $params="", $bin=0, $html=1, $script=NULL) {
863
864    global $ewiki_config, $ewiki_plugins;
865
866    #-- get base script url from config vars
867    if (empty($script)) {
868       $script = &$ewiki_config[!$bin?"script":"script_binary"];
869    }
870    $alt_style = (EWIKI_USE_ACTION_PARAM >= 2);
871    $ins_prefix = (EWIKI_ACTION_TAKE_ASIS);
872
873    #-- separate $action and $id for old style requests
874    if ($id === false) {
875       if (strpos($asid, EWIKI_ACTION_SEP_CHAR) !== false) {
876          $asid = strtok($asid, EWIKI_ACTION_SEP_CHAR);
877          $id = strtok("\000");
878       }
879       else {
880          $id = $asid;
881          $asid = "";
882       }
883    }
884
885    #-- prepare params
886    if (is_array($params)) {
887       $uu = $params;
888       $params = "";
889       if ($uu) foreach ($uu as $k=>$v) {
890          $params .= (strlen($params)? "&" : "") . rawurlencode($k) . "=" . rawurlencode($v);
891       }
892    }
893    #-- use action= parameter instead of prefix/
894    if ($alt_style) {
895       $params = "action=$asid" . (strlen($params)? "&": "") . $params;
896       if (!$ins_prefix) {
897          $asid = "";
898       }
899    }
900
901    #-- workaround slashes in $id
902    if (empty($asid) && (strpos($id, EWIKI_ACTION_SEP_CHAR) !== false) && !$bin && $ins_prefix) {
903       $asid = EWIKI_DEFAULT_ACTION;
904    }
905    /*paranoia*/ $asid = trim($asid, EWIKI_ACTION_SEP_CHAR);
906
907    #-- make url
908    if (EWIKI_URLENCODE) {
909       $id = urlencode($id);
910       $asid = urlencode($asid);
911    }
912    else {
913       // only urlencode &, %, ? for example
914    }
915    $url = $script;
916    if ($asid) {
917       $id = $asid . EWIKI_ACTION_SEP_CHAR . $id;  #= "action/PageName"
918    }
919    if (strpos($url, "%s") !== false) {
920       $url = str_replace("%s", $id, $url);
921    }
922    else {
923       $url .= $id;
924    }
925
926    #-- add url params
927    if (strlen($params)) {
928       $url .= (strpos($url,"?")!==false ? "&":"?") . $params;
929    }
930
931    #-- fin
932    if ($html) {
933       $url = str_replace("&", "&amp;", $url);
934    }
935    return($url);
936 }
937
938
939 /*  this ewiki_script() wrapper is used to generate URLs to binary
940     content in the ewiki database
941 */
942 function ewiki_script_binary($asid, $id=false, $params=array(), $upload=0) {
943
944    $upload |= is_string($params) && strlen($params) || count($params);
945
946    #-- generate URL directly to the plainly saved data file,
947    #   see also plugins/db/binary_store
948    if (defined("EWIKI_DB_STORE_URL") && !$upload) {
949       $url = EWIKI_DB_STORE_URL . urlencode(rawurlencode(strtok($id, "?")));
950    }
951
952    #-- else get standard URL (thru ewiki.php) from ewiki_script()
953    else {
954       $url = ewiki_script($asid, $id, $params, "_BINARY=1");
955    }
956
957    return($url);
958 }
959
960
961 /*  this function returns the absolute ewiki_script url, if EWIKI_SCRIPT_URL
962     is set, else it guesses the value
963 */
964 function ewiki_script_url($asid="", $id="", $params="") {
965
966    global $ewiki_action, $ewiki_id, $ewiki_config;
967
968    if ($asid||$id) {
969       return ewiki_script($asid, $id, $params, false, true, ewiki_script_url());
970    }
971    if ($url = $ewiki_config["script_url"]) {
972       return($url);
973    }
974
975    $scr_template = $ewiki_config["script"];
976    $scr_current = ewiki_script($ewiki_action, $ewiki_id);
977    $req_uri = $_SERVER["REQUEST_URI"];
978    $qs = $_SERVER["QUERY_STRING"]?1:0;
979    $sn = $_SERVER["SCRIPT_NAME"];
980
981    if (($p = strpos($req_uri, $scr_current)) !== false) {
982       $url = substr($req_uri, 0, $p) . $scr_template;
983    }
984    elseif (($qs) && (strpos($scr_template, "?") !== false)) {
985       $url = substr($req_uri, 0, strpos($req_uri, "?"))
986            . substr($scr_template, strpos($scr_template, "?"));
987    }
988    elseif (($p = strrpos($sn, "/")) && (strncmp($req_uri, $sn, $p) == 0)) {
989       $url = $sn . "?id=";
990    }
991    else {
992       return(NULL);   #-- could not guess it
993    }
994  
995    $url = (@$_SERVER["HTTPS"] ? "https" : "http") . "://"
996         . EWIKI_SERVER . $url; 
997         
998    return($ewiki_config["script_url"] = $url);
999 }
1000
1001
1002
1003
1004 #------------------------------------------------------------ page plugins ---
1005
1006
1007 #-- links/ action
1008 function ewiki_page_links($id, &$data, $action) {
1009    $o = ewiki_make_title($id, ewiki_t("PAGESLINKINGTO", array("title"=>$id)), 1, $action, "", "_MAY_SPLIT=1");
1010    if ($pages = ewiki_get_backlinks($id)) {
1011       $o .= ewiki_list_pages($pages);
1012    } else {
1013       $o .= ewiki_t("This page isn't linked from anywhere else.");
1014    }
1015    return($o);
1016 }
1017
1018 #-- get all pages, that are linking to $id
1019 function ewiki_get_backlinks($id) {
1020    $result = ewiki_db::SEARCH("refs", $id);
1021    $pages = array();
1022    $id_i = EWIKI_CASE_INSENSITIVE ? strtolower($id) : $id;
1023    while ($row = $result->get(0, 0x0077)) {
1024       if (strpos(EWIKI_CASE_INSENSITIVE ?strtolower($row["refs"]) :$row["refs"], "\n$id_i\n") !== false) {
1025          $pages[] = $row["id"];
1026       }
1027    }
1028    return($pages);
1029 }
1030
1031 #-- get all existing pages (as array of pagenames), that are linked from $id
1032 function ewiki_get_links($id) {
1033    if ($data = ewiki_db::GET($id)) {
1034       $refs = explode("\n", trim($data["refs"]));
1035       $r = array();
1036       foreach (ewiki_db::FIND($refs) as $id=>$exists) {
1037          if ($exists) {
1038             $r[] = $id;
1039          }
1040       }
1041       return($r);
1042    }
1043 }
1044
1045
1046
1047 #-- outputs listing from page name array
1048 function ewiki_list_pages($pages=array(), $limit=NULL,
1049                           $value_as_title=0, $pf_list=false)
1050 {
1051    global $ewiki_plugins;
1052    $o = "";
1053
1054    if (!isset($limit)) {
1055       ($limit = 0 + $_REQUEST[EWIKI_UP_LISTLIM])
1056       or ($limit = EWIKI_LIST_LIMIT);
1057    }
1058    $is_num = !empty($pages[0]);
1059    $lines = array();
1060    $n = 0;
1061
1062    if ($pages) foreach ($pages as $id=>$add_text) {
1063
1064       $title = $id;
1065       $params = "";
1066
1067       if (is_array($add_text)) {
1068          list($id, $params, $title, $add_text) = $add_text;
1069          if (!$title) { $title = $id; }
1070       }
1071       elseif ($is_num) {
1072          $id = $title = $add_text;
1073          $add_text = "";
1074       }
1075       elseif ($value_as_title) {
1076          $title = $add_text;
1077          $add_text = "";
1078       }
1079
1080       $lines[] = '<a href="' . ewiki_script("", $id, $params) . '">' . ewiki_split_title($title) . '</a> ' . $add_text;
1081
1082       if (($limit > 0)  &&  ($n++ >= $limit)) {
1083          break;
1084       }
1085    }
1086
1087    if ($pf_a = @$ewiki_plugins["list_transform"]) {
1088       foreach ($pf_a as $pf_transform) {
1089          $pf_transform($lines);
1090       }
1091    }
1092
1093    if (($pf_list) || ($pf_list = @$ewiki_plugins["list_pages"][0])) {
1094       $o = $pf_list($lines);
1095    }
1096    elseif($lines) {
1097       $o = "&middot; " . implode("<br />\n&middot; ", $lines) . "<br />\n";
1098    }
1099
1100    return($o);
1101 }
1102
1103
1104 #---------------------------------------------------------- page plugins ---
1105
1106
1107 #-- list of all existing pages (without hidden + protected)
1108 function ewiki_page_index($id=0, $data=0, $action=0, $args=array()) {
1109
1110    global $ewiki_plugins;
1111
1112    $o = ewiki_make_title($id, $id, 2);
1113
1114    $exclude = $args ? ("\n" . implode("\n", preg_split("/\s*[,;:\|]\s*/", $args["exclude"])) . "\n") : "";
1115    $sorted = array();
1116    $sorted = array_keys($ewiki_plugins["page"]);
1117
1118    $result = ewiki_db::GETALL(array("flags"), EWIKI_DB_F_TYPE, EWIKI_DB_F_TEXT);
1119    while ($row = $result->get(0, 0x0037, EWIKI_DB_F_TEXT)) {
1120       if (!stristr($exclude, "\n".$row["id"]."\n")) {
1121          $sorted[] = $row["id"];
1122       }
1123    }
1124    natcasesort($sorted);
1125
1126    $o .= ewiki_list_pages($sorted, 0, 0, $ewiki_plugins["list_dict"][0]);
1127    return($o);
1128 }
1129
1130
1131
1132 #-- scans database for extremes (by given page meta data information),
1133 #   generates page listing then from list
1134 //@TODO: split $asc parameter into $asc and $firstver
1135 function ewiki_page_ordered_list($orderby="created", $asc=0, $print="%n", $title="", $bad_flags=0) {
1136
1137    $o = ewiki_make_title("", $title, 2, ".list", "links", 0);
1138
1139    $sorted = array();
1140    $result = ewiki_db::GETALL(array($orderby));
1141
1142    while ($row = $result->get(0, 0x0037, EWIKI_DB_F_TEXT)) {
1143       if ($asc >= 0) {
1144          // version 1 is most accurate for {hits}
1145          $row = ewiki_db::GET($row["id"], 1);
1146       }
1147       if (! ($bad_flags & $row["flags"])) {
1148          $sorted[$row["id"]] = $row[$orderby];
1149       }
1150    }
1151
1152    if ($asc != 0) { arsort($sorted); }
1153    else { asort($sorted); }
1154
1155    if ($sorted) foreach ($sorted as $name => $value) { 
1156       if (empty($value)) { $value = "0"; }
1157       $sorted[$name] = strftime(str_replace('%n', $value, $print), $value);
1158    }
1159    $o .= ewiki_list_pages($sorted);
1160    
1161    return($o);
1162 }
1163
1164
1165
1166 function ewiki_page_newest($id, $data, $action) {
1167    return( ewiki_page_ordered_list("created", -1, ewiki_t("LASTCHANGED"), ewiki_t("NEWESTPAGES")) );
1168 }
1169
1170 function ewiki_page_updates($id, $data, $action) {
1171    return ewiki_page_ordered_list("lastmodified", -1, ewiki_t("LASTCHANGED"), EWIKI_PAGE_UPDATES, EWIKI_DB_F_MINOR);
1172 }
1173
1174 function ewiki_page_hits($id, $data, $action) {
1175    return( ewiki_page_ordered_list("hits", 1, "%n hits", EWIKI_PAGE_HITS) );
1176 }
1177
1178 function ewiki_page_versions($id, $data, $action) {
1179    return( ewiki_page_ordered_list("version", -1, "%n changes", EWIKI_PAGE_VERSIONS) );
1180 }
1181
1182
1183
1184
1185
1186
1187
1188 function ewiki_page_search($id, &$data, $action) {
1189
1190    $o = ewiki_make_title($id, $id, 2, $action);
1191
1192    if (! ($q = @$_REQUEST["q"])) {
1193         $o .= "None Found<br />";
1194       $o .= '<form action="' . ewiki_script("", $id) . '" method="POST">';
1195       $o .= ewiki_form("q::30", "") . '<br /><br />';
1196       $o .= ewiki_form(":submit", $id);
1197       $o .= '</form>';
1198    }
1199    else {
1200       $found = array();
1201
1202       $q = preg_replace('/\s*[^\041-\175\200-\377]\s*/', ' ', $q);
1203       if ($q) foreach (explode(" ", $q) as $search) {
1204
1205          if (empty($search)) { continue; }
1206
1207          $result = ewiki_db::SEARCH("content", $search);
1208
1209          while ($row = $result->get()) {
1210
1211             #-- show this entry in page listings?
1212             if (EWIKI_PROTECTED_MODE && EWIKI_PROTECTED_MODE_HIDING && !ewiki_auth($row["id"], $row, "view")) {
1213                continue;
1214             }
1215
1216             $found[] = $row["id"];
1217          }
1218       }
1219       $o .= ewiki_list_pages($found);
1220    }
1221
1222    
1223  
1224    return($o);
1225 }
1226
1227
1228 function ewiki_page_info($id, &$data, $action) {
1229
1230    global $ewiki_plugins, $ewiki_config, $ewiki_links;
1231
1232    $o = ewiki_make_title($id, ewiki_t("INFOABOUTPAGE")." '{$id}'", 2, $action,"", "_MAY_SPLIT=1"); 
1233
1234    $flagnames = array(
1235       "TEXT", "BIN", "DISABLED", "HTML", "READONLY", "WRITEABLE",
1236       "APPENDONLY", "SYSTEM", "PART", 10=>"HIDDEN", 17=>"EXEC",
1237    );
1238    $show = array(
1239       "version", "author",
1240       "lastmodified",  "created", "hits", "refs",
1241       "flags", "meta", "content"
1242    );
1243    $no_refs = (boolean)$ewiki_config["info_refs_once"];
1244
1245    #-- versions to show
1246    $v_start = $data["version"];
1247    if ( ($uu=0+@$_REQUEST[EWIKI_UP_PAGENUM]) && ($uu<=$v_start) ) {
1248       $v_start = $uu;
1249    }
1250    $v_end = $v_start - $ewiki_config["list_limit"];
1251    if ( ($uu=0+@$_REQUEST[EWIKI_UP_PAGEEND]) && ($uu<=$v_start) ) {
1252       $v_end = $uu;
1253    }
1254    $v_end = max($v_end, 1);
1255
1256    #-- go
1257    # the very ($first) entry is rendered more verbosely than the others
1258    for ($v=$v_start,$first=1; ($v>=$v_end); $v--,$first=0) {
1259
1260       $current = ewiki_db::GET($id, $v);
1261
1262       if (!strlen(trim($current["id"])) || !$current["version"]) {
1263          continue;
1264       }
1265
1266       $o .= '<table class="version-info" border="0" cellpadding="2" cellspacing="1">' . "\n";
1267
1268       #-- additional info-actions
1269       $o .= '<tr><td></td><td class="action-links">';
1270       $o .= ewiki_control_links_list($id, $data, $ewiki_config["action_links"]["info"], $current["version"]);
1271       $o .= "</td></tr>\n";
1272
1273       #-- print page database entry
1274       foreach($show as $i) {
1275
1276          $value = @$current[$i];
1277
1278          #-- show database {fields} differently
1279          if ($i == "meta") {
1280             $str = "";
1281             if ($value) foreach ($value as $n2=>$d2) {
1282                foreach ((array)$d2 as $n=>$d) {
1283                   if (is_int($n)) { $n = $n2; } else { $n = "_$n"; }
1284                   $str .= htmlentities("$n: $d") . "<br />\n";
1285                }
1286             }
1287             $value = $str;
1288          }
1289          elseif (($i =="lastmodified")||($i =="created")) {    #-- {lastmodified}, {created}
1290             $value = strftime("%c", $value);
1291          }
1292          elseif ($i == "content") {
1293             $value = strlen(trim($value)) . " bytes";
1294             $i = "content size";
1295          }
1296          elseif ($first && ($i == "refs") && !(EWIKI_PROTECTED_MODE && (EWIKI_PROTECTED_MODE_HIDING>=2))) {
1297             $a = explode("\n", trim($value));
1298             $ewiki_links = ewiki_db::FIND($a);
1299             ewiki_merge_links($ewiki_links);
1300             foreach ($a as $n=>$link) {
1301                $a[$n] = ewiki_link_regex_callback(array("$link"), "force_noimg");
1302             }
1303             $value = implode(", ", $a);
1304          }
1305          elseif (strpos($value, "\n") !== false) {       #-- also for {refs}
1306             if ($no_refs && ($i == "refs")) { continue; }
1307             $value = str_replace("\n", ", ", trim($value));
1308          }
1309          elseif ($i == "version") {
1310             $value = '<a href="' .
1311                ewiki_script("", $id, array("version"=>$value)) . '">' .
1312                $value . '</a>';
1313          }
1314          elseif ($i == "flags") {
1315             $fstr = "";
1316             for ($n = 0; $n < 32; $n++) {
1317               if ($value & (1 << $n)) {
1318                  if (! ($s=$flagnames[$n])) { $s = "UU$n"; }
1319                  $fstr .= $s . " ";
1320               }
1321             }
1322             $value = $fstr;
1323          }
1324          elseif ($i == "author") {
1325             $value = ewiki_author_html($value);
1326          }
1327
1328          $o .= '<tr class="page-' . $i . '"><td valign="top"><b>' . $i . '</b></td>' .
1329                '<td>' . $value . "</td></tr>\n";
1330
1331       }
1332
1333       $o .= "</table><br />\n";
1334    }
1335
1336    #-- page result split
1337    if ($v >= 1) {
1338       $o .= "<br /><div class=\"chunk-list\">\n" . ewiki_chunked_page($action, $id, -1, $v+1, 1) . "\n</div><br />";
1339    }
1340    #-- ext info actions
1341    $o .= '<div class="summary control-links">' . ewiki_control_links_list($id, $data, $ewiki_config["action_links"]["summary"]) . "</div>\n";
1342
1343    return($o);
1344 }
1345
1346
1347
1348
1349 function ewiki_chunked_page($action, $id, $dir=-1, $start=10, $end=1, $limit=0, $overlap=0.25, $collapse_last=0.67) {
1350
1351    global $ewiki_config;
1352
1353    if (empty($limit)) {
1354       $limit = $ewiki_config["list_limit"];
1355    }
1356    if ($overlap < 1) {
1357       $overlap = (int) ($limit * $overlap);
1358    }
1359
1360    $p = "";
1361    $n = $start;
1362
1363    while ($n) {
1364
1365       $n -= $dir * $overlap;
1366
1367       $e = $n + $dir * ($limit + $overlap);
1368
1369       if ($dir<0) {
1370          $e = max(1, $e);
1371          if ($e <= $collapse_last * $limit) {
1372             $e = 1;
1373          }
1374       }
1375       else {
1376          $e = min($end, $e);
1377          if ($e >= $collapse_last * $limit) {
1378             $e = $end;
1379          }
1380       }
1381
1382       $o .= ($o?" &middot; ":"")
1383          . '<a href="xxx'.ewiki_script($action, $id, array(EWIKI_UP_PAGENUM=>$n, EWIKI_UP_PAGEEND=>$e))
1384          . '">'. "$n-$e" . '</a>';
1385
1386       if (($n=$e) <= $end) {
1387          $n = false;
1388       }
1389    }
1390
1391    return('<div class="chunked-result">'. $o .'</div>');
1392 }
1393
1394
1395
1396
1397
1398
1399 function ewiki_page_edit($id, $data, $action) {
1400
1401    global $ewiki_links, $ewiki_author, $ewiki_plugins, $ewiki_ring,
1402       $ewiki_errmsg, $ewiki_config;
1403
1404    $hidden_postdata = array();
1405
1406    #-- previous version come back
1407    if ($ewiki_config["forced_version"]) {
1408
1409       $current = ewiki_db::GET($id);
1410       $data["version"] = $current["version"];
1411       unset($current);
1412
1413       unset($_REQUEST["content"]);
1414       unset($_REQUEST["version"]);
1415    }
1416
1417    #-- edit interception
1418    if ($pf_a = @$ewiki_plugins["edit_hook"]) foreach ($pf_a as $pf) {
1419       if ($output = $pf($id, $data, $hidden_postdata)) {
1420          return($output);
1421       }
1422    }
1423
1424    #-- permission checks   //@TODO: move into above hook, split out flag checks
1425    if (isset($ewiki_ring)) {
1426       $ring = $ewiki_ring;
1427    } else { 
1428       $ring = 3;
1429    }
1430    $flags = @$data["flags"];
1431    if (!($flags & EWIKI_DB_F_WRITEABLE)) {
1432
1433       #-- perform auth
1434       $edit_ring = (EWIKI_PROTECTED_MODE>=2) ? (2) : (NULL);
1435       if (EWIKI_PROTECTED_MODE && !ewiki_auth($id, $data, $action, $edit_ring, "FORCE")) {
1436          return($ewiki_errmsg);
1437       }
1438
1439       #-- flag checking
1440       if (($flags & EWIKI_DB_F_READONLY) and ($ring >= 2)) {
1441          return(ewiki_t("CANNOTCHANGEPAGE"));
1442       }
1443       if (($flags) and (($flags & EWIKI_DB_F_TYPE) != EWIKI_DB_F_TEXT) and ($ring >= 1)) {
1444          return(ewiki_t("CANNOTCHANGEPAGE"));
1445       }
1446    }
1447
1448    #-- "Edit Me"
1449    $o = ewiki_make_title($id, ewiki_t("EDITTHISPAGE").(" '{$id}'"), 2, $action, "", "_MAY_SPLIT=1");
1450
1451    #-- normalize to UNIX newlines
1452    $_REQUEST["content"] = str_replace("\015\012", "\012", $_REQUEST["content"]);
1453    $_REQUEST["content"] = str_replace("\015", "\012", $_REQUEST["content"]);
1454
1455    #-- preview
1456    if (isset($_REQUEST["preview"])) {
1457       $o .= $ewiki_plugins["edit_preview"][0]($data);
1458    }
1459
1460    #-- save
1461    if (isset($_REQUEST["save"])) {
1462
1463          #-- check for concurrent version saving
1464          $error = 0;
1465          if ((@$data["version"] >= 1) && (($data["version"] != @$_REQUEST["version"]) || (@$_REQUEST["version"] < 1))) {
1466
1467             $pf = $ewiki_plugins["edit_patch"][0];
1468
1469             if (!$pf || !$pf($id, $data)) {
1470                $error = 1;
1471                $o .= ewiki_t("ERRVERSIONSAVE") . "<br /><br />";
1472             }
1473
1474          }
1475          if (!$error) {
1476
1477             #-- new pages` flags
1478             $set_flags = (@$data["flags"] & EWIKI_DB_F_COPYMASK);
1479             if (($set_flags & EWIKI_DB_F_TYPE) == 0) {
1480                $set_flags = EWIKI_DB_F_TEXT;
1481             }
1482             if (EWIKI_ALLOW_HTML) {
1483                $set_flags |= EWIKI_DB_F_HTML;
1484             }
1485
1486             #-- mk db entry
1487             $save = array(
1488                "id" => $id,
1489                "version" => @$data["version"] + 1,
1490                "flags" => $set_flags,
1491                "content" => $_REQUEST["content"],
1492                "created" => ($uu=@$data["created"]) ? $uu : time(),
1493                "meta" => ($uu=@$data["meta"]) ? $uu : "",
1494                "hits" => ($uu=@$data["hits"]) ? $uu : "0",
1495             );
1496             ewiki_data_update($save);
1497
1498             #-- edit storage hooks
1499             if ($pf_a = @$ewiki_plugins["edit_save"]) {
1500                foreach ($pf_a as $pf) {
1501                   $pf($save, $data);
1502                }
1503             }
1504
1505             #-- save
1506             if (!$save || !ewiki_db::WRITE($save)) {
1507
1508                $o .= $ewiki_errmsg ? $ewiki_errmsg : ewiki_t("ERRORSAVING");
1509
1510             }
1511             else {
1512                #-- prevent double saving, when ewiki_page() is re-called
1513                $_REQUEST = $_GET = $_POST = array();
1514
1515                $o = ewiki_t("THANKSFORCONTRIBUTION") . "<br /><br />";
1516
1517                if (EWIKI_EDIT_REDIRECT) {
1518                   $url = ewiki_script("", $id, "thankyou=1", 0, 0, ewiki_script_url());
1519                   $o .= ewiki_t("EDITCOMPLETE", array("url"=>htmlentities($url)));
1520
1521                   if (EWIKI_HTTP_HEADERS && !headers_sent()) {
1522                      header("Status: 303 Redirect for GET");
1523                      $sid = defined("SID") ? EWIKI_ADDPARAMDELIM.SID : "";
1524                      header("Location: $url$sid");
1525                      exit;
1526                      #header("URI: $url");
1527                      #header("Refresh: 0; URL=$url");
1528                   }
1529                   else {
1530                      $o .= '<meta http-equiv="Location" content="'.htmlentities($url).'">';
1531                   }
1532                }
1533                else {
1534                   $o .= ewiki_page($id);
1535                }
1536
1537             }
1538
1539          }
1540
1541          //@REWORK
1542          // header("Reload-Location: " . ewiki_script("", $id, "", 0, 0, ewiki_script_url()) );
1543
1544    }
1545    else {
1546       #-- Edit <form>
1547       $o .= ewiki_page_edit_form($id, $data, $hidden_postdata);
1548
1549       #-- additional forms
1550       if ($pf_a = $ewiki_plugins["edit_form_final"]) foreach ($pf_a as $pf) {
1551          $pf($o, $id, $data, $action);
1552       }
1553    }
1554
1555    return($o);
1556 }
1557
1558
1559 function ewiki_data_update(&$data, $author="") {
1560    ewiki_db::UPDATE($data, $author);
1561 }
1562
1563
1564 function ewiki_new_data($id, $flags=EWIKI_DB_F_TEXT, $author="") {
1565    return(ewiki_db::CREATE($id, $flags, $author));
1566 }
1567
1568
1569
1570 #-- edit <textarea>
1571 function ewiki_page_edit_form(&$id, &$data, &$hidden_postdata) {
1572
1573    global $ewiki_plugins, $ewiki_config;
1574
1575    #-- previously edited, or db fetched content
1576    if (@$_REQUEST["content"] || @$_REQUEST["version"]) {
1577       $data = array(
1578          "version" => &$_REQUEST["version"],
1579          "content" => &$_REQUEST["content"]
1580       );
1581    }
1582    else {
1583       if (empty($data["version"])) {
1584          $data["version"] = 1;
1585       }
1586       @$data["content"] .= "";
1587    }
1588
1589    #-- normalize to DOS newlines
1590    $data["content"] = str_replace("\015\012", "\012", $data["content"]);
1591    $data["content"] = str_replace("\015", "\012", $data["content"]);
1592    $data["content"] = str_replace("\012", "\015\012", $data["content"]);
1593
1594    $hidden_postdata["version"] = &$data["version"];
1595
1596    #-- edit textarea/form
1597    $o .= ewiki_t("EDIT_FORM_1")
1598        . '<form method="POST" enctype="multipart/form-data" action="'
1599        . ewiki_script("edit", $id) . '" name="ewiki"'
1600        . ' accept-charset="'.EWIKI_CHARSET.'">' . "\n";
1601
1602    #-- additional POST vars
1603    if ($hidden_postdata) foreach ($hidden_postdata as $name => $value) {
1604        $o .= '<input type="hidden" name="' . $name . '" value="' . $value . '" />' ."\n";
1605    }
1606
1607    if (EWIKI_CHARSET=="UTF-8") {
1608       $data["content"] = utf8_encode($data["content"]);
1609    }
1610    ($cols = strtok($ewiki_config["edit_box_size"], "x*/,;:")) && ($rows = strtok("x, ")) || ($cols=70) && ($rows=15);
1611    $o .= '<textarea wrap="soft" id="ewiki_content" name="content" rows="'.$rows . '" cols="' .$cols. '">'
1612       . htmlentities($data["content"]) . "</textarea>"
1613       . $GLOBALS["ewiki_t"]["C"]["EDIT_TEXTAREA_RESIZE_JS"];
1614
1615    #-- more <input> elements before the submit button
1616    if ($pf_a = $ewiki_plugins["edit_form_insert"]) foreach ($pf_a as $pf) {
1617       $o .= $pf($id, $data, $action);
1618    }
1619
1620    $o .= "\n<br />\n"
1621       . ewiki_form("save:submit", " &nbsp; ".ewiki_t("SAVE")." &nbsp; ")
1622       . " &nbsp; "
1623       . ewiki_form("preview:submit", " &nbsp; ".ewiki_t("PREVIEW")." &nbsp; ")
1624       . ' &nbsp; <a class="cancel" href="'. ewiki_script("", $id) . '">' . ewiki_t("CANCEL_EDIT") . '</a><br />';
1625
1626    #-- additional form elements
1627    if ($pf_a = $ewiki_plugins["edit_form_append"]) foreach ($pf_a as $pf) {
1628       $o .= $pf($id, $data, $action);
1629    }
1630
1631    $o .= "\n</form>\n"
1632       . ewiki_t("EDIT_FORM_2");
1633
1634    return('<div class="edit-box">'. $o .'</div>');
1635 }
1636
1637
1638
1639 #-- pic upload form
1640 function ewiki_page_edit_form_final_imgupload(&$o, &$id, &$data, &$action) {
1641    if (EWIKI_SCRIPT_BINARY && EWIKI_UP_UPLOAD && EWIKI_IMAGE_MAXSIZE) {
1642       $o .= "\n<br />\n". '<div class="image-upload">'
1643       . '<form action='
1644       . '"'. ewiki_script_binary("", EWIKI_IDF_INTERNAL, "", "_UPLOAD=1") .'"'
1645       . ' method="POST" enctype="multipart/form-data" target="_upload">'
1646       . '<input type="file" name="'.EWIKI_UP_UPLOAD.'"'
1647       . (defined("EWIKI_IMAGE_ACCEPT") ? ' accept="'.EWIKI_IMAGE_ACCEPT.'" />' : "")
1648       . '<input type="hidden" name="'.EWIKI_UP_BINARY.'" value="'.EWIKI_IDF_INTERNAL.'">'
1649       . '<input type="hidden" name="'.EWIKI_UP_PARENTID.'" value="'.htmlentities($id).'">'
1650       . '&nbsp;&nbsp;&nbsp;'
1651       . '<input type="submit" value="'.ewiki_t("UPLOAD_PICTURE_BUTTON").'">'
1652       . '</form></div>'. "\n";
1653   }
1654 }
1655
1656
1657 function ewiki_page_edit_preview(&$data) {
1658    return( '<div class="preview">'
1659            . '<hr noshade="noshade" />'
1660            . "<div align=\"right\">" . ewiki_t("PREVIEW") . "</div><hr noshade=\"noshade\" /><br />\n"
1661            . $GLOBALS["ewiki_plugins"]["render"][0]($_REQUEST["content"], 1, EWIKI_ALLOW_HTML || (@$data["flags"]&EWIKI_DB_F_HTML))
1662            . '<hr noshade="noshade" /><br />'
1663            . "</div>"
1664    );
1665 }
1666
1667
1668
1669
1670
1671
1672
1673 function ewiki_control_links($id, &$data, $action, $hide_hr=0, $hide_mtime=0) {
1674
1675    global $ewiki_plugins, $ewiki_ring, $ewiki_config;
1676    $action_links = & $ewiki_config["action_links"][$action];
1677
1678    #-- disabled
1679    if (!$ewiki_config["control_line"]) {
1680       return("");
1681    }
1682
1683    $o = "\n"
1684       . '<div align="right" class="action-links control-links"><br />';
1685    if (!$hide_hr && !@$ewiki_config["control_line.no_deco"]) {
1686         $o .= '<p><small>' . strftime(ewiki_t("LASTCHANGED"), @$data["lastmodified"]) . '</small></p>';
1687       $o .=  "\n\n" . '<hr noshade="noshade" />' . "\n";
1688    }
1689
1690    if (@$ewiki_config["forced_version"] && ewiki_auth($id, $data, "edit")) {
1691
1692       $o .= '<form action="' . ewiki_script("edit", $id) . '" method="POST">' .
1693             '<input type="hidden" name="edit" value="old">' .
1694             '<input type="hidden" name="version" value="'.$ewiki_config["forced_version"].'">' .
1695             '<input type="submit" value="' . ewiki_t("OLDVERCOMEBACK") . '"></form> ';
1696    }
1697    else {
1698       $o .= ewiki_control_links_list($id, $data, $action_links);
1699    }
1700
1701    if (!$hide_mtime && ($data["lastmodified"] >= UNIX_MILLENNIUM)) { 
1702      // $o .= '<small>' . strftime(ewiki_t("LASTCHANGED"), @$data["lastmodified"]) . '</small>';
1703    }
1704
1705    $o .= "</div>\n";
1706    return($o);
1707 }
1708
1709
1710 #-- the core of ewiki_control_links, separated for use in info and plugins
1711 function ewiki_control_links_list($id, &$data, $action_links, $version=0) {
1712    global $ewiki_plugins, $ewiki_config, $o;
1713    $o = "";
1714    ($ins = @$ewiki_config["control_links_enclose"]) or ($ins = "    ");
1715
1716    if ($action_links) foreach ($action_links as $action => $title)
1717    if (!empty($ewiki_plugins["action"][$action]) || !empty($ewiki_plugins["action_always"][$action]) || strpos($action, ":/"))
1718    {
1719       if (EWIKI_PROTECTED_MODE && EWIKI_PROTECTED_MODE_HIDING && !ewiki_auth($id, $data, $action)) {
1720          continue;
1721       }
1722       $o .= $ins[1] . '<a href="' .
1723          ( strpos($action, "://")
1724             ? $action   # an injected "action" URL
1725             : ewiki_script($action, $id, $version?array("version"=>$version):NULL)
1726          ) . '">' . ewiki_t($title) .'</a> ' . $ins[2];
1727          
1728          
1729 #++ &nbsp;
1730    }
1731    
1732    $o = $ins[0] . $o . $ins[3];
1733   return($o);
1734 }
1735
1736
1737
1738
1739 # ============================================================= rendering ===
1740
1741
1742
1743
1744
1745 ########  ###   ###  #########  ###  ###   ###  #######
1746 ########  ####  ###  #########  ###  ####  ###  #######
1747 ###       ##### ###  ###             ##### ###  ###
1748 ######    #########  ###  ####  ###  #########  ######
1749 ######    #########  ###  ####  ###  #########  ######
1750 ###       ### #####  ###   ###  ###  ### #####  ###
1751 ########  ###  ####  #########  ###  ###  ####  #######
1752 ########  ###   ###  #########  ###  ###   ###  #######
1753
1754
1755 /*
1756    The _format() function transforms $wiki_source pages into <html> strings,
1757    also calls various markup and helper plugins during the transformation
1758    process. The $params array can activate various features and extensions.
1759    only accepts UNIX newlines!
1760 */
1761 function ewiki_format (
1762             $wiki_source,
1763             $params = array()
1764          )
1765 {
1766    global $ewiki_links, $ewiki_plugins, $ewiki_config;
1767
1768    #-- state vars
1769    $params = (array)$ewiki_config["format_params"] + (array)$params;
1770    $s = array(
1771       "in" => 0,         # current input $iii[] block array index
1772       "para" => "",
1773       "line" => "",
1774       "post" => "",      # string to append after current line/paragraph
1775       "line_i" => 0,
1776       "lines" => array(),
1777       "list" => "",      # lists
1778       "tbl" => 0,        # open table?
1779       "indent" => 0,     # indentation
1780       "close" => array(),
1781    );
1782    #-- aliases
1783    $in = &$s["in"]; 
1784    $line = &$s["line"];
1785    $lines = &$s["lines"];
1786    $para = &$s["para"];
1787    $post = &$s["post"];
1788    $list = &$s["list"];
1789
1790    #-- input and output arrays
1791    if ($wiki_source[0] == "<") {            # also prepend an empty line 
1792       $wiki_source = "\n" . $wiki_source;    # for faster strpos() searchs
1793    }
1794    $core_flags = 0x137F;            # (0x0001=WikiMarkup, 0x0002=WikiLinks, 0x1000=MoreBlockPlugins)
1795    $iii = array(
1796       0 => array(
1797          0 => $wiki_source."\n",    # body + empty line
1798          1 => $core_flags,          # rendering / behaviour options
1799          2 => "core",               # block plugin name
1800       )
1801    );
1802    $ooo = array(
1803    );
1804    unset($wiki_source);
1805
1806    #-- plugins
1807    $pf_tbl = @$ewiki_plugins["format_table"][0];
1808    $pf_line = @$ewiki_plugins["format_line"];
1809
1810    #-- wikimarkup (wm)
1811    $htmlentities = $ewiki_config["htmlentities"];
1812    $wm_indent = &$ewiki_config["wm_indent"];
1813    $s["wm_indent_close"] = "</" . strtok($wm_indent, "< />"). ">";
1814    $wm_table_defaults = &$ewiki_config["wm_table_defaults"];
1815    $wm_source = &$ewiki_config["wm_source"];
1816    $wm_list = &$ewiki_config["wm_list"];
1817    $wm_list_chars = implode("", array_keys($wm_list));
1818    $wm_style = &$ewiki_config["wm_style"];
1819    $wm_start_end = &$ewiki_config["wm_start_end"];
1820    $wm_max_header = &$ewiki_config["wm_max_header"];
1821    $wm_publishing_headers = &$ewiki_config["wm_publishing_headers"];
1822    $wm_whole_line = &$ewiki_config["wm_whole_line"];
1823
1824    #-- eleminate html
1825    $iii[0][0] = strtr($iii[0][0], $htmlentities);
1826    unset($htmlentities["&"]);
1827
1828    #-- pre-processing plugins (working on wiki source)
1829    if ($pf_source = $ewiki_plugins["format_source"]) {
1830       foreach ($pf_source as $pf) $pf($iii[0][0]);
1831    }
1832
1833    #-- simple markup
1834    $iii[0][0] = strtr($iii[0][0], $wm_source);
1835
1836
1837    #-- separate input into blocks ------------------------------------------
1838    if ($ewiki_config["format_block"])
1839    foreach ($ewiki_config["format_block"] as $btype=>$binfo) {
1840
1841       #-- disabled block plugin?
1842       if ($binfo[2] && !$params[$binfo[2]])  {
1843          continue;
1844       }
1845
1846       #-- traverse $iii[]
1847       $in = -1;
1848       while ((++$in) < count($iii)) {
1849
1850          #-- search fragment delimeters
1851          if ($iii[$in][1] & 0x0100)
1852          while (
1853             ($c = & $iii[$in][0]) &&
1854             (($l = strpos($c, $binfo[0])) !== false) &&
1855             ($r = strpos($c, $binfo[1], $l))   )
1856          {
1857             $l_len = strlen($binfo[0]);
1858             $r_len = strlen($binfo[1]);
1859
1860             $repl = array();
1861             // pre-text
1862             if (($l > 0) && trim($text = substr($c, 0, $l))) {
1863                $repl[] = array($text, $core_flags, "core");
1864             }
1865             // the extracted part
1866             if (trim($text = substr($c, $l+$l_len, $r-$l-$l_len))) {
1867                $repl[] = array($text, $binfo[3], "$btype");
1868             }
1869             // rest
1870             if (($r+$r_len < strlen($c)) && trim($text = substr($c, $r+$r_len))) {
1871                $repl[] = array($text, $core_flags, "core");
1872             }
1873             array_splice($iii, $in, 1, $repl);
1874
1875             $in += 1;
1876          }
1877       }
1878    }
1879
1880    #-- run format_block plugins
1881    $in = -1;
1882    while ((++$in) < count($iii)) {
1883       if (($btype = $iii[$in][2]) && ($pf_a = @$ewiki_plugins["format_block"][$btype])) {
1884          $c = &$iii[$in][0];
1885          if ($iii[$in][1] & 0x0400) {
1886             $c = strtr($c, array_flip($htmlentities));
1887          }
1888          foreach ($pf_a as $pf) {   
1889             # current buffer $c and pointer $in into $iii[] and state $s
1890             $pf($c, $in, $iii, $s, $btype);
1891          }
1892       }
1893    }
1894
1895    #-- wiki markup ------------------------------------------------------
1896    $para = "";
1897    $in = -1;   
1898    while ((++$in) < count($iii)) {
1899       #-- wikimarkup
1900       if ($iii[$in][1] & 0x0001) {
1901
1902          #-- input $lines buffer, and output buffer $ooo array
1903          $lines = explode("\n", $iii[$in][0]);
1904          $ooo[$in] = array(
1905             0 => "",
1906             1 => $iii[$in][1]
1907          );
1908          $out = &$ooo[$in][0];
1909          $s["bmarkup"] = ($iii[$in][1] & 0x0008);   # lists/tables/paras
1910          $s["nopara"] = !($s["bmarkup"]);   # disables indentation & paragraphs
1911 # should this disable lists and tables and ...
1912 # shouldn't it rather be a bit flag?
1913
1914          #-- walk through wiki source lines
1915          $line_max = count($lines);
1916          if ($lines) foreach ($lines as $s["line_i"]=>$line) {
1917  #echo "line={$s[line_i]}:$line\n";
1918
1919             #-- empty lines separate paragraphs
1920             if (!ltrim($line)) {
1921                ewiki_format_close_para($ooo, $s);
1922                ewiki_format_close_tags($ooo, $s);
1923                if (!$s["nopara"]) {
1924                   $out .= "\n";
1925                }
1926                $line = '';
1927             }
1928     
1929     
1930             #-- list/table/headline "BlockMarkup" ---------------------------
1931             if ($s["bmarkup"]) {
1932         
1933                 #-- horiz bar
1934                 if (!$list && !strncmp($line, "----", 4)) {
1935                    $s["para"] .= "<hr noshade=\"noshade\" />\n";
1936                    continue;
1937                 }
1938                 #-- html comment
1939                 if (!strncmp($line, "&lt;!--", 7)) {
1940                    $out .= "<!-- " . htmlentities(str_replace("--", "__", substr($line, 7))) . " -->\n";
1941                    continue;
1942                 }
1943
1944                 strlen($line) && ($c0 = $line[0])
1945                 or ($c0 = "\000");
1946
1947                 #-- tables ------------------------
1948                 if (($c0 == "|") && ($s["tbl"] || ($line[strlen($line)-1] == "|"))) {
1949                    if (!$s["tbl"]) {
1950                       ewiki_format_close_para($ooo, $s);
1951                       ewiki_format_close_tags($ooo, $s);
1952                       $s["list"] = "";
1953                    }
1954                    $line = substr($line, 1);
1955                    if ($line[strlen($line)-1] == "|") {
1956                       $line = substr($line, 0, -1);
1957                    }
1958                    if ($pf_tbl) { 
1959                       $pf_tbl($line, $ooo, $s);
1960                    }
1961                    else {
1962                       if (!$s["tbl"]) {  
1963                          $out .= "<table " . $wm_table_defaults . ">\n";
1964                          $s["close"][] = "\n</table>"; 
1965                       }
1966                       $line = "<tr>\n<td>" . str_replace("|", "</td>\n<td>", $line) . "</td>\n</tr>";
1967                    }
1968                    $s["tbl"] = 1;
1969                    $para = false;
1970                 }
1971                 elseif ($s["tbl"]) {
1972                    $s["tbl"] = 0;
1973                 }
1974
1975
1976                 #-- headlines
1977                 if (($c0 == "!") && ($excl = strspn($line, "!"))) {
1978                 
1979                    if ($excl > $wm_max_header) { 
1980                       $excl = $wm_max_header;
1981                    }
1982                    $line = substr($line, $excl);
1983                    //publishing headers go from h2 smaller "like word"
1984                    $excl = $wm_publishing_headers? (1+$excl) :5 - $excl;
1985                    $line = "<h$excl>" . $line . "</h$excl>";
1986                    if ($para) {
1987                       ewiki_format_close_para($ooo, $s);
1988                    }
1989                    ewiki_format_close_tags($ooo, $s);
1990                    $para = false;
1991                 }
1992
1993
1994                 #-- whole-line wikimarkup
1995                 foreach ($wm_whole_line as $find=>$replace) {
1996                   if (substr($line, 0, strlen($find)) == $find) {
1997                      $line = "<$replace>" . ltrim(substr($line,strlen($find))) . "</".strtok($replace," ").">";
1998                   }
1999                 }
2000
2001                 #-- indentation (space/tab markup)
2002                 $n_indent = 0;
2003                 if (!$list && (!$s["nopara"]) && ($n_indent = strspn($line, " "))) {
2004                    $n_indent = (int) ($n_indent / 2.65);
2005                    while ($n_indent > $s["indent"]) { 
2006                       $s["para"] .= $wm_indent;
2007                       $s["indent"]++;
2008                    }
2009                 }
2010                 while ($n_indent < $s["indent"]) { 
2011                    $s["para"] .= $s["wm_indent_close"] . "\n";
2012                    $s["indent"]--;
2013                 }
2014
2015
2016                 #-- list markup -------------------
2017                 if (isset($wm_list[$c0])) {
2018                    if (!$list) {
2019                       ewiki_format_close_para($ooo, $s);
2020                       ewiki_format_close_tags($ooo, $s);
2021                    }
2022                    $new_len = strspn($line, $wm_list_chars);
2023                    $new_list = substr($line, 0, $new_len);
2024                    $old_len = strlen($list);
2025                    $lchar = $new_list[$new_len-1];
2026                    list($lopen, $ltag1, $ltag2) = $wm_list[$lchar];
2027
2028                    #-- exception: "--" is treated as literal
2029                    if (($old_len===0) && (($new_len>=2) && ($new_list=="--"))) {
2030                       $list = '';         # change this ^^ to an OR (||)
2031                                           # to filter bad list markup
2032                    }
2033                    else {
2034                       #-- cut line
2035                       $line = substr($line, $new_len);
2036                       $lspace = "";
2037                       $linsert = "";
2038                       if ($ltag1) {
2039                          $linsert = "<$ltag1>" . strtok($line, $lchar) . "</$ltag1> ";
2040                          $line = strtok("\000");
2041                       }
2042
2043                       #-- enum list types
2044                       if (($lchar == "#") && ($line[1] == " ") && ($ltype = $line[0])) {
2045                          if (($ltype >= "0") || ($ltype <= "z")) {
2046                             $line = substr($line, 2);
2047                          } else {
2048                             $ltype = "";
2049                          }
2050                       }
2051
2052                       #-- add another <li>st entry
2053                       if ($new_len == $old_len) {
2054                          $lspace = str_repeat("  ", $new_len);
2055                          $out .=  "</$ltag2>\n" . $lspace . $linsert . "<$ltag2>";
2056                       }
2057                       #-- add list
2058                       elseif ($new_len > $old_len) {
2059                          while ($new_len > ($old_len=strlen($list))) {
2060                             $lchar = $new_list[$old_len];
2061                             $list .= $lchar;
2062                             list($lopen, $ltag1, $ltag2) = $wm_list[$lchar];
2063                             $lclose = strtok($lopen, " ");
2064                             $lspace = str_repeat("  ", $new_len);
2065
2066                             if (isset($ltype) && $ltype) {
2067                                $ltype = ($ltype<"A"?"1": ($ltype=="I"?"I": ($ltype=="i"?"i": ($ltype<"a"?"A": "a"))));
2068                                $lopen .= " type=\"$rltype\"";
2069                                if ($rltype!=$ltype) { $lopen .= " start=\"$ltype\""; }
2070                             }
2071                             
2072                             $out .= "\n$lspace<$lopen>\n" . "$lspace". $linsert . "<$ltag2>";
2073                             $s["close"][] = "$lspace</$lclose>";
2074                             $s["close"][] = "$lspace</$ltag2>";
2075                          }
2076                       }
2077                       #-- close lists
2078                       else {
2079                          while ($new_len < ($old_len=strlen($list))) {
2080                             $remove = $old_len-$new_len;
2081                             ewiki_format_close_tags($ooo, $s, 2*$remove);
2082                             $list = substr($list, 0, -$remove);
2083                          }
2084                          if ($new_len) {
2085                             $lspace = str_repeat("  ", $new_len);
2086                             $out .= "$lspace</$ltag2>\n" . $lspace . $linsert . "<$ltag2>";
2087                          }
2088                       }
2089
2090                       $list = $new_list;
2091                       $para = false;
2092                    }
2093                 }
2094                 elseif ($list) {
2095                    if ($c0 == " ") {
2096                       $para = false;
2097                    }
2098                    else {
2099                       ewiki_format_close_tags($ooo, $s);
2100                       $list = "";
2101                    }
2102                 }
2103
2104             }#--if $s["bmarkup"] --------------------------------------------
2105
2106
2107             #-- text style triggers
2108             foreach ($wm_style as $find=>$replace) {
2109                $find_len = strlen($find);
2110                $loop = 20;
2111                while(($loop--) && (($l = strpos($line, $find)) !== false) && ($r = strpos($line, $find, $l + $find_len))) {
2112                   $line = substr($line, 0, $l) . $replace[0] .
2113                           substr($line, $l + strlen($find), $r - $l - $find_len) .
2114                           $replace[1] . substr($line, $r + $find_len);
2115                }
2116             }
2117
2118             #-- start-end markup
2119             foreach ($wm_start_end as $d) {
2120                $len0 = strlen($d[0]);
2121                $loop = 20;
2122                while(($loop--) && (($l = strpos($line, $d[0])) !== false) && ($r = strpos($line, $d[1], $l + $len0))) {
2123                   $len1 = strlen($d[1]);
2124                   $line = substr($line, 0, $l) . $d[2] .
2125                           substr($line, $l + $len0, $r - $l - $len0) .
2126                           $d[1] . substr($line, $r + $len1);
2127                }
2128             }
2129
2130             #-- call wiki source formatting plugins that work on current line
2131             if ($pf_line) {
2132                foreach ($pf_line as $pf) $pf($out, $line, $post);
2133             }
2134
2135
2136             #-- add formatted line to page-output
2137             $line .= $post;
2138             if ($para === false) {
2139                $out .= $line;
2140                $para = "";
2141             }
2142             else {
2143                $para .= $line . "\n";
2144             }
2145
2146          }
2147
2148          #-- last block, or flags dictate a WikiSource blocks/para break?
2149          if (!isset($iii[$in+1]) || (($iii[$in+1][1] & 0x0010) ^ ($iii[$in][1] & 0x0010)) ) {
2150             ewiki_format_close_para($ooo, $s);
2151             ewiki_format_close_tags($ooo, $s);
2152          }
2153       }
2154       #-- copy as is into output buffer
2155       else {
2156          $ooo[$in] = $iii[$in];
2157       }
2158       $iii[$in] = array();
2159    }
2160
2161
2162    #-- wiki linking ------------------------------------------------------
2163    $scan_src = "";
2164    for ($in=0; $in<count($ooo); $in++) {
2165 // BUG: does not respect the (absence of) flags of individual blocks
2166       #-- join together multiple WikiSource blocks
2167       if ($ooo[$in][1] & 0x0022) {
2168          while (isset($ooo[$in+1]) && ($ooo[$in][1] & 0x0002) && ($ooo[$in+1][1] & 0x0002)) {
2169             $ooo[$in] = array(
2170                0 => $ooo[$in][0] . "\n" . $ooo[$in+1][0],
2171                1 => $ooo[$in][1] | $ooo[$in+1][1],
2172             );
2173             array_splice($ooo, $in+1, 1);
2174          }
2175       }
2176       #-- html character entities
2177       if (EWIKI_HTML_CHARS || ($ooo[$in][1] & 0x0004)) {
2178          $ooo[$in][0] = str_replace("&amp;#", "&#", $ooo[$in][0]);
2179       }
2180       $scan_src .= $ooo[$in][0];
2181    }
2182
2183    #-- pre-scan
2184    if ($params["scan_links"]) {
2185       ewiki_scan_wikiwords($scan_src, $ewiki_links);
2186    }
2187    if ($pf_linkprep = @$ewiki_plugins["format_prepare_linking"]) {
2188       foreach ($pf_linkprep as $pf) $pf($scan_src);
2189    }
2190    $scan_src = NULL;
2191
2192    #-- finally the link-creation-regex
2193    for ($in=0; $in<count($ooo); $in++) {
2194       if ($ooo[$in][1] & 0x0002) {
2195          ewiki_render_wiki_links($ooo[$in][0]);
2196       }
2197    }
2198
2199
2200    #-- fin: combine all blocks into html string ----------------------------
2201    $html = "";
2202    for ($in=0; $in<count($ooo); $in++) {
2203       $html .= $ooo[$in][0] . "\n";
2204       $ooo[$in] = 0;
2205    }
2206    #-- call post processing plugins
2207    if ($pf_final = $ewiki_plugins["format_final"]) {
2208       foreach ($pf_final as $pf) $pf($html);
2209    }
2210    return($html);
2211 }
2212
2213
2214
2215 function ewiki_format_close_para(&$ooo, &$s) {
2216    $out = &$ooo[$s["in"]][0];
2217    #-- output text block
2218    if (trim($s["para"])) {
2219       #-- indentation
2220       while ($s["indent"]) {
2221          $s["para"] .= $s["wm_indent_close"];
2222          $s["indent"]--;
2223       }
2224       #-- enclose in <p> tags
2225       if (!$s["nopara"]) {
2226          $s["para"] = "\n<p>" . ltrim($s["para"], "\n") . "</p>\n";
2227       }
2228       #-- paragraph formation plugins
2229       if ($pf_a = @$GLOBALS["ewiki_plugins"]["format_para"]) {
2230          foreach ($pf_a as $pf) {
2231             $pf($s["para"], $ooo, $s);
2232          }
2233       }
2234       $out .= $s["para"];
2235       $s["para"] = "";
2236    }
2237 }
2238
2239
2240 function ewiki_format_close_tags(&$ooo, &$s, $count=100) {
2241    $out = &$ooo[$s["in"]][0];
2242    if (!is_array($s) || !is_array($s["close"])) { 
2243       die("\$s is garbaged == $s!!");
2244    }
2245    while (($count--) && ($add = array_pop($s["close"]))) {
2246       $out .= $add . "\n";
2247    }
2248 }
2249
2250
2251 function ewiki_format_pre(&$str, &$in, &$iii, &$s, $btype) {
2252    $str = "<pre class=\"markup $btype\">" . $str . "</pre>";
2253 }
2254
2255
2256 function ewiki_format_html(&$str, &$in, &$iii, &$s) {
2257    $he = array_reverse($GLOBALS["ewiki_config"]["htmlentities"]);
2258    $str = strtr($str, array_flip($he));
2259    $str = "<span class=\"markup html\">" . $str . "\n</span>\n"; 
2260 }
2261
2262
2263 function ewiki_format_comment(&$str, &$in, &$iii, &$s, $btype) {
2264    $str = "<!-- "  . str_replace("--", "¯¯", $str) . " -->";
2265 }
2266
2267
2268
2269
2270 /* unclean pre-scanning for WikiWords in a page,
2271    pre-query to the db */
2272 function ewiki_scan_wikiwords(&$wiki_source, &$ewiki_links, $se=0) {
2273
2274    global $ewiki_config, $ewiki_id;
2275
2276    #-- find matches
2277    preg_match_all($ewiki_config["wiki_pre_scan_regex"], $wiki_source, $uu);
2278    $uu = array_merge((array)$uu[1], (array)$uu[2], (array)@$uu[3], (array)$uu[4]);
2279
2280    #-- clean up list, trim() spaces (allows more unclean regex) - page id unification
2281    foreach ($uu as $i=>$id) {
2282       $uu[$i] = trim($id);
2283    }
2284    unset($uu[""]);
2285    $uu = array_unique($uu);
2286
2287    #-- unfold SubPage names
2288    if (EWIKI_SUBPAGE_START) {
2289       foreach ($uu as $i=>$id) {
2290          if ($id && (strpos(EWIKI_SUBPAGE_START, $id[0]) !== false)) {
2291             if ($id[1] == "/") { $id = substr($id, 1); }
2292             $uu[$i] = $ewiki_id . $id;
2293          }
2294    }  }
2295
2296    #-- query db
2297    $ewiki_links = ewiki_db::FIND($uu);
2298
2299    #-- strip email adresses
2300    if ($se) {
2301       foreach ($ewiki_links as $c=>$uu) {
2302          if (strpos($c, "@") && (strpos($c, ".") || strpos($c, ":"))) {
2303             unset($ewiki_links[$c]);
2304          }
2305    }  }
2306 }
2307
2308
2309
2310 /* regex on page content,
2311    handled by callback (see below)
2312 */
2313 function ewiki_render_wiki_links(&$o) {
2314    global $ewiki_links, $ewiki_config, $ewiki_plugins;
2315
2316    #-- merge with dynamic pages list
2317    ewiki_merge_links($ewiki_links);
2318
2319    #-- replace WikiWords
2320    $link_regex = &$ewiki_config["wiki_link_regex"];
2321    $o = preg_replace_callback($link_regex, "ewiki_link_regex_callback", $o);
2322
2323    #-- cleanup
2324 ///////////   unset($ewiki_links);
2325 }
2326
2327
2328 /* combines with page plugin list,
2329    and makes all case-insensitive (=lowercased)
2330    in accord with EWIKI_CASE_INSENSITIVE 
2331                 (handled within ewiki_array)
2332 */
2333 function ewiki_merge_links(&$ewiki_links) {
2334    global $ewiki_plugins;
2335    if ($ewiki_links !== true) {
2336       foreach ($ewiki_plugins["page"] as $page=>$uu) {
2337          $ewiki_links[$page] = 1;
2338       }
2339       $ewiki_links = ewiki_array($ewiki_links);
2340    }
2341 }
2342
2343
2344
2345 /* link rendering (p)regex callback
2346    (ooutch, this is a complicated one)
2347 */
2348 function ewiki_link_regex_callback($ii, $force_noimg=0) {
2349
2350    global $ewiki_links, $ewiki_plugins, $ewiki_config, $ewiki_id;
2351
2352    $str = trim($ii[0]);
2353    $type = array();
2354    $states = array();
2355
2356    #-- link bracket '[' escaped with '!' or '~'
2357    if (($str[0] == "!") || ($str[0] == "~") || ($str[0] == "\\")) {
2358       return(substr($str, 1));
2359    }
2360    if ($str[0] == "#") {
2361       $states["define"] = 1;
2362       $str = substr($str, 1);
2363    }
2364    if ($str[0] == "[") {
2365       $states["brackets"] = 1;
2366       $str = substr($str, 1, -1);
2367       if (!strlen($str)) { return("[]"); }  //better: $ii[0]
2368    }
2369
2370    #-- explicit title given via [ title | WikiLink ]
2371    $href = $title = strtok($str, "|");
2372    if ($uu = strtok("|")) {
2373       $href = $uu;
2374       $states["titled"] = 1;
2375    }
2376    #-- title and href swapped: swap back
2377    if (strpos("://", $title) || strpos($title, ":") && !strpos($href, ":")) {
2378       $uu = $title; $title = $href; $href = $uu;
2379    }
2380    #-- new entitling scheme [ url "title" ]
2381    if ((($l=strpos($str, '"')) < ($r=strrpos($str, '"'))) && ($l!==false) ) {
2382       $title = substr($str, $l + 1, $r - $l - 1);
2383       $href = substr($str, 0, $l) . substr($str, $r + 1);
2384       $states["titled"] = 1;
2385       if (!$href) { return($ii[0]); }
2386    }
2387
2388    #-- strip spaces
2389    $spaces_l = ($href[0]==" ") ?1:0;
2390    $spaces_r = ($href[strlen($href)-1]==" ") ?1:0;
2391    $title = ltrim(trim($title), "^");
2392    $href = ltrim(trim($href), "^");
2393
2394    #-- strip_htmlentities()
2395    if (1&&    (strpos($href, "&")!==false) && strpos($href, ";")) {
2396       ewiki_stripentities($href);
2397    }
2398  
2399    #-- anchors
2400    $href2 = "";
2401    if (($p = strrpos($href, "#")) && ($p) && ($href[$p-1] != "&")) {
2402       $href2 = trim(substr($href, $p));
2403       $href = trim(substr($href, 0, $p));
2404    }
2405    elseif ($p === 0) {
2406       $states["define"] = 1;
2407    }
2408    if ($href == ".") {
2409       $href = $ewiki_id;
2410    }
2411    
2412
2413
2414    #-- SubPages
2415    $c0 = $href[0];
2416    if ($c0 && (strpos(EWIKI_SUBPAGE_START, $c0) !== false)) {
2417       $_set = EWIKI_SUBPAGE_LONGTITLE && ($href==$title);
2418       if (($href[1] == "/")) {   ##($c0 == ".") && 
2419          $href = substr($href, 1);
2420       }
2421       $href = $ewiki_id . $href;
2422       if ($_set) {
2423          $title = $href;
2424       }
2425    }
2426
2427    #-- for case-insensitivines
2428    $href_i = EWIKI_CASE_INSENSITIVE ? strtolower($href) : ($href);
2429
2430    #-- injected URLs
2431    if (isset($ewiki_links[$href_i]) && strpos($inj_url = $ewiki_links[$href_i], "://")) {
2432       if ($href==$title) { $href = $inj_url; }
2433    }
2434    $states["title"] = &$title;
2435
2436    #-- interwiki links
2437    if (strpos($href, ":") && ($uu = ewiki_interwiki($href, $type, $states))) {
2438       $href = $uu;
2439       $str = "<a href=\"$href$href2\">$title</a>";
2440    }
2441    #-- action:WikiLinks
2442    elseif (isset($ewiki_plugins["action"][$a=strtolower(strtok($href, ":"))])) {
2443       $type = array($a, "action", "wikipage");
2444       $str = '<a href="' . ewiki_script($a, strtok("\000")) . '">' . $title . '</a>';
2445    }
2446    #-- page anchor definitions, if ($href[0]=="#")
2447    elseif (@$states["define"]) {
2448       $type = array("anchor");
2449       if ($title==$href) { 
2450          $title = "";   // was "&nbsp;" before, but that's not required
2451       }
2452       $str = '<a name="' . htmlentities(ltrim($href, "#")) . '">' . ltrim($title, "#") . '</a>';
2453    }
2454    #-- inner page anchor jumps
2455    elseif (strlen($href2) && ($href==$ewiki_id) || ($href[0]=="#") && ($href2=&$href)) {
2456       $type = array("jump");
2457       $str = '<a href="' . htmlentities($href2) . '">' . $title . '</a>';
2458    }
2459    #-- ordinary internal WikiLinks
2460    elseif (($ewiki_links === true) || @$ewiki_links[$href_i]) {
2461       $type = array("wikipage");
2462       $str = '<a href="' . ewiki_script("", $href) . htmlentities($href2)
2463            . '">' . $title . '</a>';
2464    }
2465    #-- guess for mail@addresses, convert to URI if
2466    elseif (strpos($href, "@") && !strpos($href, ":")) {
2467       $type = array("email");
2468       $href = "mailto:" . $href;
2469    }
2470    #-- not found fallback
2471    else {
2472       $str = "";
2473       #-- a plugin may take care
2474       if ($pf_a = @$ewiki_plugins["link_notfound"]) {
2475          foreach ($pf_a as $pf) {
2476             if ($str = $pf($title, $href, $href2, $type)) {
2477                break;
2478          }  }
2479       }
2480       #-- (QuestionMarkLink to edit/ action)
2481       if (!$str) {
2482          $type = array("notfound");
2483          $t = $ewiki_config["qmark_links"];
2484          $str = ewiki_script(isset($t[4]) ? "edit" : "", $href);
2485          if (strlen($t) >= 3) {
2486             $str = ($t[0] ? "<a href=\"$str\">$t[0]</a>" :'')
2487                  . ($t[1] ? "<$t[1]>$title</$t[1]>" : $title)
2488                  . ($t[2] ? "<a href=\"$str\">$t[2]</a>" :'');
2489          } else {
2490             $str = "<a href=\"$str\">" . $title . "</a>";
2491             if ($t<0) { $str .= "?"; }
2492          }
2493          $str = '<span class="NotFound">' . $str . '</span>';
2494       }
2495    }
2496
2497    #-- convert standard and internal:// URLs
2498    $is_url = preg_match('°^('.implode('|', $ewiki_config["idf"]["url"]).')°', $href);
2499    $is_internal = 0;
2500    //
2501    if (!$is_url && ($ewiki_links[$href_i]["flags"] & EWIKI_DB_F_BINARY)) {
2502       $is_url = 1;
2503       $is_internal = 1;
2504    }
2505    if ($is_url) {
2506       $type[-2] = "url";
2507       $type[-1] = strtok($href, ":");
2508
2509       #-- [http://url titles]
2510       if (strpos($href, " ") && ($title == $href)) {
2511          $href = strtok($href, " ");
2512          $title = strtok("\377");
2513       }
2514
2515       #-- URL plugins
2516       if ($pf_a = $ewiki_plugins["link_url"]) foreach ($pf_a as $pf) {
2517          if ($str = $pf($href, $title, $status)) { break 2; }
2518       }
2519       $meta = @$ewiki_links[$href];
2520
2521       #-- check for image files
2522       $ext = substr($href, strrpos($href,"."));
2523       $nocache = strpos($ext, "no");
2524       $ext = strtok($ext, "?&#");
2525       $obj = in_array($ext, $ewiki_config["idf"]["obj"]);
2526       $img = (strncmp(strtolower($href), "data:image/", 11) == 0) && ($nocache=1)
2527              || $obj || in_array($ext, $ewiki_config["idf"]["img"]);
2528
2529       #-- internal:// references (binary files)
2530       $id = $href; 
2531       if (EWIKI_SCRIPT_BINARY && ((strpos($href, EWIKI_IDF_INTERNAL)===0)  ||
2532           EWIKI_IMAGE_MAXSIZE && EWIKI_CACHE_IMAGES && $img && !$nocache) ||
2533           $is_internal )
2534       {
2535          $type = array("binary");
2536          $href = ewiki_script_binary("", $href);
2537       }
2538
2539       #-- output html reference
2540       if (!$img || $force_noimg || !$states["brackets"] || (strpos($href, EWIKI_IDF_INTERNAL) === 0)) {
2541 //@FIX: #add1   || $href2  (breaks #.jpeg hack, but had a purpose?)
2542          $str = '<a href="' . $href /*#($href2)*/ . '">' . $title . '</a>';
2543       }
2544       #-- img tag
2545       else {
2546          $type = array("image");
2547          if (is_string($meta)) {
2548             $meta = unserialize($meta);
2549          }
2550          $str = ewiki_link_img($href, $id, $title, $meta, $spaces_l+2*$spaces_r, $obj, $states);
2551       }
2552    }
2553
2554    #-- icon/transform plugins
2555    ksort($type);
2556    if ($pf_a = @$ewiki_plugins["link_final"]) {
2557       foreach ($pf_a as $pf) { $pf($str, $type, $href, $title, $states); }
2558    }
2559    if (isset($states["xhtml"]) && $states["xhtml"]) {
2560       foreach ($states["xhtml"] as $attr=>$val) {
2561          $str = str_replace("<a ", "<a $attr=\"$val\" ", $str);
2562       }
2563    }
2564
2565    return($str);
2566 }
2567
2568
2569 /*
2570    assembles an <img> tag
2571 */
2572 function ewiki_link_img($href, $id, $title, $meta, $spaces, $obj, $states) {
2573
2574    #-- size of cached image
2575    $x = $meta["width"];
2576    $y = $meta["height"];
2577
2578    #-- width/height given in url
2579    if ($p = strpos($id, '?')) {
2580       $id = str_replace("&amp;", "&", substr($id, $p+1));
2581       parse_str($id, $meta);
2582       if ($uu = $meta["x"].$meta["width"]) {
2583          $x = $uu;
2584       }
2585       if ($uu = $meta["y"].$meta["height"]) {
2586          $y = $uu;
2587       }
2588       if ($scale = $meta["r"] . $meta["scale"]) {
2589          if ($p = strpos($scale, "%")) {
2590             $scale = strpos($scale, 0, $p) / 100;
2591          }
2592          $x *= $scale;
2593          $y *= $scale;
2594       }
2595    }
2596
2597    #-- alignment
2598    $align = array('', ' align="right"', ' align="left"', ' align="center"');
2599    $align = $align[$spaces];
2600    $size = ($x && $y ? " width=\"$x\" height=\"$y\"" : "");
2601    
2602    #-- remove annoyances
2603    if ($href==$title) {
2604       $title = "";
2605    }
2606
2607    #-- do
2608    return
2609      ($obj ? '<embed' : '<img')
2610      . ' src="' . $href . '"'
2611      . ' alt="' . htmlentities($title) . '"'
2612      . (@$states["titled"] ? ' title="' . htmlentities($title) . '"' : '')
2613      . $size . $align
2614      . ($obj ? "></embed>" : " />");
2615    # htmlentities($title)
2616 }
2617
2618
2619 function ewiki_stripentities(&$str) {
2620    static $un = array("&lt;"=>"<", "&gt;"=>">", "&amp;"=>"&");
2621    $str = strtr($str, $un);
2622 }
2623
2624
2625 /*
2626    Returns URL if it encounters an InterWiki:Link or workalike.
2627 */
2628 function ewiki_interwiki(&$href, &$type, &$s) {
2629    global $ewiki_config, $ewiki_plugins;
2630
2631    $l = strpos($href, ":");
2632    if ($l and (strpos($href,"//") != $l+1) and ($p1 = strtok($href, ":"))) {
2633       $page = strtok("\000");
2634
2635       if (($p2 = ewiki_array($ewiki_config["interwiki"], $p1)) !== NULL) {
2636          $p1 = $p2;
2637          $type = array("interwiki", $uu);
2638          while ($p1_alias = $ewiki_config["interwiki"][$p1]) {
2639              $type[] = $p1;
2640              $p1 = $p1_alias;
2641          }
2642          if (!strpos($p1, "%s")) {
2643              $p1 .= "%s";
2644          }
2645          $href = str_replace("%s", $page, $p1);
2646          return($href);
2647       }
2648       elseif ($pf = $ewiki_plugins["intermap"][$p1]) {
2649          return($pf($p1, $page));
2650       }
2651       elseif ($pf_a = $ewiki_plugins["interxhtml"]) {
2652          foreach($pf_a as $pf) {
2653             $pf($p1, $page, $s);
2654          }
2655          $href = $page;
2656       }
2657    }
2658 }
2659
2660
2661 /* 
2662    implements FeatureWiki:InterMapWalking
2663 */
2664 function ewiki_intermap_walking($id, &$data, $action) {
2665    if (empty($data["version"]) && ($href = ewiki_interwiki($id, $uu, $uu))) {
2666       header("Location: $href$sid");
2667       return("<a href=\"$href\">$href</a>");
2668    }
2669 }
2670
2671
2672
2673 function ewiki_link($pagename, $title="") {
2674    if (!($url = ewiki_interwiki($pagename, $uu, $uu))) {
2675       $url = ewiki_script("", $pagename);
2676    }
2677    if (!$title) { $title = $pagename; }
2678    return("<a href=\"$url\">".htmlentities($title)."</a>");
2679 }
2680
2681
2682
2683 # =========================================================================
2684
2685
2686
2687 #####    ##  ##   ##    ##    #####   ##  ##
2688 ######   ##  ###  ##   ####   ######  ##  ##
2689 ##  ##   ##  ###  ##  ######  ##  ##  ##  ##
2690 #####    ##  #### ##  ##  ##  ######  ######
2691 #####    ##  #######  ######  ####     ####
2692 ##  ###  ##  ## ####  ######  #####     ##
2693 ##  ###  ##  ##  ###  ##  ##  ## ###    ##
2694 ######   ##  ##  ###  ##  ##  ##  ##    ##
2695 ######   ##  ##   ##  ##  ##  ##  ##    ##
2696
2697
2698
2699
2700 /*  fetch & store
2701 */
2702 function ewiki_binary($break=0) {
2703
2704    global $ewiki_plugins;
2705
2706    #-- reject calls
2707    if (!strlen($id = @$_REQUEST[EWIKI_UP_BINARY]) || !EWIKI_IDF_INTERNAL) {
2708       return(false);
2709    }
2710    if (headers_sent()) die("ewiki-binary configuration error");
2711
2712    #-- upload requests
2713    $upload_file = @$_FILES[EWIKI_UP_UPLOAD];
2714    $add_meta = array();
2715    if ($orig_name = @$upload_file["name"]) {
2716       $add_meta["Content-Location"] = urlencode($orig_name);
2717       $add_meta["Content-Disposition"] = 'inline; filename="'.urlencode(basename("remote://$orig_name")).'"';
2718    }
2719
2720    #-- what are we doing here?
2721    if (($id == EWIKI_IDF_INTERNAL) && ($upload_file)) { 
2722       $do = "upload";
2723    }
2724    else {
2725       $data = ewiki_db::GET($id);
2726       $flags = @$data["flags"];
2727       if (EWIKI_DB_F_BINARY == ($flags & EWIKI_DB_F_TYPE)) { 
2728          $do = "get";
2729       }
2730       elseif (empty($data["version"]) and EWIKI_CACHE_IMAGES) {
2731          $do = "cache";
2732       }
2733       else { 
2734          $do = "nop";
2735       }
2736    }
2737
2738    #-- auth only happens when enforced with _PROTECTED_MODE_XXL setting
2739    #   (authentication for inline images in violation of the WWW spirit)
2740    if ((EWIKI_PROTECTED_MODE>=5) && !ewiki_auth($id, $data, "binary-{$do}")) {
2741       return($_REQUEST["id"]="view/BinaryPermissionError");
2742    }
2743
2744    #-- upload an image
2745    if ($do == "upload"){
2746
2747       $id = ewiki_binary_save_image($upload_file["tmp_name"], "", $return=0, $add_meta);
2748       @unlink($upload_file["tmp_name"]);
2749       ($title = trim($orig_name, "/")) && ($title = preg_replace("/[^-._\w\d]+/", "_", substr(substr($orig_name, strrpos($title, "/")), 0, 20)))
2750       && ($title = ' \\"'.$title.'\\"') || ($title="");
2751
2752       if ($id) {
2753          echo<<<EOF
2754 <html><head><title>File/Picture Upload</title><script language="JavaScript" type="text/javascript"><!--
2755  opener.document.forms["ewiki"].elements["content"].value += "\\nUPLOADED PICTURE: [$id$title]\\n";
2756  window.setTimeout("self.close()", 5000);
2757 //--></script></head><body bgcolor="#440707" text="#FFFFFF">Your uploaded file was saved as<br /><big><b>
2758 [$id]
2759 </b></big>.<br /><br /><noscript>Please copy this &uarr; into the text input box:<br />select/mark it with your mouse, press [Ctrl]+[Insert], go back<br />to the previous screen and paste it into the textbox by pressing<br />[Shift]+[Insert] inside there.</noscript></body></html>
2760 EOF;
2761       }
2762    }
2763
2764    #-- request for contents from the db
2765    elseif ($do == "get") {
2766
2767       #-- send http_headers from meta
2768       if (is_array($data["meta"])) {
2769          foreach ($data["meta"] as $hdr=>$val) {
2770             if (($hdr[0] >= "A") && ($hdr[0] <= "Z")) {
2771                header("$hdr: $val");
2772             }
2773          }
2774       }
2775
2776       #-- fetch from binary store
2777       if ($pf_a = $ewiki_plugins["binary_get"]) {
2778          foreach ($pf_a as $pf) { $pf($id, $data["meta"]); }
2779       }
2780
2781       #-- else fpassthru
2782       echo $data["content"];
2783    }
2784
2785    #-- fetch & cache requested URL,
2786    elseif ($do == "cache") {
2787
2788       #-- check for standard protocol names, to prevent us from serving
2789       #   evil requests for '/etc/passwd.jpeg' or '../.htaccess.gif'
2790       if (preg_match('@^\w?(http|ftp|https|ftps|sftp)\w?://@', $id)) {
2791
2792          #-- generate local copy
2793          $filename = tempnam(EWIKI_TMP, "ewiki.local.temp.");
2794             if(!copy($id, $filename)){
2795               ewiki_log("ewiki_binary: error copying $id to $filename", 0);
2796             } else {
2797             $add_meta = array(
2798                "Content-Location" => urlencode($id),
2799                "Content-Disposition" => 'inline; filename="'.urlencode(basename($id)).'"',
2800                'PageType' => 'CachedImage'
2801             );
2802
2803             $result = ewiki_binary_save_image($filename, $id, "RETURN", $add_meta);
2804          }
2805       }      
2806
2807       #-- deliver
2808       if ($result && !$break) {
2809          ewiki_binary($break=1);
2810       }
2811       #-- mark URL as unavailable
2812       else {
2813          $data = array(
2814             "id" => $id,
2815             "version" => 1, 
2816             "flags" => EWIKI_DB_F_DISABLED,
2817             "lastmodified" => time(),
2818             "created" => time(),
2819             "author" => ewiki_author("ewiki_binary_cache"),
2820             "content" => "",
2821             "meta" => array("Status"=>"404 Absent"),
2822          );
2823          ewiki_db::WRITE($data);
2824          header("Location: $id");
2825          ewiki_log("imgcache: did not find '$id', and marked it now in database as DISABLED", 2);
2826       }
2827       
2828    }
2829
2830    #-- "we don't sell this!"
2831    else {
2832       if (strpos($id, EWIKI_IDF_INTERNAL) === false) {
2833          header("Status: 301 Located SomeWhere Else");
2834          header("Location: $id");
2835       }
2836       else {
2837          header("Status: 404 Absent");
2838          header("X-Broken-URI: $id");
2839       }
2840    }
2841
2842    // you should not remove this one, it is really a good idea to use it!
2843    die();
2844 }
2845
2846
2847
2848
2849
2850
2851 function ewiki_binary_save_image($filename, $id="", $return=0,
2852 $add_meta=array(), $accept_all=EWIKI_ACCEPT_BINARY, $care_for_images=1)
2853 {
2854    global $ewiki_plugins;
2855
2856    #-- break on empty files
2857    if (!filesize($filename)) {
2858       return(false);
2859    }
2860
2861    #-- check for image type and size
2862    $mime_types = array(
2863       "application/octet-stream",
2864       "image/gif",
2865       "image/jpeg",
2866       "image/png",
2867       "application/x-shockwave-flash"
2868    );
2869    $ext_types = array(
2870       "bin", "gif", "jpeg", "png", "swf"
2871    );
2872    list($width, $height, $mime_i, $uu) = getimagesize($filename);
2873    (!$mime_i) && ($mime_i=0) || ($mime = $mime_types[$mime_i]);
2874
2875    #-- images expected
2876    if ($care_for_images) {
2877
2878       #-- mime type
2879       if (!$mime_i && !$accept_all || !filesize($filename)) {
2880          ewiki_die(ewiki_t("BIN_NOIMG"), $return);
2881          return;
2882       }
2883
2884       #-- resize image
2885       if ((strpos($mime,"image/")!==false)
2886       && (EWIKI_IMAGE_RESIZE)) {   // filesize() check now in individual resize plugins
2887          if ($pf_a = $ewiki_plugins["image_resize"]) foreach ($pf_a as $pf) {
2888             $pf($filename, $mime, $return);
2889             clearstatcache();
2890          }
2891       }
2892
2893       #-- reject image if too large
2894       if(filesize($filename) > EWIKI_IMAGE_MAXSIZE) {
2895          ewiki_die(ewiki_t("BIN_IMGTOOLARGE"), $return);
2896          return;
2897       }
2898
2899       #-- again check mime type and image sizes
2900       list($width, $height, $mime_i, $uu) = getimagesize($filename);
2901       (!$mime_i) && ($mime_i=0) || ($mime = $mime_types[$mime_i]);
2902
2903    }
2904    ($ext = $ext_types[$mime_i]) or ($ext = $ext_types[0]);
2905
2906    #-- binary files
2907    if ((!$mime_i) && ($pf = $ewiki_plugins["mime_magic"][0])) {
2908       if ($tmp = $pf($content)) {
2909          $mime = $tmp;
2910       }
2911    }
2912    if (!strlen($mime)) {
2913       $mime = $mime_types[0];
2914    }
2915
2916    #-- store size of binary file
2917    $add_meta["size"] = filesize($filename);
2918    $content = "";
2919
2920    #-- handler for (large/) binary content?
2921    if ($pf_a = $ewiki_plugins["binary_store"]) {
2922       foreach ($pf_a as $pf) {
2923          $pf($filename, $id, $add_meta, $ext);
2924       }
2925    }
2926
2927    #-- read file into memory (2MB), to store it into the database
2928    if ($filename) {
2929       $f = fopen($filename, "rb");
2930       $content = fread($f, 1<<21);
2931       fclose($f);
2932    }
2933
2934    #-- generate db file name
2935    if (empty($id)) {
2936       $md5sum = md5($content);
2937       $id = EWIKI_IDF_INTERNAL . $md5sum . ".$ext";
2938       ewiki_log("generated md5sum '$md5sum' from file content");
2939    }
2940
2941    #-- prepare meta data
2942    $meta = array(
2943       "class" => $mime_i ? "image" : "file",
2944       "Content-Type" => $mime,
2945       "Pragma" => "cache",
2946    ) + (array)$add_meta;
2947    if ($mime_i) {
2948       $meta["width"] = $width;
2949       $meta["height"] = $height;
2950    }
2951
2952    #-- database entry
2953    $data = array(
2954       "id" => $id,
2955       "version" => "1", 
2956       "author" => ewiki_author(),
2957       "flags" => EWIKI_DB_F_BINARY | EWIKI_DB_F_READONLY,
2958       "created" => time(),
2959       "lastmodified" => time(),
2960       "meta" => &$meta,
2961       "content" => &$content,
2962    );
2963    
2964    #-- write if not exist
2965    $exists = ewiki_db::FIND(array($id));
2966    if (! $exists[$id] ) {
2967       $result = ewiki_db::WRITE($data);
2968       ewiki_log("saving of '$id': " . ($result ? "ok" : "error"));
2969    }
2970    else {
2971       ewiki_log("binary_save_image: '$id' was already in the database", 2);
2972    }
2973
2974    return($id);
2975 }
2976
2977
2978
2979
2980 # =========================================================================
2981
2982
2983 ####     ####  ####   ########     ########
2984 #####   #####  ####  ##########   ##########
2985 ###### ######  ####  ####   ###   ####    ###
2986 #############        ####        ####
2987 #############  ####   ########   ####
2988 #### ### ####  ####    ########  ####
2989 ####  #  ####  ####        ####  ####
2990 ####     ####  ####  ###   ####  ####    ###
2991 ####     ####  ####  #########    ##########
2992 ####     ####  ####   #######      ########
2993
2994
2995
2996 /* yes! it is not necessary to annoy people with country flags, if
2997    HTTP already provides means to determine the prefered language!
2998 */
2999 function ewiki_localization() {
3000
3001    global $ewiki_t;
3002
3003    $deflangs = ','.@$_ENV["LANGUAGE"] . ','.@$_ENV["LANG"]
3004              . ",".EWIKI_DEFAULT_LANG . ",en,C";
3005
3006    foreach (explode(",", @$_SERVER["HTTP_ACCEPT_LANGUAGE"].$deflangs) as $l) {
3007
3008       $l = strtok($l, ";");
3009       $l = strtok($l, "-"); $l = strtok($l, "_"); $l = strtok($l, ".");
3010
3011       if ($l = trim($l)) {
3012          $ewiki_t["languages"][] = strtolower($l);
3013       }
3014    }
3015    
3016    $ewiki_t["languages"] = array_unique($ewiki_t["languages"]);
3017 }
3018
3019
3020
3021
3022 /* poor mans gettext, $repl is an array of string replacements to get
3023    applied to the fetched text chunk,
3024    "$const" is either an entry from $ewiki_t[] or a larger text block
3025    containing _{text} replacement braces of the form "_{...}"
3026 */
3027 function ewiki_t($const, $repl=array(), $pref_langs=array()) {
3028
3029    global $ewiki_t;
3030
3031    #-- use default language wishes
3032    if (empty($pref_langs)) {
3033       $pref_langs = $ewiki_t["languages"];
3034    }
3035
3036    #-- large text snippet replacing
3037    if (strpos($const, "_{") !== false) {
3038       while ( (($l=strpos($const,"_{")) || ($l===0)) && ($r=strpos($const,"}",$l)) ) {
3039          $const = substr($const, 0, $l)
3040                 . ewiki_t(substr($const, $l+2, $r-$l-2))
3041                 . substr($const,$r+1);
3042       }
3043    }
3044
3045    #-- just one string
3046    else foreach ($pref_langs as $l) {
3047
3048       if (is_string($r = @$ewiki_t[$l][$const]) || ($r = @$ewiki_t[$l][strtoupper($const)])) {
3049
3050          foreach ($repl as $key=>$value) {
3051             if ($key[0] != '$') {
3052                $key = '$'.$key;
3053             }
3054             $r = str_replace($key, $value, $r);
3055          }
3056          return($r);
3057
3058       }
3059    }
3060
3061    return($const);
3062 }
3063
3064
3065
3066
3067 /* takes all ISO-8859-1 characters into account
3068    but won't work with all databases
3069 */
3070 function ewiki_lowercase($s) {
3071    $len = strlen($s);
3072    for ($i=0; $i<$len; $i++) {
3073       if (ord($s[$i]) >= 192) {
3074          $s[$i] = chr(ord($s[$i]) | 0x20);
3075       }
3076    }
3077    return(strtolower($s));
3078 }
3079
3080
3081
3082
3083 function ewiki_log($msg, $error_type=3) {
3084
3085    if ((EWIKI_LOGLEVEL >= 0) && ($error_type <= EWIKI_LOGLEVEL)) {
3086
3087       $msg = time() . " - " .
3088              $_SERVER["REMOTE_ADDR"] . ":" . $_SERVER["REMOTE_PORT"] . " - " .
3089              $_SERVER["REQUEST_METHOD"] . " " . $_SERVER["REQUEST_URI"] . " - " .
3090              strtr($msg, "\n\r\000\377\t\f", "\r\r\r\r\t\f") . "\n";
3091       error_log($msg, 3, EWIKI_LOGFILE);
3092    }
3093 }
3094
3095
3096
3097
3098 function ewiki_die($msg, $return=0) {
3099    ewiki_log($msg, 1);
3100    if ($return) {
3101       return($GLOBALS["ewiki_error"] = $msg);
3102    }
3103    else {
3104       die($msg);
3105    }
3106 }
3107
3108
3109
3110 function ewiki_array_hash(&$a) {
3111    return(count($a) . ":" . implode(":", array_keys(array_slice($a, 0, 3))));
3112 }
3113
3114
3115
3116 /* provides an case-insensitive in_array replacement to search a page name
3117    in a list of others;
3118    the supplied $array WILL be lowercased afterwards, unless $dn was set
3119 */
3120 function ewiki_in_array($value, &$array, $dn=0, $ci=EWIKI_CASE_INSENSITIVE) {
3121
3122    static $as = array();
3123
3124    #-- work around pass-by-reference
3125    if ($dn && $ci) {   $dest = array();   }
3126               else {   $dest = &$array;   }
3127
3128    #-- make everything lowercase
3129    if ($ci) {
3130       $value = strtolower($value);
3131       if (empty($as[ewiki_array_hash($array)])) {  // prevent working on the
3132          foreach ($array as $i=>$v) {              // same array multiple times
3133             $dest[$i] = strtolower($v);
3134          }
3135          $as[ewiki_array_hash($dest)] = 1;
3136       }
3137    }
3138
3139    #-- search in values
3140    return(in_array($value, $dest));
3141 }
3142
3143
3144
3145 /* case-insensitively retrieves an entry from an $array,
3146    or returns the given $array lowercased if $key was obmitted
3147 */
3148 function ewiki_array($array, $key=false, $am=1, $ci=EWIKI_CASE_INSENSITIVE) {
3149
3150    #-- make everything lowercase
3151    if ($ci) {
3152       $key = strtolower($key);
3153
3154       $r = array();
3155       foreach ($array as $i=>$v) {
3156          $i = strtolower($i);
3157          if (!$am || empty($r[$i])) {
3158             $r[$i] = $v;
3159          }
3160          else {
3161             $r[$i] .= $v;       //RET: doubling for images`meta won't happen
3162          }                      // but should be "+" here for integers
3163       }
3164       $array = &$r;
3165    }
3166
3167    #-- search in values
3168    if ($key) {
3169       return(@$array[$key]);
3170    }
3171    else {
3172       return($array);
3173    }
3174 }
3175
3176
3177
3178
3179
3180 /*
3181    generates {author} string field for page database entry updates
3182 */
3183 function ewiki_author($defstr="") {
3184
3185    $author = @$GLOBALS["ewiki_author"];
3186    ($ip = &$_SERVER["REMOTE_ADDR"]) or ($ip = "127.0.0.0");
3187    ($port = $_SERVER["REMOTE_PORT"]) or ($port = "null");
3188
3189    #-- this call may be very slow (~20 sec)
3190    if (EWIKI_RESOLVE_DNS) {
3191       $hostname = gethostbyaddr($ip);
3192    }
3193    $remote = (($ip != $hostname) ? $hostname . " " : "")
3194            . $ip . ":" . $port;
3195
3196    (empty($author)) && (
3197       ($author = $defstr) ||
3198       ($author = $_SERVER["HTTP_FROM"]) ||      // RFC2068 sect 14.22
3199       ($author = $_SERVER["PHP_AUTH_USER"])
3200    );
3201
3202    (empty($author))
3203       && ($author = $remote)
3204       || ($author = addslashes($author) . " (" . $remote . ")" );
3205
3206    return($author);
3207 }
3208
3209 /*
3210    decodes {author} field for display in pages
3211 */
3212 function ewiki_author_html($orig, $tail=1) {
3213    $str = strtok($orig, " (|,;/[{<+");
3214    $tail = $tail ? " " . strtok("\000") : "";
3215    #-- only IP
3216    if (strpos($str, ":")) {
3217       return('<a href="'. strtok($str, ":") . "\">$orig</a>");
3218    }
3219    #-- mail address
3220    elseif (strpos($str, "@")) {
3221       // email_protect_*() now takes care of plugin pages
3222       return("<a href=\"mailto:$str\">$str</a>$tail");
3223    }
3224    #-- host name
3225    elseif (strpos($str, ".") < strrpos($str, ".")) {
3226       return("<a href=\"http://$str/\">$str</a>$tail");
3227    }
3228    #-- eventually an AuthorName
3229    else {
3230       return('<a href="' . ewiki_script("", $str) . '">' . $str . '</a>' . $tail);
3231    }
3232    return($orig);
3233 }
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243 /*  Returns a value of (true) if the currently logged in user (this must
3244     be handled by one of the plugin backends) is authenticated to do the
3245     current $action, or to view the current $id page.
3246   - alternatively just checks current authentication $ring permission level
3247   - errors are returned via the global $ewiki_errmsg
3248 */
3249 function ewiki_auth($id, &$data, $action, $ring=false, $request_auth=0) {
3250
3251    global $ewiki_plugins, $ewiki_ring, $ewiki_author,
3252       $ewiki_errmsg, $ewiki_config;
3253    $ok = true;
3254    $ewiki_errmsg="";
3255
3256 #echo "_a($id,dat,$action,$ring,$request_auth)<br />\n";
3257
3258    if (EWIKI_PROTECTED_MODE) {
3259
3260       #-- set required vars
3261       if (!isset($ewiki_ring)) {
3262          $ewiki_ring = (int)EWIKI_AUTH_DEFAULT_RING;
3263       }
3264       if ($ring===false) {
3265          $ring = NULL;
3266       }
3267       if ($ewiki_config["create"] && ($action=="edit")) {
3268          $action = "create";  // used only/primarily in authentication plugins
3269       }
3270
3271       #-- plugins to call
3272       $pf_login = @$ewiki_plugins["auth_query"][0];
3273       $pf_perm = $ewiki_plugins["auth_perm"][0];
3274
3275       #-- nobody is currently logged in, so try to fetch username,
3276       #   the login <form> is not yet enforced
3277       if ($pf_login && empty($ewiki_auth_user)) {
3278          $pf_login($data, 0);
3279       }
3280
3281       #-- check permission for current request (page/action/ring)
3282       if ($pf_perm) {
3283
3284          #-- via _auth handler
3285          $ok = $pf_perm($id, $data, $action, $ring, $request_auth);
3286
3287          #-- if it failed, we really depend on the login <form>,
3288          #   and then recall the _perm plugin
3289          if ($pf_login && (($request_auth >= 2) || !$ok && $request_auth && (empty($ewiki_auth_user) || EWIKI_AUTO_LOGIN) && empty($ewiki_errmsg))) {
3290 //@FIXME: complicated if()  - strip empty(errmsg) ??
3291             $pf_login($data, $request_auth);
3292             $ok = $pf_perm($id, $data, $action, $ring, $request_auth=0);
3293          }
3294       }
3295       else {
3296          $ok = !isset($ring) || isset($ring) && ($ewiki_ring <= $ring);
3297       }
3298
3299       #-- return error string
3300       if (!$ok && empty($ewiki_errmsg)) {
3301          ewiki_log("ewiki_auth: Access Denied ($action/$id, $ring/$ewiki_ring, $request_auth)");
3302          $ewiki_errmsg = ewiki_t("FORBIDDEN");
3303       }
3304    }
3305
3306    return($ok);
3307 }
3308
3309
3310 /*
3311    Queries all registered ["auth_userdb"] plugins for the given
3312    username, and compares password to against "db" value, sets
3313    $ewiki_ring and returns(true) if valid.
3314 */
3315 function ewiki_auth_user($username, $password) {
3316   global $ewiki_ring, $ewiki_errmsg, $ewiki_auth_user, $ewiki_plugins, $ewiki_author;
3317
3318   if (empty($username)) {
3319      return(false);
3320   }
3321   if (($password[0] == "$") || (strlen($password) > 12)) {
3322      ewiki_log("_auth_userdb: password was transmitted in encoded form, or is just too long (login attemp for user '$username')", 2);
3323      return(false);
3324   }
3325
3326   if ($pf_u = $ewiki_plugins["auth_userdb"])
3327   foreach ($pf_u as $pf) {
3328
3329      if (function_exists($pf) && ($entry = $pf($username, $password))) {
3330
3331         #-- get and compare password
3332         if ($entry = (array) $entry) {
3333            $enc_pw = $entry[0];
3334         }
3335         $success = false
3336                 || ($enc_pw == substr($password, 0, 12))
3337                 || ($enc_pw == md5($password))
3338                 || ($enc_pw == crypt($password, substr($enc_pw, 0, 2)))
3339                 || function_exists("sha1") && ($enc_pw == sha1($password));
3340         $success &= $enc_pw != "*";
3341
3342         #-- return if it matches
3343         if ($success) {
3344            if (isset($entry[1])) { 
3345               $ewiki_ring = (int)($entry[1]);
3346            } else {
3347               $ewiki_ring = 2;  //(EWIKI_AUTH_DEFAULT_RING - 1);
3348            }
3349            if (empty($ewiki_author)) {
3350               ($ewiki_author = $entry[2]) or
3351               ($ewiki_author = $username);
3352            }
3353            return($success && ($ewiki_auth_user=$username));
3354         }
3355      }
3356   }
3357
3358   if ($username || $password) {
3359      ewiki_log("_auth_userdb: wrong password supplied for user '$username', not verified against any userdb", 3);
3360      $ewiki_errmsg = "wrong username and/or password";
3361 #     ewiki_auth($uu, $uu, $uu, $uu, 2);
3362   }
3363   return(false);
3364 }
3365
3366
3367
3368
3369 /*
3370    Returns <form> field html strings, looks up previously selected values.
3371    Don't use it for textareas, $value magically elects the input field type.
3372 */
3373 function ewiki_form($name, $value, $label="", $_text="| |\n", $inj="") {
3374    global $_EWIKI, $ewiki_id;
3375    static $fid = 50;
3376    static $_sel = ' selected="selected"', $_chk = ' checked="checked"';
3377
3378    #-- prepare
3379    $o = "";
3380    $_text = explode("|", $_text);
3381    list($name, $type, $width, $height) = explode(":", $name);
3382    $type = $type ? (strpos($type, "a") ? "a" : (strpos($type, "b") ? "b" : $type[0])) : "t";
3383    if ($inj) { $inj = " $inj"; }
3384    $old_value = @$_EWIKI["form"][$ewiki_id][$name];
3385
3386    #-- select fields
3387    if ((($type=="s") || strpos($value, "|")) && ($v = explode("|", $value))) {
3388       $value = array();
3389       foreach ($v as $opt) {
3390          $opt = strtok($opt, "="); ($title = strtok("|")) or ($title = $opt);
3391          $value[$opt] = $title;
3392       }
3393    }
3394
3395    #-- label, surrounding text
3396    $o .= "$_text[0]";
3397    if ($fid++ && $label) {
3398       $o .= "<label for=\"ff$fid\">$label</label>";
3399    }
3400    //echo "$_text[1]";
3401
3402    #-- submit (as "button")
3403    if (!$name || ($type=="b")) {
3404       if ($name) { $name = " name=\"$name\""; }
3405       $o .= "<input type=\"submit\" id=\"ff$fid\"$name value=\"$value\"$inj />";
3406    }
3407    #-- select
3408    elseif (is_array($value)) {
3409       $o .= "<select name=\"$name\" id=\"ff$fid\"$inj>";
3410       $no_val = isset($value[0]);
3411       foreach ($value as $val=>$title) {
3412          if ($no_val) { $val = $title; }
3413          $sel = (!$old_value && strpos($val, "!") || ($old_value==$val)) ? $_sel : "";
3414          $o .= '<option value="' . trim(rtrim($val, "!")) . '"' . $sel . '>'
3415             . trim(rtrim($title, "!")) . '</option>';
3416       }
3417       $o .= "</select>";
3418    }
3419    #-- checkbox
3420    elseif (($type=="c") || strpos($value, "]")) {
3421       if (isset($old_value)) { $value = $old_value ? "1x" : ""; }
3422       $sel = strpos($value, "x") ? $_chk : "";
3423       $o .= "<input type=\"checkbox\" id=\"ff$fid\" name=\"$name\" value=\"1\"$sel$inj />";
3424    }
3425    #-- textarea
3426    elseif ($type=="a") {
3427       if ($width && $height) { $inj .= " cols=\"$width\" rows=\"$height\""; }
3428       $o .= "<textarea id=\"ff$fid\" name=\"$name\"$inj>"
3429          . htmlentities($value) . "</textarea>";
3430    }
3431    #-- input field; text or hidden
3432    else {
3433       if ($width) { $inj .= " size=\"$width\""; }
3434       if (isset($old_value)) { $value = $old_value; }
3435       $type = ($type == "t") ? "text" : "hidden";
3436       $o .= "<input type=\"$type\" id=\"ff$fid\" name=\"$name\" value=\""
3437          . htmlentities($value) . "\"$inj />";
3438    }
3439
3440    #-- fin
3441    $o .= "$_text[2]";
3442    return($o);
3443 }
3444
3445
3446
3447
3448 /*  reads all files from "./init-pages/" into the database,
3449     when ewiki is run for the very first time and the FrontPage
3450     does not yet exist in the database
3451 */
3452 function ewiki_eventually_initialize(&$id, &$data, &$action) {
3453
3454    #-- initialize database only if frontpage missing
3455    if (($id==EWIKI_PAGE_INDEX) && ($action=="edit") && empty($data["version"])) {
3456
3457       ewiki_db::INIT();
3458       if ($dh = @opendir($path=EWIKI_INIT_PAGES)) {
3459          while ($filename = readdir($dh)) {
3460             if (preg_match('/^(['.EWIKI_CHARS_U.']+['.EWIKI_CHARS_L.']+\w*)+/', $filename)) {
3461                $found = ewiki_db::FIND(array($filename));
3462                if (! $found[$filename]) {
3463                   $content = implode("", file("$path/$filename"));
3464                   ewiki_scan_wikiwords($content, $ewiki_links, "_STRIP_EMAIL=1");
3465                   $refs = "\n\n" . implode("\n", array_keys($ewiki_links)) . "\n\n";
3466                   $save = array(
3467                      "id" => "$filename",
3468                      "version" => "1",
3469                      "flags" => EWIKI_DB_F_TEXT,
3470                      "content" => $content,
3471                      "author" => ewiki_author("ewiki_initialize"),
3472                      "refs" => $refs,
3473                      "lastmodified" => filemtime("$path/$filename"),
3474                      "created" => filectime("$path/$filename")   // (not exact)
3475                   );
3476                   ewiki_db::WRITE($save);
3477                }
3478             }
3479          }
3480          closedir($dh);
3481       }
3482       else {
3483          echo "<b>ewiki error</b>: could not read from directory ". realpath($path) ."<br />\n";
3484       }
3485
3486       #-- try to view/ that newly inserted page
3487       if ($data = ewiki_db::GET($id)) {
3488          $action = "view";
3489       }
3490    }
3491 }
3492
3493
3494
3495
3496 #---------------------------------------------------------------------------
3497
3498
3499
3500 ########     ###    ########    ###    ########     ###     ######  ########
3501 ########     ###    ########    ###    ########     ###     ######  ########
3502 ##     ##   ## ##      ##      ## ##   ##     ##   ## ##   ##    ## ##
3503 ##     ##   ## ##      ##      ## ##   ##     ##   ## ##   ##    ## ##
3504 ##     ##  ##   ##     ##     ##   ##  ##     ##  ##   ##  ##       ##
3505 ##     ##  ##   ##     ##     ##   ##  ##     ##  ##   ##  ##       ##
3506 ##     ## ##     ##    ##    ##     ## ########  ##     ##  ######  ######
3507 ##     ## ##     ##    ##    ##     ## ########  ##     ##  ######  ######
3508 ##     ## #########    ##    ######### ##     ## #########       ## ##
3509 ##     ## #########    ##    ######### ##     ## #########       ## ##
3510 ##     ## ##     ##    ##    ##     ## ##     ## ##     ## ##    ## ##
3511 ##     ## ##     ##    ##    ##     ## ##     ## ##     ## ##    ## ##
3512 ########  ##     ##    ##    ##     ## ########  ##     ##  ######  ########
3513 ########  ##     ##    ##    ##     ## ########  ##     ##  ######  ########
3514
3515
3516
3517
3518 #-- database API (static wrapper around backends)
3519 class ewiki_db {
3520
3521
3522    #-- load page
3523    # returns database entry as array for the page whose name was given in
3524    # $id key, usually fetches the latest version of a page, unless a specific
3525    # $version was requested
3526    #
3527    function GET($id, $version=false) {
3528       global $ewiki_db;
3529       $r = $ewiki_db->GET($id, 0+$version);
3530       ewiki_db::expand($r);
3531       return($r);
3532    }
3533
3534    
3535    #-- save page
3536    # stores the page $hash into the database, while not overwriting existing
3537    # entries unless $overwrite was set; returns 0 on failure, 1 if completed
3538    #
3539    function WRITE($hash, $overwrite=0) {
3540       global $ewiki_db;
3541       if (is_array($hash) && count($hash) && !defined("EWIKI_DB_LOCK")) {
3542          #-- settype (for flat-file databases)
3543          $hash["version"] += 0;
3544          $hash["hits"] += 0;
3545          $hash["lastmodified"] += 0;
3546          $hash["created"] += 0;
3547          ewiki_db::shrink($hash);
3548          return $ewiki_db->WRITE($hash, $overwrite);
3549       }
3550    }
3551
3552    #-- search
3553    # returns dbquery_result object of database entries (also arrays), where
3554    # the one specified column matches the specified content string;
3555    # it is not guaranteed to only search/return the latest version of a page;
3556    # $field may be an array, in which case an OR-search is emulated
3557    #
3558    function SEARCH($field, $content, $ci=1, $regex=0, $mask=0x0000, $filter=0x0000) {
3559       global $ewiki_db;
3560       $ci = ($ci ? "i" : false);
3561       #-- multisearch (or connected)
3562       if (is_array($field)) {
3563          if (isset($field[0])) { // multiple $field names, just one $content string
3564             $uu = $field; $field = array();
3565             foreach ($uu as $f) {
3566                $field[$f] = $content;
3567             }
3568          }
3569          $r = new ewiki_dbquery_result(array($field));
3570          foreach ($field as $f=>$c) {
3571             $add = $ewiki_db->SEARCH($f, $c, $ci, $regex, $mask, $filter);
3572             $r->entries = array_merge($r->entries, $add->entries);
3573             unset($add);  // dispose, hopefully
3574          }
3575       }
3576       #-- single query
3577       else {
3578          $r = $ewiki_db->SEARCH($field, $content, $ci, $regex, $mask, $filter);
3579          ewiki_db::dbquery_result($r);
3580       }
3581       return($r);
3582    }
3583
3584    
3585    #-- full page list
3586    # returns an dbquery_result object with __all__ pages, where each entry
3587    # is made up of at least the fields from the database requested with the
3588    # $fields array, e.g. array("flags","meta","lastmodified");
3589    #
3590    function GETALL($fields, $mask=0x0000, $filter=0x0000) {
3591       global $ewiki_db;
3592       $fields[] = "flags";
3593       $fields[] = "version";
3594       $fields = array_flip($fields);
3595       unset($fields["id"]);
3596       $fields = array_flip($fields);
3597       $r = $ewiki_db->GETALL($fields);
3598       ewiki_db::dbquery_result($r);
3599       return($r);
3600    }
3601
3602
3603    #-- check page existence
3604    # searches for all given page names (in $list) in the database and returns
3605    # an associative array with page names as keys and booleans as values;
3606    # (int)0 for missing pages, and for existing ones the associated value is
3607    # the page {flags} value or the {meta} data array (for binary entries)
3608    #
3609    function FIND($list) {
3610       global $ewiki_db;
3611       if (!count($list)) {
3612          return($list);
3613       }
3614       return $ewiki_db->FIND($list);
3615    }
3616
3617
3618    #-- page hits
3619    # increases the {hit} counter for the page given by $id
3620    #
3621    function HIT($id) {
3622       global $ewiki_db;
3623       return $ewiki_db->HIT($id);
3624    }
3625
3626
3627    #-- admin functions
3628    function DELETE($id, $version=false) {
3629       global $ewiki_db;
3630       if (!defined("EWIKI_DB_LOCK"))
3631         return $ewiki_db->DELETE($id, $version);
3632    }
3633    function INIT() {
3634       global $ewiki_db;
3635       return $ewiki_db->INIT();
3636    }
3637    
3638    
3639    #-- virtual features
3640    # ::CREATE() creates a new page hash (template)
3641    # ::UPDATE() renews meta data (except version) to allow ::WRITE()
3642    # ::APPEND() adds content to an existing text page
3643    #
3644    function CREATE($id, $flags=EWIKI_DB_F_TEXT, $author="") {
3645       $data = array(
3646          "id"=>$id, "version"=>1, "flags"=>$flags,
3647          "content"=>"", "meta"=>array(),
3648          "hits"=>0, "created"=>time(),
3649          "lastmodified"=>time(),
3650          "author"=>ewiki_author($author),
3651       );
3652       return($data);
3653    }
3654    function UPDATE(&$data, $author="") {
3655       global $ewiki_links;
3656       #-- regenerate backlinks entry
3657       ewiki_scan_wikiwords($data["content"], $ewiki_links, "_STRIP_EMAIL=1");
3658       $data["refs"] = "\n\n".implode("\n", array_keys($ewiki_links))."\n\n";
3659       #-- update meta info
3660       $data["lastmodified"] = time();
3661       $data["author"] = ewiki_author($author);
3662       $data["meta"]["user-agent"] = trim($_SERVER["HTTP_USER_AGENT"]);
3663    }
3664    function APPEND($id, $text, $textonly=1) {
3665       ($data = ewiki_db::GET($id))
3666       or ($data = ewiki_db::CREATE($id));
3667       if (!strlen(trim($text)) or $textonly && (($data["flags"]&EWIKI_DB_F_TYPE) != EWIKI_DB_F_TEXT)) {
3668          return;
3669       }
3670       $data["content"] .= $text;
3671       ewiki_db::UPDATE($data);
3672       $data["version"]++;
3673       return ewiki_db::WRITE($data);
3674    }
3675
3676
3677    #-- helper code
3678    function expand(&$r) {
3679       if (isset($r["meta"]) && is_string($r["meta"]) && strlen($r["meta"])) {
3680          $r["meta"] = unserialize($r["meta"]);
3681       }
3682    }
3683    function shrink(&$r) {
3684       if (isset($r["meta"]) && is_array($r["meta"])) {
3685          $r["meta"] = serialize($r["meta"]);
3686       }
3687    }
3688    function dbquery_result(&$r) {
3689       if (is_array($r)) {
3690          $z = new ewiki_dbquery_result(array_keys($args));
3691          foreach ($r as $id=>$row) {
3692             $z->add($row);
3693          }
3694          $r = $z;
3695       }
3696    }
3697
3698 } // end of class
3699
3700
3701
3702 #-- returned for SEARCH and GETALL queries, as those operations are
3703 #   otherwise too memory exhaustive
3704 class ewiki_dbquery_result {
3705
3706    var $keys = array();
3707    var $entries = array();
3708    var $buffer = EWIKI_DBQUERY_BUFFER;
3709    var $size = 0;
3710
3711    function ewiki_dbquery_result($keys) {
3712       $keys = array_merge((array)$keys, array(-50=>"id", "version", "flags"));
3713       $this->keys = array_unique($keys);
3714    }
3715
3716    function add($row) {
3717       if (is_array($row)) {
3718          if ($this->buffer) {
3719             $this->size += strlen(serialize($row));
3720             $this->buffer = $this->size <= EWIKI_DBQUERY_BUFFER;
3721             ewiki_db::expand($row);
3722          }
3723          else {
3724             $row = $row["id"];
3725          }
3726       }
3727       $this->entries[] = $row;
3728    }
3729
3730    function get($all=0, $flags=0x0000, $type=0) {
3731       $row = array();
3732
3733       $prot_hide = ($flags&0x0020) && EWIKI_PROTECTED_MODE && EWIKI_PROTECTED_MODE_HIDING;
3734       $flag_hide = ($flags&0x0003);
3735       do {
3736          if (count($this->entries)) {
3737
3738             #-- fetch very first entry from $entries list
3739             $r = array_shift($this->entries);
3740
3741             #-- finish if buffered entry
3742             if (is_array($r) && !$all) {
3743                $row = $r;
3744             }
3745             #-- else refetch complete entry from database
3746             else {
3747                if (is_array($r)) {
3748                   $r = $r["id"];
3749                }
3750                $r = ewiki_db::GET($r);
3751                if (!$all) {
3752                   foreach ($this->keys as $key) {
3753                      $row[$key] = $r[$key];
3754                   }
3755                } else { 
3756                   $row = $r;
3757                }
3758             }
3759             unset($r);
3760          }
3761          else { 
3762             return(NULL);  // no more entries
3763          }
3764
3765          #-- expand {meta} field
3766          if (is_array($row) && is_string(@$row["meta"])) {
3767             $row["meta"] = unserialize($row["meta"]);
3768          }
3769
3770          #-- drop unwanted results
3771          if ($prot_hide && !ewiki_auth($row["id"], $row, 'view')
3772          || ($flag_hide && ($row["flags"] & (EWIKI_DB_F_HIDDEN|EWIKI_DB_F_DISABLED)))
3773          || ($type) && (($row["flags"] & EWIKI_DB_F_TYPE) != $type)) {
3774             $row = array();
3775          }
3776       } while (empty($row) && ($prot_hide || $flag_hide));
3777
3778       return($row);
3779    }
3780
3781    function count() {
3782       return(count($this->entries));
3783    }
3784 }
3785
3786
3787
3788 #-- obsolete compatibility wrapper
3789 function ewiki_database($action, $args, $sw1=0, $sw2=0, $pf=false) {
3790    switch ($action) {
3791       case "GET":
3792          return ewiki_db::GET($args["id"], @$args["version"]);
3793       case "WRITE":
3794          return ewiki_db::WRITE($args, 0);
3795       case "OVERWRITE":
3796          return ewiki_db::WRITE($args, 1);
3797       case "FIND":
3798          return ewiki_db::FIND($args);
3799       case "GETALL":
3800          return ewiki_db::GETALL($args);
3801       case "SEARCH":
3802          return ewiki_db::SEARCH(implode("",array_keys($args)), implode("",$args));
3803       case "HIT":
3804          return ewiki_db::HIT($args["id"]);
3805       case "DELETE":
3806          return ewiki_db::DELETE($args["id"], $args["version"]);
3807       case "INIT":
3808          return ewiki_db::INIT();
3809    }
3810    echo "error: unknown database call '$action'<br>\n";
3811    return false;
3812 }
3813
3814
3815
3816 #-- MySQL database backend (default, but will be teared out soon)
3817 #   Note: this is of course an abuse of the relational database scheme,
3818 #   but necessary for real db independence and abstraction
3819 class ewiki_database_mysql {
3820
3821    function ewiki_database_mysql() {
3822       $this->table = EWIKI_DB_TABLE_NAME;
3823    }
3824
3825
3826    function GET($id, $version=false) {
3827       $id = mysql_escape_string($id);
3828       if ($version) {
3829          $version = "AND (version=$version)";
3830       } else  { 
3831          $version="";
3832       }
3833       $result = mysql_query("SELECT *, pagename as id FROM {$this->table}
3834           WHERE (pagename='$id') $version  ORDER BY version DESC  LIMIT 1"
3835       );
3836       echo mysql_error();
3837       if ($result && ($r = mysql_fetch_array($result, MYSQL_ASSOC))) {
3838          unset($r["pagename"]);
3839          return($r);
3840       }
3841    }
3842
3843    
3844    function HIT($id) {
3845       $id = mysql_escape_string($id);
3846       mysql_query("UPDATE {$this->table} SET hits=(hits+1) WHERE pagename='$id'");
3847    }
3848
3849
3850    function WRITE($hash, $overwrite=0) {
3851
3852       $COMMAND = $overwrite ? "REPLACE" : "INSERT";
3853       $sql1 = $sql2 = "";
3854       $hash["pagename"] = $hash["id"];
3855       unset($hash["id"]);
3856       foreach ($hash as $index=>$value) {
3857          if (is_int($index)) {
3858             continue;
3859          }
3860          $a = ($sql1 ? ', ' : '');
3861          $sql1 .= $a . $index;
3862          $sql2 .= $a . "'" . mysql_escape_string($value) . "'";
3863       }
3864
3865       $result = mysql_query("$COMMAND INTO {$this->table} ($sql1) VALUES ($sql2)");
3866       $result = ($result && mysql_affected_rows()) ?1:0;
3867       return($result);
3868    }
3869
3870
3871    function FIND($list) {
3872       $r = array();
3873       $sql = "";
3874       foreach (array_values($list) as $id) {
3875          if (strlen($id)) {
3876             $r[$id] = 0;
3877             $sql .= ($sql ? " OR " : "") .
3878                  "(pagename='" . mysql_escape_string($id) . "')";
3879          }
3880       }
3881       $result = mysql_query("SELECT pagename AS id, meta, flags FROM {$this->table} WHERE $sql");
3882       if ($result) {
3883          while ($row = mysql_fetch_array($result)) {
3884             $id = $row["id"];
3885             if (strlen($row["meta"])) {
3886                $r[$id] = unserialize($row["meta"]);
3887                $r[$id]["flags"] = $row["flags"];
3888             } else {
3889                $r[$id] = $row["flags"];
3890             }
3891          }
3892       }
3893       return($r);
3894    }
3895
3896
3897    function GETALL($fields, $mask=0, $filter=0) {
3898       $fields = implode(", ", $fields);
3899       $f_sql = $mask ? "WHERE ((flags & $mask) = $filter)" : "";
3900       $result = mysql_query("SELECT pagename AS id, $fields FROM
3901           {$this->table} $f_sql GROUP BY id, version DESC"
3902       );
3903       $r = new ewiki_dbquery_result($fields);
3904       $last = "";
3905       if ($result) while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
3906          $drop = EWIKI_CASE_INSENSITIVE ? strtolower($row["id"]) : $row["id"];
3907          if (($last != $drop) && ($last = $drop)) {
3908             $r->add($row);
3909          }
3910       }
3911       return($r);
3912    }
3913
3914
3915    function SEARCH($field, $content, $ci="i", $regex=0, $mask=0, $filter=0) {
3916
3917       $sql_fields = ", $field";
3918       if ($field == "id") {
3919          $field = "pagename";
3920          $sql_fields = "";
3921       }
3922       $content = mysql_escape_string($content);
3923       if ($mask) {
3924          $sql_flags = "AND ((flags & $mask) = $filter)";
3925       }
3926       if ($regex) {
3927          $sql_strsearch = "($field REGEXP '$content')";
3928       }
3929       elseif ($ci) {
3930          $sql_strsearch = "LOCATE('".strtolower($content)."', LCASE($field))";
3931       }
3932       else {
3933          $sql_strsearch = "LOCATE('$content', $field)";
3934       }
3935       
3936       $result = mysql_query(
3937        "SELECT pagename AS id, version, flags  $sql_fields
3938           FROM {$this->table}
3939          WHERE $sql_strsearch $sql_flags
3940          GROUP BY id, version DESC
3941       ");
3942
3943       $r = new ewiki_dbquery_result(array("id","version",$field));
3944       $last = "";
3945       if ($result) while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
3946          $drop = EWIKI_CASE_INSENSITIVE ? strtolower($row["id"]) : $row["id"];
3947          if (($last != $drop) && ($last = $drop)) {
3948             $r->add($row);
3949          }
3950       }
3951       return($r);
3952    }
3953
3954
3955    function DELETE($id, $version) {
3956       $id = mysql_escape_string($id);
3957       mysql_query("DELETE FROM {$this->table} WHERE pagename='$id' AND version=$version");
3958    }
3959
3960
3961    function INIT() {
3962       mysql_query("CREATE TABLE {$this->table}
3963          (pagename VARCHAR(160) NOT NULL,
3964          version INTEGER UNSIGNED NOT NULL DEFAULT 0,
3965          flags INTEGER UNSIGNED DEFAULT 0,
3966          content MEDIUMTEXT,
3967          author VARCHAR(100) DEFAULT 'ewiki',
3968          created INTEGER UNSIGNED DEFAULT ".time().",
3969          lastmodified INTEGER UNSIGNED DEFAULT 0,
3970          refs MEDIUMTEXT,
3971          meta MEDIUMTEXT,
3972          hits INTEGER UNSIGNED DEFAULT 0,
3973          PRIMARY KEY id (pagename, version) )
3974       ");
3975       echo mysql_error();
3976    }
3977
3978
3979 } // end of class ewiki_database_mysql
3980
3981 ?>