function iframe_alter_save($args) { $elem = $args['elem']; $obj =& $args['obj']; if (!elem_has_class($elem, 'iframe')) { return false; } // parse children elements to find iframe $childs = html_parse(elem_val($elem)); $i = false; foreach ($childs as $c) { if (elem_tag($c) == 'iframe') { $i = $c; break; } } if (!$i) { log_msg('warn', 'iframe_alter_save: no iframe element found, inner html is ' . var_dump_inl($childs)); return false; } // url if (elem_attr($i, 'src') !== NULL) { $obj['iframe-url'] = elem_attr($i, 'src'); } else { unset($obj['iframe-url']); } // scrolling if (elem_css($i, 'overflow') == 'hidden' || elem_css($i, 'overflow-x') == 'hidden' && elem_css($i, 'overflow-y') == 'hidden') { unset($obj['iframe-scroll']); } else { $obj['iframe-scroll'] = 'scroll'; } return true; }
function video_alter_save($args) { $elem = $args['elem']; $obj =& $args['obj']; if (!elem_has_class($elem, 'video')) { return false; } // parse children elements to find video $childs = html_parse(elem_val($elem)); $v = false; foreach ($childs as $c) { if (elem_tag($c) == 'video') { $v = $c; break; } } if (!$v) { log_msg('warn', 'video_alter_save: no video element found, inner html is ' . var_dump_inl($childs)); return false; } // autoplay if (elem_attr($v, 'autoplay') !== NULL) { $obj['video-autoplay'] = 'autoplay'; } else { $obj['video-autoplay'] = ''; } // loop if (elem_attr($v, 'loop') !== NULL) { $obj['video-loop'] = 'loop'; } else { unset($obj['video-loop']); } // controls if (elem_attr($v, 'controls') !== NULL) { $obj['video-controls'] = 'controls'; } else { unset($obj['video-controls']); } // volume if (elem_attr($v, 'audio') == 'muted') { $obj['video-volume'] = '0'; } else { unset($obj['video-volume']); } }
/** * try to turn a mixture of html code an plain text into valid html * * @param string $html input * @return string encoded output */ function html_encode_str_smart($html) { // TODO (later): remove debug code log_msg('debug', 'html_encode_str_smart: string is ' . quot(var_dump_inl($html))); // encode ampersand characters where needed $cur = 0; while ($cur < strlen($html)) { // check & if (substr($html, $cur, 1) == '&') { $replace = true; // DEBUG $reason = false; // check &#?? if ($cur + 3 < strlen($html) && substr($html, $cur + 1, 1) == '#') { // check $#x or not if (strtolower(substr($html, $cur + 2, 1)) == 'x') { // check for hexadecimal characters before ; $ahead = $cur + 3; while ($ahead < strlen($html)) { $char = strtolower(substr($html, $ahead, 1)); if (48 <= ord($char) && ord($char) <= 57 || 97 <= ord($char) && ord($char) <= 102) { // valid hexadecimal character $ahead++; } elseif ($char == ';') { if ($cur + 3 < $ahead) { // valid entitiy $replace = false; break; } else { // invalid entity // DEBUG $reason = 1; break; } } else { // invalid entity // DEBUG $reason = 2; break; } } if ($ahead == strlen($html)) { // DEBUG $reason = 3; } } elseif (is_numeric(substr($html, $cur + 2, 1))) { // check for for decimal characters before ; $ahead = $cur + 3; while ($ahead < strlen($html)) { $char = substr($html, $ahead, 1); if (48 <= ord($char) && ord($char) <= 57) { // valid decimal character $ahead++; } elseif ($char == ';') { // valid entity $replace = false; break; } else { // invalid entity $reason = 4; break; } } if ($ahead == strlen($html)) { // DEBUG $reason = 5; } } else { // DEBUG $reason = 6; } } else { // assume a named entity // it turns out we can't use get_html_translation_table() // for this as the HTML_ENTITIES table is not complete $ahead = $cur + 1; while ($ahead < strlen($html)) { $char = strtolower(substr($html, $ahead, 1)); if (48 <= ord($char) && ord($char) <= 57 || 97 <= ord($char) && ord($char) <= 122) { // valid alphanumeric character $ahead++; } elseif ($char == ';') { if ($cur + 1 < $ahead) { // (supposedly) valid entity $replace = false; break; } else { // invalid entity // DEBUG $reason = 7; break; } } else { // invalid entity // DEBUG $reason = 8; break; } } if ($ahead == strlen($html)) { $reason = 9; break; } } if ($replace) { log_msg('debug', 'html_encode_str_smart: replacing ampersand at ' . $cur . ' because of ' . $reason); $html = substr($html, 0, $cur) . '&' . substr($html, $cur + 1); log_msg('debug', 'html_encode_str_smart: new string is ' . quot(var_dump_inl($html))); $cur += 5; } else { log_msg('debug', 'html_encode_str_smart: not replacing ampersand at ' . $cur); $cur++; } } else { $cur++; } } // encode < and > where needed $cur = 0; while ($cur < strlen($html)) { $char = substr($html, $cur, 1); $replace = true; if ($char == '<') { // a possible tag // search for a closing bracket $ahead = $cur + 1; while ($ahead < strlen($html)) { $c = strtolower(substr($html, $ahead, 1)); if ($c == '<') { // found another opening bracket // the first one can't be legit // DEBUG $reason = 1; break; } elseif ($c == '>') { if ($cur + 1 < $ahead) { // can be a valid tag $replace = false; // forward till after the closing bracket $cur = $ahead; break; } else { // invalid (empty) tag // DEBUG $reason = 2; break; } } elseif ($ahead == $cur + 1) { if (48 <= ord($c) && ord($c) <= 57 || 97 <= ord($c) && ord($c) <= 122 || $c == '/') { // starts with an alphanumeric character or a slash, can be valid } else { // DEBUG $reason = 3; break; } } $ahead++; } if ($ahead == strlen($html)) { // DEBUG $reason = 4; } } else { if ($char == '>') { // we should be getting all valid tags through the code above // DEBUG $reason = 5; } } if ($replace && $char == '<') { log_msg('debug', 'html_encode_str_smart: replacing opening bracket at ' . $cur . ' because of ' . $reason); $html = substr($html, 0, $cur) . '<' . substr($html, $cur + 1); log_msg('debug', 'html_encode_str_smart: new string is ' . quot(var_dump_inl($html))); $cur += 4; } elseif ($replace && $char == '>') { log_msg('debug', 'html_encode_str_smart: replacing closing bracket at ' . $cur . ' because of ' . $reason); $html = substr($html, 0, $cur) . '>' . substr($html, $cur + 1); log_msg('debug', 'html_encode_str_smart: new string is ' . quot(var_dump_inl($html))); $cur += 4; } else { $cur++; } } return $html; }
<?php /* * index.php * Main HTTP request handler * * Copyright Gottfried Haider, Danja Vasiliev 2010. * This source code is licensed under the GNU General Public License. * See the file COPYING for more details. */ @(require_once 'config.inc.php'); require_once 'log.inc.php'; log_msg('info', '--- request ---'); require_once 'controller.inc.php'; require_once 'modules.inc.php'; $args = parse_query_string(); log_msg('info', 'index: query arguments ' . var_dump_inl($args)); log_msg('debug', 'index: base url is ' . quot(base_url())); invoke_controller($args);
/** * invoke a controller based on the query arguments given * * this function does not return in case of an error. * @param array $args query-arguments array * @return mixed return value of controller that was called */ function invoke_controller($args) { global $controllers; // change query-arguments so that we always have a arg0 and arg1 if (!isset($args[0])) { $args[0] = array('', ''); } elseif (is_string($args[0])) { $args[0] = array($args[0], ''); } // load all modules // TODO (later): fastpath for serving cached pages or files (the latter one // is only doable when we store in the object file which module to load) load_modules(); $match = false; if (isset($controllers[$args[0][0] . '-' . $args[0][1]])) { // foo/bar would match controller for "foo/bar" $match = $controllers[$args[0][0] . '-' . $args[0][1]]; $reason = $args[0][0] . '/' . $args[0][1]; } elseif (isset($controllers[$args[0][0] . '-*'])) { // foo/bar would match "foo/*" $match = $controllers[$args[0][0] . '-*']; $reason = $args[0][0] . '/*'; } elseif (isset($controllers['*-' . $args[0][1]])) { // foo/bar would match "*/bar" $match = $controllers['*-' . $args[0][1]]; $reason = '*/' . $args[0][1]; } elseif (isset($controllers['*-*'])) { // foo/bar would match "*/*" $match = $controllers['*-*']; $reason = '*/*'; } if ($match !== false) { // check authentication for those controllers that require it if (isset($match['auth']) && $match['auth']) { if (!is_auth()) { prompt_auth(); } // also check the referer to prevent against cross site request // forgery (xsrf) // this is not really optimal, since proxies can filter the referer // header, but as a first step.. if (!empty($_SERVER['HTTP_REFERER'])) { $bu = base_url(); if (substr($_SERVER['HTTP_REFERER'], 0, strlen($bu)) != $bu) { log_msg('warn', 'controller: possible xsrf detected, referer is ' . quot($_SERVER['HTTP_REFERER']) . ', arguments ' . var_dump_inl($args)); hotglue_error(400); } } } log_msg('info', 'controller: invoking controller ' . quot($reason) . ' => ' . $match['func']); return $match['func']($args); } else { // normally we won't reach this as some default (*/*) controller will // be present log_msg('warn', 'controller: no match for ' . quot($args[0][0] . '/' . $args[0][1])); hotglue_error(400); } }
/** * upload one or more files * * @param array $args arguments * key 'page' page to upload the files to (i.e. page.rev) * key 'preferred_module' (optional) try first to invoke the upload method * on this module * @return array response * array of rendered, newly created objects */ function upload_files($args) { if (empty($args['page'])) { return response('Required argument "page" missing or empty', 400); } if (!page_exists($args['page'])) { return response('Page ' . quot($args['page']) . ' does not exist', 404); } $ret = array(); log_msg('debug', 'upload_files: $_FILES is ' . var_dump_inl($_FILES)); foreach ($_FILES as $f) { $existed = false; $fn = upload_file($f['tmp_name'], $args['page'], $f['name'], $existed); if ($fn === false) { continue; } else { $args = array_merge($args, array('file' => $fn, 'mime' => $f['type'], 'size' => $f['size'])); // clear mime type if set to default application/octet-stream if ($args['mime'] == 'application/octet-stream') { $args['mime'] = ''; } } $s = false; // check preferred_module first if (!empty($args['preferred_module'])) { // make sure all modules are loaded load_modules(); $func = $args['preferred_module'] . '_upload'; if (is_callable($func)) { log_msg('debug', 'upload_files: invoking hook upload, calling ' . $func); $s = $func($args); if ($s !== false) { log_msg('info', 'upload_object: ' . quot($fn) . ' was handled by ' . quot($args['preferred_module'])); } } } // check all other modules next if ($s === false) { $r = invoke_hook_while('upload', false, $args); if (count($r) == 1) { $s = array_pop(array_values($r)); log_msg('info', 'upload_object: ' . quot($fn) . ' was handled by ' . quot(array_pop(array_keys($r)))); } } // check fallback hook last if ($s === false) { $r = invoke_hook_while('upload_fallback', false, $args); if (count($r) == 1) { $s = array_pop(array_values($r)); log_msg('info', 'upload_object: ' . quot($fn) . ' was (fallback-) handled by ' . quot(array_pop(array_keys($r)))); } } if ($s === false) { log_msg('warn', 'upload_files: nobody cared about file ' . quot($fn) . ', type ' . $f['type']); // delete file again unless it did already exist if (!$existed) { $a = expl('.', $args['page']); @unlink(CONTENT_DIR . '/' . $a[0] . '/shared/' . $fn); } } else { $ret[] = $s; } } return response($ret); }
/** * check if the user is authenticated or not * * @return true if authenticated, false if not */ function is_auth() { if (AUTH_METHOD == 'none') { log_msg('debug', 'common: auth success (auth_method none)'); return true; } elseif (AUTH_METHOD == 'basic') { if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) { if ($_SERVER['PHP_AUTH_USER'] == AUTH_USER && $_SERVER['PHP_AUTH_PW'] == AUTH_PASSWORD) { log_msg('debug', 'common: auth success (auth_method basic)'); return true; } else { log_msg('info', 'common: auth failure (auth_method basic)'); return false; } } else { if (!empty($_SERVER['HTTP_AUTHORIZATION'])) { log_msg('warn', 'common: no auth data (auth_method basic) but HTTP_AUTHORIZATION is ' . quot(var_dump_inl($_SERVER['HTTP_AUTHORIZATION']))); } else { log_msg('debug', 'common: no auth data (auth_method basic)'); } return false; } } elseif (AUTH_METHOD == 'digest') { if (isset($_SERVER['PHP_AUTH_DIGEST'])) { log_msg('debug', 'common: auth digest ' . var_dump_inl($_SERVER['PHP_AUTH_DIGEST'])); $res = http_digest_check(array(AUTH_USER => AUTH_PASSWORD), SITE_NAME); if ($res == 0) { log_msg('debug', 'common: auth success (auth_method digest)'); return true; } else { log_msg('info', 'common: auth failure ' . $res . ' (auth_method digest)'); return false; } } else { if (!empty($_SERVER['HTTP_AUTHORIZATION'])) { log_msg('warn', 'common: no auth data (auth_method digest) but HTTP_AUTHORIZATION is ' . quot(var_dump_inl($_SERVER['HTTP_AUTHORIZATION']))); } else { log_msg('debug', 'common: no auth data (auth_method digest)'); } return false; } } else { log_msg('error', 'common: invalid or missing AUTH_METHOD config setting'); return false; } }
/** * invoke a hook while the returned result is $while * * this function also takes care of loading all modules. * @param string $hook hook to invoke * @param mixed $while value to compare the returned result with * @param array $args arguments-array * @return array with result (module=>result) or empty result if there was none */ function invoke_hook_while($hook, $while, $args = array()) { global $modules; // make sure all modules are loaded load_modules(); foreach ($modules as $m) { if (is_callable($m . '_' . $hook)) { $func = $m . '_' . $hook; // DEBUG log_msg('debug', 'modules: invoking hook ' . $hook . ', calling ' . $func); $cur = $func($args); if ($cur !== $while) { $ret = array($m => $cur); // DEBUG //log_msg('debug', 'modules: invoke_hook_while on '.$hook.' returned '.var_dump_inl($ret)); return $ret; } } } log_msg('debug', 'modules: invoke_hook_while on ' . $hook . ' returned ' . var_dump_inl(array())); return array(); }
log_msg('warn', 'json: ' . $err['#data']); die; } // check authentication if (isset($m['auth']) && $m['auth']) { if (!is_auth()) { prompt_auth(true); } } if (isset($m['cross-origin']) && $m['cross-origin']) { // output cross-origin header if requested header('Access-Controll-Allow-Origin: *'); } else { // otherwise check the referer to make xsrf harder if (!empty($_SERVER['HTTP_REFERER'])) { $bu = base_url(); if (substr($_SERVER['HTTP_REFERER'], 0, strlen($bu)) != $bu) { echo json_encode(response('Cross-origin requests not supported for this method', 400)); log_msg('warn', 'json: possible xsrf detected, referer is ' . quot($_SERVER['HTTP_REFERER']) . ', arguments ' . var_dump_inl($args)); die; } } } // run service and output result $ret = run_service($method, $args); if (is_array($ret) && isset($ret['#error']) && $ret['#error']) { log_msg('warn', 'json: service ' . $method . ' returned error ' . quot($ret['#data'])); } elseif (is_array($ret) && isset($ret['#data'])) { log_msg('debug', 'json: service returned ' . var_dump_inl($ret['#data'])); } echo json_encode($ret);