/** * fetch the content of one url * * @param string the link to fetch * @param array of strings optional headers (eg, 'array("Content-Type: text/xml")') * @param string optional data to send * @param string cookie, if any * @return the actual content, of FALSE on error */ function fetch($url, $headers = '', $data = '', $cookie = '') { global $context; // use the http library return http::proceed($url, $headers, $data, $cookie); }
/** * ping back links referenced in some text * * This is the client implementation of * [link=trackback]http://www.movabletype.org/docs/mttrackback.html[/link] * and [link=pingback]http://www.hixie.ch/specs/pingback/pingback[/link] specifications. * * This function is triggered by publishing scripts, either [script]articles/publish.php[/script], * [script]services/blog.php[/script], [script]agents/messages.php[/script] or [script]agents/uploads.php[/script]. * * @see articles/publish.php * @see services/blog.php * @see agents/messages.php * @see agents/uploads.php * * It is used to efficiently link pages across a set of web sites according to the following mechanism: * - The list of external links is built for this page * - Only the 7 first links are kept from the list; others are stripped * - If links do not exist, create additional records in the table used for links * - Each link (actually, only the 7 most recent) is checked, to see if it's trackback- or pingback-enabled or not * - Each trackback-/pingback-enabled link is activated, providing the full URL of the anchor page * * We are claiming to support most of the trackback client interface here, as described in the [link=trackback]http://www.movabletype.org/docs/mttrackback.html[/link] specification. * A foreign page is considered as being trackback-enabled if it has a special RDF section * linking its reference (i.e., URL) to a Trackback Ping URL. * * Note that YACS also implements the server part of the trackback specification in [script]links/trackback.php[/script], * which supports POST REST calls. * * @see links/trackback.php * * We are claiming to fully support the pingback client interface here, as described in the [link=pingback]http://www.hixie.ch/specs/pingback/pingback[/link] specification. * A foreign page is considered to be pingback-enabled if it has a meta link to a Pingback Ping URL. * * Note that YACS also implements the server part of the pingback specification in [script]services/ping.php[/script], * which supports XML-RPC calls. * * @see services/ping.php * * This function transforms every YACS codes into HTML before extracting links, * and before submitting the excerpt to remote site. * * @param string the referencing text that has to be scanned * @param string the local anchor of the referencing text (e.g., 'article:124') * @return array list($links, $advertised, $skipped) * * @link http://www.movabletype.org/docs/mttrackback.html TrackBack Technical Specification * @link http://www.hixie.ch/specs/pingback/pingback Pingback specification */ public static function ping($text, $anchor) { global $context; // render all codes if (is_callable(array('Codes', 'beautify'))) { $text = Codes::beautify($text); } // suppress all links not coming from anchors (eg, <img src=...) $text = strip_tags($text, '<a>'); // extract all links from the text, including those that have been encoded by YACS preg_match_all('/((http:\\/\\/|http%3A%2F%2F)[^ <"]+)/i', $text, $links); // nothing to do if (!@count($links[1])) { return; } // process each link only once $unique_links = array(); foreach ($links[1] as $url) { // decode raw url encoding, if any $url = rawurldecode($url); // strip the clicking indirection, if any $url = rawurldecode(preg_replace('/^' . preg_quote($context['url_to_home'] . $context['url_to_root'] . 'links/click.php?url=', '/') . '/i', '', $url)); $unique_links[$url] = 1; } // analyze found links $links_processed = array(); $links_advertised = array(); $links_skipped = array(); foreach ($unique_links as $url => $dummy) { // analyze no more than 7 links if (@count($links_processed) >= 7) { break; } // skip links that point to ourself, and not to an article if (preg_match('/^' . preg_quote($context['url_to_home'], '/') . '\\b/i', $url) && !preg_match('/\\/article(-|s\\/view.php)/i', $url)) { $links_skipped[] = $url; continue; } // skip invalid links if (($content = http::proceed($url)) === FALSE) { $links_skipped[] = $url; continue; } // we will use the content to locate pingback and trackback interfaces $pages[$url] = $content; // ensure enough execution time Safe::set_time_limit(30); // stats $links_processed[] = $url; } // locate the anchor object for this text, we need its url $anchor = Anchors::get($anchor); if (!is_object($anchor)) { return; } // build an excerpt from anchor $excerpt = $anchor->get_teaser('basic'); // find blog name for anchor if ($parent = $anchor->get_value('anchor')) { $blog = Anchors::get($parent); if (is_object($blog)) { $blog_name = $blog->get_title(); } } // build an absolute URL for the source $source = $context['url_to_home'] . $context['url_to_root'] . $anchor->get_url(); // process each link if (@count($pages)) { foreach ($pages as $target => $content) { // try trackback, if implemented if (Links::ping_as_trackback($content, $source, $target, $anchor->get_title(), $excerpt, $blog_name)) { $links_advertised[] = $target; } elseif (Links::ping_as_pingback($content, $source, $target)) { $links_advertised[] = $target; } } } return array($links_processed, $links_advertised, $links_skipped); }
/** * the URL to stop and to leave a meeting * * @see overlays/events/stop.php * * @return string the URL to redirect the user from the meeting, or NULL on error */ function get_stop_url() { global $context; // almost random passwords $this->initialize_passwords(); // parameters to end the meeting $parameters = array(); $parameters[] = 'meetingID=' . urlencode($this->attributes['id']); $parameters[] = 'password='******'end', $parameters); // we don't care about the return code http::proceed($url); // back to the yacs page if (is_callable(array($this->anchor, 'get_url'))) { return $context['url_to_home'] . $context['url_to_root'] . $this->anchor->get_url(); } // this should not happen return NULL; }
continue; } } // do we have a suitable copy in staging repository? $staged = 'scripts/staging/' . $file; if (is_readable($context['path_to_root'] . $staged) && ($result = Scripts::hash($staged))) { // used staged file $context['text'] .= sprintf(i18n::s('Using staged file %s'), $file) . BR . "\n"; $staging_files++; continue; } // download the updated script from the reference server $context['text'] .= sprintf(i18n::s('Staging %s'), $file) . BR . "\n"; // get the file -- reference server have to be installed at the root $url = 'http://' . $context['reference_server'] . '/scripts/fetch.php?script=' . $file; if (!($content = http::proceed($url))) { $context['text'] .= sprintf(i18n::s('Impossible to read %s.'), $url) . BR . "\n"; $errors++; continue; } // ensure enough execution time Safe::set_time_limit(30); // save it in the staging store if (!Safe::file_put_contents('scripts/staging/' . $file, $content)) { $context['text'] .= '<p>' . sprintf(i18n::s('Impossible to write to %s.'), 'scripts/staging/' . $file) . "</p>\n"; $errors++; continue; } // ensure we have an exact copy by comparing hashes $staging_hash = Scripts::hash('scripts/staging/' . $file); if ($attributes[1] != $staging_hash[1]) {
/** * the URL to start and to join the meeting * * @see overlays/events/start.php * * @return string the URL to redirect the user to the meeting, or NULL on error */ function get_start_url() { global $context; // almost random passwords $this->initialize_passwords(); // link to authenticate $url = 'https://my.dimdim.com/api/auth/login'; // parameters to authenticate $parameters = array(); $parameters['account'] = $this->attributes['account']; $parameters['password'] = $this->attributes['password']; $parameters['group'] = 'all'; // encode in json $data = array('request' => Safe::json_encode($parameters)); // do authenticate if ($response = http::proceed($url, '', $data)) { // successful authentication $output = Safe::json_decode($response); if (isset($output['result']) && $output['result']) { // remember the authentication token $fields = array('token' => $output['response']['authToken']); $this->set_values($fields); // link to create a meeting $url = 'https://my.dimdim.com/api/conf/start_meeting'; // provide authentication token $headers = 'X-Dimdim-Auth-Token: ' . $this->attributes['token'] . CRLF; // parameters to create a meeting $parameters = array(); $parameters['authToken'] = $this->attributes['token']; $parameters['account'] = $this->attributes['account']; $parameters['clientId'] = Surfer::get_id(); $parameters['displayName'] = Surfer::get_name(); $parameters['meetingName'] = $this->anchor->get_title(); $parameters['roomName'] = $this->anchor->get_title(); $parameters['meetingLengthMinutes'] = $this->attributes['duration']; $parameters['attendeeKey'] = $this->attendee_password; $parameters['assistantEnabled'] = 'false'; // disable some features $parameters['displayDialInfo'] = 'false'; // message displayed within the BigBlueButton session $welcome = ''; // meeting title if (is_object($this->anchor)) { $welcome .= sprintf(i18n::s('%s: %s'), i18n::s('Title'), $this->anchor->get_title()) . "\n"; } // meeting date if (isset($this->attributes['date_stamp'])) { $welcome .= sprintf(i18n::s('%s: %s'), i18n::s('Date'), Skin::build_date($this->attributes['date_stamp'], 'standalone')) . "\n"; } // meeting duration if (isset($this->attributes['duration'])) { $welcome .= sprintf(i18n::s('%s: %s'), i18n::s('Duration'), $this->attributes['duration'] . ' ' . i18n::s('minutes')) . "\n"; } // build a link to the owner page, if any if (is_object($this->anchor) && ($user = Users::get($this->anchor->get_value('owner_id')))) { $welcome .= sprintf(i18n::s('%s: %s'), i18n::s('Chairman'), $user['full_name']) . "\n"; } // welcome message $parameters['agenda'] = $welcome; // return URL if (is_callable(array($this->anchor, 'get_url'))) { $parameters['returnurl'] = $context['url_to_home'] . $context['url_to_root'] . $this->anchor->get_url(); } // encode in json $data = array('request' => Safe::json_encode($parameters)); // do the transaction if ($response = http::proceed($url, $headers, $data)) { // successful transaction $output = Safe::json_decode($response); if (isset($output['result']) && $output['result']) { // redirect to the target page return 'https://my.dimdim.com/redirect?clientId=' . urlencode(Surfer::get_id()) . '&account=' . urlencode($this->attributes['account']); } } } } // don't know where to go return NULL; }
// if the current skin has been set to 'foo', YACS includes skins/foo/skin.php // then the static class Skin is available to format output components (lists, links, etc.) load_skin('tools'); // content of the target web object $input = ''; // maybe we have a cookie string to process $cookie = ''; if (isset($_REQUEST['cookie']) && strlen($_REQUEST['cookie'])) { $cookie = $_REQUEST['cookie']; } // we have an URL to scan $reference = ''; if (isset($_REQUEST['reference']) && strlen($_REQUEST['reference'])) { $reference = $_REQUEST['reference']; // fetch the object through the web if (!($input = http::proceed($reference, '', '', $cookie))) { // the standard way to localize string throughout YACS is to invoke i18n::s() -- see i18n/i18n.php Logger::error(sprintf(i18n::s('error while fetching %s'), $reference) . ' (' . http::get_error() . ')'); } // the user has submitted some content to crunch } elseif (isset($_REQUEST['input']) && strlen($_REQUEST['input'])) { $input = $_REQUEST['input']; } // the path to this page $context['path_bar'] = array('tools/' => i18n::s('Tools')); // the title of the page // in YACS templates, it is placed into $context['title'] $context['page_title'] = i18n::s('Fat Index'); // the splash message $context['text'] .= '<p>' . i18n::s('This script strips tags and white space to evaluate the amount of useful bytes. Then it computes the fat index as follows:') . '</p>' . "\n" . '<dl><dd>' . i18n::s('FAT Index = 10 log( Raw Bytes / Useful Bytes )') . '</dd></dl>' . "\n"; // we at least ask for registration
$count = 0; foreach ($copy as $file) { // content of the updated file $content = ''; // expected location in staging repository $local_reference = $context['path_to_root'] . 'scripts/staging/' . $file; // don't execute PHP scripts, just get them if (preg_match('/\\.php$/i', $file)) { $remote_reference = 'http://' . $context['reference_server'] . '/scripts/fetch.php?script=' . urlencode($file); } else { $remote_reference = 'http://' . $context['reference_server'] . '/scripts/reference/' . $file; } // get the file locally if (file_exists($local_reference)) { $content = Safe::file_get_contents($local_reference); } elseif (($content = http::proceed($remote_reference)) === FALSE) { $local['error_en'] = 'Unable to get ' . $file; $local['error_fr'] = 'Impossible d\'obtenir ' . $file; echo i18n::user('error') . "<br />\n"; } // we have something in hand if ($content) { // create missing directories where applicable Safe::make_path(dirname($file)); // create backups, if possible if (file_exists($context['path_to_root'] . $file)) { Safe::unlink($context['path_to_root'] . $file . '.bak'); Safe::rename($context['path_to_root'] . $file, $context['path_to_root'] . $file . '.bak'); } // update the target file if (!Safe::file_put_contents($file, $content)) {
$response = 'Thanks for the ping'; Links::clear($fields); } } } break; // ping an external reference to some page on this site // ping an external reference to some page on this site case 'weblogUpdates.ping': list($label, $url) = $parameters['params']; // caller has been banned if ($_SERVER['REMOTE_HOST'] && ($server = Servers::get($_SERVER['REMOTE_HOST']) && $server['process_ping'] != 'Y')) { $response = array('flerror' => 49, 'message' => 'Access denied'); } elseif (preg_match('/\\b(127\\.0\\.0\\.1|localhost)\\b/', $url)) { $response = array('flerror' => 1, 'message' => 'We don\'t accept local references ' . $url); } elseif (($content = http::proceed($url)) === FALSE && ($content = http::proceed($url . '/')) === FALSE) { $response = array('flerror' => 1, 'message' => 'Cannot read source address ' . $url); } else { $response = Servers::ping(strip_tags($label), $url); if ($response) { Logger::remember('services/ping.php: failing ping', $response, 'debug'); $response = array('flerror' => 1, 'message' => $response); } else { $response = array('flerror' => 0, 'message' => 'Thanks for the ping'); } } break; default: $response = array('faultCode' => 1, 'faultString' => 'Do not know how to process ' . $parameters['methodName']); Logger::remember('services/ping.php: ping unsupported methodName', $parameters, 'debug'); }
Safe::header('Status: 401 Unauthorized', TRUE, 401); echo i18n::s('You are not allowed to perform this operation.'); // we reject requests not originating from this server } elseif (!isset($_SERVER['HTTP_REFERER']) || strncmp($_SERVER['HTTP_REFERER'], $context['url_to_home'], strlen($context['url_to_home']))) { Safe::header('Status: 400 Bad Request', TRUE, 400); echo i18n::s('You are not allowed to perform this operation.'); // process the provided url } elseif (isset($_REQUEST['url']) && $_REQUEST['url']) { // read raw content $raw_data = file_get_contents("php://input"); // save the raw request if debug mode if (isset($context['with_debug']) && $context['with_debug'] == 'Y') { Logger::remember('services/proxy.php: proxy request', $raw_data, 'debug'); } // forward the request, and process the response $response = http::proceed($_REQUEST['url']); // save response headers if debug mode if (isset($context['with_debug']) && $context['with_debug'] == 'Y') { Logger::remember('services/proxy.php: proxy response headers', http::get_headers(), 'debug'); } // transmit response headers $headers = explode("\n", http::get_headers()); for ($index = 1; $index < count($headers); $index++) { // assume we will provide our own encoding if (!strncmp($headers[$index], 'Content-Encoding', strlen('Content-Encoding'))) { continue; } // only one header per type $replace = TRUE; // maybe several cookies can be set if (!strncmp($headers[$index], 'Set-Cookie', strlen('Set-Cookie'))) {
// stop crawlers if (Surfer::is_crawler()) { Safe::header('Status: 401 Unauthorized', TRUE, 401); Logger::error(i18n::s('You are not allowed to perform this operation.')); // process uploaded data } elseif (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') { // save the request if debug mode if (isset($context['debug_trackback']) && $context['debug_trackback'] == 'Y') { Logger::remember('links/trackback.php: trackback request', $_REQUEST, 'debug'); } // do we have a valid target to track? if (!$anchor || !is_object($anchor)) { $response = array('faultCode' => 1, 'faultString' => 'Nothing to track'); } elseif (Links::have($source, $anchor->get_reference())) { $response = array('faultCode' => 1, 'faultString' => 'The source has already been registered'); } elseif (($content = http::proceed($source)) === FALSE) { $response = array('faultCode' => 1, 'faultString' => 'Cannot read source address ' . $source); } else { // ensure enough execution time Safe::set_time_limit(30); // we are coming from this form -- stop robots if (strpos($_SERVER['HTTP_REFERER'], $context['script_url']) !== FALSE) { if (Surfer::may_be_a_robot()) { $response = array('faultCode' => 1, 'faultString' => 'Please prove you are not a robot'); } // remote call -- get network address a.b.c.d of caller } elseif (!isset($_SERVER['REMOTE_ADDR']) || !($ip = preg_replace('/[^0-9.]/', '', $_SERVER['REMOTE_ADDR']))) { $response = array('faultCode' => 1, 'faultString' => 'Invalid request'); } elseif (!($items = @parse_url($source)) || !isset($items['host'])) { $response = array('faultCode' => 1, 'faultString' => 'Invalid request'); } elseif ($items['host'] != $ip && is_callable('gethostbyname') && gethostbyname($items['host']) != $ip) {
/** * remember an action once it's done * * @see articles/delete.php * @see articles/edit.php * * @param string the action 'insert', 'update' or 'delete' * @param array the hosting record * @param string reference of the hosting record (e.g., 'article:123') * @return FALSE on error, TRUE otherwise */ function remember($action, $host, $reference) { global $context; // set default values for this editor Surfer::check_default_editor($this->attributes); // add a notification to the anchor page $comments = array(); // on page creation if ($action == 'insert') { // expose all of the anchor interface to the contained overlay $this->anchor = Anchors::get($reference); // embed an object referenced by address if ($this->attributes['embed_type'] == 'href') { // ask some oEmbed provider to tell us more about this if ($this->attributes['embed_href'] && ($fields = $this->oembed($this->attributes['embed_href']))) { // we do want a photo, right? if (preg_match('/\\.(gif|jpg|jpeg|png)$/i', $this->attributes['embed_href'])) { $fields['type'] = 'photo'; } // because deviant-art returns non-standard type 'file' ??? if (isset($fields['url']) && preg_match('/\\.(gif|jpg|jpeg|png)$/i', $fields['url'])) { $fields['type'] = 'photo'; } // save meta data in the overlay itself $fields['id'] = $host['id']; $this->set_values($fields); // notify this contribution switch ($this->attributes['type']) { case 'link': $comments[] = sprintf(i18n::s('%s has shared a link'), Surfer::get_name()); break; case 'photo': $comments[] = sprintf(i18n::s('%s has shared a photo'), Surfer::get_name()); break; case 'rich': $comments[] = sprintf(i18n::s('%s has shared some information'), Surfer::get_name()); break; case 'video': $comments[] = sprintf(i18n::s('%s has shared a video'), Surfer::get_name()); break; default: // default label is the link itself $label = $this->attributes['embed_href']; // fetch page title if possible if ($this->attributes['embed_href'] && ($content = http::proceed($this->attributes['embed_href']))) { if (preg_match('/<title>(.*)<\\/title>/siU', $content, $matches)) { $label = trim(strip_tags(preg_replace('/\\s+/', ' ', $matches[1]))); } } // update the record $fields = array(); $fields['type'] = 'link'; $fields['label'] = $label; $this->set_values($fields); $comments[] = sprintf(i18n::s('%s has shared a link'), Surfer::get_name()); break; } } // uploaded files are turned to comments automatically in articles/article.php } } // add a comment if allowed if ($comments && !$this->anchor->has_option('no_comments')) { include_once $context['path_to_root'] . 'comments/comments.php'; $fields = array(); $fields['anchor'] = $reference; $fields['description'] = join(BR, $comments); $fields['type'] = 'notification'; Comments::post($fields); } // job done return TRUE; }
/** * process one single HTTP request * * This function removes any PHPSESSID data in the query string, if any * * @return void * * @see agents/referrals_hook.php */ public static function check_request() { global $context; // don't bother with HEAD requests if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'HEAD') { return; } // the target url if (!isset($_SERVER['REQUEST_URI']) || !($url = $_SERVER['REQUEST_URI'])) { return; } // only remember viewed pages and index pages if (!preg_match('/\\/(index|view).php/', $url)) { return; } // continue only if we have a referer if (!isset($_SERVER['HTTP_REFERER']) || !($referer = $_SERVER['HTTP_REFERER'])) { return; } // do not memorize cache referrals if (preg_match('/cache:/i', $referer)) { return; } // block pernicious attacks $referer = strip_tags($referer); // only remember external referrals if (preg_match('/\\b' . preg_quote(str_replace('www.', '', $context['host_name']), '/') . '\\b/i', $referer)) { return; } // stop crawlers if (Surfer::is_crawler()) { return; } // avoid banned sources include_once $context['path_to_root'] . 'servers/servers.php'; if (preg_match(Servers::get_banned_pattern(), $referer)) { return; } // normalize the referral, extract keywords, and domain list($referer, $domain, $keywords) = Referrals::normalize($referer); // if a record exists for this url $query = "SELECT id FROM " . SQL::table_name('referrals') . " AS referrals" . " WHERE referrals.url LIKE '" . SQL::escape($url) . "' AND referrals.referer LIKE '" . SQL::escape($referer) . "'"; if (!($item = SQL::query_first($query))) { return; } // update figures if (isset($item['id'])) { $query = "UPDATE " . SQL::table_name('referrals') . " SET" . " hits=hits+1," . " stamp='" . gmstrftime('%Y-%m-%d %H:%M:%S') . "'" . " WHERE id = " . $item['id']; // create a new record } else { // ensure the referer is accessible if (($content = http::proceed($referer)) === FALSE) { return; } // we have to find a reference to ourself in this page if (strpos($content, $context['url_to_home']) === FALSE) { return; } $query = "INSERT INTO " . SQL::table_name('referrals') . " SET" . " url='" . SQL::escape($url) . "'," . " referer='" . SQL::escape($referer) . "'," . " domain='" . SQL::escape($domain) . "'," . " keywords='" . SQL::escape($keywords) . "'," . " hits=1," . " stamp='" . gmstrftime('%Y-%m-%d %H:%M:%S') . "'"; } // actual database update if (SQL::query($query) === FALSE) { return; } // prune with a probability of 1/100 if (rand(1, 100) != 50) { return; } // purge oldest records -- 100 days = 8640000 seconds $query = "DELETE FROM " . SQL::table_name('referrals') . " WHERE stamp < '" . gmstrftime('%Y-%m-%d %H:%M:%S', time() - 8640000) . "'"; SQL::query($query); }
/** * the URL to stop and to leave a meeting * * @see overlays/events/stop.php * * @return string the URL to redirect the user from the meeting, or NULL on error */ function get_stop_url() { global $context; // export pad content $url = 'http://' . $this->get_hostname() . '/ep/pad/export/' . $this->attributes['meeting_id'] . '/latest?format=html'; // we don't care about the return code if (($result = http::proceed($url)) && preg_match('|\\<body[^>]*>(.*)</body[^>]*>|isU', $result, $matches)) { // put the text of the pad in a comment include_once $context['path_to_root'] . 'comments/comments.php'; $fields = array(); $fields['anchor'] = $this->anchor->get_reference(); $fields['description'] = i18n::s('Resulting text') . '<div style="border: 2px solid #ccc; margin: 1em; padding: 1em;">' . $matches[1] . '</div>'; $fields['type'] = 'notification'; Comments::post($fields); } // back to the yacs page if (is_callable(array($this->anchor, 'get_url'))) { return $context['url_to_home'] . $context['url_to_root'] . $this->anchor->get_url(); } // this should not happen return NULL; }