* @license GNU General Public License, version 2 (GPL-2.0) * * For full copyright and license information, please see * the docs/CREDITS.txt file. * */ /** * @ignore */ if (!defined('IN_PHPBB')) { exit; } /** * Messenger */ class messenger { var $msg, $extra_headers, $replyto, $from, $subject; var $addresses = array(); var $mail_priority = MAIL_NORMAL_PRIORITY; var $use_queue = true; /** @var \phpbb\template\template */ protected $template; var $eol = "\n"; /** * Constructor */ function messenger($use_queue = true) { global $config; $this->use_queue = (!$config['email_package_size']) ? false : $use_queue; $this->subject = ''; // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac) $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL; $this->eol = (!$this->eol) ? "\n" : $this->eol; } /** * Resets all the data (address, template file, etc etc) to default */ function reset() { $this->addresses = $this->extra_headers = array(); $this->msg = $this->replyto = $this->from = ''; $this->mail_priority = MAIL_NORMAL_PRIORITY; } /** * Set addresses for to/im as available * * @param array $user User row */ function set_addresses($user) { if (isset($user['user_email']) && $user['user_email']) { $this->to($user['user_email'], (isset($user['username']) ? $user['username'] : '')); } if (isset($user['user_jabber']) && $user['user_jabber']) { $this->im($user['user_jabber'], (isset($user['username']) ? $user['username'] : '')); } } /** * Sets an email address to send to */ function to($address, $realname = '') { global $config; if (!trim($address)) { return; } $pos = isset($this->addresses['to']) ? sizeof($this->addresses['to']) : 0; $this->addresses['to'][$pos]['email'] = trim($address); // If empty sendmail_path on windows, PHP changes the to line if (!$config['smtp_delivery'] && DIRECTORY_SEPARATOR == '\\') { $this->addresses['to'][$pos]['name'] = ''; } else { $this->addresses['to'][$pos]['name'] = trim($realname); } } /** * Sets an cc address to send to */ function cc($address, $realname = '') { if (!trim($address)) { return; } $pos = isset($this->addresses['cc']) ? sizeof($this->addresses['cc']) : 0; $this->addresses['cc'][$pos]['email'] = trim($address); $this->addresses['cc'][$pos]['name'] = trim($realname); } /** * Sets an bcc address to send to */ function bcc($address, $realname = '') { if (!trim($address)) { return; } $pos = isset($this->addresses['bcc']) ? sizeof($this->addresses['bcc']) : 0; $this->addresses['bcc'][$pos]['email'] = trim($address); $this->addresses['bcc'][$pos]['name'] = trim($realname); } /** * Sets a im contact to send to */ function im($address, $realname = '') { // IM-Addresses could be empty if (!trim($address)) { return; } $pos = isset($this->addresses['im']) ? sizeof($this->addresses['im']) : 0; $this->addresses['im'][$pos]['uid'] = trim($address); $this->addresses['im'][$pos]['name'] = trim($realname); } /** * Set the reply to address */ function replyto($address) { $this->replyto = trim($address); } /** * Set the from address */ function from($address) { $this->from = trim($address); } /** * set up subject for mail */ function subject($subject = '') { $this->subject = trim($subject); } /** * set up extra mail headers */ function headers($headers) { $this->extra_headers[] = trim($headers); } /** * Adds X-AntiAbuse headers * * @param array $config Configuration array * @param user $user A user object * * @return null */ function anti_abuse_headers($config, $user) { $this->headers('X-AntiAbuse: Board servername - ' . mail_encode($config['server_name'])); $this->headers('X-AntiAbuse: User_id - ' . $user->data['user_id']); $this->headers('X-AntiAbuse: Username - ' . mail_encode($user->data['username'])); $this->headers('X-AntiAbuse: User IP - ' . $user->ip); } /** * Set the email priority */ function set_mail_priority($priority = MAIL_NORMAL_PRIORITY) { $this->mail_priority = $priority; } /** * Set email template to use */ function template($template_file, $template_lang = '', $template_path = '', $template_dir_prefix = '') { global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager; $template_dir_prefix = (!$template_dir_prefix || $template_dir_prefix[0] === '/') ? $template_dir_prefix : '/' . $template_dir_prefix; $this->setup_template(); if (!trim($template_file)) { trigger_error('No template file for emailing set.', E_USER_ERROR); } if (!trim($template_lang)) { // fall back to board default language if the user's language is // missing $template_file. If this does not exist either, // $this->template->set_filenames will do a trigger_error $template_lang = basename($config['default_lang']); } if ($template_path) { $template_paths = array( $template_path . $template_dir_prefix, ); } else { $template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; $template_path .= $template_lang . '/email'; $template_paths = array( $template_path . $template_dir_prefix, ); // we can only specify default language fallback when the path is not a custom one for which we // do not know the default language alternative if ($template_lang !== basename($config['default_lang'])) { $fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/'; $fallback_template_path .= basename($config['default_lang']) . '/email'; $template_paths[] = $fallback_template_path . $template_dir_prefix; } } $this->set_template_paths(array( array( 'name' => $template_lang . '_email', 'ext_path' => 'language/' . $template_lang . '/email' . $template_dir_prefix, ), ), $template_paths); $this->template->set_filenames(array( 'body' => $template_file . '.txt', )); return true; } /** * assign variables to email template */ function assign_vars($vars) { $this->setup_template(); $this->template->assign_vars($vars); } function assign_block_vars($blockname, $vars) { $this->setup_template(); $this->template->assign_block_vars($blockname, $vars); } /** * Send the mail out to the recipients set previously in var $this->addresses */ function send($method = NOTIFY_EMAIL, $break = false) { global $config, $user; // We add some standard variables we always use, no need to specify them always $this->assign_vars(array( 'U_BOARD' => generate_board_url(), 'EMAIL_SIG' => str_replace('
', "\n", "-- \n" . htmlspecialchars_decode($config['board_email_sig'])), 'SITENAME' => htmlspecialchars_decode($config['sitename']), )); // Parse message through template $this->msg = trim($this->template->assign_display('body')); // Because we use \n for newlines in the body message we need to fix line encoding errors for those admins who uploaded email template files in the wrong encoding $this->msg = str_replace("\r\n", "\n", $this->msg); // We now try and pull a subject from the email body ... if it exists, // do this here because the subject may contain a variable $drop_header = ''; $match = array(); if (preg_match('#^(Subject:(.*?))$#m', $this->msg, $match)) { $this->subject = (trim($match[2]) != '') ? trim($match[2]) : (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']); $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#'); } else { $this->subject = (($this->subject != '') ? $this->subject : $user->lang['NO_EMAIL_SUBJECT']); } if ($drop_header) { $this->msg = trim(preg_replace('#' . $drop_header . '#s', '', $this->msg)); } if ($break) { return true; } switch ($method) { case NOTIFY_EMAIL: $result = $this->msg_email(); break; case NOTIFY_IM: $result = $this->msg_jabber(); break; case NOTIFY_BOTH: $result = $this->msg_email(); $this->msg_jabber(); break; } $this->reset(); return $result; } /** * Add error message to log */ function error($type, $msg) { global $user, $phpEx, $phpbb_root_path, $config, $request; // Session doesn't exist, create it if (!isset($user->session_id) || $user->session_id === '') { $user->session_begin(); } $calling_page = htmlspecialchars_decode($request->server('PHP_SELF')); $message = ''; switch ($type) { case 'EMAIL': $message = 'EMAIL/' . (($config['smtp_delivery']) ? 'SMTP' : 'PHP/' . $config['email_function_name'] . '()') . ''; break; default: $message = "$type"; break; } $message .= '
' . htmlspecialchars($calling_page) . '

' . $msg . '
'; add_log('critical', 'LOG_ERROR_' . $type, $message); } /** * Save to queue */ function save_queue() { global $config; if ($config['email_package_size'] && $this->use_queue && !empty($this->queue)) { $this->queue->save(); return; } } /** * Generates a valid message id to be used in emails * * @return string message id */ function generate_message_id() { global $config, $request; $domain = ($config['server_name']) ?: $request->server('SERVER_NAME', 'phpbb.generated'); return md5(unique_id(time())) . '@' . $domain; } /** * Return email header */ function build_header($to, $cc, $bcc) { global $config; // We could use keys here, but we won't do this for 3.0.x to retain backwards compatibility $headers = array(); $headers[] = 'From: ' . $this->from; if ($cc) { $headers[] = 'Cc: ' . $cc; } if ($bcc) { $headers[] = 'Bcc: ' . $bcc; } $headers[] = 'Reply-To: ' . $this->replyto; $headers[] = 'Return-Path: <' . $config['board_email'] . '>'; $headers[] = 'Sender: <' . $config['board_email'] . '>'; $headers[] = 'MIME-Version: 1.0'; $headers[] = 'Message-ID: <' . $this->generate_message_id() . '>'; $headers[] = 'Date: ' . date('r', time()); $headers[] = 'Content-Type: text/plain; charset=UTF-8'; // format=flowed $headers[] = 'Content-Transfer-Encoding: 8bit'; // 7bit $headers[] = 'X-Priority: ' . $this->mail_priority; $headers[] = 'X-MSMail-Priority: ' . (($this->mail_priority == MAIL_LOW_PRIORITY) ? 'Low' : (($this->mail_priority == MAIL_NORMAL_PRIORITY) ? 'Normal' : 'High')); $headers[] = 'X-Mailer: phpBB3'; $headers[] = 'X-MimeOLE: phpBB3'; $headers[] = 'X-phpBB-Origin: phpbb://' . str_replace(array('http://', 'https://'), array('', ''), generate_board_url()); if (sizeof($this->extra_headers)) { $headers = array_merge($headers, $this->extra_headers); } return $headers; } /** * Send out emails */ function msg_email() { global $config, $user; if (empty($config['email_enable'])) { return false; } // Addresses to send to? if (empty($this->addresses) || (empty($this->addresses['to']) && empty($this->addresses['cc']) && empty($this->addresses['bcc']))) { // Send was successful. ;) return true; } $use_queue = false; if ($config['email_package_size'] && $this->use_queue) { if (empty($this->queue)) { $this->queue = new queue(); $this->queue->init('email', $config['email_package_size']); } $use_queue = true; } $contact_name = htmlspecialchars_decode($config['board_contact_name']); $board_contact = (($contact_name !== '') ? '"' . mail_encode($contact_name) . '" ' : '') . '<' . $config['board_contact'] . '>'; if (empty($this->replyto)) { $this->replyto = $board_contact; } if (empty($this->from)) { $this->from = $board_contact; } $encode_eol = ($config['smtp_delivery']) ? "\r\n" : $this->eol; // Build to, cc and bcc strings $to = $cc = $bcc = ''; foreach ($this->addresses as $type => $address_ary) { if ($type == 'im') { continue; } foreach ($address_ary as $which_ary) { ${$type} .= ((${$type} != '') ? ', ' : '') . (($which_ary['name'] != '') ? mail_encode($which_ary['name'], $encode_eol) . ' <' . $which_ary['email'] . '>' : $which_ary['email']); } } // Build header $headers = $this->build_header($to, $cc, $bcc); // Send message ... if (!$use_queue) { $mail_to = ($to == '') ? 'undisclosed-recipients:;' : $to; $err_msg = ''; if ($config['smtp_delivery']) { $result = smtpmail($this->addresses, mail_encode($this->subject), wordwrap(utf8_wordwrap($this->msg), 997, "\n", true), $err_msg, $headers); } else { $result = phpbb_mail($mail_to, $this->subject, $this->msg, $headers, $this->eol, $err_msg); } if (!$result) { $this->error('EMAIL', $err_msg); return false; } } else { $this->queue->put('email', array( 'to' => $to, 'addresses' => $this->addresses, 'subject' => $this->subject, 'msg' => $this->msg, 'headers' => $headers) ); } return true; } /** * Send jabber message out */ function msg_jabber() { global $config, $db, $user, $phpbb_root_path, $phpEx; if (empty($config['jab_enable']) || empty($config['jab_host']) || empty($config['jab_username']) || empty($config['jab_password'])) { return false; } if (empty($this->addresses['im'])) { // Send was successful. ;) return true; } $use_queue = false; if ($config['jab_package_size'] && $this->use_queue) { if (empty($this->queue)) { $this->queue = new queue(); $this->queue->init('jabber', $config['jab_package_size']); } $use_queue = true; } $addresses = array(); foreach ($this->addresses['im'] as $type => $uid_ary) { $addresses[] = $uid_ary['uid']; } $addresses = array_unique($addresses); if (!$use_queue) { include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx); $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], htmlspecialchars_decode($config['jab_password']), $config['jab_use_ssl']); if (!$this->jabber->connect()) { $this->error('JABBER', $user->lang['ERR_JAB_CONNECT'] . '
' . $this->jabber->get_log()); return false; } if (!$this->jabber->login()) { $this->error('JABBER', $user->lang['ERR_JAB_AUTH'] . '
' . $this->jabber->get_log()); return false; } foreach ($addresses as $address) { $this->jabber->send_message($address, $this->msg, $this->subject); } $this->jabber->disconnect(); } else { $this->queue->put('jabber', array( 'addresses' => $addresses, 'subject' => $this->subject, 'msg' => $this->msg) ); } unset($addresses); return true; } /** * Setup template engine */ protected function setup_template() { global $config, $phpbb_path_helper, $user, $phpbb_extension_manager; if ($this->template instanceof \phpbb\template\template) { return; } $this->template = new \phpbb\template\twig\twig($phpbb_path_helper, $config, $user, new \phpbb\template\context(), $phpbb_extension_manager); } /** * Set template paths to load */ protected function set_template_paths($path_name, $paths) { $this->setup_template(); $this->template->set_custom_style($path_name, $paths); } } /** * handling email and jabber queue */ class queue { var $data = array(); var $queue_data = array(); var $package_size = 0; var $cache_file = ''; var $eol = "\n"; /** * constructor */ function queue() { global $phpEx, $phpbb_root_path; $this->data = array(); $this->cache_file = "{$phpbb_root_path}cache/queue.$phpEx"; // Determine EOL character (\n for UNIX, \r\n for Windows and \r for Mac) $this->eol = (!defined('PHP_EOL')) ? (($eol = strtolower(substr(PHP_OS, 0, 3))) == 'win') ? "\r\n" : (($eol == 'mac') ? "\r" : "\n") : PHP_EOL; $this->eol = (!$this->eol) ? "\n" : $this->eol; } /** * Init a queue object */ function init($object, $package_size) { $this->data[$object] = array(); $this->data[$object]['package_size'] = $package_size; $this->data[$object]['data'] = array(); } /** * Put object in queue */ function put($object, $scope) { $this->data[$object]['data'][] = $scope; } /** * Process queue * Using lock file */ function process() { global $db, $config, $phpEx, $phpbb_root_path, $user; $lock = new \phpbb\lock\flock($this->cache_file); $lock->acquire(); // avoid races, check file existence once $have_cache_file = file_exists($this->cache_file); if (!$have_cache_file || $config['last_queue_run'] > time() - $config['queue_interval']) { if (!$have_cache_file) { set_config('last_queue_run', time(), true); } $lock->release(); return; } set_config('last_queue_run', time(), true); include($this->cache_file); foreach ($this->queue_data as $object => $data_ary) { @set_time_limit(0); if (!isset($data_ary['package_size'])) { $data_ary['package_size'] = 0; } $package_size = $data_ary['package_size']; $num_items = (!$package_size || sizeof($data_ary['data']) < $package_size) ? sizeof($data_ary['data']) : $package_size; /* * This code is commented out because it causes problems on some web hosts. * The core problem is rather restrictive email sending limits. * This code is nly useful if you have no such restrictions from the * web host and the package size setting is wrong. // If the amount of emails to be sent is way more than package_size than we need to increase it to prevent backlogs... if (sizeof($data_ary['data']) > $package_size * 2.5) { $num_items = sizeof($data_ary['data']); } */ switch ($object) { case 'email': // Delete the email queued objects if mailing is disabled if (!$config['email_enable']) { unset($this->queue_data['email']); continue 2; } break; case 'jabber': if (!$config['jab_enable']) { unset($this->queue_data['jabber']); continue 2; } include_once($phpbb_root_path . 'includes/functions_jabber.' . $phpEx); $this->jabber = new jabber($config['jab_host'], $config['jab_port'], $config['jab_username'], htmlspecialchars_decode($config['jab_password']), $config['jab_use_ssl']); if (!$this->jabber->connect()) { $messenger = new messenger(); $messenger->error('JABBER', $user->lang['ERR_JAB_CONNECT']); continue 2; } if (!$this->jabber->login()) { $messenger = new messenger(); $messenger->error('JABBER', $user->lang['ERR_JAB_AUTH']); continue 2; } break; default: $lock->release(); return; } for ($i = 0; $i < $num_items; $i++) { // Make variables available... extract(array_shift($this->queue_data[$object]['data'])); switch ($object) { case 'email': $err_msg = ''; $to = (!$to) ? 'undisclosed-recipients:;' : $to; if ($config['smtp_delivery']) { $result = smtpmail($addresses, mail_encode($subject), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $err_msg, $headers); } else { $result = phpbb_mail($to, $subject, $msg, $headers, $this->eol, $err_msg); } if (!$result) { $messenger = new messenger(); $messenger->error('EMAIL', $err_msg); continue 2; } break; case 'jabber': foreach ($addresses as $address) { if ($this->jabber->send_message($address, $msg, $subject) === false) { $messenger = new messenger(); $messenger->error('JABBER', $this->jabber->get_log()); continue 3; } } break; } } // No more data for this object? Unset it if (!sizeof($this->queue_data[$object]['data'])) { unset($this->queue_data[$object]); } // Post-object processing switch ($object) { case 'jabber': // Hang about a couple of secs to ensure the messages are // handled, then disconnect $this->jabber->disconnect(); break; } } if (!sizeof($this->queue_data)) { @unlink($this->cache_file); } else { if ($fp = @fopen($this->cache_file, 'wb')) { fwrite($fp, "queue_data = unserialize(" . var_export(serialize($this->queue_data), true) . ");\n\n?>"); fclose($fp); if (function_exists('opcache_invalidate')) { @opcache_invalidate($this->cache_file); } phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE); } } $lock->release(); } /** * Save queue */ function save() { if (!sizeof($this->data)) { return; } $lock = new \phpbb\lock\flock($this->cache_file); $lock->acquire(); if (file_exists($this->cache_file)) { include($this->cache_file); foreach ($this->queue_data as $object => $data_ary) { if (isset($this->data[$object]) && sizeof($this->data[$object])) { $this->data[$object]['data'] = array_merge($data_ary['data'], $this->data[$object]['data']); } else { $this->data[$object]['data'] = $data_ary['data']; } } } if ($fp = @fopen($this->cache_file, 'w')) { fwrite($fp, "queue_data = unserialize(" . var_export(serialize($this->data), true) . ");\n\n?>"); fclose($fp); if (function_exists('opcache_invalidate')) { @opcache_invalidate($this->cache_file); } phpbb_chmod($this->cache_file, CHMOD_READ | CHMOD_WRITE); $this->data = array(); } $lock->release(); } } /** * Replacement or substitute for PHP's mail command */ function smtpmail($addresses, $subject, $message, &$err_msg, $headers = false) { global $config, $user; // Fix any bare linefeeds in the message to make it RFC821 Compliant. $message = preg_replace("#(?lang['NO_EMAIL_SUBJECT'])) ? $user->lang['NO_EMAIL_SUBJECT'] : 'No email subject specified'; return false; } if (trim($message) == '') { $err_msg = (isset($user->lang['NO_EMAIL_MESSAGE'])) ? $user->lang['NO_EMAIL_MESSAGE'] : 'Email message was blank'; return false; } $mail_rcpt = $mail_to = $mail_cc = array(); // Build correct addresses for RCPT TO command and the client side display (TO, CC) if (isset($addresses['to']) && sizeof($addresses['to'])) { foreach ($addresses['to'] as $which_ary) { $mail_to[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>'; $mail_rcpt['to'][] = '<' . trim($which_ary['email']) . '>'; } } if (isset($addresses['bcc']) && sizeof($addresses['bcc'])) { foreach ($addresses['bcc'] as $which_ary) { $mail_rcpt['bcc'][] = '<' . trim($which_ary['email']) . '>'; } } if (isset($addresses['cc']) && sizeof($addresses['cc'])) { foreach ($addresses['cc'] as $which_ary) { $mail_cc[] = ($which_ary['name'] != '') ? mail_encode(trim($which_ary['name'])) . ' <' . trim($which_ary['email']) . '>' : '<' . trim($which_ary['email']) . '>'; $mail_rcpt['cc'][] = '<' . trim($which_ary['email']) . '>'; } } $smtp = new smtp_class(); $errno = 0; $errstr = ''; $smtp->add_backtrace('Connecting to ' . $config['smtp_host'] . ':' . $config['smtp_port']); // Ok we have error checked as much as we can to this point let's get on it already. if (!class_exists('\phpbb\error_collector')) { global $phpbb_root_path, $phpEx; include($phpbb_root_path . 'includes/error_collector.' . $phpEx); } $collector = new \phpbb\error_collector; $collector->install(); $smtp->socket = fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 20); $collector->uninstall(); $error_contents = $collector->format_errors(); if (!$smtp->socket) { if ($errstr) { $errstr = utf8_convert_message($errstr); } $err_msg = (isset($user->lang['NO_CONNECT_TO_SMTP_HOST'])) ? sprintf($user->lang['NO_CONNECT_TO_SMTP_HOST'], $errno, $errstr) : "Could not connect to smtp host : $errno : $errstr"; $err_msg .= ($error_contents) ? '

' . htmlspecialchars($error_contents) : ''; return false; } // Wait for reply if ($err_msg = $smtp->server_parse('220', __LINE__)) { $smtp->close_session($err_msg); return false; } // Let me in. This function handles the complete authentication process if ($err_msg = $smtp->log_into_server($config['smtp_host'], $config['smtp_username'], htmlspecialchars_decode($config['smtp_password']), $config['smtp_auth_method'])) { $smtp->close_session($err_msg); return false; } // From this point onward most server response codes should be 250 // Specify who the mail is from.... $smtp->server_send('MAIL FROM:<' . $config['board_email'] . '>'); if ($err_msg = $smtp->server_parse('250', __LINE__)) { $smtp->close_session($err_msg); return false; } // Specify each user to send to and build to header. $to_header = implode(', ', $mail_to); $cc_header = implode(', ', $mail_cc); // Now tell the MTA to send the Message to the following people... [TO, BCC, CC] $rcpt = false; foreach ($mail_rcpt as $type => $mail_to_addresses) { foreach ($mail_to_addresses as $mail_to_address) { // Add an additional bit of error checking to the To field. if (preg_match('#[^ ]+\@[^ ]+#', $mail_to_address)) { $smtp->server_send("RCPT TO:$mail_to_address"); if ($err_msg = $smtp->server_parse('250', __LINE__)) { // We continue... if users are not resolved we do not care if ($smtp->numeric_response_code != 550) { $smtp->close_session($err_msg); return false; } } else { $rcpt = true; } } } } // We try to send messages even if a few people do not seem to have valid email addresses, but if no one has, we have to exit here. if (!$rcpt) { $user->session_begin(); $err_msg .= '

'; $err_msg .= (isset($user->lang['INVALID_EMAIL_LOG'])) ? sprintf($user->lang['INVALID_EMAIL_LOG'], htmlspecialchars($mail_to_address)) : '' . htmlspecialchars($mail_to_address) . ' possibly an invalid email address?'; $smtp->close_session($err_msg); return false; } // Ok now we tell the server we are ready to start sending data $smtp->server_send('DATA'); // This is the last response code we look for until the end of the message. if ($err_msg = $smtp->server_parse('354', __LINE__)) { $smtp->close_session($err_msg); return false; } // Send the Subject Line... $smtp->server_send("Subject: $subject"); // Now the To Header. $to_header = ($to_header == '') ? 'undisclosed-recipients:;' : $to_header; $smtp->server_send("To: $to_header"); // Now the CC Header. if ($cc_header != '') { $smtp->server_send("CC: $cc_header"); } // Now any custom headers.... if ($headers !== false) { $smtp->server_send("$headers\r\n"); } // Ok now we are ready for the message... $smtp->server_send($message); // Ok the all the ingredients are mixed in let's cook this puppy... $smtp->server_send('.'); if ($err_msg = $smtp->server_parse('250', __LINE__)) { $smtp->close_session($err_msg); return false; } // Now tell the server we are done and close the socket... $smtp->server_send('QUIT'); $smtp->close_session($err_msg); return true; } /** * SMTP Class * Auth Mechanisms originally taken from the AUTH Modules found within the PHP Extension and Application Repository (PEAR) * See docs/AUTHORS for more details */ class smtp_class { var $server_response = ''; var $socket = 0; protected $socket_tls = false; var $responses = array(); var $commands = array(); var $numeric_response_code = 0; var $backtrace = false; var $backtrace_log = array(); function smtp_class() { // Always create a backtrace for admins to identify SMTP problems $this->backtrace = true; $this->backtrace_log = array(); } /** * Add backtrace message for debugging */ function add_backtrace($message) { if ($this->backtrace) { $this->backtrace_log[] = utf8_htmlspecialchars($message); } } /** * Send command to smtp server */ function server_send($command, $private_info = false) { fputs($this->socket, $command . "\r\n"); (!$private_info) ? $this->add_backtrace("# $command") : $this->add_backtrace('# Omitting sensitive information'); // We could put additional code here } /** * We use the line to give the support people an indication at which command the error occurred */ function server_parse($response, $line) { global $user; $this->server_response = ''; $this->responses = array(); $this->numeric_response_code = 0; while (substr($this->server_response, 3, 1) != ' ') { if (!($this->server_response = fgets($this->socket, 256))) { return (isset($user->lang['NO_EMAIL_RESPONSE_CODE'])) ? $user->lang['NO_EMAIL_RESPONSE_CODE'] : 'Could not get mail server response codes'; } $this->responses[] = substr(rtrim($this->server_response), 4); $this->numeric_response_code = (int) substr($this->server_response, 0, 3); $this->add_backtrace("LINE: $line <- {$this->server_response}"); } if (!(substr($this->server_response, 0, 3) == $response)) { $this->numeric_response_code = (int) substr($this->server_response, 0, 3); return (isset($user->lang['EMAIL_SMTP_ERROR_RESPONSE'])) ? sprintf($user->lang['EMAIL_SMTP_ERROR_RESPONSE'], $line, $this->server_response) : "Ran into problems sending Mail at Line $line. Response: $this->server_response"; } return 0; } /** * Close session */ function close_session(&$err_msg) { fclose($this->socket); if ($this->backtrace) { $message = '

Backtrace

' . implode('
', $this->backtrace_log) . '

'; $err_msg .= $message; } } /** * Log into server and get possible auth codes if neccessary */ function log_into_server($hostname, $username, $password, $default_auth_method) { global $user; $err_msg = ''; // Here we try to determine the *real* hostname (reverse DNS entry preferrably) $local_host = $user->host; if (function_exists('php_uname')) { $local_host = php_uname('n'); // Able to resolve name to IP if (($addr = @gethostbyname($local_host)) !== $local_host) { // Able to resolve IP back to name if (($name = @gethostbyaddr($addr)) !== $addr) { $local_host = $name; } } } // If we are authenticating through pop-before-smtp, we // have to login ones before we get authenticated // NOTE: on some configurations the time between an update of the auth database takes so // long that the first email send does not work. This is not a biggie on a live board (only // the install mail will most likely fail) - but on a dynamic ip connection this might produce // severe problems and is not fixable! if ($default_auth_method == 'POP-BEFORE-SMTP' && $username && $password) { global $config; $errno = 0; $errstr = ''; $this->server_send("QUIT"); fclose($this->socket); $result = $this->pop_before_smtp($hostname, $username, $password); $username = $password = $default_auth_method = ''; // We need to close the previous session, else the server is not // able to get our ip for matching... if (!$this->socket = @fsockopen($config['smtp_host'], $config['smtp_port'], $errno, $errstr, 10)) { if ($errstr) { $errstr = utf8_convert_message($errstr); } $err_msg = (isset($user->lang['NO_CONNECT_TO_SMTP_HOST'])) ? sprintf($user->lang['NO_CONNECT_TO_SMTP_HOST'], $errno, $errstr) : "Could not connect to smtp host : $errno : $errstr"; return $err_msg; } // Wait for reply if ($err_msg = $this->server_parse('220', __LINE__)) { $this->close_session($err_msg); return $err_msg; } } $hello_result = $this->hello($local_host); if (!is_null($hello_result)) { return $hello_result; } // SMTP STARTTLS (RFC 3207) if (!$this->socket_tls) { $this->socket_tls = $this->starttls(); if ($this->socket_tls) { // Switched to TLS // RFC 3207: "The client MUST discard any knowledge obtained from the server, [...]" // So say hello again $hello_result = $this->hello($local_host); if (!is_null($hello_result)) { return $hello_result; } } } // If we are not authenticated yet, something might be wrong if no username and passwd passed if (!$username || !$password) { return false; } if (!isset($this->commands['AUTH'])) { return (isset($user->lang['SMTP_NO_AUTH_SUPPORT'])) ? $user->lang['SMTP_NO_AUTH_SUPPORT'] : 'SMTP server does not support authentication'; } // Get best authentication method $available_methods = explode(' ', $this->commands['AUTH']); // Define the auth ordering if the default auth method was not found $auth_methods = array('PLAIN', 'LOGIN', 'CRAM-MD5', 'DIGEST-MD5'); $method = ''; if (in_array($default_auth_method, $available_methods)) { $method = $default_auth_method; } else { foreach ($auth_methods as $_method) { if (in_array($_method, $available_methods)) { $method = $_method; break; } } } if (!$method) { return (isset($user->lang['NO_SUPPORTED_AUTH_METHODS'])) ? $user->lang['NO_SUPPORTED_AUTH_METHODS'] : 'No supported authentication methods'; } $method = strtolower(str_replace('-', '_', $method)); return $this->$method($username, $password); } /** * SMTP EHLO/HELO * * @return mixed Null if the authentication process is supposed to continue * False if already authenticated * Error message (string) otherwise */ protected function hello($hostname) { // Try EHLO first $this->server_send("EHLO $hostname"); if ($err_msg = $this->server_parse('250', __LINE__)) { // a 503 response code means that we're already authenticated if ($this->numeric_response_code == 503) { return false; } // If EHLO fails, we try HELO $this->server_send("HELO $hostname"); if ($err_msg = $this->server_parse('250', __LINE__)) { return ($this->numeric_response_code == 503) ? false : $err_msg; } } foreach ($this->responses as $response) { $response = explode(' ', $response); $response_code = $response[0]; unset($response[0]); $this->commands[$response_code] = implode(' ', $response); } } /** * SMTP STARTTLS (RFC 3207) * * @return bool Returns true if TLS was started * Otherwise false */ protected function starttls() { if (!function_exists('stream_socket_enable_crypto')) { return false; } if (!isset($this->commands['STARTTLS'])) { return false; } $this->server_send('STARTTLS'); if ($err_msg = $this->server_parse('220', __LINE__)) { return false; } $result = false; $stream_meta = stream_get_meta_data($this->socket); if (socket_set_blocking($this->socket, 1)) { $result = stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); socket_set_blocking($this->socket, (int) $stream_meta['blocked']); } return $result; } /** * Pop before smtp authentication */ function pop_before_smtp($hostname, $username, $password) { global $user; if (!$this->socket = @fsockopen($hostname, 110, $errno, $errstr, 10)) { if ($errstr) { $errstr = utf8_convert_message($errstr); } return (isset($user->lang['NO_CONNECT_TO_SMTP_HOST'])) ? sprintf($user->lang['NO_CONNECT_TO_SMTP_HOST'], $errno, $errstr) : "Could not connect to smtp host : $errno : $errstr"; } $this->server_send("USER $username", true); if ($err_msg = $this->server_parse('+OK', __LINE__)) { return $err_msg; } $this->server_send("PASS $password", true); if ($err_msg = $this->server_parse('+OK', __LINE__)) { return $err_msg; } $this->server_send('QUIT'); fclose($this->socket); return false; } /** * Plain authentication method */ function plain($username, $password) { $this->server_send('AUTH PLAIN'); if ($err_msg = $this->server_parse('334', __LINE__)) { return ($this->numeric_response_code == 503) ? false : $err_msg; } $base64_method_plain = base64_encode("\0" . $username . "\0" . $password); $this->server_send($base64_method_plain, true); if ($err_msg = $this->server_parse('235', __LINE__)) { return $err_msg; } return false; } /** * Login authentication method */ function login($username, $password) { $this->server_send('AUTH LOGIN'); if ($err_msg = $this->server_parse('334', __LINE__)) { return ($this->numeric_response_code == 503) ? false : $err_msg; } $this->server_send(base64_encode($username), true); if ($err_msg = $this->server_parse('334', __LINE__)) { return $err_msg; } $this->server_send(base64_encode($password), true); if ($err_msg = $this->server_parse('235', __LINE__)) { return $err_msg; } return false; } /** * cram_md5 authentication method */ function cram_md5($username, $password) { $this->server_send('AUTH CRAM-MD5'); if ($err_msg = $this->server_parse('334', __LINE__)) { return ($this->numeric_response_code == 503) ? false : $err_msg; } $md5_challenge = base64_decode($this->responses[0]); $password = (strlen($password) > 64) ? pack('H32', md5($password)) : ((strlen($password) < 64) ? str_pad($password, 64, chr(0)) : $password); $md5_digest = md5((substr($password, 0, 64) ^ str_repeat(chr(0x5C), 64)) . (pack('H32', md5((substr($password, 0, 64) ^ str_repeat(chr(0x36), 64)) . $md5_challenge)))); $base64_method_cram_md5 = base64_encode($username . ' ' . $md5_digest); $this->server_send($base64_method_cram_md5, true); if ($err_msg = $this->server_parse('235', __LINE__)) { return $err_msg; } return false; } /** * digest_md5 authentication method * A real pain in the *** */ function digest_md5($username, $password) { global $config, $user; $this->server_send('AUTH DIGEST-MD5'); if ($err_msg = $this->server_parse('334', __LINE__)) { return ($this->numeric_response_code == 503) ? false : $err_msg; } $md5_challenge = base64_decode($this->responses[0]); // Parse the md5 challenge - from AUTH_SASL (PEAR) $tokens = array(); while (preg_match('/^([a-z-]+)=("[^"]+(?host; } // Maxbuf if (empty($tokens['maxbuf'])) { $tokens['maxbuf'] = 65536; } // Required: nonce, algorithm if (empty($tokens['nonce']) || empty($tokens['algorithm'])) { $tokens = array(); } $md5_challenge = $tokens; if (!empty($md5_challenge)) { $str = ''; for ($i = 0; $i < 32; $i++) { $str .= chr(mt_rand(0, 255)); } $cnonce = base64_encode($str); $digest_uri = 'smtp/' . $config['smtp_host']; $auth_1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $username, $md5_challenge['realm'], $password))), $md5_challenge['nonce'], $cnonce); $auth_2 = 'AUTHENTICATE:' . $digest_uri; $response_value = md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($auth_1), $md5_challenge['nonce'], $cnonce, md5($auth_2))); $input_string = sprintf('username="%s",realm="%s",nonce="%s",cnonce="%s",nc="00000001",qop=auth,digest-uri="%s",response=%s,%d', $username, $md5_challenge['realm'], $md5_challenge['nonce'], $cnonce, $digest_uri, $response_value, $md5_challenge['maxbuf']); } else { return (isset($user->lang['INVALID_DIGEST_CHALLENGE'])) ? $user->lang['INVALID_DIGEST_CHALLENGE'] : 'Invalid digest challenge'; } $base64_method_digest_md5 = base64_encode($input_string); $this->server_send($base64_method_digest_md5, true); if ($err_msg = $this->server_parse('334', __LINE__)) { return $err_msg; } $this->server_send(' '); if ($err_msg = $this->server_parse('235', __LINE__)) { return $err_msg; } return false; } } /** * Encodes the given string for proper display in UTF-8. * * This version is using base64 encoded data. The downside of this * is if the mail client does not understand this encoding the user * is basically doomed with an unreadable subject. * * Please note that this version fully supports RFC 2045 section 6.8. * * @param string $eol End of line we are using (optional to be backwards compatible) */ function mail_encode($str, $eol = "\r\n") { // define start delimimter, end delimiter and spacer $start = "=?UTF-8?B?"; $end = "?="; $delimiter = "$eol "; // Maximum length is 75. $split_length *must* be a multiple of 4, but <= 75 - strlen($start . $delimiter . $end)!!! $split_length = 60; $encoded_str = base64_encode($str); // If encoded string meets the limits, we just return with the correct data. if (strlen($encoded_str) <= $split_length) { return $start . $encoded_str . $end; } // If there is only ASCII data, we just return what we want, correctly splitting the lines. if (strlen($str) === utf8_strlen($str)) { return $start . implode($end . $delimiter . $start, str_split($encoded_str, $split_length)) . $end; } // UTF-8 data, compose encoded lines $array = utf8_str_split($str); $str = ''; while (sizeof($array)) { $text = ''; while (sizeof($array) && intval((strlen($text . $array[0]) + 2) / 3) << 2 <= $split_length) { $text .= array_shift($array); } $str .= $start . base64_encode($text) . $end . $delimiter; } return substr($str, 0, -strlen($delimiter)); } /** * Wrapper for sending out emails with the PHP's mail function */ function phpbb_mail($to, $subject, $msg, $headers, $eol, &$err_msg) { global $config, $phpbb_root_path, $phpEx; // We use the EOL character for the OS here because the PHP mail function does not correctly transform line endings. On Windows SMTP is used (SMTP is \r\n), on UNIX a command is used... // Reference: http://bugs.php.net/bug.php?id=15841 $headers = implode($eol, $headers); if (!class_exists('\phpbb\error_collector')) { include($phpbb_root_path . 'includes/error_collector.' . $phpEx); } $collector = new \phpbb\error_collector; $collector->install(); // On some PHP Versions mail() *may* fail if there are newlines within the subject. // Newlines are used as a delimiter for lines in mail_encode() according to RFC 2045 section 6.8. // Because PHP can't decide what is wanted we revert back to the non-RFC-compliant way of separating by one space (Use '' as parameter to mail_encode() results in SPACE used) $result = $config['email_function_name']($to, mail_encode($subject, ''), wordwrap(utf8_wordwrap($msg), 997, "\n", true), $headers); $collector->uninstall(); $err_msg = $collector->format_errors(); return $result; } ify the CDs you currently have (in Expert mode only). Check the CD labels and highlight the boxes corresponding to the CDs you have available for installation. Click \"OK\" when you are ready to continue. Packages are sorted in groups corresponding to a particular use of your machine. The groups themselves are sorted into four sections: * \"Workstation\": if you plan to use your machine as a workstation, select one or more of the corresponding groups. * \"Development\": if the machine is to be used for programming, choose the desired group(s). * \"Server\": if the machine is intended to be a server, you will be able to select which of the most common services you wish to see installed on the machine. * \"Graphical Environment\": finally, this is where you will choose your preferred graphical environment. At least one must be selected if you want to have a graphical workstation! Moving the mouse cursor over a group name will display a short explanatory text about that group. You can check the \"Individual package selection\" box, which is useful if you are familiar with the packages being offered or if you want to have total control over what will be installed. If you started the installation in \"Update\" mode, you can unselect all groups to avoid installing any new package. This is useful for repairing or updating an existing system."), choosePackagesTree => __("Finally, depending on your choice of whether or not to select individual packages, you will be presented a tree containing all packages classified by groups and subgroups. While browsing the tree, you can select entire groups, subgroups, or individual packages. Whenever you select a package on the tree, a description appears on the right. When your selection is finished, click the \"Install\" button which will then launch the installation process. Depending on the speed of your hardware and the number of packages that need to be installed, it may take a while to complete the process. A time to complete estimate is displayed on the screen to help you gauge if there is sufficient time to enjoy a cup of coffee. !! If a server package has been selected either intentionally or because it was part of a whole group, you will be asked to confirm that you really want those servers to be installed. Under Mandrake Linux, any installed servers are started by default at boot time. Even if they are safe and have no known issues at the time the distribution was shipped, it may happen that security holes are discovered after this version of Mandrake Linux was finalized. If you do not know what a particular service is supposed to do or why it is being installed, then click \"No\". Clicking \"Yes\" will install the listed services and they will be started automatically by default. !! The \"Automatic dependencies\" option simply disables the warning dialog which appears whenever the installer automatically selects a package. This occurs because it has determined that it needs to satisfy a dependency with another package in order to successfully complete the installation. The tiny floppy disc icon at the bottom of the list allows to load the packages list chosen during a previous installation. Clicking on this icon will ask you to insert a floppy disk previously created at the end of another installation. See the second tip of last step on how to create such a floppy."), configureNetwork => __("If you wish to connect your computer to the Internet or to a local network, please choose the correct option. Please turn on your device before choosing the correct option to let DrakX detect it automatically. Mandrake Linux proposes the configuration of an Internet connection at installation time. Available connections are: traditional modem, ISDN modem, ADSL connection, cable modem, and finally a simple LAN connection (Ethernet). Here, we will not detail each configuration. Simply make sure that you have all the parameters from your Internet Service Provider or system administrator. You can consult the manual chapter about Internet connections for details about the configuration, or simply wait until your system is installed and use the program described there to configure your connection. If you wish to configure the network later after installation or if you have finished configuring your network connection, click \"Cancel\"."), configureServices => __("You may now choose which services you wish to start at boot time. Here are presented all the services available with the current installation. Review them carefully and uncheck those which are not always needed at boot time. You can get a short explanatory text about a service by selecting a specific service. However, if you are not sure whether a service is useful or not, it is safer to leave the default behavior. At this stage, be very careful if you intend to use your machine as a server: you will probably not want to start any services that you do not need. Please remember that several services can be dangerous if they are enabled on a server. In general, select only the services you really need."), configureTimezoneGMT => __("GNU/Linux manages time in GMT (Greenwich Mean Time) and translates it in local time according to the time zone you selected."), configureX => __("X (for X Window System) is the heart of the GNU/Linux graphical interface on which all the graphics environments (KDE, Gnome, AfterStep, WindowMaker...) bundled with Mandrake Linux rely. In this section, DrakX will try to configure X automatically. It is extremely rare for it to fail, unless the hardware is very old (or very new). If it succeeds, it will start X automatically with the best resolution possible depending on the size of the monitor. A window will then appear and ask you if you can see it. If you are doing an \"Expert\" install, you will enter the X configuration wizard. See the corresponding section of the manual for more information about this wizard. If you can see the message and answer \"Yes\", then DrakX will proceed to the next step. If you cannot see the message, it simply means that the configuration was wrong and the test will automatically end after 10 seconds, restoring the screen."), configureXmain => __("The first time you try the X configuration, you may not be very satisfied with its display (screen is too small, shifted left or right...). Hence, even if X starts up correctly, DrakX then asks you if the configuration suits you. It will also propose to change it by displaying a list of valid modes it could find, asking you to select one. As a last resort, if you still cannot get X to work, choose \"Change graphics card\", select \"Unlisted card\", and when prompted on which server you want, choose \"FBDev\". This is a failsafe option which works with any modern graphics card. Then choose \"Test again\" to be sure."), configureXxdm => __("Finally, you will be asked whether you want to see the graphical interface at boot. Note this question will be asked even if you chose not to test the configuration. Obviously, you want to answer \"No\" if your machine is to act as a server, or if you were not successful in getting the display configured."), createBootdisk => __("The Mandrake Linux CDROM has a built-in rescue mode. You can access it by booting from the CDROM, press the >>F1<< key at boot and type >>rescue<< at the prompt. But in case your computer cannot boot from the CDROM, you should come back to this step for help in at least two situations: * when installing the boot loader, DrakX will rewrite the boot sector (MBR) of your main disk (unless you are using another boot manager) so that you can start up with either Windows or GNU/Linux (assuming you have Windows in your system). If you need to reinstall Windows, the Microsoft install process will rewrite the boot sector, and then you will not be able to start GNU/Linux! * if a problem arises and you cannot start up GNU/Linux from the hard disk, this floppy disk will be the only means of starting up GNU/Linux. It contains a fair number of system tools for restoring a system, which has crashed due to a power failure, an unfortunate typing error, a typo in a password, or any other reason. When you click on this step, you will be asked to enter a disk inside the drive. The floppy disk you will insert must be empty or contain data which you do not need. You will not have to format it since DrakX will rewrite the whole disk."), doPartitionDisks => __("At this point you need to choose where on your hard drive to install your Mandrake Linux operating system. If your hard drive is empty or if an existing operating system is using all the space available, you will need to partition it. Basically, partitioning a hard drive consists of logically dividing it to create space to install your new Mandrake Linux system. Because the effects of the partitioning process are usually irreversible, partitioning can be intimidating and stressful if you are an inexperienced user. Fortunately, there is a wizard which simplifies this process. Before beginning, please consult the manual and take your time. If you are running the install in Expert mode, you will enter DiskDrake, the Mandrake Linux partitioning tool, which allows you to fine-tune your partitions. See the DiskDrake chapter of the manual. From the installation interface, you can use the wizards as described here by clicking the \"Wizard\" button of the dialog. If partitions have already been defined, either from a previous installation or from another partitioning tool, simply select those to install your Linux system. If partitions are not defined, you will need to create them using the wizard. Depending on your hard drive configuration, several options are available: * \"Use free space\": this option will simply lead to an automatic partitioning of your blank drive(s). You will not be prompted further. * \"Use existing partition\": the wizard has detected one or more existing Linux partitions on your hard drive. If you want to use them, choose this option. * \"Use the free space on the Windows partition\": if Microsoft Windows is installed on your hard drive and takes all the space available on it, you have to create free space for Linux data. To do that, you can delete your Microsoft Windows partition and data (see \"Erase entire disk\" or \"Expert mode\" solutions) or resize your Microsoft Windows partition. Resizing can be performed without the loss of any data. This solution is recommended if you want to use both Mandrake Linux and Microsoft Windows on same computer. Before choosing this option, please understand that after this procedure, the size of your Microsoft Windows partition will be smaller than at the present time. You will have less free space under Microsoft Windows to store your data or to install new software. * \"Erase entire disk\": if you want to delete all data and all partitions present on your hard drive and replace them with your new Mandrake Linux system, choose this option. Be careful with this solution because you will not be able to revert your choice after confirmation. !! If you choose this option, all data on your disk will be lost. !! * \"Remove Windows\": this will simply erase everything on the drive and begin fresh, partitioning everything from scratch. All data on your disk will be lost. !! If you choose this option, all data on your disk will be lost. !! * \"Expert mode\": choose this option if you want to manually partition your hard drive. Be careful - it is a powerful but dangerous choice. You can very easily lose all your data. Hence, do not choose this unless you know what you are doing."), exitInstall => __("There you are. Installation is now complete and your GNU/Linux system is ready to use. Just click \"OK\" to reboot the system. You can start GNU/Linux or Windows, whichever you prefer (if you are dual-booting), as soon as the computer has booted up again. The \"Advanced\" button (in Expert mode only) shows two more buttons to: * \"generate auto-install floppy\": to create an installation floppy disk which will automatically perform a whole installation without the help of an operator, similar to the installation you just configured. Note that two different options are available after clicking the button: * \"Replay\". This is a partially automated install as the partitioning step (and only this one) remains interactive. * \"Automated\". Fully automated install: the hard disk is completely rewritten, all data is lost. This feature is very handy when installing a great number of similar machines. See the Auto install section at our web site. * \"Save packages selection\"(*) : saves the packages selection as made previously. Then, when doing another installation, insert the floppy inside the driver and run the installation going to the help screen by pressing on the [F1] key, and by issuing >>linux defcfg=\"floppy\"<<. (*) You need a FAT-formatted floppy (to create one under GNU/Linux, type \"mformat a:\")"), formatPartitions => __("Any partitions that have been newly defined must be formatted for use (formatting means creating a file system). At this time, you may wish to reformat some already existing partitions to erase any data they contain. If you wish to do that, please select those partitions as well. Please note that it is not necessary to reformat all pre-existing partitions. You must reformat the partitions containing the operating system (such as \"/\", \"/usr\" or \"/var\") but you do not have to reformat partitions containing data that you wish to keep (typically \"/home\"). Please be careful when selecting partitions. After formatting, all data on the selected partitions will be deleted and you will not be able to recover any of them. Click on \"OK\" when you are ready to format partitions. Click on \"Cancel\" if you want to choose another partition for your new Mandrake Linux operating system installation. Click on \"Advanced\" if you wish to select partitions that will be checked for bad blocks on the disc."), installPackages => __("Your new Mandrake Linux operating system is currently being installed. Depending on the number of packages you will be installing and the speed of your computer, this operation could take from a few minutes to a significant amount of time. Please be patient."), license => __("Before continuing you should read carefully the terms of the license. It covers the whole Mandrake Linux distribution, and if you do not agree with all the terms in it, click on the \"Refuse\" button which will immediately terminate the installation. To continue with the installation, click the \"Accept\" button."), miscellaneous => __("At this point, it is time to choose the security level desired for the machine. As a rule of thumb, the more exposed the machine is, and the more the data stored in it is crucial, the higher the security level should be. However, a higher security level is generally obtained at the expenses of easiness of use. Refer to the MSEC chapter of the ``Reference Manual'' to get more information about the meaning of these levels. If you do not know what to choose, keep the default option."), partition_with_diskdrake => __("At this point, you need to choose what partition(s) will be used for the installation of your Mandrake Linux system. If partitions have been already defined, either from a previous installation of GNU/Linux or from another partitioning tool, you can use existing partitions. Otherwise hard drive partitions must be defined. To create partitions, you must first select a hard drive. You can select the disk for partitioning by clicking on \"hda\" for the first IDE drive, \"hdb\" for the second, \"sda\" for the first SCSI drive and so on. To partition the selected hard drive, you can use these options: * \"Clear all\": this option deletes all partitions on the selected hard drive. * \"Auto allocate\": this option allows you to automatically create Ext2 and swap partitions in free space of your hard drive. * \"Rescue partition table\": if your partition table is damaged, you can try to recover it using this option. Please be careful and remember that it can fail. * \"Undo\": use this option to cancel your changes. * \"Reload\": you can use this option if you wish to undo all changes and load your initial partitions table. * \"Wizard\": use this option if you wish to use a wizard to partition your hard drive. This is recommended if you do not have a good knowledge of partitioning. * \"Restore from floppy\": this option will allow you to restore a previously saved partition table from floppy disk. * \"Save to floppy\": saves the partition table to a floppy. Useful for later partition-table recovery if necessary. It is strongly recommended to perform this step. * \"Done\": when you have finished partitioning your hard drive, this will save your changes back to disc. Note: you can reach any option using the keyboard. Navigate through the partitions using [Tab] and [Up/Down] arrows. When a partition is selected, you can use: * Ctrl-c to create a new partition (when an empty partition is selected); * Ctrl-d to delete a partition; * Ctrl-m to set the mount point. If you are installing on a PPC machine, you will want to create a small HFS \"bootstrap\" partition of at least 1MB which will be used by the yaboot boot loader. If you opt to make the partition a bit larger, say 50MB, you may find it a useful place to store a spare kernel and ramdisk images for emergency boot situations."), resizeFATChoose => __("More than one Microsoft Windows partition has been detected on your hard drive. Please choose the one you want to resize in order to install your new Mandrake Linux operating system. Each partition is listed as follows: \"Linux name\", \"Windows name\" \"Capacity\". \"Linux name\" is structured: \"hard drive type\", \"hard drive number\", \"partition number\" (for example, \"hda1\"). \"Hard drive type\" is \"hd\" if your hard dive is an IDE hard drive and \"sd\" if it is a SCSI hard drive. \"Hard drive number\" is always a letter after \"hd\" or \"sd\". With IDE hard drives: * \"a\" means \"master hard drive on the primary IDE controller\", * \"b\" means \"slave hard drive on the primary IDE controller\", * \"c\" means \"master hard drive on the secondary IDE controller\", * \"d\" means \"slave hard drive on the secondary IDE controller\". With SCSI hard drives, an \"a\" means \"lowest SCSI ID\", a \"b\" means \"second lowest SCSI ID\", etc. \"Windows name\" is the letter of your hard drive under Windows (the first disk or partition is called \"C:\")."), resizeFATWait => __("Please be patient. This operation can take several minutes."), selectInstallClass => __("DrakX now needs to know if you want to perform a default (\"Recommended\") installation or if you want to have greater control (\"Expert\"). You also have the choice of performing a new install or an upgrade of an existing Mandrake Linux system. Clicking \"Install\" will completely wipe out the old system. Select \"Upgrade\" if you are upgrading or repairing an existing system. Please choose \"Install\" if there are no previous version of Mandrake Linux installed or if you wish to boot between various operating systems. Please choose \"Update\" if you wish to update or repair an already installed version of Mandrake Linux. Depending on your knowledge of GNU/Linux, please choose one of the following to install or update your Mandrake Linux operating system: * Recommended: choose this if you have never installed a GNU/Linux operating system. The installation will be very easy and you will only be asked a few questions. * Expert: if you have a good knowledge of GNU/Linux, you can choose this installation class. The expert installation will allow you to perform a highly customized installation. Answering some of the questions can be difficult if you do not have a good knowledge of GNU/Linux so do not choose this unless you know what you are doing."), selectKeyboard => __("Normally, DrakX selects the right keyboard for you (depending on the language you have chosen) and you will not even see this step. However, you might not have a keyboard that corresponds exactly to your language: for example, if you are an English speaking Swiss person, you may still want your keyboard to be a Swiss keyboard. Or if you speak English but are located in Quebec, you may find yourself in the same situation. In both cases, you will have to go back to this installation step and select an appropriate keyboard from the list. Click on the \"More\" button to be presented with the complete list of supported keyboards."), selectLanguage => __("Please choose your preferred language for installation and system usage. Clicking on the \"Advanced\" button will allow you to select other languages to be installed on your workstation. Selecting other languages will install the language-specific files for system documentation and applications. For example, if you will host users from Spain on your machine, select English as the main language in the tree view and in the Advanced section click on the grey star corresponding to \"Spanish|Spain\". Note that multiple languages may be installed. Once you have selected any additional locales click the \"OK\" button to continue."), selectMouse => __("By default, DrakX assumes you have a two-button mouse and will set it up for third-button emulation. DrakX will automatically know whether it is a PS/2, serial or USB mouse. If you wish to specify a different type of mouse select the appropriate type from the list provided. If you choose a mouse other than the default you will be presented with a mouse test screen. Use the buttons and wheel to verify that the settings are good. If the mouse is not working correctly press the space bar or RETURN to \"Cancel\" and choose again."), selectSerialPort => __("Please select the correct port. For example, the COM1 port under MS Windows is named ttyS0 under GNU/Linux."), setRootPassword => __("This is the most crucial decision point for the security of your GNU/Linux system: you have to enter the \"root\" password. \"root\" is the system administrator and is the only one authorized to make updates, add users, change the overall system configuration, and so on. In short, \"root\" can do everything! That is why you must choose a password that is difficult to guess - DrakX will tell you if it is too easy. As you can see, you can choose not to enter a password, but we strongly advise you against this if only for one reason: do not think that because you booted GNU/Linux that your other operating systems are safe from mistakes. Since \"root\" can overcome all limitations and unintentionally erase all data on partitions by carelessly accessing the partitions themselves, it is important for it to be difficult to become \"root\". The password should be a mixture of alphanumeric characters and at least 8 characters long. Never write down the \"root\" password - it makes it too easy to compromise a system. However, please do not make the password too long or complicated because you must be able to remember it without too much effort. The password will not be displayed on screen as you type it in. Hence, you will have to type the password twice to reduce the chance of a typing error. If you do happen to make the same typing error twice, this \"incorrect\" password will have to be used the first time you connect. In expert mode, you will be asked if you will be connecting to an authentication server, like NIS or LDAP. If your network uses LDAP (or NIS) protocol for authentication, select \"LDAP\" (or \"NIS\") as authentication. If you do not know, ask your network administrator. If your computer is not connected to any administrated network, you will want to choose \"Local files\" for authentication."), setupBootloader => __("LILO and GRUB are boot loaders for GNU/Linux. This stage, normally, is totally automated. In fact, DrakX analyzes the disk boot sector and acts accordingly, depending on what it finds here: * if Windows boot sector is found, it will replace it with a GRUB/LILO boot sector. Hence, you will be able to load either GNU/Linux or another OS; * if a GRUB or LILO boot sector is found, it will replace it with a new one; If in doubt, DrakX will display a dialog with various options. * \"Boot loader to use\": you have three choices: * \"LILO with graphical menu\": if you prefer LILO with its graphical interface. * \"GRUB\": if you prefer GRUB (text menu). * \"LILO with text menu\": if you prefer LILO with its text menu interface. * \"Boot device\": in most cases, you will not change the default (\"/dev/hda\"), but if you prefer, the boot loader can be installed on the second hard drive (\"/dev/hdb\"), or even on a floppy disk (\"/dev/fd0\"). * \"Delay before booting the default image\": when rebooting the computer, this is the delay granted to the user to choose - in the boot loader menu, another boot entry than the default one. !! Beware that if you choose not to install a boot loader (by selecting \"Cancel\" here), you must ensure that you have a way to boot your Mandrake Linux system! Also be sure you know what you do before changing any of the options. !! Clicking the \"Advanced\" button in this dialog will offer many advanced options, which are reserved to the expert user. Mandrake Linux installs its own boot loader, which will let you boot either GNU/Linux or any other operating systems which you have on your system. If there is another operating system installed on your machine, it will be automatically added to the boot menu. Here, you can choose to fine-tune the existing options. Double-clicking on an existing entry allows you to change its parameters or remove it; \"Add\" creates a new entry; and \"Done\" goes on to the next installation step."), setupBootloaderAddEntry => __("LILO (the LInux LOader) and GRUB are boot loaders: they are able to boot either GNU/Linux or any other operating system present on your computer. Normally, these other operating systems are correctly detected and installed. If this is not the case, you can add an entry by hand in this screen. Be careful to choose the correct parameters. You may also not want to give access to these other operating systems to anyone. In which case, you can delete the corresponding entries. But then, you will need a boot disk in order to boot those other operating systems!"), setupBootloaderBeginner => __("You must indicate where you wish to place the information required to boot to GNU/Linux. Unless you know exactly what you are doing, choose \"First sector of drive (MBR)\"."), setupDefaultSpooler => __("Here we select a printing system for your computer to use. Other OSes may offer you one, but Mandrake offers three. * \"pdq\" - which means ``print, don't queue'', is the choice if you have a direct connection to your printer and you want to be able to panic out of printer jams, and you do not have any networked printers. It will handle only very simple network cases and is somewhat slow for networks. Pick \"pdq\" if this is your maiden voyage to GNU/Linux. You can change your choices after install by running PrinterDrake from the Mandrake Control Center and clicking the expert button. * \"CUPS\" - ``Common Unix Printing System'' is excellent at printing to your local printer and also halfway round the planet. It is simple and can act like a server or a client for the ancient \"lpd\" printing system, so it is compatible with the systems that went before. It can do many tricks, but the basic setup is almost as easy as \"pdq\". If you need this to emulate an \"lpd\" server, you must turn on the \"cups-lpd\" daemon. It has graphical front-ends for printing or choosing printer options. * \"lprNG\" - ``line printer daemon New Generation''. This system can do approximately the same things the others can do, but it will print to printers mounted on a Novell Network, because it supports IPX protocol, and it can print directly to shell commands. If you have need of Novell or printing to commands without using a separate pipe construct, use lprNG. Otherwise, CUPS is preferable as it is simpler and better at working over networks."), setupSCSI => __("DrakX is now detecting any IDE devices present in your computer. It will also scan for one or more PCI SCSI card(s) on your system. If a SCSI card is found, DrakX will automatically install the appropriate driver. Because hardware detection will sometimes not detect a piece of hardware, DrakX will ask you to confirm if a PCI SCSI card is present. Click \"Yes\" if you know that there is a SCSI card installed in your machine. You will be presented a list of SCSI cards to choose from. Click \"No\" if you have no SCSI hardware. If you are unsure you can check the list of hardware detected in your machine by selecting \"See hardware info\" and clicking \"OK\". Examine the list of hardware and then click on the \"OK\" button to return to the SCSI interface question. If you have to manually specify your adapter, DrakX will ask if you want to specify options for it. You should allow DrakX to probe the hardware for the card-specific options that the hardware needs to initialize. This usually works well. If DrakX is not able to probe for the options that need to be passed, you will need to manually provide options to the driver. Please review the ``User Guide'' (chapter 3, section \"Collecting Information on Your Hardware\") for hints on retrieving the parameters required from hardware documentation, from the manufacturer's web site (if you have Internet access) or from Microsoft Windows (if you used this hardware with Windows on your system)."), setupYabootAddEntry => __("You can add additional entries for yaboot, either for other operating systems, alternate kernels, or for an emergency boot image. For other OS's, the entry consists only of a label and the root partition. For Linux, there are a few possible options: * Label: this is simply the name you will have to type at the yaboot prompt to select this boot option. * Image: this would be the name of the kernel to boot. Typically, vmlinux or a variation of vmlinux with an extension. * Root: the \"root\" device or \"/\" for your Linux installation. * Append: on Apple hardware, the kernel append option is used quite often to assist in initializing video hardware, or to enable keyboard mouse button emulation for the often lacking 2nd and 3rd mouse buttons on a stock Apple mouse. The following are some examples: video=aty128fb:vmode:17,cmode:32,mclk:71 adb_buttons=103,111 hda=autotune video=atyfb:vmode:12,cmode:24 adb_buttons=103,111 * Initrd: this option can be used either to load initial modules, before the boot device is available, or to load a ramdisk image for an emergency boot situation. * Initrd-size: the default ramdisk size is generally 4,096 bytes. If you need to allocate a large ramdisk, this option can be used. * Read-write: normally the \"root\" partition is initially brought up in read-only, to allow a file system check before the system becomes \"live\". Here, you can override this option. * NoVideo: should the Apple video hardware prove to be exceptionally problematic, you can select this option to boot in \"novideo\" mode, with native frame buffer support. * Default: selects this entry as being the default Linux selection, selectable by just pressing ENTER at the yaboot prompt. This entry will also be highlighted with a \"*\", if you press [Tab] to see the boot selections."), setupYabootGeneral => __("Yaboot is a boot loader for NewWorld MacIntosh hardware. It is able to boot either GNU/Linux, MacOS or MacOSX if present on your computer. Normally, these other operating systems are correctly detected and installed. If this is not the case, you can add an entry by hand in this screen. Be careful as to choose the correct parameters. Yaboot's main options are: * Init Message: a simple text message that is displayed before the boot prompt. * Boot Device: indicate where you want to place the information required to boot to GNU/Linux. Generally, you setup a bootstrap partition earlier to hold this information. * Open Firmware Delay: unlike LILO, there are two delays available with yaboot. The first delay is measured in seconds and at this point, you can choose between CD, OF boot, MacOS or Linux. * Kernel Boot Timeout: this timeout is similar to the LILO boot delay. After selecting Linux, you will have this delay in 0.1 second before your default kernel description is selected. * Enable CD Boot?: checking this option allows you to choose \"C\" for CD at the first boot prompt. * Enable OF Boot?: checking this option allows you to choose \"N\" for Open Firmware at the first boot prompt. * Default OS: you can select which OS will boot by default when the Open Firmware Delay expires."), summary => __("Here are presented various parameters concerning your machine. Depending on your installed hardware, you may - or not, see the following entries: * \"Mouse\": check the current mouse configuration and click on the button to change it if necessary. * \"Keyboard\": check the current keyboard map configuration and click on the button to change that if necessary. * \"Timezone\": DrakX, by default, guesses your time zone from the language you have chosen. But here again, as for the choice of a keyboard, you may not be in the country for which the chosen language should correspond.