/** * validate a link * * This function submits a HTTP request to the target server to check that the page actually exists * * @param the link to validate * @return A date if Last-Modified has been provided, or TRUE if the link is reachable, FALSE otherwise */ function validate($url) { global $context; // parse this url $items = @parse_url($url); // assume the link is correct if not http if ($items['scheme'] && $items['scheme'] != 'http') { return TRUE; } // no host, assume it's us if (!($host = $items['host'])) { $host = $context['host_name']; } // sometime parse_url() adds a '_' $host = rtrim($host, '_'); // no port, assume the standard if (!($port = $items['port'])) { $port = 80; } // assume the link is correct when outbound web is not authorized if (isset($context['without_outbound_http']) && $context['without_outbound_http'] == 'Y') { return TRUE; } // open a network connection -- wait for up to 10 seconds for the TCP connection if (!($handle = Safe::fsockopen($host, $port, $errno, $errstr, 10))) { if ($context['with_debug'] == 'Y') { logger::remember('links/link.php: ' . $host . ':' . $port . ' is not reachable', $url, 'debug'); } return FALSE; } // ensure enough execution time Safe::set_time_limit(30); // build the path $path = $items['path']; if (!$path) { $path = '/'; } // sometime parse_url() adds a '_' $path = rtrim($path, '_'); // include any query if ($items['query']) { $path .= '?' . $items['query']; } // send an HTTP request fputs($handle, 'HEAD ' . $path . " HTTP/1.0" . CRLF . 'Host: ' . $host . CRLF . "User-Agent: YACS (www.yacs.fr)" . CRLF . "Connection: close" . CRLF . CRLF); // we are interested into the header only $response = ''; while (!feof($handle) && strlen($response) < 5242880) { // ask for Ethernet-sized chunks $chunk = fread($handle, 1500); // split on headers boundary $here = strpos($chunk, CRLF . CRLF); if ($here !== FALSE) { $chunk = substr($chunk, 0, $here); $response .= $chunk; break; } // gather header information $response .= $chunk; } fclose($handle); // split headers into lines $lines = explode(CRLF, $response); // ensure we have a valid HTTP status line if (!preg_match('/^HTTP\\/[0-9\\.]+ 20\\d /', $lines[0])) { if ($context['with_debug'] == 'Y') { logger::remember('links/link.php: bad status: ' . $lines[0], $url, 'debug'); } return FALSE; } // scan lines for "Last-Modified" header foreach ($lines as $line) { if (preg_match('/^Last-Modified: (.*?)/', $line, $matches)) { // return the stamp for this link return date("Y-m-d H:i:s", strtotime($matches[1])); } } // no date, but the link has been validated anyway return TRUE; }
/** * dynamically generate the page * * @see skins/index.php */ function send_body() { global $context; // only associates can proceed if (!Surfer::is_associate()) { Safe::header('Status: 401 Unauthorized', TRUE, 401); echo '<p>' . i18n::s('You are not allowed to perform this operation.') . "</p>\n"; // forward to the index page $menu = array('scripts/' => i18n::s('Server software')); echo Skin::build_list($menu, 'menu_bar'); // ask for confirmation } elseif (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'GET') { // the splash message echo '<p>' . i18n::s('This tool will include most of the running reference PHP scripts. Any syntax error should be spotted easily.') . '</p>'; // the submit button echo '<form method="post" action="' . $context['script_url'] . '" id="main_form"><p>' . Skin::build_submit_button(i18n::s('Yes, I want to validate scripts'), NULL, NULL, 'confirmed') . '</p></form>'; // set the focus on the button Page::insert_script('$("#confirmed").focus();'); // this may take some time echo '<p>' . i18n::s('When you will click on the button the server will be immediately requested to proceed. However, because of the so many things to do on the back-end, you may have to wait for minutes before getting a response displayed. Thank you for your patience.') . '</p>'; // just do it } else { // the splash message echo '<p>' . i18n::s('All reference scripts are included, to show evidence of possible syntax errors.') . "</p>\n"; // list running scripts echo '<p>' . i18n::s('Listing files...') . BR . "\n"; // locate script files starting at root $scripts = Scripts::list_scripts_at(NULL); if (is_array($scripts) && count($scripts)) { echo BR . sprintf(i18n::s('%d scripts have been found.'), count($scripts)) . "\n"; natsort($scripts); } echo "</p>\n"; // including scripts echo '<p>' . i18n::s('Including reference scripts...') . BR . "\n"; // strip as much output as possible $_SERVER['REQUEST_METHOD'] = 'HEAD'; // we will finalize this page later on global $finalizing_fuse; $finalizing_fuse = FALSE; // take care of dependancies include_once '../behaviors/behavior.php'; include_once '../services/codec.php'; include_once '../users/authenticator.php'; // analyse each script $included_files = 0; $links_to_be_checked_manually = array(); foreach ($scripts as $file) { // ensure we have enough time to process this script Safe::set_time_limit(30); // skip run once scripts if (strpos($file, 'run_once/')) { continue; } // don't include ourself if ($file == 'scripts/validate.php') { continue; } // process only reference scripts if (!Scripts::hash($file)) { continue; } // check file content if (!($handle = Safe::fopen($file, 'rb'))) { echo sprintf(i18n::s('%s has no readable content.'), $file) . BR . "\n"; continue; } // look at the beginning of the file if (!($header = fread($handle, 16384))) { echo sprintf(i18n::s('%s has no readable content.'), $file) . BR . "\n"; fclose($handle); continue; } fclose($handle); // skip scripts that generate content asynchronously if (stripos($header, 'send_body') || stripos($header, 'page::content')) { $links_to_be_checked_manually[$file] = '(asynchronous)'; continue; } // skip scripts that would redefine our skin if (stripos($header, 'extends skin_skeleton')) { $links_to_be_checked_manually[$file] = '(skin)'; continue; } // log script inclusion on development host if ($context['with_debug'] == 'Y') { logger::remember('scripts/validate.php: inclusion of ' . $file, '', 'debug'); } // include the script and display any error $included_files += 1; $validate_stamp = time(); echo sprintf(i18n::s('inclusion of %s'), $file) . "\n"; Safe::chdir($context['path_to_root'] . dirname($file)); include_once $context['path_to_root'] . $file; $duration = time() - $validate_stamp; if ($duration) { echo ' (' . $duration . 's.)'; } echo BR; } // memory status $used_memory = ''; if (is_callable('memory_get_usage')) { $used_memory = ' (' . memory_get_usage() . ' bytes)'; } // report of included files if ($included_files > 1) { echo '<p>' . sprintf(i18n::s('%d files have been included.'), $included_files) . $used_memory . '</p>'; } // list files to be checked manually if (count($links_to_be_checked_manually)) { echo '<p>' . i18n::s('Following scripts have to be included separately:') . BR . "\n"; ksort($links_to_be_checked_manually); foreach ($links_to_be_checked_manually as $file => $label) { echo Skin::build_link($file, $file, 'basic') . ' ' . $label . BR . "\n"; } echo sprintf(i18n::s('%d files to be checked manually.'), count($links_to_be_checked_manually)) . '</p>' . "\n"; } // display the execution time $time = round(get_micro_time() - $context['start_time'], 2); echo '<p>' . sprintf(i18n::s('Script terminated in %.2f seconds.'), $time) . '</p>'; // forward to the referential building echo '<form method="get" action="' . $context['url_to_root'] . 'scripts/build.php"><p>' . "\n" . Skin::build_submit_button(i18n::s('If no error has popped up, build the reference set >>')) . "\n" . '</p></form>' . "\n"; // this may take some time echo '<p>' . i18n::s('When you will click on the button the server will be immediately requested to proceed. However, because of the so many things to do on the back-end, you may have to wait for minutes before getting a response displayed. Thank you for your patience.') . '</p>'; // clear text some scripts could have added $context['debug'] = ''; $context['extra'] = ''; $context['navigation'] = ''; $context['suffix'] = ''; $context['text'] = ''; $context['page_details'] = ''; $context['page_footer'] = ''; $context['page_menu'] = array(); $context['page_tags'] = ''; $context['page_tools'] = ''; // now we will finalize this page global $finalizing_fuse; unset($finalizing_fuse); } }
/** * remember the last action for this category * * @param string the description of the last action * @param string the id of the item related to this update * @param boolean TRUE to not change the edit date of this anchor, default is FALSE * * @see shared/anchor.php */ function touch($action, $origin = NULL, $silently = FALSE) { global $context; // don't go further on import if (preg_match('/import$/i', $action)) { return; } // no category bound if (!isset($this->item['id'])) { return; } // sanity check if (!$origin) { logger::remember('categories/category.php: unexpected NULL origin at touch()'); return; } // components of the query $query = array(); // append a reference to a new image to the description if ($action == 'image:create') { if (!Codes::check_embedded($this->item['description'], 'image', $origin)) { // the overlay may prevent embedding if (is_object($this->overlay) && !$this->overlay->should_embed_files()) { } else { // list has already started if (preg_match('/\\[image=[^\\]]+?\\]\\s*$/', $this->item['description'])) { $query[] = "description = '" . SQL::escape($this->item['description'] . ' [image=' . $origin . ']') . "'"; } else { $query[] = "description = '" . SQL::escape($this->item['description'] . "\n\n" . '[image=' . $origin . ']') . "'"; } } } // also use it as thumnail if none has been defined yet if (!isset($this->item['thumbnail_url']) || !trim($this->item['thumbnail_url'])) { include_once $context['path_to_root'] . 'images/images.php'; if (($image = Images::get($origin)) && ($url = Images::get_thumbnail_href($image))) { $query[] = "thumbnail_url = '" . SQL::escape($url) . "'"; } } // refresh stamp only if image update occurs within 6 hours after last edition if (SQL::strtotime($this->item['edit_date']) + 6 * 60 * 60 < time()) { $silently = TRUE; } // suppress a reference to an image that has been deleted } elseif ($action == 'image:delete') { // suppress reference in main description field $query[] = "description = '" . SQL::escape(Codes::delete_embedded($this->item['description'], 'image', $origin)) . "'"; // suppress references as icon and thumbnail as well include_once $context['path_to_root'] . 'images/images.php'; if ($image = Images::get($origin)) { if ($url = Images::get_icon_href($image)) { if ($this->item['icon_url'] == $url) { $query[] = "icon_url = ''"; } if ($this->item['thumbnail_url'] == $url) { $query[] = "thumbnail_url = ''"; } } if ($url = Images::get_thumbnail_href($image)) { if ($this->item['icon_url'] == $url) { $query[] = "icon_url = ''"; } if ($this->item['thumbnail_url'] == $url) { $query[] = "thumbnail_url = ''"; } } } // set an existing image as the category icon } elseif ($action == 'image:set_as_icon') { include_once $context['path_to_root'] . 'images/images.php'; if ($image = Images::get($origin)) { if ($url = Images::get_icon_href($image)) { $query[] = "icon_url = '" . SQL::escape($url) . "'"; } // also use it as thumnail if none has been defined yet if (!(isset($this->item['thumbnail_url']) && trim($this->item['thumbnail_url'])) && ($url = Images::get_thumbnail_href($image))) { $query[] = "thumbnail_url = '" . SQL::escape($url) . "'"; } } $silently = TRUE; // set an existing image as the category thumbnail } elseif ($action == 'image:set_as_thumbnail') { include_once $context['path_to_root'] . 'images/images.php'; if ($image = Images::get($origin)) { if ($url = Images::get_thumbnail_href($image)) { $query[] = "thumbnail_url = '" . SQL::escape($url) . "'"; } } $silently = TRUE; // append a new image, and set it as the article thumbnail } elseif ($action == 'image:set_as_both') { if (!Codes::check_embedded($this->item['description'], 'image', $origin)) { $query[] = "description = '" . SQL::escape($this->item['description'] . ' [image=' . $origin . ']') . "'"; } include_once $context['path_to_root'] . 'images/images.php'; if ($image = Images::get($origin)) { if ($url = Images::get_thumbnail_href($image)) { $query[] = "thumbnail_url = '" . SQL::escape($url) . "'"; } } elseif ($origin) { $query[] = "thumbnail_url = '" . SQL::escape($origin) . "'"; } // do not remember minor changes $silently = TRUE; // add a reference to a new table in the category description } elseif ($action == 'table:create') { if (!Codes::check_embedded($this->item['description'], 'table', $origin)) { $query[] = "description = '" . SQL::escape($this->item['description'] . ' [table=' . $origin . ']') . "'"; } // suppress a reference to a table that has been deleted } elseif ($action == 'table:delete') { $query[] = "description = '" . SQL::escape(Codes::delete_embedded($this->item['description'], 'table', $origin)) . "'"; } // stamp the update if (!$silently) { $query[] = "edit_name='" . Surfer::get_name() . "'," . "edit_id=" . Surfer::get_id() . "," . "edit_address='" . Surfer::get_email_address() . "'," . "edit_action='{$action}'," . "edit_date='" . strftime('%Y-%m-%d %H:%M:%S') . "'"; } // ensure we have a valid update query if (!@count($query)) { return; } // update the anchor category $query = "UPDATE " . SQL::table_name('categories') . " SET " . implode(', ', $query) . " WHERE id = " . SQL::escape($this->item['id']); if (SQL::query($query) === FALSE) { return; } // always clear the cache, even on no update Categories::clear($this->item); // get the parent if (!$this->anchor) { $this->anchor = Anchors::get($this->item['anchor']); } // propagate the touch upwards silently -- we only want to purge the cache if (is_object($this->anchor)) { $this->anchor->touch('category:update', $this->item['id'], TRUE); } }
/** * remember the last action for this section * * @see articles/article.php * @see shared/anchor.php * * @param string the description of the last action * @param string the id of the item related to this update * @param boolean TRUE to not change the edit date of this anchor, default is FALSE */ function touch($action, $origin = NULL, $silently = FALSE) { global $context; // we make extensive use of comments below include_once $context['path_to_root'] . 'comments/comments.php'; // don't go further on import if (preg_match('/import$/i', $action)) { return; } // no section bound if (!isset($this->item['id'])) { return; } // delegate to overlay if (is_object($this->overlay) && $this->overlay->touch($action, $origin, $silently) === false) { return; // stop on false } // sanity check if (!$origin) { logger::remember('sections/section.php: unexpected NULL origin at touch()'); return; } // components of the query $query = array(); // a new page has been added to the section if ($action == 'article:publish' || $action == 'article:submit') { // limit the number of items attached to this section if (isset($this->item['maximum_items']) && $this->item['maximum_items'] > 10) { Articles::purge_for_anchor('section:' . $this->item['id'], $this->item['maximum_items']); } // a new comment has been posted } elseif ($action == 'comment:create') { // purge oldest comments Comments::purge_for_anchor('section:' . $this->item['id']); // file upload } elseif ($action == 'file:create' || $action == 'file:upload') { // actually, several files have been added $label = ''; if (!$origin) { $fields = array(); $fields['anchor'] = 'section:' . $this->item['id']; $fields['description'] = i18n::s('Several files have been added'); $fields['type'] = 'notification'; Comments::post($fields); // one file has been added } elseif (!Codes::check_embedded($this->item['description'], 'embed', $origin) && ($item = Files::get($origin, TRUE))) { // this file is eligible for being embedded in the page if (isset($item['file_name']) && Files::is_embeddable($item['file_name'])) { // the overlay may prevent embedding if (is_object($this->overlay) && !$this->overlay->should_embed_files()) { } else { $label = '[embed=' . $origin . ']'; } // else add a comment to take note of the upload } elseif (Comments::allow_creation($this->item, null, 'section')) { $fields = array(); $fields['anchor'] = 'section:' . $this->item['id']; if ($action == 'file:create') { $fields['description'] = '[file=' . $item['id'] . ',' . $item['file_name'] . ']'; } else { $fields['description'] = '[download=' . $item['id'] . ',' . $item['file_name'] . ']'; } Comments::post($fields); } } // include flash videos in a regular page if ($label) { $query[] = "description = '" . SQL::escape($this->item['description'] . ' ' . $label) . "'"; } // suppress references to a deleted file } elseif ($action == 'file:delete') { // suppress reference in main description field $text = Codes::delete_embedded($this->item['description'], 'download', $origin); $text = Codes::delete_embedded($text, 'embed', $origin); $text = Codes::delete_embedded($text, 'file', $origin); // save changes $query[] = "description = '" . SQL::escape($text) . "'"; // append a reference to a new image to the description } elseif ($action == 'image:create') { if (!Codes::check_embedded($this->item['description'], 'image', $origin)) { // the overlay may prevent embedding if (is_object($this->overlay) && !$this->overlay->should_embed_files()) { } else { // list has already started if (preg_match('/\\[image=[^\\]]+?\\]\\s*$/', $this->item['description'])) { $query[] = "description = '" . SQL::escape($this->item['description'] . ' [image=' . $origin . ']') . "'"; } else { $query[] = "description = '" . SQL::escape($this->item['description'] . "\n\n" . '[image=' . $origin . ']') . "'"; } } } // also use it as thumnail if none has been defined yet if (!isset($this->item['thumbnail_url']) || !trim($this->item['thumbnail_url'])) { include_once $context['path_to_root'] . 'images/images.php'; if (($image = Images::get($origin)) && ($url = Images::get_thumbnail_href($image))) { $query[] = "thumbnail_url = '" . SQL::escape($url) . "'"; } } // refresh stamp only if image update occurs within 6 hours after last edition if (SQL::strtotime($this->item['edit_date']) + 6 * 60 * 60 < time()) { $silently = TRUE; } // suppress a reference to an image that has been deleted } elseif ($action == 'image:delete') { // suppress reference in main description field $query[] = "description = '" . SQL::escape(Codes::delete_embedded($this->item['description'], 'image', $origin)) . "'"; // suppress references as icon and thumbnail as well include_once $context['path_to_root'] . 'images/images.php'; if ($image = Images::get($origin)) { if ($url = Images::get_icon_href($image)) { if ($this->item['icon_url'] == $url) { $query[] = "icon_url = ''"; } if ($this->item['thumbnail_url'] == $url) { $query[] = "thumbnail_url = ''"; } } if ($url = Images::get_thumbnail_href($image)) { if ($this->item['icon_url'] == $url) { $query[] = "icon_url = ''"; } if ($this->item['thumbnail_url'] == $url) { $query[] = "thumbnail_url = ''"; } } } // set an existing image as the section icon } elseif ($action == 'image:set_as_icon') { include_once $context['path_to_root'] . 'images/images.php'; if ($image = Images::get($origin)) { if ($url = Images::get_icon_href($image)) { $query[] = "icon_url = '" . SQL::escape($url) . "'"; } // also use it as thumnail if none has been defined yet if (!(isset($this->item['thumbnail_url']) && trim($this->item['thumbnail_url'])) && ($url = Images::get_thumbnail_href($image))) { $query[] = "thumbnail_url = '" . SQL::escape($url) . "'"; } } elseif ($origin) { $query[] = "icon_url = '" . SQL::escape($origin) . "'"; } $silently = TRUE; // set an existing image as the section thumbnail } elseif ($action == 'image:set_as_thumbnail') { include_once $context['path_to_root'] . 'images/images.php'; if ($image = Images::get($origin)) { // use the thumbnail for large files, or the image itself for smaller files if ($image['image_size'] > $context['thumbnail_threshold']) { $url = Images::get_thumbnail_href($image); } else { $url = Images::get_icon_href($image); } $query[] = "thumbnail_url = '" . SQL::escape($url) . "'"; } elseif ($origin) { $query[] = "thumbnail_url = '" . SQL::escape($origin) . "'"; } $silently = TRUE; // append a new image, and set it as the article thumbnail } elseif ($action == 'image:set_as_both') { if (!Codes::check_embedded($this->item['description'], 'image', $origin)) { $query[] = "description = '" . SQL::escape($this->item['description'] . ' [image=' . $origin . ']') . "'"; } include_once $context['path_to_root'] . 'images/images.php'; if ($image = Images::get($origin)) { // use the thumbnail for large files, or the image itself for smaller files if ($image['image_size'] > $context['thumbnail_threshold']) { $url = Images::get_thumbnail_href($image); } else { $url = Images::get_icon_href($image); } $query[] = "thumbnail_url = '" . SQL::escape($url) . "'"; } elseif ($origin) { $query[] = "thumbnail_url = '" . SQL::escape($origin) . "'"; } // do not remember minor changes $silently = TRUE; // add a reference to a new table in the section description } elseif ($action == 'table:create') { if (!Codes::check_embedded($this->item['description'], 'table', $origin)) { $query[] = "description = '" . SQL::escape($this->item['description'] . ' [table=' . $origin . ']') . "'"; } // suppress a reference to a table that has been deleted } elseif ($action == 'table:delete') { $query[] = "description = '" . SQL::escape(Codes::delete_embedded($this->item['description'], 'table', $origin)) . "'"; } // stamp the update if (!$silently) { $query[] = "edit_name='" . SQL::escape(Surfer::get_name()) . "'," . "edit_id=" . SQL::escape(Surfer::get_id()) . "," . "edit_address='" . SQL::escape(Surfer::get_email_address()) . "'," . "edit_action='{$action}'," . "edit_date='" . SQL::escape(gmstrftime('%Y-%m-%d %H:%M:%S')) . "'"; } // update the database if (@count($query)) { $query = "UPDATE " . SQL::table_name('sections') . " SET " . implode(', ', $query) . " WHERE id = " . SQL::escape($this->item['id']); SQL::query($query); } // always clear the cache, even on no update Sections::clear($this->item); // get the parent if (!$this->anchor) { $this->anchor = Anchors::get($this->item['anchor']); } // propagate the touch upwards silently -- we only want to purge the cache if (is_object($this->anchor)) { $this->anchor->touch('section:touch', $this->item['id'], TRUE); } }
/** * connect to the mail server * * This function opens a network connection to the server, authenticate if required to do so, * and set $context['mail_handle'] to be used for actual transmissions. * * If parameter $context['mail_variant'] is set to 'smtp', a SMTP connection is * established with the computer specified in $context['mail_server']. If some credentials * are provided in $context['mail_account'] and $context['mail_password'], they are * transmitted to the server as per protocol extension. CRAM-MD5, LOGIN and PLAIN authentication * schemes have been implemented. * * @link http://tools.ietf.org/rfc/rfc2104.txt HMAC * @link http://www.fehcom.de/qmail/smtpauth.html * * If parameter $context['mail_variant'] is set to 'pop3', and if credentials have been * set in $context['mail_account'] and in $context['mail_password'], a POP3 connection * is made to the mail server just to authenticate, and then a SMTP connection * is established to actually transmit messages. If a secured communication has been * configured for SMTP, then a secured POP3 communication is performed on port 995. Else * a vanilla POP3 transaction is done on regular port 110. * * For any other value of $context['mail_variant'], or if the parameter is not set, * the function relies on the PHP mail() function to do the job. If the parameter * $context['mail_server'] is set, it overloads php.ini settings. Therefore you can change * the SMTP server used for transmission without the needs to edit the php.ini file. * * The parameter $context['mail_server'] can call for SSL/TLS support, or use a specific * port number, as in the following examples: * * [snippet] * ssl://mail.server.com * mail.server.com:234 * [/snippet] * * @return mixed the socket handle itself, of FALSE on error * * @see control/configure.php */ private static function connect() { global $context; // we already have an open handle if (isset($context['mail_handle'])) { return $context['mail_handle']; } // email services have to be activated if (!isset($context['with_email']) || $context['with_email'] != 'Y') { Logger::error(i18n::s('E-mail has not been enabled on this system.')); return FALSE; } // define target smtp server $port = 25; if (isset($context['mail_server'])) { $server = $context['mail_server']; // use alternate port if required to do so if (preg_match('/^(.+):([0-9]+)$/', $server, $matches)) { $server = $matches[1]; $port = intval($matches[2]); } } // ensure that we can support tls communications if (isset($server) && !strncmp($server, 'ssl://', 6) && is_callable('extension_loaded') && !extension_loaded('openssl')) { logger::remember('shared/mailer.php: Load the OpenSSL extension to support secured transmissions to mail server ' . $server); return FALSE; } // go for POP authentication if (isset($server) && isset($context['mail_variant']) && $context['mail_variant'] == 'pop3') { // authenticate to a pop3 server if (isset($context['mail_account']) && isset($context['mail_password'])) { // select which port to use if (strncmp($server, 'ssl://', 6)) { $pop3_port = 110; } else { $pop3_port = 995; } // open a network connection if (!($handle = Safe::fsockopen($server, $pop3_port, $errno, $errstr, 10))) { if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: fsockopen:', $errstr . ' (' . $errno . ')', 'debug'); } Logger::remember('shared/mailer.php: ' . sprintf('Impossible to connect to %s', $server . ':' . $pop3_port)); return FALSE; } // ensure enough execution time Safe::set_time_limit(30); // get server banner if (($reply = fgets($handle)) === FALSE) { Logger::remember('shared/mailer.php: Impossible to get banner of ' . $server); fclose($handle); return FALSE; } if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: POP <-', $reply, 'debug'); } // expecting an OK if (strncmp($reply, '+OK', 3)) { Logger::remember('shared/mailer.php: Mail service is closed at ' . $server, $reply); fclose($handle); return FALSE; } // send user name $request = 'USER ' . $context['mail_account']; fputs($handle, $request . CRLF); if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: POP ->', $request, 'debug'); } // expecting an OK if (($reply = fgets($handle)) === FALSE) { Logger::remember('shared/mailer.php: No reply to USER command at ' . $server); fclose($handle); return FALSE; } if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: POP <-', $reply, 'debug'); } if (strncmp($reply, '+OK', 3)) { Logger::remember('shared/mailer.php: Unknown account ' . $context['mail_account'] . ' at ' . $server, $reply); fclose($handle); return FALSE; } // send password $request = 'PASS ' . $context['mail_password']; fputs($handle, $request . CRLF); if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: POP ->', $request, 'debug'); } // expecting an OK if (($reply = fgets($handle)) === FALSE) { Logger::remember('shared/mailer.php: No reply to PASS command at ' . $server); fclose($handle); return FALSE; } if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: POP <-', $reply, 'debug'); } if (strncmp($reply, '+OK', 3)) { Logger::remember('shared/mailer.php: Invalid password for account ' . $account . ' at ' . $server, $reply); fclose($handle); return FALSE; } // we just wanted to authenticate fclose($handle); } } // we manage directly the SMTP transaction if (isset($server) && isset($context['mail_variant']) && ($context['mail_variant'] == 'pop3' || $context['mail_variant'] == 'smtp')) { // open a network connection if (!($handle = Safe::fsockopen($server, $port, $errno, $errstr, 10))) { if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: fsockopen:', $errstr . ' (' . $errno . ')', 'debug'); } Logger::remember('shared/mailer.php: ' . sprintf('Impossible to connect to %s', $server . ':' . $port)); return FALSE; } // ensure enough execution time Safe::set_time_limit(30); // get server banner if (($response = Mailer::parse_response($handle, 220)) === FALSE) { Logger::remember('shared/mailer.php: Impossible to get banner of ' . $server); fclose($handle); return FALSE; } // provide our logical name if (strpos($response, 'ESMTP')) { $request = 'EHLO ' . $context['host_name']; } else { $request = 'HELO ' . $context['host_name']; } fputs($handle, $request . CRLF); if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: SMTP ->', $request, 'debug'); } // expecting a welcome message if (($response = Mailer::parse_response($handle, 250)) === FALSE) { Logger::remember('shared/mailer.php: Command EHLO has been rejected at ' . $server); fclose($handle); return FALSE; } // authenticate as per SMTP protocol extension if (isset($context['mail_account']) && isset($context['mail_password']) && preg_match('/^AUTH (.+)$/m', $response, $matches)) { // CRAM-MD5 -- the preferred method if (strpos($matches[1], 'CRAM-MD5') !== FALSE) { // get the challenge $request = 'AUTH CRAM-MD5'; fputs($handle, $request . CRLF); if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: SMTP ->', $request, 'debug'); } if (($response = Mailer::parse_response($handle, 334)) === FALSE) { Logger::remember('shared/mailer.php: Command AUTH has been rejected at ' . $server); fclose($handle); return FALSE; } $challenge = base64_decode($response); // from password to a 64 bytes block if (strlen($context['mail_password']) < 64) { $key = str_pad($context['mail_password'], 64, chr(0)); } elseif (strlen($context['mail_password']) > 64) { $key = str_pad(pack('H32', md5($context['mail_password'])), 64, chr(0)); } else { $key = $context['mail_password']; } // compute HMAC-MD5 $inner = $key ^ str_repeat(chr(0x36), 64); $outer = $key ^ str_repeat(chr(0x5c), 64); $digest = md5($outer . pack('H32', md5($inner . $challenge))); // answer the challenge $request = base64_encode($context['mail_account'] . ' ' . $digest); fputs($handle, $request . CRLF); if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: SMTP ->', $request, 'debug'); } // LOGIN } elseif (strpos($matches[1], 'LOGIN') !== FALSE) { $request = 'AUTH LOGIN'; fputs($handle, $request . CRLF); if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: SMTP ->', $request, 'debug'); } if (Mailer::parse_response($handle, 334) === FALSE) { Logger::remember('shared/mailer.php: Command AUTH has been rejected at ' . $server); fclose($handle); return FALSE; } $request = base64_encode($context['mail_account']); fputs($handle, $request . CRLF); if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: SMTP ->', $request, 'debug'); } if (Mailer::parse_response($handle, 334) === FALSE) { Logger::remember('shared/mailer.php: Command AUTH has been rejected at ' . $server); fclose($handle); return FALSE; } $request = base64_encode($context['mail_password']); fputs($handle, $request . CRLF); if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: SMTP ->', $request, 'debug'); } // PLAIN } elseif (strpos($matches[1], 'PLAIN') !== FALSE) { $request = 'AUTH PLAIN ' . base64_encode("" . $context['mail_account'] . "" . $context['mail_password']); fputs($handle, $request . CRLF); if ($context['debug_mail'] == 'Y') { Logger::remember('shared/mailer.php: SMTP ->', $request, 'debug'); } } // expecting an OK if (Mailer::parse_response($handle, 235) === FALSE) { Logger::remember('shared/mailer.php: Command AUTH has been rejected at ' . $server); fclose($handle); return FALSE; } } // ready to submit messages $context['mail_handle'] = $handle; return $handle; // rely on system settings and PHP } elseif (is_callable('mail')) { // set the SMTP server if ($server) { Safe::ini_set('SMTP', $server); } // set the SMTP sender if (isset($context['mail_from']) && $context['mail_from']) { Safe::ini_set('sendmail_from', $context['mail_from']); } // ready to submit messages $context['mail_handle'] = TRUE; return TRUE; } // no SMTP configuration return FALSE; }
/** * remember the last action for this user * * @param string the description of the last action * @param string the id of the item related to this update * @param boolean TRUE to not change the edit date of this anchor, default is FALSE * * @see shared/anchor.php */ function touch($action, $origin = NULL, $silently = FALSE) { global $context; // don't go further on import if (preg_match('/import$/i', $action)) { return; } // no item bound if (!isset($this->item['id'])) { return; } // sanity check if (!$origin) { logger::remember('users/user.php: unexpected NULL origin at touch()'); return; } // components of the query $query = array(); // append a reference to a new image to the description if ($action == 'image:create') { if (!Codes::check_embedded($this->item['description'], 'image', $origin)) { // the overlay may prevent embedding if (is_object($this->overlay) && !$this->overlay->should_embed_files()) { } else { // list has already started if (preg_match('/\\[image=[^\\]]+?\\]\\s*$/', $this->item['description'])) { $query[] = "description = '" . SQL::escape($this->item['description'] . ' [image=' . $origin . ']') . "'"; } else { $query[] = "description = '" . SQL::escape($this->item['description'] . "\n\n" . '[image=' . $origin . ']') . "'"; } } } // refresh stamp only if image update occurs within 6 hours after last edition if (SQL::strtotime($this->item['edit_date']) + 6 * 60 * 60 < time()) { $silently = TRUE; } // suppress a reference to an image that has been deleted } elseif ($action == 'image:delete') { // suppress reference in main description field $query[] = "description = '" . SQL::escape(Codes::delete_embedded($this->item['description'], 'image', $origin)) . "'"; // suppress references as icon and thumbnail as well include_once $context['path_to_root'] . 'images/images.php'; if ($image = Images::get($origin)) { if ($url = Images::get_icon_href($image)) { if ($this->item['avatar_url'] == $url) { $query[] = "avatar_url = ''"; } } if ($url = Images::get_thumbnail_href($image)) { if ($this->item['avatar_url'] == $url) { $query[] = "avatar_url = ''"; } } } // set an existing image as the user avatar } elseif ($action == 'image:set_as_avatar') { include_once $context['path_to_root'] . 'images/images.php'; if ($image = Images::get($origin)) { if ($url = Images::get_icon_href($image)) { $query[] = "avatar_url = '" . SQL::escape($url) . "'"; } } $silently = TRUE; // set an existing image as the user thumbnail } elseif ($action == 'image:set_as_thumbnail') { include_once $context['path_to_root'] . 'images/images.php'; if ($image = Images::get($origin)) { if ($url = Images::get_thumbnail_href($image)) { $query[] = "avatar_url = '" . SQL::escape($url) . "'"; } } $silently = TRUE; // append a new image } elseif ($action == 'image:set_as_both') { if (!Codes::check_embedded($this->item['description'], 'image', $origin)) { $query[] = "description = '" . SQL::escape($this->item['description'] . ' [image=' . $origin . ']') . "'"; } // do not remember minor changes $silently = TRUE; // add a reference to a location in the article description } elseif ($action == 'location:create') { if (!Codes::check_embedded($this->item['description'], 'location', $origin)) { $query[] = "description = '" . SQL::escape($this->item['description'] . ' [location=' . $origin . ']') . "'"; } // suppress a reference to a location that has been deleted } elseif ($action == 'location:delete') { $query[] = "description = '" . SQL::escape(Codes::delete_embedded($this->item['description'], 'location', $origin)) . "'"; // add a reference to a new table in the user description } elseif ($action == 'table:create') { if (!Codes::check_embedded($this->item['description'], 'table', $origin)) { $query[] = "description = '" . SQL::escape($this->item['description'] . ' [table=' . $origin . ']') . "'"; } // suppress a reference to a table that has been deleted } elseif ($action == 'table:delete') { $query[] = "description = '" . SQL::escape(Codes::delete_embedded($this->item['description'], 'table', $origin)) . "'"; } // stamp the update if (!$silently) { $query[] = "edit_name='" . SQL::escape(Surfer::get_name()) . "'," . "edit_id=" . SQL::escape(Surfer::get_id()) . "," . "edit_address='" . SQL::escape(Surfer::get_email_address()) . "'," . "edit_action='{$action}'," . "edit_date='" . SQL::escape(gmstrftime('%Y-%m-%d %H:%M:%S')) . "'"; } // clear the cache for users, even for minor updates (e.g., image deletion) Users::clear($this->item); // ensure we have a valid update query if (!@count($query)) { return; } // update the anchor user $query = "UPDATE " . SQL::table_name('users') . " SET " . implode(', ', $query) . " WHERE id = " . SQL::escape($this->item['id']); SQL::query($query, FALSE, $context['users_connection']); }
/** * post a new user profile * * @param array an array of fields * @return the id of the new user profile, or FALSE on error * * @see control/populate.php * @see users/edit.php * @see users/populate.php * @see query.php **/ public static function post(&$fields) { global $context; // nick_name is required if (!isset($fields['nick_name']) || !trim($fields['nick_name'])) { Logger::error(i18n::s('Please indicate a nick name.')); return FALSE; } // some weird users put spaces around $fields['nick_name'] = trim($fields['nick_name']); // names used on shadow records are quite long (eg, tom@foo.bar.com) if (preg_match('/^(.+)@(.+)$/', $fields['nick_name'], $matches)) { // if short name is free if (!Users::get($matches[1])) { // use it instead (eg, tom) $fields['nick_name'] = $matches[1]; } } // nickname may be already used if (Users::get($fields['nick_name'])) { Logger::error(i18n::s('Another member already has this nick name. Please select a different one.')); return FALSE; } // ensure we have a full name if (!isset($fields['full_name']) || !trim($fields['full_name'])) { $fields['full_name'] = $fields['nick_name']; } // password is required if (!isset($fields['password']) || !trim($fields['password'])) { Logger::error(i18n::s('Please indicate a password.')); return FALSE; } // hash password if coming from a human facing a form if (isset($fields['confirm']) && $fields['confirm'] == $fields['password']) { $fields['password'] = md5($fields['password']); } // open community, accept subscribers and members if (!isset($fields['capability']) || !in_array($fields['capability'], array('A', 'M', 'S', '?'))) { $fields['capability'] = 'M'; } // control user capability if (!Surfer::is_associate()) { // closed community, accept only subscribers if (isset($context['users_with_approved_members']) && $context['users_with_approved_members'] == 'Y') { $fields['capability'] = 'S'; } elseif (isset($context['users_with_email_validation']) && $context['users_with_email_validation'] == 'Y') { $fields['capability'] = 'S'; } } // remember who is changing this record Surfer::check_default_editor($fields); // save new settings in session and in cookie if (isset($fields['id']) && Surfer::is($fields['id'])) { // change preferred editor $_SESSION['surfer_editor'] = $fields['editor']; Safe::setcookie('surfer_editor', $fields['editor'], NULL, '/'); // change preferred language if (isset($fields['language']) && $_SESSION['surfer_language'] != $fields['language']) { $_SESSION['surfer_language'] = $fields['language']; $_SESSION['l10n_modules'] = array(); } } // fields to update $query = array(); // on import if (isset($fields['id'])) { $query[] = "id=" . SQL::escape($fields['id']); } if (!isset($fields['active']) || !trim($fields['active'])) { $fields['active'] = 'Y'; } $query[] = "active='" . SQL::escape($fields['active']) . "'"; $query[] = "aim_address='" . SQL::escape(isset($fields['aim_address']) ? $fields['aim_address'] : '') . "'"; $query[] = "alternate_number='" . SQL::escape(isset($fields['alternate_number']) ? $fields['alternate_number'] : '') . "'"; // protect from hackers if (isset($fields['avatar_url'])) { $fields['avatar_url'] = encode_link($fields['avatar_url']); } $query[] = "avatar_url='" . SQL::escape(isset($fields['avatar_url']) ? $fields['avatar_url'] : '') . "'"; if (!isset($fields['birth_date']) || !$fields['birth_date']) { $fields['birth_date'] = NULL_DATE; } $query[] = "birth_date='" . SQL::escape($fields['birth_date']) . "'"; $query[] = "capability='" . SQL::escape($fields['capability']) . "'"; $query[] = "create_name='" . SQL::escape(isset($fields['create_name']) ? $fields['create_name'] : $fields['edit_name']) . "'"; if (isset($fields['create_id']) || $fields['edit_id']) { $query[] = "create_id=" . SQL::escape(isset($fields['create_id']) ? $fields['create_id'] : $fields['edit_id']); } $query[] = "create_address='" . SQL::escape(isset($fields['create_address']) ? $fields['create_address'] : $fields['edit_address']) . "'"; if (!isset($fields['create_date']) || $fields['create_date'] <= NULL_DATE) { $fields['create_date'] = $fields['edit_date']; } $query[] = "create_date='" . SQL::escape($fields['create_date']) . "'"; $query[] = "description='" . SQL::escape(isset($fields['description']) ? $fields['description'] : '') . "'"; $query[] = "edit_name='" . SQL::escape($fields['edit_name']) . "'"; $query[] = "edit_id=" . SQL::escape($fields['edit_id']); $query[] = "edit_address='" . SQL::escape($fields['edit_address']) . "'"; $query[] = "edit_action='" . SQL::escape(isset($fields['edit_action']) ? $fields['edit_action'] : 'new') . "'"; $query[] = "edit_date='" . SQL::escape($fields['edit_date']) . "'"; if (isset($fields['selected_editor'])) { $fields['editor'] = $fields['selected_editor']; } elseif (isset($context['users_default_editor'])) { $fields['editor'] = $context['users_default_editor']; } else { $fields['editor'] = 'yacs'; } $query[] = "editor='" . SQL::escape($fields['editor']) . "'"; $query[] = "email='" . SQL::escape(isset($fields['email']) ? $fields['email'] : '') . "'"; $query[] = "from_where='" . SQL::escape(isset($fields['from_where']) ? $fields['from_where'] : '') . "'"; $query[] = "full_name='" . SQL::escape(isset($fields['full_name']) ? $fields['full_name'] : '') . "'"; // always create a handle for this user $fields['handle'] = md5(rand()); $query[] = "handle='" . SQL::escape($fields['handle']) . "'"; $query[] = "icq_address='" . SQL::escape(isset($fields['icq_address']) ? $fields['icq_address'] : '') . "'"; if (!isset($fields['interface']) || $fields['interface'] != 'C') { $fields['interface'] = 'I'; } $query[] = "interface='" . SQL::escape($fields['interface']) . "'"; $query[] = "introduction='" . SQL::escape(isset($fields['introduction']) ? $fields['introduction'] : '') . "'"; $query[] = "irc_address='" . SQL::escape(isset($fields['irc_address']) ? $fields['irc_address'] : '') . "'"; $query[] = "jabber_address='" . SQL::escape(isset($fields['jabber_address']) ? $fields['jabber_address'] : '') . "'"; $query[] = "language='" . SQL::escape(isset($fields['language']) ? $fields['language'] : 'none') . "'"; $query[] = "msn_address='" . SQL::escape(isset($fields['msn_address']) ? $fields['msn_address'] : '') . "'"; $query[] = "nick_name='" . SQL::escape($fields['nick_name']) . "'"; $query[] = "options='" . SQL::escape(isset($fields['options']) ? $fields['options'] : '') . "'"; $query[] = "overlay='" . SQL::escape(isset($fields['overlay']) ? $fields['overlay'] : '') . "'"; $query[] = "overlay_id='" . SQL::escape(isset($fields['overlay_id']) ? $fields['overlay_id'] : '') . "'"; $query[] = "password='******'password']) ? $fields['password'] : '') . "'"; $query[] = "pgp_key='" . SQL::escape(isset($fields['pgp_key']) ? $fields['pgp_key'] : '') . "'"; $query[] = "phone_number='" . SQL::escape(isset($fields['phone_number']) ? $fields['phone_number'] : '') . "'"; if (!isset($fields['post_date']) || $fields['post_date'] <= NULL_DATE) { $fields['post_date'] = $fields['edit_date']; } $query[] = "post_date='" . SQL::escape($fields['post_date']) . "'"; $query[] = "posts=" . SQL::escape(isset($fields['posts']) ? $fields['posts'] : '0'); $query[] = "signature='" . SQL::escape(isset($fields['signature']) ? $fields['signature'] : '') . "'"; $query[] = "skype_address='" . SQL::escape(isset($fields['skype_address']) ? $fields['skype_address'] : '') . "'"; // clean provided tags if (isset($fields['tags'])) { $fields['tags'] = trim($fields['tags'], " \t.:,!?"); } $query[] = "tags='" . SQL::escape(isset($fields['tags']) ? $fields['tags'] : '') . "'"; $query[] = "twitter_address='" . SQL::escape(isset($fields['twitter_address']) ? $fields['twitter_address'] : '') . "'"; $query[] = "vcard_agent='" . SQL::escape(isset($fields['vcard_agent']) ? $fields['vcard_agent'] : '') . "'"; $query[] = "vcard_label='" . SQL::escape(isset($fields['vcard_label']) ? $fields['vcard_label'] : '') . "'"; $query[] = "vcard_organization='" . SQL::escape(isset($fields['vcard_organization']) ? $fields['vcard_organization'] : '') . "'"; $query[] = "vcard_title='" . SQL::escape(isset($fields['vcard_title']) ? $fields['vcard_title'] : '') . "'"; $query[] = "web_address='" . SQL::escape(isset($fields['web_address']) ? $fields['web_address'] : '') . "'"; if (!isset($fields['with_newsletters']) || $fields['with_newsletters'] != 'N') { $fields['with_newsletters'] = 'Y'; } $query[] = "with_newsletters='" . $fields['with_newsletters'] . "'"; if (!isset($fields['without_alerts']) || $fields['without_alerts'] != 'Y') { $fields['without_alerts'] = 'N'; } $query[] = "without_alerts='" . $fields['without_alerts'] . "'"; if (!isset($fields['without_confirmations']) || $fields['without_confirmations'] != 'Y') { $fields['without_confirmations'] = 'N'; } $query[] = "without_confirmations='" . $fields['without_confirmations'] . "'"; if (!isset($fields['without_messages']) || $fields['without_messages'] != 'Y') { $fields['without_messages'] = 'N'; } $query[] = "without_messages='" . $fields['without_messages'] . "'"; $query[] = "yahoo_address='" . SQL::escape(isset($fields['yahoo_address']) ? $fields['yahoo_address'] : '') . "'"; // insert statement $query = "INSERT INTO " . SQL::table_name('users') . " SET " . implode(', ', $query); // actual insert if (SQL::query($query, FALSE, $context['users_connection']) === FALSE) { return FALSE; } // remember the id of the new item if (!($fields['id'] = SQL::get_last_id($context['users_connection']))) { logger::remember('users/users.php: unable to retrieve id of new record'); return FALSE; } // list the user in categories Categories::remember('user:'******'id'], NULL_DATE, isset($fields['tags']) ? $fields['tags'] : ''); // clear the cache for users Users::clear($fields); // send a confirmation message if (isset($fields['email']) && trim($fields['email']) && isset($context['with_email']) && $context['with_email'] == 'Y') { // message title $subject = sprintf(i18n::s('Your account at %s'), strip_tags($context['site_name'])); // top of the message $message = '<p>' . i18n::s('Welcome!') . '</p>' . '<p>' . sprintf(i18n::s('This message relates to your account at %s.'), '<a href="' . $context['url_to_home'] . $context['url_to_root'] . '">' . strip_tags($context['site_name']) . '</a>') . '</p>'; // mention nick name $message .= '<p>' . sprintf(i18n::s('Your nick name is %s'), $fields['nick_name']) . '</p>'; // direct link to login page --see users/login.php $link = $context['url_to_home'] . $context['url_to_root'] . Users::get_login_url('login', $fields['id'], rand(1000, 9999), $fields['handle']); $message .= '<p>' . i18n::s('Record this message and use the following link to authenticate to the site at any time:') . '</p>' . '<p><a href="' . $link . '">' . $link . '</a></p>'; // caution note $message .= '<p>' . i18n::s('Caution: This hyperlink contains your login credentials encrypted. Please be aware anyone who uses this link will have full access to your account.') . '</p>'; // confirmation link if (isset($context['users_with_email_validation']) && $context['users_with_email_validation'] == 'Y') { $message .= '<p>' . i18n::s('Click on the link below to activate your new account.') . '</p>'; // use the secret handle $link = $context['url_to_home'] . $context['url_to_root'] . Users::get_url($fields['handle'], 'validate'); $message .= '<p><a href="' . $link . '">' . $link . '</a></p>'; } // bottom of the message $message .= '<p>' . sprintf(i18n::s('On-line help is available at %s'), '<a href="' . $context['url_to_home'] . $context['url_to_root'] . 'help/' . '">' . $context['url_to_home'] . $context['url_to_root'] . 'help/' . '</a>') . '</p>' . '<p>' . sprintf(i18n::s('Thank you for your interest into %s.'), '<a href="' . $context['url_to_home'] . $context['url_to_root'] . '">' . strip_tags($context['site_name']) . '</a>') . '</p>'; // enable threading $headers = Mailer::set_thread('user:'******'id']); // post the confirmation message Mailer::notify(NULL, $fields['email'], $subject, $message, $headers); } // automatic login if (!Surfer::get_id() && is_callable(array('Surfer', 'set'))) { Surfer::set($fields, TRUE); } // return the id of the new item return $fields['id']; }
/** * look for a localized string * * @param array the array containing localized strings * @param string the label identifying string * @return string the localized string, if any */ public static function &lookup(&$strings, $name) { global $context; // match on hashed name if (($hash = i18n::hash($name)) && array_key_exists($hash, $strings)) { $text = $strings[$hash]; } else { // log information on development platform if ($context['with_debug'] == 'Y' && file_exists($context['path_to_root'] . 'parameters/switch.on')) { logger::remember('i18n/i18n.php: ' . $name . ' is not localized', '', 'debug'); } // degrade to provided string $text = $name; } // provide the localized string return $text; }