4 If you load this plugin, you'll get WikiFeatures:AutomaticFeatureInstall
5 with ewiki unique ".xpi" plugins, that can simply be uploaded through a
6 web interface. The name xpi was borought from the Mozilla project, the
7 actual files are very different.
9 To install plugins, one must provide a correct password. Most types of
10 plugins can come in .xpi form. The .xpi must be generated using the
13 In conjunction with phpjs and the WikiApi plugin, this also allows for
14 installation of .jpi plugins, which contain JavaScript code, that is
15 compiled into sandboxed PHP script - such plugins could therefore be
16 uploaded and installed by anybody, because they're guaranteed to be
17 harmless to server security.
21 define("XPI_DB", "system/xpi/registry");
22 define("XPI_EVERYBODY_JPI", 1);
24 $ewiki_config["xpi_pw"] = array( // plain password or md5 hash
25 # "3389dae361af79b04c9c8e7057f60cc6",
28 defined("EWIKI_ADMIN_PW") ? EWIKI_ADMIN_PW : "*",
30 $ewiki_config["xpi_dirs"] = array(
31 "http://erfurtwiki.sourceforge.net/downloads/contrib-add-ons/xpi/",
32 "http://erfurtwiki.sourceforge.net/?XpiPlugins",
33 # "http://erfurtwiki.sourceforge.net/?JpiPlugins",
34 # "http://erfurtwiki.sf.net/xpi/",
37 $ewiki_plugins["page"]["PlugInstall"] = "ewiki_page_pluginstall";
38 $ewiki_plugins["handler"][] = "ewiki_xpi_exec";
39 $ewiki_plugins["init"][] = "ewiki_xpi_init_plugins";
42 #-- executes pages with the _EXEC flag set
43 function ewiki_xpi_exec($id, $data, $action) {
45 global $ewiki_id, $ewiki_title, $ewiki_action, $ewiki_data,
46 $ewiki_config, $ewiki_t, $ewiki_plugins, $_EWIKI;
48 if ($data["flags"] & EWIKI_DB_F_EXEC) {
49 eval($data["content"]);
55 #-- runs plugins at init time
56 function ewiki_xpi_init_plugins() {
58 global $ewiki_id, $ewiki_title, $ewiki_action, $ewiki_data,
59 $ewiki_config, $ewiki_t, $ewiki_plugins, $_EWIKI;
62 $conf = ewiki_db::GET(XPI_DB);
63 if ($conf && ($conf["flags"] & EWIKI_DB_F_SYSTEM)
64 && ($conf = unserialize($conf["content"]))) {
68 #-- collect xpi code, execute it
69 foreach ($conf as $xpi) {
70 if ($xpi["state"] && ($xpi["type"] != "page")) {
71 $d = ewiki_db::GET($xpi["id"]);
72 if ($d && ($d["flags"] & EWIKI_DB_F_EXEC)) {
73 $eval_this .= $d["content"];
83 #-- provides the upload <form> and installation procedures for .xpi
84 function ewiki_page_pluginstall($id, $data, $action) {
86 global $ewiki_config, $ewiki_plugins;
87 $jpi_support = function_exists("js_compile") && function_exists("jsa_generate");
88 $jpi_access = XPI_EVERYBODY_JPI && $jsi_support;
91 $o .= ewiki_make_title($id, $id, 2);
92 $o .= '<form action="'.$_SERVER["REQUEST_URI"].'" method="POST" enctype="multipart/form-data">'
93 . '<input type="hidden" name="id" value="'.htmlentities($id).'">';
97 $o .= ewiki_xpi_password($access, $jpi_access);
101 if ($access || $jpi_access) {
102 $o .= '<div class="xpi-upload"><h4>.xpi plugin upload</h4>';
104 #-- if filename received => upload+install
105 if (($xpi_install_fn = $_REQUEST["install_remote_xpi"])
106 or ($_REQUEST["install_xpi"])
107 && ($xpi_install_fn = $_FILES["xpi_file"]["tmp_name"]) )
109 $o .= ewiki_xpi_install($xpi_install_fn, $access, $jsi_access, $jsi_support);
113 $o .= ewiki_xpi_show_remote_repository();
114 $o .= ewiki_xpi_upload_form();
117 $o .= '</div><br />';
122 $o .= ewiki_xpi_plugin_control_centre();
129 #-- install given file as .xpi plugin
130 function ewiki_xpi_install($xpi_install_fn, $access, $jsi_access, $jsi_support) {
131 ewiki_xpi_load_registry($registry, $registry_hash);
133 #-- load (possibly remote) .xpi file
134 $xpi = ewiki_xpi_read($xpi_install_fn, "rb");
136 return "not a valid .xpi plugin (or wrong/inacceptable xpi plugin type/version)";
138 if (strlen($xpi["id"]) < 3) {
139 return "missing .xpi header";
142 #-- it's a .jpi plugin
143 if (($access || $jsi_access) && ($xpi["type"] == "jpi")) {
145 #-- compile from JS (WikiScript) into sandboxed PHP
147 $xpi["type"] = "page";
148 js_compile($xpi["code"]);
150 $xpi["code"] = jsa_generate();
153 return "<b>ERROR</b>: cannot handle .jpi plugins without installed JavaScript interpreter";
155 if ($GLOBALS["js_err"]) {
156 ewiki_log("failed compiling .jpi plugin '$xpi[id]'", 0);
157 return "<b>ERROR</b>: broken .jpi plugin!";
161 #-- else no permission to upload anything
163 return "<b>ERROR</b>: You don't have permission to install this type of plugin.<br /><br />";
167 #-- proceed with setup
169 if (function_exists("php_check_syntax")) {
170 if (!php_check_syntax($xpi["code"])) {
171 return "<b>ERROR</b>: plugin code is broken";
175 #-- create new database entry
176 $new = ewiki_new_data($xpi["id"], EWIKI_DB_F_SYSTEM|EWIKI_DB_F_EXEC);
177 $new["content"] = $xpi["code"];
181 #-- check for old version
183 $old = ewiki_db::GET($new["id"]);
184 if ($old["version"]) {
185 $new["version"] = $old["version"]+1;
186 $o .= "(overwriting plugin [version {$old[version]}])<br />";
190 #-- store plugin into database
191 if (ewiki_db::WRITE($new)) {
192 ewiki_log("successfully installed .xpi plugin '$xpi[id]'", 0);
193 $o .= ($xpi["type"] == "page") ? ewiki_link($xpi[id]) : "<b>{$xpi[id]}</b>";
194 $o .= " plugin stored. ";
196 #-- update .xpi registry
197 $registry[$xpi["id"]] = $xpi;
198 $registry_hash["content"] = serialize($registry);
199 ewiki_data_update($registry_hash);
200 $registry_hash["version"]++;
201 ewiki_db::WRITE($registry_hash);
204 $o .= "<b>error</b> saving";
205 ewiki_log("error installing .xpi/.jpi plugin '$xpi[id]'", 0);
213 #-- check pw, set cookie
214 function ewiki_xpi_password(&$access, &$jpi_access) {
217 $o = '<div class="xpi-login">';
218 $pw = $_REQUEST["xpi_pw"];
219 $access = (strlen($pw) >= 3) && (in_array($pw, $ewiki_config["xpi_pw"]) || in_array(md5($pw), $ewiki_config["xpi_pw"]));
220 if (isset($_POST["xpi_pw"]) && ($_COOKIE["xpi_pw"] != $pw) || ($_REQUEST["xpi_logout"])) {
221 $pw = $_POST["xpi_pw"];
222 if ($access || !$pw) {
223 setcookie("xpi_pw", $pw);
224 $access = $access && $pw;
231 $o .= "On this Wiki everybody is allowed to install safe .jpi (phpjs) plugins. ";
233 $o .= "To install click-and-run .xpi plugins you must be administrator and provide the correct password. ";
234 $o .= '<p><b>access password</b><br /><input type="password" name="xpi_pw" size="16"><br /><input type="submit" value="log in"></p>';
237 $o .= '<input type="submit" name="xpi_logout" value="log out">';
246 #-- load .xpi plugin registry
247 function ewiki_xpi_load_registry(&$registry, &$registry_hash) {
248 $registry_hash = ewiki_db::GET(XPI_DB);
249 if (!$registry_hash || !($registry_hash["flags"] & EWIKI_DB_F_SYSTEM)) {
250 $registry_hash = ewiki_new_data(XPI_DB, EWIKI_DB_F_SYSTEM);
251 $registry_hash["version"] = 0;
255 $registry = unserialize($registry_hash["content"]);
261 #-- delete + disable plugins
262 function ewiki_xpi_plugin_control_centre() {
263 ewiki_xpi_load_registry($registry, $registry_hash);
266 $o = '<div class="xpi-settings"><h4>plugin control</h4>';
269 if ($access && ($uu = $_REQUEST["xpi_rm"])) {
270 foreach ($uu as $id=>$del) {
272 $id = rawurldecode($id);
273 $dat = ewiki_db::GET($id);
274 $vZ = $dat["version"];
275 for ($v=1; $v<=$vZ; $v++) {
276 ewiki_db::DELETE($id, $v);
278 unset($registry[$id]);
280 $o .= "<b>i</b>: Purged $vZ versions of '$id' and removed xpi registry entry.<br /><br />";
281 ewiki_log("uninstalled .xpi/.jpi plugin '$id'", 0);
284 $_REQUEST["setup_xpi"]=1;
287 #-- update config settings
288 if ($_REQUEST["setup_xpi"]) {
291 foreach ($registry as $id=>$uu) {
292 $registry[$id]["state"] = $_REQUEST["xpi_set"][rawurlencode($id)] ?1:0;
295 $registry_hash["content"] = serialize($registry);
296 ewiki_data_update($registry_hash);
297 $registry_hash["version"]++;
298 ewiki_db::WRITE($registry_hash);
301 $o .= "You have no privileges to change the status of installed .xpi plugins.<br />\n";
305 #-- enable/disable checkboxes
306 $o .= '<table border="0" cellspacing="1" cellpadding="2">';
307 foreach ($registry as $dat) {
308 $enabled = ($dat["state"]==1);
309 $hard = ($dat["type"]=="page");
310 $title = $hard ? ewiki_link($dat["id"]) : $dat["id"];
312 . '<td><tt>' . $dat["type"] . '</tt></td>'
313 . '<td class="xs-check"><input type="checkbox" name="xpi_set['.rawurlencode($dat["id"])
314 . ']" value="1"' . ($enabled?" checked":"")
315 . ($hard?" disabled":"") . '></td>'
316 . '<td class="xs-id">' . $title . '</td>'
317 . '<td><small>' . htmlentities($dat["description"]) . '</small></td>'
318 . '<td>' . $dat["author"] . ", " . $dat["license"] . '</td>'
319 . '<td class="xs-check"><input type="submit" name="xpi_rm['.rawurlencode($dat["id"]).']" value="rm" title="uninstall plugin"'.($access?"":" disabled").'></td>'
323 $o .= '<br /><input type="submit" name="setup_xpi" value="configure"'.($access?"":" disabled").'>';
324 $o .= '</form></div>';
331 #-- plugin upload <form>
332 function ewiki_xpi_upload_form() {
333 $o = '<b>Warning</b>: before uploading an extension plugin, you should check its source, because you\'ll otherwise may open big security leaks in your installation. <br /><br /> <input type="file" name="xpi_file"> <br /> <input type="submit" name="install_xpi" value="install"> <br /><br />';
334 $o .= 'Or install a plugin from one of the registered plugin directories:<br />';
335 foreach ($ewiki_config["xpi_dirs"] as $s) {
336 $o .= '<input type="submit" name="xpidir" value="'.htmlentities($s).'"><br />';
343 #-- show remote .xpi directory
344 function ewiki_xpi_show_remote_repository() {
345 if ($url = $_REQUEST["xpidir"]) {
347 if ($urls = ewiki_util_getlinks($url, '[^\"\'\>\s]+?\.[jx]pi')) {
348 foreach ($urls as $fn) {
349 if ($xpi = ewiki_xpi_read($fn)) {
350 $xpi["XPI"] = $xpi["code"] = NULL;
351 if (!$access && $xpi["JPI"]) {
358 $o .= "install .xpi plugins from remote directory<br /><a href=\"$url\">$url</a>:<br />"
359 . "<small>don't do this, if you don't know the operator of the provided extension plugins (could contain malicious code)</small><br />\n";
360 $o .= '<table border="0" cellspacing="1" cellpadding="2">';
361 foreach ($r as $fn=>$xpi) {
362 $o .= "\n".'<tr><td colspan="3">'
363 . '<input type="submit" name="install_remote_xpi" value="'
364 .htmlentities($fn).'" title="'.$xpi["id"].'">'
366 . "<td class=\"xs-id\">[{$xpi[type]}] {$xpi[id]} {$xpi[version]}</td>"
367 . "<td>{$xpi[description]}<br /></td>"
368 . "<td>{$xpi[author]}, {$xpi[license]}</td>"
371 $o .= '</table><br />';
379 #-- open binary .xpi file
380 function ewiki_xpi_read($fn, $maxsize=0x020000) {
381 if ($f = gzopen($fn, "rb")) {
382 $xpi = gzread($f, $maxsize);
386 $xpi = unserialize($xpi);
387 if (($xpi["XPI"]=="0.1")
388 and (($xpi["engine"]=="ewiki") || ($xpi["type"]=="jpi"))
389 and $xpi["id"] && $xpi["type"] && $xpi["code"]) {
397 #-- read out file names from directory listing in .html format
398 function ewiki_util_getlinks($url, $regex='.+?') {
401 if ($html = @file($url)) {
402 $html = implode("", $html);
404 $url_b = substr("$url", 0, strrpos($url, "/"));
405 $url_s = substr("$url", 0, strpos($url, "/", 10));
407 preg_match_all('#<a[^>]+href=["\']?('.$regex.')["\'\s>]#i', $html, $uu);
408 foreach ($uu[1] as $fn) {
412 elseif (strpos($fn, "://")) {
415 $fn = $url_b . "/" . $fn;