Example #1
0
function send_reset_confirmation_request($name)
{
    global $sitename;
    $rs = safe_row('email, nonce', 'txp_users', "name = '" . doSlash($name) . "'");
    if ($rs) {
        extract($rs);
        $confirm = bin2hex(pack('H*', substr(md5($nonce), 0, 10)) . $name);
        $message = gTxt('greeting') . ' ' . $name . ',' . n . n . gTxt('password_reset_confirmation') . ': ' . n . hu . 'textpattern/index.php?confirm=' . $confirm;
        if (txpMail($email, "[{$sitename}] " . gTxt('password_reset_confirmation_request'), $message)) {
            return gTxt('password_reset_confirmation_request_sent');
        } else {
            return gTxt('could_not_mail');
        }
    } else {
        return gTxt('unknown_author', array('{name}' => htmlspecialchars($name)));
    }
}
function send_reset_confirmation_request($name)
{
    global $sitename;
    $rs = safe_row('email, nonce', 'txp_users', "name = '" . doSlash($name) . "'");
    if ($rs) {
        extract($rs);
        $confirm = bin2hex(pack('H*', substr(md5($nonce), 0, 10)) . $name);
        $message = gTxt('greeting') . ' ' . $name . ',' . n . n . gTxt('password_reset_confirmation') . ': ' . n . hu . 'textpattern/index.php?confirm=' . $confirm;
        if (txpMail($email, "[{$sitename}] " . gTxt('password_reset_confirmation_request'), $message)) {
            return gTxt('password_reset_confirmation_request_sent');
        } else {
            return array(gTxt('could_not_mail'), E_ERROR);
        }
    } else {
        // Though 'unknown_author' could be thrown, send generic 'request_sent' message
        // instead so that (non-)existence of account names are not leaked
        return gTxt('password_reset_confirmation_request_sent');
    }
}
Example #3
0
function mail_comment($message, $cname, $cemail, $cweb, $parentid, $discussid)
{
    global $sitename;
    $parentid = assert_int($parentid);
    $discussid = assert_int($discussid);
    $article = safe_row("Section, Posted, ID, url_title, AuthorID, Title", "textpattern", "ID = {$parentid}");
    extract($article);
    extract(safe_row("RealName, email", "txp_users", "name = '" . doSlash($AuthorID) . "'"));
    $evaluator =& get_comment_evaluator();
    $out = gTxt('greeting') . " {$RealName}," . n . n;
    $out .= str_replace('{title}', $Title, gTxt('comment_recorded')) . n;
    $out .= permlinkurl_id($parentid) . n;
    if (has_privs('discuss', $AuthorID)) {
        $out .= hu . 'textpattern/index.php?event=discuss&step=discuss_edit&discussid=' . $discussid . n;
    }
    $out .= gTxt('status') . ": " . $evaluator->get_result('text') . '. ' . implode(',', $evaluator->get_result_message()) . n;
    $out .= n;
    $out .= gTxt('comment_name') . ": {$cname}" . n;
    $out .= gTxt('comment_email') . ": {$cemail}" . n;
    $out .= gTxt('comment_web') . ": {$cweb}" . n;
    $out .= gTxt('comment_comment') . ": {$message}";
    $subject = strtr(gTxt('comment_received'), array('{site}' => $sitename, '{title}' => $Title));
    $success = txpMail($email, $subject, $out, $cemail);
}
Example #4
0
function send_new_password($NewPass, $themail, $name)
{
    global $txp_user, $sitename;
    $message = gTxt('greeting') . ' ' . $name . ',' . "\r\n" . gTxt('your_password_is') . ': ' . $NewPass . "\r\n" . "\r\n" . gTxt('log_in_at') . ' ' . hu . 'textpattern/index.php';
    return txpMail($themail, "[{$sitename}] " . gTxt('your_new_password'), $message);
}
Example #5
0
function mail_comment($message, $cname, $cemail, $cweb, $parentid)
{
    global $sitename;
    extract(safe_row("AuthorID,Title", "textpattern", "ID = '{$parentid}'"));
    extract(safe_row("RealName, email", "txp_users", "name = '" . doSlash($AuthorID) . "'"));
    $out = gTxt('greeting') . " {$RealName},\r\n\r\n";
    $out .= str_replace('{title}', $Title, gTxt('comment_recorded') . "\r\n\r\n");
    $out .= gTxt('comment_name') . ": {$cname}\r\n";
    $out .= gTxt('comment_email') . ": {$cemail}\r\n";
    $out .= gTxt('comment_web') . ": {$cweb}\r\n";
    $out .= gTxt('comment_comment') . ": {$message}";
    $subject = strtr(gTxt('comment_received'), array('{site}' => $sitename, '{title}' => $Title));
    $success = txpMail($email, $subject, $out, $cemail);
}
/**
 * Sends a password reset link to a user's email address.
 *
 * This function will return a success message even when the specified user
 * doesn't exist. Though an error message could be thrown when a user isn't
 * found, this is done due to security, which prevents the function from
 * leaking existing account names.
 *
 * @param  string $name The login name
 * @return string A localized message string
 * @see    send_new_password()
 * @see    reset_author_pass()
 * @example
 * echo send_reset_confirmation_request('username');
 */
function send_reset_confirmation_request($name)
{
    global $sitename;
    $expiryTimestamp = time() + 60 * RESET_EXPIRY_MINUTES;
    $expiry = strftime('%Y-%m-%d %H:%M:%S', $expiryTimestamp);
    $rs = safe_query("SELECT\n            txp_users.user_id, txp_users.email,\n            txp_users.nonce, txp_users.pass,\n            txp_token.type\n        FROM " . safe_pfx('txp_users') . " txp_users\n        LEFT JOIN " . safe_pfx('txp_token') . " txp_token\n        ON txp_users.user_id = txp_token.reference_id\n        WHERE txp_users.name = '" . doSlash($name) . "'\n        AND TIMESTAMPDIFF(SECOND, txp_token.expires, '" . $expiry . "') > " . 60 * RESET_RATE_LIMIT_MINUTES . "\n        AND txp_token.type = 'password_reset'");
    $row = nextRow($rs);
    if ($row) {
        extract($row);
        $uid = assert_int($user_id);
        // The selector becomes an indirect reference to the txp_users row,
        // which does not leak information.
        $selector = Txp::get('\\Textpattern\\Password\\Random')->generate(12);
        $expiryYear = safe_strftime('%Y', $expiryTimestamp);
        $expiryMonth = safe_strftime('%B', $expiryTimestamp);
        $expiryDay = safe_strftime('%Oe', $expiryTimestamp);
        $expiryTime = safe_strftime('%H:%M', $expiryTimestamp);
        // Use a hash of the nonce, selector and password.
        // This ensures that confirmation requests expire automatically when:
        //  a) The person next logs in, or
        //  b) They successfully change their password (usually as a result of this reset request)
        // Using the selector in the hash just injects randomness, otherwise two requests
        // back-to-back would generate the same confirmation code.
        // Old requests for the same user id are purged every time a new request is made.
        $token = bin2hex(pack('H*', substr(hash(HASHING_ALGORITHM, $nonce . $selector . $pass), 0, SALT_LENGTH)));
        $confirm = $token . $selector;
        // Remove any previous reset tokens and insert the new one.
        safe_delete("txp_token", "reference_id = {$uid} AND type = 'password_reset'");
        safe_insert("txp_token", "reference_id = {$uid},\n                type = 'password_reset',\n                selector = '" . doSlash($selector) . "',\n                token = '" . doSlash($token) . "',\n                expires = '" . doSlash($expiry) . "'\n            ");
        $message = gTxt('salutation', array('{name}' => $name)) . n . n . gTxt('password_reset_confirmation') . n . hu . 'textpattern/index.php?confirm=' . $confirm . n . n . gTxt('link_expires', array('{year}' => $expiryYear, '{month}' => $expiryMonth, '{day}' => $expiryDay, '{time}' => $expiryTime));
        if (txpMail($email, "[{$sitename}] " . gTxt('password_reset_confirmation_request'), $message)) {
            return gTxt('password_reset_confirmation_request_sent');
        } else {
            return array(gTxt('could_not_mail'), E_ERROR);
        }
    } else {
        // Though 'unknown_author' could be thrown, send generic 'request_sent'
        // message instead so that (non-)existence of account names are not leaked.
        // Since this is a short circuit, there's a possibility of a timing attack
        // revealing the existence of an account, which we could defend against
        // to some degree.
        return gTxt('password_reset_confirmation_request_sent');
    }
}
function send_customer_password($RealName, $name, $email, $password)
{
    global $sitename;
    $message = gTxt('greeting') . ' ' . $RealName . ',' . "\r\n" . "\r\n" . gTxt('you_have_been_registered') . ' ' . $sitename . "\r\n" . "\r\n" . gTxt('your_login_is') . ': ' . $name . "\r\n" . gTxt('your_password_is') . ': ' . $password . "\r\n";
    //"\r\n"."\r\n".gTxt('log_in_at').': '.hu.'textpattern/index.php';
    return txpMail($email, "[{$sitename}] " . gTxt('your_login_info'), $message);
}
Example #8
0
function mail_comment($message, $cname, $cemail, $cweb, $parentid, $discussid)
{
    global $sitename;
    $article = safe_row("Section, Posted, ID, url_title, AuthorID, Title", "textpattern", "ID = '{$parentid}'");
    extract($article);
    extract(safe_row("RealName, email", "txp_users", "name = '" . doSlash($AuthorID) . "'"));
    $out = gTxt('greeting') . " {$RealName},\r\n\r\n";
    $out .= str_replace('{title}', $Title, gTxt('comment_recorded')) . "\r\n";
    $out .= permlinkurl_id($parentid) . "\r\n";
    if (has_privs('discuss', $AuthorID)) {
        $out .= hu . 'textpattern/?event=discuss&step=discuss_edit&discussid=' . $discussid . "\r\n";
    }
    $out .= "\r\n";
    $out .= gTxt('comment_name') . ": {$cname}\r\n";
    $out .= gTxt('comment_email') . ": {$cemail}\r\n";
    $out .= gTxt('comment_web') . ": {$cweb}\r\n";
    $out .= gTxt('comment_comment') . ": {$message}";
    $subject = strtr(gTxt('comment_received'), array('{site}' => $sitename, '{title}' => $Title));
    $success = txpMail($email, $subject, $out, $cemail);
}
Example #9
0
/**
 * Validates the sent login form and creates a session.
 *
 * During the reset request procedure, it is conceivable to verify the
 * token as soon as it's presented in the URL, but that would:
 *  a) require refactoring code similarities in both p_confirm and p_alter branches
 *  b) require some way (e.g. an Exception) to signal back to doLoginForm() that
 *     the token is bogus so the 'change your password' form is not displayed.
 *  c) leak information about the validity of a token, thus allowing rapid brute-force
 *     attempts.
 *
 * The inconvenience of a real user following an expired token and being told so
 * after they've set a password is a small price to pay for the improved security
 * and reduction of attack surface that validating after submission affords.
 *
 * @todo  Could the checks be done via a (reusable) Validator()?
 *
 * @return string A localised feedback message
 * @see    doLoginForm()
 */
function doTxpValidate()
{
    global $logout, $txp_user;
    $p_userid = ps('p_userid');
    $p_password = ps('p_password');
    $p_reset = ps('p_reset');
    $p_alter = ps('p_alter');
    $p_set = ps('p_set');
    $stay = ps('stay');
    $p_confirm = gps('confirm');
    $logout = gps('logout');
    $message = '';
    $pub_path = preg_replace('|//$|', '/', rhu . '/');
    if (cs('txp_login') && strpos(cs('txp_login'), ',')) {
        $txp_login = explode(',', cs('txp_login'));
        $c_hash = end($txp_login);
        $c_userid = join(',', array_slice($txp_login, 0, -1));
    } else {
        $c_hash = '';
        $c_userid = '';
    }
    if ($logout) {
        setcookie('txp_login', '', time() - 3600);
        setcookie('txp_login_public', '', time() - 3600, $pub_path);
    }
    if ($c_userid && strlen($c_hash) === 32) {
        // Cookie exists.
        // @todo Improve security by using a better nonce/salt mechanism. md5 and uniqid are bad.
        $r = safe_row("name, nonce", 'txp_users', "name = '" . doSlash($c_userid) . "' AND last_access > DATE_SUB(NOW(), INTERVAL 30 DAY)");
        if ($r && $r['nonce'] && $r['nonce'] === md5($c_userid . pack('H*', $c_hash))) {
            // Cookie is good.
            if ($logout) {
                // Destroy nonce.
                safe_update('txp_users', "nonce = '" . doSlash(md5(uniqid(mt_rand(), true))) . "'", "name = '" . doSlash($c_userid) . "'");
            } else {
                // Create $txp_user.
                $txp_user = $r['name'];
            }
            return $message;
        } else {
            txp_status_header('401 Your session has expired');
            setcookie('txp_login', $c_userid, time() + 3600 * 24 * 365);
            setcookie('txp_login_public', '', time() - 3600, $pub_path);
            $message = array(gTxt('bad_cookie'), E_ERROR);
        }
    } elseif ($p_userid && $p_password) {
        // Incoming login vars.
        $name = txp_validate($p_userid, $p_password);
        if ($name !== false) {
            $c_hash = md5(uniqid(mt_rand(), true));
            $nonce = md5($name . pack('H*', $c_hash));
            safe_update('txp_users', "nonce = '" . doSlash($nonce) . "'", "name = '" . doSlash($name) . "'");
            setcookie('txp_login', $name . ',' . $c_hash, $stay ? time() + 3600 * 24 * 365 : 0, null, null, null, LOGIN_COOKIE_HTTP_ONLY);
            setcookie('txp_login_public', substr(md5($nonce), -10) . $name, $stay ? time() + 3600 * 24 * 30 : 0, $pub_path);
            // Login is good, create $txp_user.
            $txp_user = $name;
            return '';
        } else {
            sleep(3);
            txp_status_header('401 Could not log in with that username/password');
            $message = array(gTxt('could_not_log_in'), E_ERROR);
        }
    } elseif ($p_reset) {
        // Reset request.
        sleep(3);
        include_once txpath . '/lib/txplib_admin.php';
        $message = $p_userid ? send_reset_confirmation_request($p_userid) : '';
    } elseif ($p_alter || $p_set) {
        // Password change/set confirmation.
        sleep(3);
        global $sitename;
        $pass = ps('p_password');
        $type = $p_alter ? 'password_reset' : 'account_activation';
        if (trim($pass) === '') {
            $message = array(gTxt('password_required'), E_ERROR);
        } else {
            $hash = gps('hash');
            $selector = substr($hash, SALT_LENGTH);
            $tokenInfo = safe_row("reference_id, token, expires", 'txp_token', "selector = '" . doSlash($selector) . "' AND type='{$type}'");
            if ($tokenInfo) {
                if (strtotime($tokenInfo['expires']) <= time()) {
                    $message = array(gTxt('token_expired'), E_ERROR);
                } else {
                    $uid = assert_int($tokenInfo['reference_id']);
                    $row = safe_row("name, email, nonce, pass AS old_pass", 'txp_users', "user_id = {$uid}");
                    if ($row && $row['nonce'] && $hash === bin2hex(pack('H*', substr(hash(HASHING_ALGORITHM, $row['nonce'] . $selector . $row['old_pass']), 0, SALT_LENGTH))) . $selector) {
                        if (change_user_password($row['name'], $pass)) {
                            $body = gTxt('salutation', array('{name}' => $row['name'])) . n . n . ($p_alter ? gTxt('password_change_confirmation') : gTxt('password_set_confirmation') . n . n . gTxt('log_in_at') . ': ' . hu . 'textpattern/index.php');
                            $message = $p_alter ? gTxt('password_changed') : gTxt('password_set');
                            txpMail($row['email'], "[{$sitename}] " . $message, $body);
                            // Invalidate all tokens in the wild for this user.
                            safe_delete("txp_token", "reference_id = {$uid} AND type IN ('password_reset', 'account_activation')");
                        }
                    } else {
                        $message = array(gTxt('invalid_token'), E_ERROR);
                    }
                }
            } else {
                $message = array(gTxt('invalid_token'), E_ERROR);
            }
        }
    }
    $txp_user = '';
    return $message;
}
Example #10
0
 function clone_for_translation()
 {
     $has_privs = has_privs('l10n.clone');
     if (!$has_privs) {
         return;
     }
     # User cannot clone articles.
     $vars = array('rendition');
     extract(gpsa($vars));
     $rendition = (int) $rendition;
     $langs = MLPLanguageHandler::get_site_langs();
     $clone_to = array();
     foreach ($langs as $lang) {
         $clone = $lang === gps($lang);
         if ($clone) {
             $new_author = gps($lang . '-AuthorID');
             $clone_to[$lang] = $new_author;
         }
     }
     if (count($clone_to) < 1) {
         $this->parent->message = gTxt('l10n-no_langs_selected');
         $_POST['step'] = 'start_clone';
         return;
     }
     #
     #	Prepare the source rendition data...
     #
     $source = safe_row('*', 'textpattern', "`ID`={$rendition}");
     $article_id = (int) $source[L10N_COL_GROUP];
     #
     #	Create the articles, substituting new authors and status as needed...
     #
     $notify = array();
     #	For email notices.
     foreach ($clone_to as $lang => $new_author) {
         $rendition_id = $this->_clone_rendition($source, $article_id, $lang, $new_author);
         #	Now we know rendition & article IDs, store against author for email notification...
         $language = MLPLanguageHandler::get_native_name_of_lang($lang);
         $notify[$new_author][$lang] = array('id' => "{$rendition_id}", 'title' => $source['Title'], 'language' => $language);
     }
     #
     #	Send the notifications?
     #
     $send_notifications = '1' == $this->pref('l10n-send_notifications') ? true : false;
     $notify_self = '1' == $this->pref('l10n-send_notice_to_self') ? true : false;
     if ($send_notifications) {
         global $sitename, $siteurl, $txp_user;
         extract(safe_row('RealName AS txp_username,email AS replyto', 'txp_users', "name='{$txp_user}'"));
         foreach ($notify as $new_user => $list) {
             #
             #	Skip if no articles...
             #
             $count = count($list);
             if ($count < 1) {
                 continue;
             }
             #
             #	Skip if users are the same and no notifications are to be sent in that case...
             #
             $same = $new_user == $txp_user;
             if ($same and !$notify_self) {
                 continue;
             }
             #
             #	Construct a list of links to the renditions assigned to this user...
             #
             $links = array();
             foreach ($list as $lang => $record) {
                 extract($record);
                 $msg = gTxt('title') . ": \"{$title}\"\r\n";
                 $msg .= gTxt('l10n-xlate_to') . "{$language} [{$lang}].\r\n";
                 $msg .= "http://{$siteurl}/textpattern/index.php?event=article&step=edit&ID={$id}\r\n";
                 $links[] = $msg;
             }
             extract(safe_row('RealName AS new_user,email', 'txp_users', "name='{$new_user}'"));
             $s = $count === 1 ? '' : 's';
             $subs = array('{sitename}' => $sitename, '{count}' => $count, '{s}' => $s, '{txp_username}' => $txp_username);
             if ($same) {
                 $body = gTxt('l10n-email_body_self', $subs);
             } else {
                 $body = gTxt('l10n-email_body_other', $subs);
             }
             $body .= join("\r\n", $links) . "\r\n" . gTxt('l10n-email_end', $subs);
             $subject = gTxt('l10n-email_xfer_subject', $subs);
             @txpMail($email, $subject, $body, $replyto);
         }
     }
 }
Example #11
0
function _l10n_changeauthor_notify_routine()
{
    global $l10n_view;
    #	Permissions for email...
    $send_notifications = '1' == $l10n_view->pref('l10n-send_notifications') ? true : false;
    $on_changeauthor = '1' == $l10n_view->pref('l10n-send_notice_on_changeauthor') ? true : false;
    $notify_self = '1' == $l10n_view->pref('l10n-send_notice_to_self') ? true : false;
    if (!$send_notifications or !$on_changeauthor) {
        return false;
    }
    global $statuses, $sitename, $siteurl, $txp_user;
    $new_user = gps('AuthorID');
    $selected = gps('selected');
    $links = array();
    $same = $new_user == $txp_user;
    if (empty($new_user)) {
        return;
    }
    if (!$same or $notify_self) {
        if ($selected and !empty($selected)) {
            foreach ($selected as $id) {
                #
                #	Make a link to the article...
                #
                $row = safe_row('Title , ' . L10N_COL_LANG . ' , `' . L10N_COL_GROUP . '` , Status', 'textpattern', "`ID`='{$id}'");
                extract($row);
                $lang = MLPLanguageHandler::get_native_name_of_lang($row[L10N_COL_LANG]);
                $status = $statuses[$Status];
                $msg = gTxt('title') . ": \"{$Title}\"\r\n" . gTxt('status') . ": {$status} , " . gTxt('language') . ": {$lang} [" . $row[L10N_COL_LANG] . '] , ' . gTxt('group') . ': ' . $row[L10N_COL_GROUP] . ".\r\n";
                $msg .= "http://{$siteurl}/textpattern/index.php?event=article&step=edit&ID={$id}\r\n";
                $links[] = $msg;
            }
        }
        extract(safe_row('RealName AS txp_username,email AS replyto', 'txp_users', "name='{$txp_user}'"));
        extract(safe_row('RealName AS new_user,email', 'txp_users', "name='{$new_user}'"));
        $count = count($links);
        $s = $count === 1 ? '' : 's';
        $subs = array('{sitename}' => $sitename, '{count}' => $count, '{s}' => $s, '{txp_username}' => $txp_username);
        if ($same) {
            $body = gTxt('l10n-email_body_self', $subs);
        } else {
            $body = gTxt('l10n-email_body_other', $subs);
        }
        $body .= join("\r\n", $links) . "\r\n\r\n" . gTxt('thanks') . "\r\n--\r\n{$txp_username}.";
        $subject = gTxt('l10n-email_xfer_subject', $subs);
        $ok = @txpMail($email, $subject, $body, $replyto);
    }
}
Example #12
0
function send_new_password($password, $email, $name)
{
    global $txp_user, $sitename;
    if (empty($name)) {
        $name = $txp_user;
    }
    $message = gTxt('greeting') . ' ' . $name . ',' . "\r\n" . "\r\n" . gTxt('your_password_is') . ': ' . $password . "\r\n" . "\r\n" . gTxt('log_in_at') . ': ' . hu . 'textpattern/index.php';
    return txpMail($email, "[{$sitename}] " . gTxt('your_new_password'), $message);
}