diff --git a/application/config/email.php b/application/config/email.php new file mode 100644 index 0000000..3b4e2bf --- /dev/null +++ b/application/config/email.php @@ -0,0 +1,19 @@ +load->helper(array('form', 'url')); + $this->load->library('form_validation'); + $this->load->library('security'); + $this->load->library('tank_auth'); + $this->lang->load('tank_auth'); + } + + function index() + { + if ($message = $this->session->flashdata('message')) { + $this->load->view('auth/general_message', array('message' => $message)); + } else { + redirect('/auth/login/'); + } + } + + /** + * Login user on the site + * + * @return void + */ + function login() + { + if ($this->tank_auth->is_logged_in()) { // logged in + redirect(''); + + } elseif ($this->tank_auth->is_logged_in(FALSE)) { // logged in, not activated + redirect('/auth/send_again/'); + + } else { + $data['login_by_username'] = ($this->config->item('login_by_username', 'tank_auth') AND + $this->config->item('use_username', 'tank_auth')); + $data['login_by_email'] = $this->config->item('login_by_email', 'tank_auth'); + + $this->form_validation->set_rules('login', 'Login', 'trim|required|xss_clean'); + $this->form_validation->set_rules('password', 'Password', 'trim|required|xss_clean'); + $this->form_validation->set_rules('remember', 'Remember me', 'integer'); + + // Get login for counting attempts to login + if ($this->config->item('login_count_attempts', 'tank_auth') AND + ($login = $this->input->post('login'))) { + $login = $this->security->xss_clean($login); + } else { + $login = ''; + } + + $data['use_recaptcha'] = $this->config->item('use_recaptcha', 'tank_auth'); + if ($this->tank_auth->is_max_login_attempts_exceeded($login)) { + if ($data['use_recaptcha']) + $this->form_validation->set_rules('recaptcha_response_field', 'Confirmation Code', 'trim|xss_clean|required|callback__check_recaptcha'); + else + $this->form_validation->set_rules('captcha', 'Confirmation Code', 'trim|xss_clean|required|callback__check_captcha'); + } + $data['errors'] = array(); + + if ($this->form_validation->run()) { // validation ok + if ($this->tank_auth->login( + $this->form_validation->set_value('login'), + $this->form_validation->set_value('password'), + $this->form_validation->set_value('remember'), + $data['login_by_username'], + $data['login_by_email'])) { // success + redirect(''); + + } else { + $errors = $this->tank_auth->get_error_message(); + if (isset($errors['banned'])) { // banned user + $this->_show_message($this->lang->line('auth_message_banned').' '.$errors['banned']); + + } elseif (isset($errors['not_activated'])) { // not activated user + redirect('/auth/send_again/'); + + } else { // fail + foreach ($errors as $k => $v) $data['errors'][$k] = $this->lang->line($v); + } + } + } + $data['show_captcha'] = FALSE; + if ($this->tank_auth->is_max_login_attempts_exceeded($login)) { + $data['show_captcha'] = TRUE; + if ($data['use_recaptcha']) { + $data['recaptcha_html'] = $this->_create_recaptcha(); + } else { + $data['captcha_html'] = $this->_create_captcha(); + } + } + $this->load->view('auth/login_form', $data); + } + } + + /** + * Logout user + * + * @return void + */ + function logout() + { + $this->tank_auth->logout(); + + $this->_show_message($this->lang->line('auth_message_logged_out')); + } + + /** + * Register user on the site + * + * @return void + */ + function register() + { + if ($this->tank_auth->is_logged_in()) { // logged in + redirect(''); + + } elseif ($this->tank_auth->is_logged_in(FALSE)) { // logged in, not activated + redirect('/auth/send_again/'); + + } elseif (!$this->config->item('allow_registration', 'tank_auth')) { // registration is off + $this->_show_message($this->lang->line('auth_message_registration_disabled')); + + } else { + $use_username = $this->config->item('use_username', 'tank_auth'); + if ($use_username) { + $this->form_validation->set_rules('username', 'Username', 'trim|required|xss_clean|min_length['.$this->config->item('username_min_length', 'tank_auth').']|max_length['.$this->config->item('username_max_length', 'tank_auth').']|alpha_dash'); + } + $this->form_validation->set_rules('email', 'Email', 'trim|required|xss_clean|valid_email'); + $this->form_validation->set_rules('password', 'Password', 'trim|required|xss_clean|min_length['.$this->config->item('password_min_length', 'tank_auth').']|max_length['.$this->config->item('password_max_length', 'tank_auth').']|alpha_dash'); + $this->form_validation->set_rules('confirm_password', 'Confirm Password', 'trim|required|xss_clean|matches[password]'); + + $captcha_registration = $this->config->item('captcha_registration', 'tank_auth'); + $use_recaptcha = $this->config->item('use_recaptcha', 'tank_auth'); + if ($captcha_registration) { + if ($use_recaptcha) { + $this->form_validation->set_rules('recaptcha_response_field', 'Confirmation Code', 'trim|xss_clean|required|callback__check_recaptcha'); + } else { + $this->form_validation->set_rules('captcha', 'Confirmation Code', 'trim|xss_clean|required|callback__check_captcha'); + } + } + $data['errors'] = array(); + + $email_activation = $this->config->item('email_activation', 'tank_auth'); + + if ($this->form_validation->run()) { // validation ok + if (!is_null($data = $this->tank_auth->create_user( + $use_username ? $this->form_validation->set_value('username') : '', + $this->form_validation->set_value('email'), + $this->form_validation->set_value('password'), + $email_activation))) { // success + + $data['site_name'] = $this->config->item('website_name', 'tank_auth'); + + if ($email_activation) { // send "activate" email + $data['activation_period'] = $this->config->item('email_activation_expire', 'tank_auth') / 3600; + + $this->_send_email('activate', $data['email'], $data); + + unset($data['password']); // Clear password (just for any case) + + $this->_show_message($this->lang->line('auth_message_registration_completed_1')); + + } else { + if ($this->config->item('email_account_details', 'tank_auth')) { // send "welcome" email + + $this->_send_email('welcome', $data['email'], $data); + } + unset($data['password']); // Clear password (just for any case) + + $this->_show_message($this->lang->line('auth_message_registration_completed_2').' '.anchor('/auth/login/', 'Login')); + } + } else { + $errors = $this->tank_auth->get_error_message(); + foreach ($errors as $k => $v) $data['errors'][$k] = $this->lang->line($v); + } + } + if ($captcha_registration) { + if ($use_recaptcha) { + $data['recaptcha_html'] = $this->_create_recaptcha(); + } else { + $data['captcha_html'] = $this->_create_captcha(); + } + } + $data['use_username'] = $use_username; + $data['captcha_registration'] = $captcha_registration; + $data['use_recaptcha'] = $use_recaptcha; + $this->load->view('auth/register_form', $data); + } + } + + /** + * Send activation email again, to the same or new email address + * + * @return void + */ + function send_again() + { + if (!$this->tank_auth->is_logged_in(FALSE)) { // not logged in or activated + redirect('/auth/login/'); + + } else { + $this->form_validation->set_rules('email', 'Email', 'trim|required|xss_clean|valid_email'); + + $data['errors'] = array(); + + if ($this->form_validation->run()) { // validation ok + if (!is_null($data = $this->tank_auth->change_email( + $this->form_validation->set_value('email')))) { // success + + $data['site_name'] = $this->config->item('website_name', 'tank_auth'); + $data['activation_period'] = $this->config->item('email_activation_expire', 'tank_auth') / 3600; + + $this->_send_email('activate', $data['email'], $data); + + $this->_show_message(sprintf($this->lang->line('auth_message_activation_email_sent'), $data['email'])); + + } else { + $errors = $this->tank_auth->get_error_message(); + foreach ($errors as $k => $v) $data['errors'][$k] = $this->lang->line($v); + } + } + $this->load->view('auth/send_again_form', $data); + } + } + + /** + * Activate user account. + * User is verified by user_id and authentication code in the URL. + * Can be called by clicking on link in mail. + * + * @return void + */ + function activate() + { + $user_id = $this->uri->segment(3); + $new_email_key = $this->uri->segment(4); + + // Activate user + if ($this->tank_auth->activate_user($user_id, $new_email_key)) { // success + $this->tank_auth->logout(); + $this->_show_message($this->lang->line('auth_message_activation_completed').' '.anchor('/auth/login/', 'Login')); + + } else { // fail + $this->_show_message($this->lang->line('auth_message_activation_failed')); + } + } + + /** + * Generate reset code (to change password) and send it to user + * + * @return void + */ + function forgot_password() + { + if ($this->tank_auth->is_logged_in()) { // logged in + redirect(''); + + } elseif ($this->tank_auth->is_logged_in(FALSE)) { // logged in, not activated + redirect('/auth/send_again/'); + + } else { + $this->form_validation->set_rules('login', 'Email or login', 'trim|required|xss_clean'); + + $data['errors'] = array(); + + if ($this->form_validation->run()) { // validation ok + if (!is_null($data = $this->tank_auth->forgot_password( + $this->form_validation->set_value('login')))) { + + $data['site_name'] = $this->config->item('website_name', 'tank_auth'); + + // Send email with password activation link + $this->_send_email('forgot_password', $data['email'], $data); + + $this->_show_message($this->lang->line('auth_message_new_password_sent')); + + } else { + $errors = $this->tank_auth->get_error_message(); + foreach ($errors as $k => $v) $data['errors'][$k] = $this->lang->line($v); + } + } + $this->load->view('auth/forgot_password_form', $data); + } + } + + /** + * Replace user password (forgotten) with a new one (set by user). + * User is verified by user_id and authentication code in the URL. + * Can be called by clicking on link in mail. + * + * @return void + */ + function reset_password() + { + $user_id = $this->uri->segment(3); + $new_pass_key = $this->uri->segment(4); + + $this->form_validation->set_rules('new_password', 'New Password', 'trim|required|xss_clean|min_length['.$this->config->item('password_min_length', 'tank_auth').']|max_length['.$this->config->item('password_max_length', 'tank_auth').']|alpha_dash'); + $this->form_validation->set_rules('confirm_new_password', 'Confirm new Password', 'trim|required|xss_clean|matches[new_password]'); + + $data['errors'] = array(); + + if ($this->form_validation->run()) { // validation ok + if (!is_null($data = $this->tank_auth->reset_password( + $user_id, $new_pass_key, + $this->form_validation->set_value('new_password')))) { // success + + $data['site_name'] = $this->config->item('website_name', 'tank_auth'); + + // Send email with new password + $this->_send_email('reset_password', $data['email'], $data); + + $this->_show_message($this->lang->line('auth_message_new_password_activated').' '.anchor('/auth/login/', 'Login')); + + } else { // fail + $this->_show_message($this->lang->line('auth_message_new_password_failed')); + } + } else { + // Try to activate user by password key (if not activated yet) + if ($this->config->item('email_activation', 'tank_auth')) { + $this->tank_auth->activate_user($user_id, $new_pass_key, FALSE); + } + + if (!$this->tank_auth->can_reset_password($user_id, $new_pass_key)) { + $this->_show_message($this->lang->line('auth_message_new_password_failed')); + } + } + $this->load->view('auth/reset_password_form', $data); + } + + /** + * Change user password + * + * @return void + */ + function change_password() + { + if (!$this->tank_auth->is_logged_in()) { // not logged in or not activated + redirect('/auth/login/'); + + } else { + $this->form_validation->set_rules('old_password', 'Old Password', 'trim|required|xss_clean'); + $this->form_validation->set_rules('new_password', 'New Password', 'trim|required|xss_clean|min_length['.$this->config->item('password_min_length', 'tank_auth').']|max_length['.$this->config->item('password_max_length', 'tank_auth').']|alpha_dash'); + $this->form_validation->set_rules('confirm_new_password', 'Confirm new Password', 'trim|required|xss_clean|matches[new_password]'); + + $data['errors'] = array(); + + if ($this->form_validation->run()) { // validation ok + if ($this->tank_auth->change_password( + $this->form_validation->set_value('old_password'), + $this->form_validation->set_value('new_password'))) { // success + $this->_show_message($this->lang->line('auth_message_password_changed')); + + } else { // fail + $errors = $this->tank_auth->get_error_message(); + foreach ($errors as $k => $v) $data['errors'][$k] = $this->lang->line($v); + } + } + $this->load->view('auth/change_password_form', $data); + } + } + + /** + * Change user email + * + * @return void + */ + function change_email() + { + if (!$this->tank_auth->is_logged_in()) { // not logged in or not activated + redirect('/auth/login/'); + + } else { + $this->form_validation->set_rules('password', 'Password', 'trim|required|xss_clean'); + $this->form_validation->set_rules('email', 'Email', 'trim|required|xss_clean|valid_email'); + + $data['errors'] = array(); + + if ($this->form_validation->run()) { // validation ok + if (!is_null($data = $this->tank_auth->set_new_email( + $this->form_validation->set_value('email'), + $this->form_validation->set_value('password')))) { // success + + $data['site_name'] = $this->config->item('website_name', 'tank_auth'); + + // Send email with new email address and its activation link + $this->_send_email('change_email', $data['new_email'], $data); + + $this->_show_message(sprintf($this->lang->line('auth_message_new_email_sent'), $data['new_email'])); + + } else { + $errors = $this->tank_auth->get_error_message(); + foreach ($errors as $k => $v) $data['errors'][$k] = $this->lang->line($v); + } + } + $this->load->view('auth/change_email_form', $data); + } + } + + /** + * Replace user email with a new one. + * User is verified by user_id and authentication code in the URL. + * Can be called by clicking on link in mail. + * + * @return void + */ + function reset_email() + { + $user_id = $this->uri->segment(3); + $new_email_key = $this->uri->segment(4); + + // Reset email + if ($this->tank_auth->activate_new_email($user_id, $new_email_key)) { // success + $this->tank_auth->logout(); + $this->_show_message($this->lang->line('auth_message_new_email_activated').' '.anchor('/auth/login/', 'Login')); + + } else { // fail + $this->_show_message($this->lang->line('auth_message_new_email_failed')); + } + } + + /** + * Delete user from the site (only when user is logged in) + * + * @return void + */ + function unregister() + { + if (!$this->tank_auth->is_logged_in()) { // not logged in or not activated + redirect('/auth/login/'); + + } else { + $this->form_validation->set_rules('password', 'Password', 'trim|required|xss_clean'); + + $data['errors'] = array(); + + if ($this->form_validation->run()) { // validation ok + if ($this->tank_auth->delete_user( + $this->form_validation->set_value('password'))) { // success + $this->_show_message($this->lang->line('auth_message_unregistered')); + + } else { // fail + $errors = $this->tank_auth->get_error_message(); + foreach ($errors as $k => $v) $data['errors'][$k] = $this->lang->line($v); + } + } + $this->load->view('auth/unregister_form', $data); + } + } + + /** + * Show info message + * + * @param string + * @return void + */ + function _show_message($message) + { + $this->session->set_flashdata('message', $message); + redirect('/auth/'); + } + + /** + * Send email message of given type (activate, forgot_password, etc.) + * + * @param string + * @param string + * @param array + * @return void + */ + function _send_email($type, $email, &$data) + { + $this->load->library('email'); + $this->email->from($this->config->item('webmaster_email', 'tank_auth'), $this->config->item('website_name', 'tank_auth')); + $this->email->reply_to($this->config->item('webmaster_email', 'tank_auth'), $this->config->item('website_name', 'tank_auth')); + $this->email->to($email); + $this->email->subject(sprintf($this->lang->line('auth_subject_'.$type), $this->config->item('website_name', 'tank_auth'))); + $this->email->message($this->load->view('email/'.$type.'-html', $data, TRUE)); + $this->email->set_alt_message($this->load->view('email/'.$type.'-txt', $data, TRUE)); + $this->email->send(); + } + + /** + * Create CAPTCHA image to verify user as a human + * + * @return string + */ + function _create_captcha() + { + $this->load->helper('captcha'); + + $cap = create_captcha(array( + 'img_path' => './'.$this->config->item('captcha_path', 'tank_auth'), + 'img_url' => base_url().$this->config->item('captcha_path', 'tank_auth'), + 'font_path' => './'.$this->config->item('captcha_fonts_path', 'tank_auth'), + 'font_size' => $this->config->item('captcha_font_size', 'tank_auth'), + 'img_width' => $this->config->item('captcha_width', 'tank_auth'), + 'img_height' => $this->config->item('captcha_height', 'tank_auth'), + 'show_grid' => $this->config->item('captcha_grid', 'tank_auth'), + 'expiration' => $this->config->item('captcha_expire', 'tank_auth'), + )); + + // Save captcha params in session + $this->session->set_flashdata(array( + 'captcha_word' => $cap['word'], + 'captcha_time' => $cap['time'], + )); + + return $cap['image']; + } + + /** + * Callback function. Check if CAPTCHA test is passed. + * + * @param string + * @return bool + */ + function _check_captcha($code) + { + $time = $this->session->flashdata('captcha_time'); + $word = $this->session->flashdata('captcha_word'); + + list($usec, $sec) = explode(" ", microtime()); + $now = ((float)$usec + (float)$sec); + + if ($now - $time > $this->config->item('captcha_expire', 'tank_auth')) { + $this->form_validation->set_message('_check_captcha', $this->lang->line('auth_captcha_expired')); + return FALSE; + + } elseif (($this->config->item('captcha_case_sensitive', 'tank_auth') AND + $code != $word) OR + strtolower($code) != strtolower($word)) { + $this->form_validation->set_message('_check_captcha', $this->lang->line('auth_incorrect_captcha')); + return FALSE; + } + return TRUE; + } + + /** + * Create reCAPTCHA JS and non-JS HTML to verify user as a human + * + * @return string + */ + function _create_recaptcha() + { + $this->load->helper('recaptcha'); + + // Add custom theme so we can get only image + $options = "\n"; + + // Get reCAPTCHA JS and non-JS HTML + $html = recaptcha_get_html($this->config->item('recaptcha_public_key', 'tank_auth')); + + return $options.$html; + } + + /** + * Callback function. Check if reCAPTCHA test is passed. + * + * @return bool + */ + function _check_recaptcha() + { + $this->load->helper('recaptcha'); + + $resp = recaptcha_check_answer($this->config->item('recaptcha_private_key', 'tank_auth'), + $_SERVER['REMOTE_ADDR'], + $_POST['recaptcha_challenge_field'], + $_POST['recaptcha_response_field']); + + if (!$resp->is_valid) { + $this->form_validation->set_message('_check_recaptcha', $this->lang->line('auth_incorrect_captcha')); + return FALSE; + } + return TRUE; + } + +} + +/* End of file auth.php */ +/* Location: ./application/controllers/auth.php */ \ No newline at end of file diff --git a/application/controllers/welcome.php b/application/controllers/welcome.php old mode 100755 new mode 100644 index 21bef43..98e53cf --- a/application/controllers/welcome.php +++ b/application/controllers/welcome.php @@ -1,25 +1,24 @@ -load->helper('url'); + $this->load->library('tank_auth'); + } - /** - * Index Page for this controller. - * - * Maps to the following URL - * http://example.com/index.php/welcome - * - or - - * http://example.com/index.php/welcome/index - * - or - - * Since this controller is set as the default controller in - * config/routes.php, it's displayed at http://example.com/ - * - * So any other public methods not prefixed with an underscore will - * map to /index.php/welcome/ - * @see http://codeigniter.com/user_guide/general/urls.html - */ - public function index() + function index() { - $this->load->view('welcome_message'); + if (!$this->tank_auth->is_logged_in()) { + redirect('/auth/login/'); + } else { + $data['user_id'] = $this->tank_auth->get_user_id(); + $data['username'] = $this->tank_auth->get_username(); + $this->load->view('welcome', $data); + } } } diff --git a/application/helpers/recaptcha_helper.php b/application/helpers/recaptcha_helper.php new file mode 100644 index 0000000..32c4f4d --- /dev/null +++ b/application/helpers/recaptcha_helper.php @@ -0,0 +1,277 @@ + $value ) + $req .= $key . '=' . urlencode( stripslashes($value) ) . '&'; + + // Cut the last '&' + $req=substr($req,0,strlen($req)-1); + return $req; +} + + + +/** + * Submits an HTTP POST to a reCAPTCHA server + * @param string $host + * @param string $path + * @param array $data + * @param int port + * @return array response + */ +function _recaptcha_http_post($host, $path, $data, $port = 80) { + + $req = _recaptcha_qsencode ($data); + + $http_request = "POST $path HTTP/1.0\r\n"; + $http_request .= "Host: $host\r\n"; + $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n"; + $http_request .= "Content-Length: " . strlen($req) . "\r\n"; + $http_request .= "User-Agent: reCAPTCHA/PHP\r\n"; + $http_request .= "\r\n"; + $http_request .= $req; + + $response = ''; + if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) { + die ('Could not open socket'); + } + + fwrite($fs, $http_request); + + while ( !feof($fs) ) + $response .= fgets($fs, 1160); // One TCP-IP packet + fclose($fs); + $response = explode("\r\n\r\n", $response, 2); + + return $response; +} + + + +/** + * Gets the challenge HTML (javascript and non-javascript version). + * This is called from the browser, and the resulting reCAPTCHA HTML widget + * is embedded within the HTML form it was called from. + * @param string $pubkey A public key for reCAPTCHA + * @param string $error The error given by reCAPTCHA (optional, default is null) + * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false) + + * @return string - The HTML to be embedded in the user's form. + */ +function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false) +{ + if ($pubkey == null || $pubkey == '') { + die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + } + + if ($use_ssl) { + $server = RECAPTCHA_API_SECURE_SERVER; + } else { + $server = RECAPTCHA_API_SERVER; + } + + $errorpart = ""; + if ($error) { + $errorpart = "&error=" . $error; + } + return ' + + '; +} + + + + +/** + * A ReCaptchaResponse is returned from recaptcha_check_answer() + */ +class ReCaptchaResponse { + var $is_valid; + var $error; +} + + +/** + * Calls an HTTP POST function to verify if the user's guess was correct + * @param string $privkey + * @param string $remoteip + * @param string $challenge + * @param string $response + * @param array $extra_params an array of extra variables to post to the server + * @return ReCaptchaResponse + */ +function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array()) +{ + if ($privkey == null || $privkey == '') { + die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + } + + if ($remoteip == null || $remoteip == '') { + die ("For security reasons, you must pass the remote ip to reCAPTCHA"); + } + + + + //discard spam submissions + if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) { + $recaptcha_response = new ReCaptchaResponse(); + $recaptcha_response->is_valid = false; + $recaptcha_response->error = 'incorrect-captcha-sol'; + return $recaptcha_response; + } + + $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify", + array ( + 'privatekey' => $privkey, + 'remoteip' => $remoteip, + 'challenge' => $challenge, + 'response' => $response + ) + $extra_params + ); + + $answers = explode ("\n", $response [1]); + $recaptcha_response = new ReCaptchaResponse(); + + if (trim ($answers [0]) == 'true') { + $recaptcha_response->is_valid = true; + } + else { + $recaptcha_response->is_valid = false; + $recaptcha_response->error = $answers [1]; + } + return $recaptcha_response; + +} + +/** + * gets a URL where the user can sign up for reCAPTCHA. If your application + * has a configuration page where you enter a key, you should provide a link + * using this function. + * @param string $domain The domain where the page is hosted + * @param string $appname The name of your application + */ +function recaptcha_get_signup_url ($domain = null, $appname = null) { + return "https://www.google.com/recaptcha/admin/create?" . _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname)); +} + +function _recaptcha_aes_pad($val) { + $block_size = 16; + $numpad = $block_size - (strlen ($val) % $block_size); + return str_pad($val, strlen ($val) + $numpad, chr($numpad)); +} + +/* Mailhide related code */ + +function _recaptcha_aes_encrypt($val,$ky) { + if (! function_exists ("mcrypt_encrypt")) { + die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed."); + } + $mode=MCRYPT_MODE_CBC; + $enc=MCRYPT_RIJNDAEL_128; + $val=_recaptcha_aes_pad($val); + return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); +} + + +function _recaptcha_mailhide_urlbase64 ($x) { + return strtr(base64_encode ($x), '+/', '-_'); +} + +/* gets the reCAPTCHA Mailhide url for a given email, public key and private key */ +function recaptcha_mailhide_url($pubkey, $privkey, $email) { + if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) { + die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " . + "you can do so at http://www.google.com/recaptcha/mailhide/apikey"); + } + + + $ky = pack('H*', $privkey); + $cryptmail = _recaptcha_aes_encrypt ($email, $ky); + + return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail); +} + +/** + * gets the parts of the email to expose to the user. + * eg, given johndoe@example,com return ["john", "example.com"]. + * the email is then displayed as john...@example.com + */ +function _recaptcha_mailhide_email_parts ($email) { + $arr = preg_split("/@/", $email ); + + if (strlen ($arr[0]) <= 4) { + $arr[0] = substr ($arr[0], 0, 1); + } else if (strlen ($arr[0]) <= 6) { + $arr[0] = substr ($arr[0], 0, 3); + } else { + $arr[0] = substr ($arr[0], 0, 4); + } + return $arr; +} + +/** + * Gets html to display an email address given a public an private key. + * to get a key, go to: + * + * http://www.google.com/recaptcha/mailhide/apikey + */ +function recaptcha_mailhide_html($pubkey, $privkey, $email) { + $emailparts = _recaptcha_mailhide_email_parts ($email); + $url = recaptcha_mailhide_url ($pubkey, $privkey, $email); + + return htmlentities($emailparts[0]) . "...@" . htmlentities ($emailparts [1]); + +} + + +?> diff --git a/application/language/chinese/tank_auth_lang.php b/application/language/chinese/tank_auth_lang.php new file mode 100644 index 0000000..3103092 --- /dev/null +++ b/application/language/chinese/tank_auth_lang.php @@ -0,0 +1,40 @@ +ci =& get_instance(); + + $this->ci->load->config('tank_auth', TRUE); + + $this->ci->load->library('session'); + $this->ci->load->database(); + $this->ci->load->model('tank_auth/users'); + + // Try to autologin + $this->autologin(); + } + + /** + * Login user on the site. Return TRUE if login is successful + * (user exists and activated, password is correct), otherwise FALSE. + * + * @param string (username or email or both depending on settings in config file) + * @param string + * @param bool + * @return bool + */ + function login($login, $password, $remember, $login_by_username, $login_by_email) + { + if ((strlen($login) > 0) AND (strlen($password) > 0)) { + + // Which function to use to login (based on config) + if ($login_by_username AND $login_by_email) { + $get_user_func = 'get_user_by_login'; + } else if ($login_by_username) { + $get_user_func = 'get_user_by_username'; + } else { + $get_user_func = 'get_user_by_email'; + } + + if (!is_null($user = $this->ci->users->$get_user_func($login))) { // login ok + + // Does password match hash in database? + $hasher = new PasswordHash( + $this->ci->config->item('phpass_hash_strength', 'tank_auth'), + $this->ci->config->item('phpass_hash_portable', 'tank_auth')); + if ($hasher->CheckPassword($password, $user->password)) { // password ok + + if ($user->banned == 1) { // fail - banned + $this->error = array('banned' => $user->ban_reason); + + } else { + $this->ci->session->set_userdata(array( + 'user_id' => $user->id, + 'username' => $user->username, + 'status' => ($user->activated == 1) ? STATUS_ACTIVATED : STATUS_NOT_ACTIVATED, + )); + + if ($user->activated == 0) { // fail - not activated + $this->error = array('not_activated' => ''); + + } else { // success + if ($remember) { + $this->create_autologin($user->id); + } + + $this->clear_login_attempts($login); + + $this->ci->users->update_login_info( + $user->id, + $this->ci->config->item('login_record_ip', 'tank_auth'), + $this->ci->config->item('login_record_time', 'tank_auth')); + return TRUE; + } + } + } else { // fail - wrong password + $this->increase_login_attempt($login); + $this->error = array('password' => 'auth_incorrect_password'); + } + } else { // fail - wrong login + $this->increase_login_attempt($login); + $this->error = array('login' => 'auth_incorrect_login'); + } + } + return FALSE; + } + + /** + * Logout user from the site + * + * @return void + */ + function logout() + { + $this->delete_autologin(); + + // See http://codeigniter.com/forums/viewreply/662369/ as the reason for the next line + $this->ci->session->set_userdata(array('user_id' => '', 'username' => '', 'status' => '')); + + $this->ci->session->sess_destroy(); + } + + /** + * Check if user logged in. Also test if user is activated or not. + * + * @param bool + * @return bool + */ + function is_logged_in($activated = TRUE) + { + return $this->ci->session->userdata('status') === ($activated ? STATUS_ACTIVATED : STATUS_NOT_ACTIVATED); + } + + /** + * Get user_id + * + * @return string + */ + function get_user_id() + { + return $this->ci->session->userdata('user_id'); + } + + /** + * Get username + * + * @return string + */ + function get_username() + { + return $this->ci->session->userdata('username'); + } + + /** + * Create new user on the site and return some data about it: + * user_id, username, password, email, new_email_key (if any). + * + * @param string + * @param string + * @param string + * @param bool + * @return array + */ + function create_user($username, $email, $password, $email_activation) + { + if ((strlen($username) > 0) AND !$this->ci->users->is_username_available($username)) { + $this->error = array('username' => 'auth_username_in_use'); + + } elseif (!$this->ci->users->is_email_available($email)) { + $this->error = array('email' => 'auth_email_in_use'); + + } else { + // Hash password using phpass + $hasher = new PasswordHash( + $this->ci->config->item('phpass_hash_strength', 'tank_auth'), + $this->ci->config->item('phpass_hash_portable', 'tank_auth')); + $hashed_password = $hasher->HashPassword($password); + + $data = array( + 'username' => $username, + 'password' => $hashed_password, + 'email' => $email, + 'last_ip' => $this->ci->input->ip_address(), + ); + + if ($email_activation) { + $data['new_email_key'] = md5(rand().microtime()); + } + if (!is_null($res = $this->ci->users->create_user($data, !$email_activation))) { + $data['user_id'] = $res['user_id']; + $data['password'] = $password; + unset($data['last_ip']); + return $data; + } + } + return NULL; + } + + /** + * Check if username available for registering. + * Can be called for instant form validation. + * + * @param string + * @return bool + */ + function is_username_available($username) + { + return ((strlen($username) > 0) AND $this->ci->users->is_username_available($username)); + } + + /** + * Check if email available for registering. + * Can be called for instant form validation. + * + * @param string + * @return bool + */ + function is_email_available($email) + { + return ((strlen($email) > 0) AND $this->ci->users->is_email_available($email)); + } + + /** + * Change email for activation and return some data about user: + * user_id, username, email, new_email_key. + * Can be called for not activated users only. + * + * @param string + * @return array + */ + function change_email($email) + { + $user_id = $this->ci->session->userdata('user_id'); + + if (!is_null($user = $this->ci->users->get_user_by_id($user_id, FALSE))) { + + $data = array( + 'user_id' => $user_id, + 'username' => $user->username, + 'email' => $email, + ); + if (strtolower($user->email) == strtolower($email)) { // leave activation key as is + $data['new_email_key'] = $user->new_email_key; + return $data; + + } elseif ($this->ci->users->is_email_available($email)) { + $data['new_email_key'] = md5(rand().microtime()); + $this->ci->users->set_new_email($user_id, $email, $data['new_email_key'], FALSE); + return $data; + + } else { + $this->error = array('email' => 'auth_email_in_use'); + } + } + return NULL; + } + + /** + * Activate user using given key + * + * @param string + * @param string + * @param bool + * @return bool + */ + function activate_user($user_id, $activation_key, $activate_by_email = TRUE) + { + $this->ci->users->purge_na($this->ci->config->item('email_activation_expire', 'tank_auth')); + + if ((strlen($user_id) > 0) AND (strlen($activation_key) > 0)) { + return $this->ci->users->activate_user($user_id, $activation_key, $activate_by_email); + } + return FALSE; + } + + /** + * Set new password key for user and return some data about user: + * user_id, username, email, new_pass_key. + * The password key can be used to verify user when resetting his/her password. + * + * @param string + * @return array + */ + function forgot_password($login) + { + if (strlen($login) > 0) { + if (!is_null($user = $this->ci->users->get_user_by_login($login))) { + + $data = array( + 'user_id' => $user->id, + 'username' => $user->username, + 'email' => $user->email, + 'new_pass_key' => md5(rand().microtime()), + ); + + $this->ci->users->set_password_key($user->id, $data['new_pass_key']); + return $data; + + } else { + $this->error = array('login' => 'auth_incorrect_email_or_username'); + } + } + return NULL; + } + + /** + * Check if given password key is valid and user is authenticated. + * + * @param string + * @param string + * @return bool + */ + function can_reset_password($user_id, $new_pass_key) + { + if ((strlen($user_id) > 0) AND (strlen($new_pass_key) > 0)) { + return $this->ci->users->can_reset_password( + $user_id, + $new_pass_key, + $this->ci->config->item('forgot_password_expire', 'tank_auth')); + } + return FALSE; + } + + /** + * Replace user password (forgotten) with a new one (set by user) + * and return some data about it: user_id, username, new_password, email. + * + * @param string + * @param string + * @return bool + */ + function reset_password($user_id, $new_pass_key, $new_password) + { + if ((strlen($user_id) > 0) AND (strlen($new_pass_key) > 0) AND (strlen($new_password) > 0)) { + + if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) { + + // Hash password using phpass + $hasher = new PasswordHash( + $this->ci->config->item('phpass_hash_strength', 'tank_auth'), + $this->ci->config->item('phpass_hash_portable', 'tank_auth')); + $hashed_password = $hasher->HashPassword($new_password); + + if ($this->ci->users->reset_password( + $user_id, + $hashed_password, + $new_pass_key, + $this->ci->config->item('forgot_password_expire', 'tank_auth'))) { // success + + // Clear all user's autologins + $this->ci->load->model('tank_auth/user_autologin'); + $this->ci->user_autologin->clear($user->id); + + return array( + 'user_id' => $user_id, + 'username' => $user->username, + 'email' => $user->email, + 'new_password' => $new_password, + ); + } + } + } + return NULL; + } + + /** + * Change user password (only when user is logged in) + * + * @param string + * @param string + * @return bool + */ + function change_password($old_pass, $new_pass) + { + $user_id = $this->ci->session->userdata('user_id'); + + if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) { + + // Check if old password correct + $hasher = new PasswordHash( + $this->ci->config->item('phpass_hash_strength', 'tank_auth'), + $this->ci->config->item('phpass_hash_portable', 'tank_auth')); + if ($hasher->CheckPassword($old_pass, $user->password)) { // success + + // Hash new password using phpass + $hashed_password = $hasher->HashPassword($new_pass); + + // Replace old password with new one + $this->ci->users->change_password($user_id, $hashed_password); + return TRUE; + + } else { // fail + $this->error = array('old_password' => 'auth_incorrect_password'); + } + } + return FALSE; + } + + /** + * Change user email (only when user is logged in) and return some data about user: + * user_id, username, new_email, new_email_key. + * The new email cannot be used for login or notification before it is activated. + * + * @param string + * @param string + * @return array + */ + function set_new_email($new_email, $password) + { + $user_id = $this->ci->session->userdata('user_id'); + + if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) { + + // Check if password correct + $hasher = new PasswordHash( + $this->ci->config->item('phpass_hash_strength', 'tank_auth'), + $this->ci->config->item('phpass_hash_portable', 'tank_auth')); + if ($hasher->CheckPassword($password, $user->password)) { // success + + $data = array( + 'user_id' => $user_id, + 'username' => $user->username, + 'new_email' => $new_email, + ); + + if ($user->email == $new_email) { + $this->error = array('email' => 'auth_current_email'); + + } elseif ($user->new_email == $new_email) { // leave email key as is + $data['new_email_key'] = $user->new_email_key; + return $data; + + } elseif ($this->ci->users->is_email_available($new_email)) { + $data['new_email_key'] = md5(rand().microtime()); + $this->ci->users->set_new_email($user_id, $new_email, $data['new_email_key'], TRUE); + return $data; + + } else { + $this->error = array('email' => 'auth_email_in_use'); + } + } else { // fail + $this->error = array('password' => 'auth_incorrect_password'); + } + } + return NULL; + } + + /** + * Activate new email, if email activation key is valid. + * + * @param string + * @param string + * @return bool + */ + function activate_new_email($user_id, $new_email_key) + { + if ((strlen($user_id) > 0) AND (strlen($new_email_key) > 0)) { + return $this->ci->users->activate_new_email( + $user_id, + $new_email_key); + } + return FALSE; + } + + /** + * Delete user from the site (only when user is logged in) + * + * @param string + * @return bool + */ + function delete_user($password) + { + $user_id = $this->ci->session->userdata('user_id'); + + if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) { + + // Check if password correct + $hasher = new PasswordHash( + $this->ci->config->item('phpass_hash_strength', 'tank_auth'), + $this->ci->config->item('phpass_hash_portable', 'tank_auth')); + if ($hasher->CheckPassword($password, $user->password)) { // success + + $this->ci->users->delete_user($user_id); + $this->logout(); + return TRUE; + + } else { // fail + $this->error = array('password' => 'auth_incorrect_password'); + } + } + return FALSE; + } + + /** + * Get error message. + * Can be invoked after any failed operation such as login or register. + * + * @return string + */ + function get_error_message() + { + return $this->error; + } + + /** + * Save data for user's autologin + * + * @param int + * @return bool + */ + private function create_autologin($user_id) + { + $this->ci->load->helper('cookie'); + $key = substr(md5(uniqid(rand().get_cookie($this->ci->config->item('sess_cookie_name')))), 0, 16); + + $this->ci->load->model('tank_auth/user_autologin'); + $this->ci->user_autologin->purge($user_id); + + if ($this->ci->user_autologin->set($user_id, md5($key))) { + set_cookie(array( + 'name' => $this->ci->config->item('autologin_cookie_name', 'tank_auth'), + 'value' => serialize(array('user_id' => $user_id, 'key' => $key)), + 'expire' => $this->ci->config->item('autologin_cookie_life', 'tank_auth'), + )); + return TRUE; + } + return FALSE; + } + + /** + * Clear user's autologin data + * + * @return void + */ + private function delete_autologin() + { + $this->ci->load->helper('cookie'); + if ($cookie = get_cookie($this->ci->config->item('autologin_cookie_name', 'tank_auth'), TRUE)) { + + $data = unserialize($cookie); + + $this->ci->load->model('tank_auth/user_autologin'); + $this->ci->user_autologin->delete($data['user_id'], md5($data['key'])); + + delete_cookie($this->ci->config->item('autologin_cookie_name', 'tank_auth')); + } + } + + /** + * Login user automatically if he/she provides correct autologin verification + * + * @return void + */ + private function autologin() + { + if (!$this->is_logged_in() AND !$this->is_logged_in(FALSE)) { // not logged in (as any user) + + $this->ci->load->helper('cookie'); + if ($cookie = get_cookie($this->ci->config->item('autologin_cookie_name', 'tank_auth'), TRUE)) { + + $data = unserialize($cookie); + + if (isset($data['key']) AND isset($data['user_id'])) { + + $this->ci->load->model('tank_auth/user_autologin'); + if (!is_null($user = $this->ci->user_autologin->get($data['user_id'], md5($data['key'])))) { + + // Login user + $this->ci->session->set_userdata(array( + 'user_id' => $user->id, + 'username' => $user->username, + 'status' => STATUS_ACTIVATED, + )); + + // Renew users cookie to prevent it from expiring + set_cookie(array( + 'name' => $this->ci->config->item('autologin_cookie_name', 'tank_auth'), + 'value' => $cookie, + 'expire' => $this->ci->config->item('autologin_cookie_life', 'tank_auth'), + )); + + $this->ci->users->update_login_info( + $user->id, + $this->ci->config->item('login_record_ip', 'tank_auth'), + $this->ci->config->item('login_record_time', 'tank_auth')); + return TRUE; + } + } + } + } + return FALSE; + } + + /** + * Check if login attempts exceeded max login attempts (specified in config) + * + * @param string + * @return bool + */ + function is_max_login_attempts_exceeded($login) + { + if ($this->ci->config->item('login_count_attempts', 'tank_auth')) { + $this->ci->load->model('tank_auth/login_attempts'); + return $this->ci->login_attempts->get_attempts_num($this->ci->input->ip_address(), $login) + >= $this->ci->config->item('login_max_attempts', 'tank_auth'); + } + return FALSE; + } + + /** + * Increase number of attempts for given IP-address and login + * (if attempts to login is being counted) + * + * @param string + * @return void + */ + private function increase_login_attempt($login) + { + if ($this->ci->config->item('login_count_attempts', 'tank_auth')) { + if (!$this->is_max_login_attempts_exceeded($login)) { + $this->ci->load->model('tank_auth/login_attempts'); + $this->ci->login_attempts->increase_attempt($this->ci->input->ip_address(), $login); + } + } + } + + /** + * Clear all attempt records for given IP-address and login + * (if attempts to login is being counted) + * + * @param string + * @return void + */ + private function clear_login_attempts($login) + { + if ($this->ci->config->item('login_count_attempts', 'tank_auth')) { + $this->ci->load->model('tank_auth/login_attempts'); + $this->ci->login_attempts->clear_attempts( + $this->ci->input->ip_address(), + $login, + $this->ci->config->item('login_attempt_expire', 'tank_auth')); + } + } +} + +/* End of file Tank_auth.php */ +/* Location: ./application/libraries/Tank_auth.php */ \ No newline at end of file diff --git a/application/libraries/phpass-0.1/PasswordHash.php b/application/libraries/phpass-0.1/PasswordHash.php new file mode 100644 index 0000000..550a01b --- /dev/null +++ b/application/libraries/phpass-0.1/PasswordHash.php @@ -0,0 +1,248 @@ + in 2004-2006 and placed in +# the public domain. +# +# There's absolutely no warranty. +# +# The homepage URL for this framework is: +# +# http://www.openwall.com/phpass/ +# +# Please be sure to update the Version line if you edit this file in any way. +# It is suggested that you leave the main version number intact, but indicate +# your project name (after the slash) and add your own revision information. +# +# Please do not change the "private" password hashing method implemented in +# here, thereby making your hashes incompatible. However, if you must, please +# change the hash type identifier (the "$P$") to something different. +# +# Obviously, since this code is in the public domain, the above are not +# requirements (there can be none), but merely suggestions. +# +class PasswordHash { + var $itoa64; + var $iteration_count_log2; + var $portable_hashes; + var $random_state; + + function PasswordHash($iteration_count_log2, $portable_hashes) + { + $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) + $iteration_count_log2 = 8; + $this->iteration_count_log2 = $iteration_count_log2; + + $this->portable_hashes = $portable_hashes; + + $this->random_state = microtime() . getmypid(); + } + + function get_random_bytes($count) + { + $output = ''; + if (($fh = @fopen('/dev/urandom', 'rb'))) { + $output = fread($fh, $count); + fclose($fh); + } + + if (strlen($output) < $count) { + $output = ''; + for ($i = 0; $i < $count; $i += 16) { + $this->random_state = + md5(microtime() . $this->random_state); + $output .= + pack('H*', md5($this->random_state)); + } + $output = substr($output, 0, $count); + } + + return $output; + } + + function encode64($input, $count) + { + $output = ''; + $i = 0; + do { + $value = ord($input[$i++]); + $output .= $this->itoa64[$value & 0x3f]; + if ($i < $count) + $value |= ord($input[$i]) << 8; + $output .= $this->itoa64[($value >> 6) & 0x3f]; + if ($i++ >= $count) + break; + if ($i < $count) + $value |= ord($input[$i]) << 16; + $output .= $this->itoa64[($value >> 12) & 0x3f]; + if ($i++ >= $count) + break; + $output .= $this->itoa64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + function gensalt_private($input) + { + $output = '$P$'; + $output .= $this->itoa64[min($this->iteration_count_log2 + + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; + $output .= $this->encode64($input, 6); + + return $output; + } + + function crypt_private($password, $setting) + { + $output = '*0'; + if (substr($setting, 0, 2) == $output) + $output = '*1'; + + if (substr($setting, 0, 3) != '$P$') + return $output; + + $count_log2 = strpos($this->itoa64, $setting[3]); + if ($count_log2 < 7 || $count_log2 > 30) + return $output; + + $count = 1 << $count_log2; + + $salt = substr($setting, 4, 8); + if (strlen($salt) != 8) + return $output; + + # We're kind of forced to use MD5 here since it's the only + # cryptographic primitive available in all versions of PHP + # currently in use. To implement our own low-level crypto + # in PHP would result in much worse performance and + # consequently in lower iteration counts and hashes that are + # quicker to crack (by non-PHP code). + if (PHP_VERSION >= '5') { + $hash = md5($salt . $password, TRUE); + do { + $hash = md5($hash . $password, TRUE); + } while (--$count); + } else { + $hash = pack('H*', md5($salt . $password)); + do { + $hash = pack('H*', md5($hash . $password)); + } while (--$count); + } + + $output = substr($setting, 0, 12); + $output .= $this->encode64($hash, 16); + + return $output; + } + + function gensalt_extended($input) + { + $count_log2 = min($this->iteration_count_log2 + 8, 24); + # This should be odd to not reveal weak DES keys, and the + # maximum valid value is (2**24 - 1) which is odd anyway. + $count = (1 << $count_log2) - 1; + + $output = '_'; + $output .= $this->itoa64[$count & 0x3f]; + $output .= $this->itoa64[($count >> 6) & 0x3f]; + $output .= $this->itoa64[($count >> 12) & 0x3f]; + $output .= $this->itoa64[($count >> 18) & 0x3f]; + + $output .= $this->encode64($input, 3); + + return $output; + } + + function gensalt_blowfish($input) + { + # This one needs to use a different order of characters and a + # different encoding scheme from the one in encode64() above. + # We care because the last character in our encoded string will + # only represent 2 bits. While two known implementations of + # bcrypt will happily accept and correct a salt string which + # has the 4 unused bits set to non-zero, we do not want to take + # chances and we also do not want to waste an additional byte + # of entropy. + $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + $output = '$2a$'; + $output .= chr(ord('0') + $this->iteration_count_log2 / 10); + $output .= chr(ord('0') + $this->iteration_count_log2 % 10); + $output .= '$'; + + $i = 0; + do { + $c1 = ord($input[$i++]); + $output .= $itoa64[$c1 >> 2]; + $c1 = ($c1 & 0x03) << 4; + if ($i >= 16) { + $output .= $itoa64[$c1]; + break; + } + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 4; + $output .= $itoa64[$c1]; + $c1 = ($c2 & 0x0f) << 2; + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 6; + $output .= $itoa64[$c1]; + $output .= $itoa64[$c2 & 0x3f]; + } while (1); + + return $output; + } + + function HashPassword($password) + { + $random = ''; + + if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { + $random = $this->get_random_bytes(16); + $hash = + crypt($password, $this->gensalt_blowfish($random)); + if (strlen($hash) == 60) + return $hash; + } + + if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { + if (strlen($random) < 3) + $random = $this->get_random_bytes(3); + $hash = + crypt($password, $this->gensalt_extended($random)); + if (strlen($hash) == 20) + return $hash; + } + + if (strlen($random) < 6) + $random = $this->get_random_bytes(6); + $hash = + $this->crypt_private($password, + $this->gensalt_private($random)); + if (strlen($hash) == 34) + return $hash; + + # Returning '*' on error is safe here, but would _not_ be safe + # in a crypt(3)-like function used _both_ for generating new + # hashes and for validating passwords against existing hashes. + return '*'; + } + + function CheckPassword($password, $stored_hash) + { + $hash = $this->crypt_private($password, $stored_hash); + if ($hash[0] == '*') + $hash = crypt($password, $stored_hash); + + return $hash == $stored_hash; + } +} + +?> diff --git a/application/libraries/phpass-0.1/c/Makefile b/application/libraries/phpass-0.1/c/Makefile new file mode 100644 index 0000000..fe48917 --- /dev/null +++ b/application/libraries/phpass-0.1/c/Makefile @@ -0,0 +1,21 @@ +# +# Written by Solar Designer and placed in the public domain. +# See crypt_private.c for more information. +# +CC = gcc +LD = $(CC) +RM = rm -f +CFLAGS = -Wall -O2 -fomit-frame-pointer -funroll-loops +LDFLAGS = -s +LIBS = -lcrypto + +all: crypt_private-test + +crypt_private-test: crypt_private-test.o + $(LD) $(LDFLAGS) $(LIBS) crypt_private-test.o -o $@ + +crypt_private-test.o: crypt_private.c + $(CC) -c $(CFLAGS) crypt_private.c -DTEST -o $@ + +clean: + $(RM) crypt_private-test* diff --git a/application/libraries/phpass-0.1/c/crypt_private.c b/application/libraries/phpass-0.1/c/crypt_private.c new file mode 100644 index 0000000..669c4ae --- /dev/null +++ b/application/libraries/phpass-0.1/c/crypt_private.c @@ -0,0 +1,106 @@ +/* + * This code exists for the sole purpose to serve as another implementation + * of the "private" password hashing method implemened in PasswordHash.php + * and thus to confirm that these password hashes are indeed calculated as + * intended. + * + * Other uses of this code are discouraged. There are much better password + * hashing algorithms available to C programmers; one of those is bcrypt: + * + * http://www.openwall.com/crypt/ + * + * Written by Solar Designer in 2005 and placed in + * the public domain. + * + * There's absolutely no warranty. + */ + +#include +#include + +#ifdef TEST +#include +#endif + +static char *itoa64 = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void encode64(char *dst, char *src, int count) +{ + int i, value; + + i = 0; + do { + value = (unsigned char)src[i++]; + *dst++ = itoa64[value & 0x3f]; + if (i < count) + value |= (unsigned char)src[i] << 8; + *dst++ = itoa64[(value >> 6) & 0x3f]; + if (i++ >= count) + break; + if (i < count) + value |= (unsigned char)src[i] << 16; + *dst++ = itoa64[(value >> 12) & 0x3f]; + if (i++ >= count) + break; + *dst++ = itoa64[(value >> 18) & 0x3f]; + } while (i < count); +} + +char *crypt_private(char *password, char *setting) +{ + static char output[32]; + MD5_CTX ctx; + char hash[MD5_DIGEST_LENGTH]; + char *p, *salt; + int count_log2, length, count; + + strcpy(output, "*0"); + if (!strncmp(setting, output, 2)) + output[1] = '1'; + + if (strncmp(setting, "$P$", 3)) + return output; + + p = strchr(itoa64, setting[3]); + if (!p) + return output; + count_log2 = p - itoa64; + if (count_log2 < 7 || count_log2 > 31) + return output; + + salt = setting + 4; + if (strlen(salt) < 8) + return output; + + length = strlen(password); + + MD5_Init(&ctx); + MD5_Update(&ctx, salt, 8); + MD5_Update(&ctx, password, length); + MD5_Final(hash, &ctx); + + count = 1 << count_log2; + do { + MD5_Init(&ctx); + MD5_Update(&ctx, hash, MD5_DIGEST_LENGTH); + MD5_Update(&ctx, password, length); + MD5_Final(hash, &ctx); + } while (--count); + + memcpy(output, setting, 12); + encode64(&output[12], hash, MD5_DIGEST_LENGTH); + + return output; +} + +#ifdef TEST +int main(int argc, char **argv) +{ + if (argc != 3) return 1; + + puts(crypt_private(argv[1], argv[2])); + + return 0; +} +#endif diff --git a/application/libraries/phpass-0.1/test.php b/application/libraries/phpass-0.1/test.php new file mode 100644 index 0000000..36ef653 --- /dev/null +++ b/application/libraries/phpass-0.1/test.php @@ -0,0 +1,72 @@ +HashPassword($correct); + +print "Hash: " . $hash . "\n"; + +$check = $t_hasher->CheckPassword($correct, $hash); +if ($check) $ok++; +print "Check correct: '" . $check . "' (should be '1')\n"; + +$wrong = "test12346"; +$check = $t_hasher->CheckPassword($wrong, $hash); +if (!$check) $ok++; +print "Check wrong: '" . $check . "' (should be '0' or '')\n"; + +unset($t_hasher); + +# Force the use of weaker portable hashes. +$t_hasher = new PasswordHash(8, TRUE); + +$hash = $t_hasher->HashPassword($correct); + +print "Hash: " . $hash . "\n"; + +$check = $t_hasher->CheckPassword($correct, $hash); +if ($check) $ok++; +print "Check correct: '" . $check . "' (should be '1')\n"; + +$check = $t_hasher->CheckPassword($wrong, $hash); +if (!$check) $ok++; +print "Check wrong: '" . $check . "' (should be '0' or '')\n"; + +# A correct portable hash for "test12345". +# Please note the use of single quotes to ensure that the dollar signs will +# be interpreted literally. Of course, a real application making use of the +# framework won't store password hashes within a PHP source file anyway. +# We only do this for testing. +$hash = '$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0'; + +print "Hash: " . $hash . "\n"; + +$check = $t_hasher->CheckPassword($correct, $hash); +if ($check) $ok++; +print "Check correct: '" . $check . "' (should be '1')\n"; + +$check = $t_hasher->CheckPassword($wrong, $hash); +if (!$check) $ok++; +print "Check wrong: '" . $check . "' (should be '0' or '')\n"; + +if ($ok == 6) + print "All tests have PASSED\n"; +else + print "Some tests have FAILED\n"; + +?> diff --git a/application/models/tank_auth/login_attempts.php b/application/models/tank_auth/login_attempts.php new file mode 100644 index 0000000..bee9995 --- /dev/null +++ b/application/models/tank_auth/login_attempts.php @@ -0,0 +1,74 @@ +table_name = $ci->config->item('db_table_prefix', 'tank_auth').$this->table_name; + } + + /** + * Get number of attempts to login occured from given IP-address or login + * + * @param string + * @param string + * @return int + */ + function get_attempts_num($ip_address, $login) + { + $this->db->select('1', FALSE); + $this->db->where('ip_address', $ip_address); + if (strlen($login) > 0) $this->db->or_where('login', $login); + + $qres = $this->db->get($this->table_name); + return $qres->num_rows(); + } + + /** + * Increase number of attempts for given IP-address and login + * + * @param string + * @param string + * @return void + */ + function increase_attempt($ip_address, $login) + { + $this->db->insert($this->table_name, array('ip_address' => $ip_address, 'login' => $login)); + } + + /** + * Clear all attempt records for given IP-address and login. + * Also purge obsolete login attempts (to keep DB clear). + * + * @param string + * @param string + * @param int + * @return void + */ + function clear_attempts($ip_address, $login, $expire_period = 86400) + { + $this->db->where(array('ip_address' => $ip_address, 'login' => $login)); + + // Purge obsolete login attempts + $this->db->or_where('UNIX_TIMESTAMP(time) <', time() - $expire_period); + + $this->db->delete($this->table_name); + } +} + +/* End of file login_attempts.php */ +/* Location: ./application/models/auth/login_attempts.php */ \ No newline at end of file diff --git a/application/models/tank_auth/user_autologin.php b/application/models/tank_auth/user_autologin.php new file mode 100644 index 0000000..7c7b0ac --- /dev/null +++ b/application/models/tank_auth/user_autologin.php @@ -0,0 +1,106 @@ +table_name = $ci->config->item('db_table_prefix', 'tank_auth').$this->table_name; + $this->users_table_name = $ci->config->item('db_table_prefix', 'tank_auth').$this->users_table_name; + } + + /** + * Get user data for auto-logged in user. + * Return NULL if given key or user ID is invalid. + * + * @param int + * @param string + * @return object + */ + function get($user_id, $key) + { + $this->db->select($this->users_table_name.'.id'); + $this->db->select($this->users_table_name.'.username'); + $this->db->from($this->users_table_name); + $this->db->join($this->table_name, $this->table_name.'.user_id = '.$this->users_table_name.'.id'); + $this->db->where($this->table_name.'.user_id', $user_id); + $this->db->where($this->table_name.'.key_id', $key); + $query = $this->db->get(); + if ($query->num_rows() == 1) return $query->row(); + return NULL; + } + + /** + * Save data for user's autologin + * + * @param int + * @param string + * @return bool + */ + function set($user_id, $key) + { + return $this->db->insert($this->table_name, array( + 'user_id' => $user_id, + 'key_id' => $key, + 'user_agent' => substr($this->input->user_agent(), 0, 149), + 'last_ip' => $this->input->ip_address(), + )); + } + + /** + * Delete user's autologin data + * + * @param int + * @param string + * @return void + */ + function delete($user_id, $key) + { + $this->db->where('user_id', $user_id); + $this->db->where('key_id', $key); + $this->db->delete($this->table_name); + } + + /** + * Delete all autologin data for given user + * + * @param int + * @return void + */ + function clear($user_id) + { + $this->db->where('user_id', $user_id); + $this->db->delete($this->table_name); + } + + /** + * Purge autologin data for given user and login conditions + * + * @param int + * @return void + */ + function purge($user_id) + { + $this->db->where('user_id', $user_id); + $this->db->where('user_agent', substr($this->input->user_agent(), 0, 149)); + $this->db->where('last_ip', $this->input->ip_address()); + $this->db->delete($this->table_name); + } +} + +/* End of file user_autologin.php */ +/* Location: ./application/models/auth/user_autologin.php */ \ No newline at end of file diff --git a/application/models/tank_auth/users.php b/application/models/tank_auth/users.php new file mode 100644 index 0000000..71372e4 --- /dev/null +++ b/application/models/tank_auth/users.php @@ -0,0 +1,398 @@ +table_name = $ci->config->item('db_table_prefix', 'tank_auth').$this->table_name; + $this->profile_table_name = $ci->config->item('db_table_prefix', 'tank_auth').$this->profile_table_name; + } + + /** + * Get user record by Id + * + * @param int + * @param bool + * @return object + */ + function get_user_by_id($user_id, $activated) + { + $this->db->where('id', $user_id); + $this->db->where('activated', $activated ? 1 : 0); + + $query = $this->db->get($this->table_name); + if ($query->num_rows() == 1) return $query->row(); + return NULL; + } + + /** + * Get user record by login (username or email) + * + * @param string + * @return object + */ + function get_user_by_login($login) + { + $this->db->where('LOWER(username)=', strtolower($login)); + $this->db->or_where('LOWER(email)=', strtolower($login)); + + $query = $this->db->get($this->table_name); + if ($query->num_rows() == 1) return $query->row(); + return NULL; + } + + /** + * Get user record by username + * + * @param string + * @return object + */ + function get_user_by_username($username) + { + $this->db->where('LOWER(username)=', strtolower($username)); + + $query = $this->db->get($this->table_name); + if ($query->num_rows() == 1) return $query->row(); + return NULL; + } + + /** + * Get user record by email + * + * @param string + * @return object + */ + function get_user_by_email($email) + { + $this->db->where('LOWER(email)=', strtolower($email)); + + $query = $this->db->get($this->table_name); + if ($query->num_rows() == 1) return $query->row(); + return NULL; + } + + /** + * Check if username available for registering + * + * @param string + * @return bool + */ + function is_username_available($username) + { + $this->db->select('1', FALSE); + $this->db->where('LOWER(username)=', strtolower($username)); + + $query = $this->db->get($this->table_name); + return $query->num_rows() == 0; + } + + /** + * Check if email available for registering + * + * @param string + * @return bool + */ + function is_email_available($email) + { + $this->db->select('1', FALSE); + $this->db->where('LOWER(email)=', strtolower($email)); + $this->db->or_where('LOWER(new_email)=', strtolower($email)); + + $query = $this->db->get($this->table_name); + return $query->num_rows() == 0; + } + + /** + * Create new user record + * + * @param array + * @param bool + * @return array + */ + function create_user($data, $activated = TRUE) + { + $data['created'] = date('Y-m-d H:i:s'); + $data['activated'] = $activated ? 1 : 0; + + if ($this->db->insert($this->table_name, $data)) { + $user_id = $this->db->insert_id(); + if ($activated) $this->create_profile($user_id); + return array('user_id' => $user_id); + } + return NULL; + } + + /** + * Activate user if activation key is valid. + * Can be called for not activated users only. + * + * @param int + * @param string + * @param bool + * @return bool + */ + function activate_user($user_id, $activation_key, $activate_by_email) + { + $this->db->select('1', FALSE); + $this->db->where('id', $user_id); + if ($activate_by_email) { + $this->db->where('new_email_key', $activation_key); + } else { + $this->db->where('new_password_key', $activation_key); + } + $this->db->where('activated', 0); + $query = $this->db->get($this->table_name); + + if ($query->num_rows() == 1) { + + $this->db->set('activated', 1); + $this->db->set('new_email_key', NULL); + $this->db->where('id', $user_id); + $this->db->update($this->table_name); + + $this->create_profile($user_id); + return TRUE; + } + return FALSE; + } + + /** + * Purge table of non-activated users + * + * @param int + * @return void + */ + function purge_na($expire_period = 172800) + { + $this->db->where('activated', 0); + $this->db->where('UNIX_TIMESTAMP(created) <', time() - $expire_period); + $this->db->delete($this->table_name); + } + + /** + * Delete user record + * + * @param int + * @return bool + */ + function delete_user($user_id) + { + $this->db->where('id', $user_id); + $this->db->delete($this->table_name); + if ($this->db->affected_rows() > 0) { + $this->delete_profile($user_id); + return TRUE; + } + return FALSE; + } + + /** + * Set new password key for user. + * This key can be used for authentication when resetting user's password. + * + * @param int + * @param string + * @return bool + */ + function set_password_key($user_id, $new_pass_key) + { + $this->db->set('new_password_key', $new_pass_key); + $this->db->set('new_password_requested', date('Y-m-d H:i:s')); + $this->db->where('id', $user_id); + + $this->db->update($this->table_name); + return $this->db->affected_rows() > 0; + } + + /** + * Check if given password key is valid and user is authenticated. + * + * @param int + * @param string + * @param int + * @return void + */ + function can_reset_password($user_id, $new_pass_key, $expire_period = 900) + { + $this->db->select('1', FALSE); + $this->db->where('id', $user_id); + $this->db->where('new_password_key', $new_pass_key); + $this->db->where('UNIX_TIMESTAMP(new_password_requested) >', time() - $expire_period); + + $query = $this->db->get($this->table_name); + return $query->num_rows() == 1; + } + + /** + * Change user password if password key is valid and user is authenticated. + * + * @param int + * @param string + * @param string + * @param int + * @return bool + */ + function reset_password($user_id, $new_pass, $new_pass_key, $expire_period = 900) + { + $this->db->set('password', $new_pass); + $this->db->set('new_password_key', NULL); + $this->db->set('new_password_requested', NULL); + $this->db->where('id', $user_id); + $this->db->where('new_password_key', $new_pass_key); + $this->db->where('UNIX_TIMESTAMP(new_password_requested) >=', time() - $expire_period); + + $this->db->update($this->table_name); + return $this->db->affected_rows() > 0; + } + + /** + * Change user password + * + * @param int + * @param string + * @return bool + */ + function change_password($user_id, $new_pass) + { + $this->db->set('password', $new_pass); + $this->db->where('id', $user_id); + + $this->db->update($this->table_name); + return $this->db->affected_rows() > 0; + } + + /** + * Set new email for user (may be activated or not). + * The new email cannot be used for login or notification before it is activated. + * + * @param int + * @param string + * @param string + * @param bool + * @return bool + */ + function set_new_email($user_id, $new_email, $new_email_key, $activated) + { + $this->db->set($activated ? 'new_email' : 'email', $new_email); + $this->db->set('new_email_key', $new_email_key); + $this->db->where('id', $user_id); + $this->db->where('activated', $activated ? 1 : 0); + + $this->db->update($this->table_name); + return $this->db->affected_rows() > 0; + } + + /** + * Activate new email (replace old email with new one) if activation key is valid. + * + * @param int + * @param string + * @return bool + */ + function activate_new_email($user_id, $new_email_key) + { + $this->db->set('email', 'new_email', FALSE); + $this->db->set('new_email', NULL); + $this->db->set('new_email_key', NULL); + $this->db->where('id', $user_id); + $this->db->where('new_email_key', $new_email_key); + + $this->db->update($this->table_name); + return $this->db->affected_rows() > 0; + } + + /** + * Update user login info, such as IP-address or login time, and + * clear previously generated (but not activated) passwords. + * + * @param int + * @param bool + * @param bool + * @return void + */ + function update_login_info($user_id, $record_ip, $record_time) + { + $this->db->set('new_password_key', NULL); + $this->db->set('new_password_requested', NULL); + + if ($record_ip) $this->db->set('last_ip', $this->input->ip_address()); + if ($record_time) $this->db->set('last_login', date('Y-m-d H:i:s')); + + $this->db->where('id', $user_id); + $this->db->update($this->table_name); + } + + /** + * Ban user + * + * @param int + * @param string + * @return void + */ + function ban_user($user_id, $reason = NULL) + { + $this->db->where('id', $user_id); + $this->db->update($this->table_name, array( + 'banned' => 1, + 'ban_reason' => $reason, + )); + } + + /** + * Unban user + * + * @param int + * @return void + */ + function unban_user($user_id) + { + $this->db->where('id', $user_id); + $this->db->update($this->table_name, array( + 'banned' => 0, + 'ban_reason' => NULL, + )); + } + + /** + * Create an empty profile for a new user + * + * @param int + * @return bool + */ + private function create_profile($user_id) + { + $this->db->set('user_id', $user_id); + return $this->db->insert($this->profile_table_name); + } + + /** + * Delete user profile + * + * @param int + * @return void + */ + private function delete_profile($user_id) + { + $this->db->where('user_id', $user_id); + $this->db->delete($this->profile_table_name); + } +} + +/* End of file users.php */ +/* Location: ./application/models/auth/users.php */ \ No newline at end of file diff --git a/application/views/auth/change_email_form.php b/application/views/auth/change_email_form.php new file mode 100644 index 0000000..29a3281 --- /dev/null +++ b/application/views/auth/change_email_form.php @@ -0,0 +1,29 @@ + 'password', + 'id' => 'password', + 'size' => 30, +); +$email = array( + 'name' => 'email', + 'id' => 'email', + 'value' => set_value('email'), + 'maxlength' => 80, + 'size' => 30, +); +?> +uri->uri_string()); ?> + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/application/views/auth/change_password_form.php b/application/views/auth/change_password_form.php new file mode 100644 index 0000000..bb17f67 --- /dev/null +++ b/application/views/auth/change_password_form.php @@ -0,0 +1,40 @@ + 'old_password', + 'id' => 'old_password', + 'value' => set_value('old_password'), + 'size' => 30, +); +$new_password = array( + 'name' => 'new_password', + 'id' => 'new_password', + 'maxlength' => $this->config->item('password_max_length', 'tank_auth'), + 'size' => 30, +); +$confirm_new_password = array( + 'name' => 'confirm_new_password', + 'id' => 'confirm_new_password', + 'maxlength' => $this->config->item('password_max_length', 'tank_auth'), + 'size' => 30, +); +?> +uri->uri_string()); ?> + + + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/application/views/auth/forgot_password_form.php b/application/views/auth/forgot_password_form.php new file mode 100644 index 0000000..62cb70a --- /dev/null +++ b/application/views/auth/forgot_password_form.php @@ -0,0 +1,24 @@ + 'login', + 'id' => 'login', + 'value' => set_value('login'), + 'maxlength' => 80, + 'size' => 30, +); +if ($this->config->item('use_username', 'tank_auth')) { + $login_label = 'Email or login'; +} else { + $login_label = 'Email'; +} +?> +uri->uri_string()); ?> + + + + + + +
+ + \ No newline at end of file diff --git a/application/views/auth/general_message.php b/application/views/auth/general_message.php new file mode 100644 index 0000000..498619a --- /dev/null +++ b/application/views/auth/general_message.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/application/views/auth/login_form.php b/application/views/auth/login_form.php new file mode 100644 index 0000000..9187518 --- /dev/null +++ b/application/views/auth/login_form.php @@ -0,0 +1,93 @@ + 'login', + 'id' => 'login', + 'value' => set_value('login'), + 'maxlength' => 80, + 'size' => 30, +); +if ($login_by_username AND $login_by_email) { + $login_label = 'Email or login'; +} else if ($login_by_username) { + $login_label = 'Login'; +} else { + $login_label = 'Email'; +} +$password = array( + 'name' => 'password', + 'id' => 'password', + 'size' => 30, +); +$remember = array( + 'name' => 'remember', + 'id' => 'remember', + 'value' => 1, + 'checked' => set_value('remember'), + 'style' => 'margin:0;padding:0', +); +$captcha = array( + 'name' => 'captcha', + 'id' => 'captcha', + 'maxlength' => 8, +); +?> +uri->uri_string()); ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Get another CAPTCHA + + +
+
Enter the words above
+
Enter the numbers you hear
+
+

Enter the code exactly as it appears:

+ +
+ + + + config->item('allow_registration', 'tank_auth')) echo anchor('/auth/register/', 'Register'); ?> +
+ + \ No newline at end of file diff --git a/application/views/auth/register_form.php b/application/views/auth/register_form.php new file mode 100644 index 0000000..0d16a0a --- /dev/null +++ b/application/views/auth/register_form.php @@ -0,0 +1,100 @@ + 'username', + 'id' => 'username', + 'value' => set_value('username'), + 'maxlength' => $this->config->item('username_max_length', 'tank_auth'), + 'size' => 30, + ); +} +$email = array( + 'name' => 'email', + 'id' => 'email', + 'value' => set_value('email'), + 'maxlength' => 80, + 'size' => 30, +); +$password = array( + 'name' => 'password', + 'id' => 'password', + 'value' => set_value('password'), + 'maxlength' => $this->config->item('password_max_length', 'tank_auth'), + 'size' => 30, +); +$confirm_password = array( + 'name' => 'confirm_password', + 'id' => 'confirm_password', + 'value' => set_value('confirm_password'), + 'maxlength' => $this->config->item('password_max_length', 'tank_auth'), + 'size' => 30, +); +$captcha = array( + 'name' => 'captcha', + 'id' => 'captcha', + 'maxlength' => 8, +); +?> +uri->uri_string()); ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ Get another CAPTCHA + + +
+
Enter the words above
+
Enter the numbers you hear
+
+

Enter the code exactly as it appears:

+ +
+ + \ No newline at end of file diff --git a/application/views/auth/reset_password_form.php b/application/views/auth/reset_password_form.php new file mode 100644 index 0000000..7bb25cc --- /dev/null +++ b/application/views/auth/reset_password_form.php @@ -0,0 +1,29 @@ + 'new_password', + 'id' => 'new_password', + 'maxlength' => $this->config->item('password_max_length', 'tank_auth'), + 'size' => 30, +); +$confirm_new_password = array( + 'name' => 'confirm_new_password', + 'id' => 'confirm_new_password', + 'maxlength' => $this->config->item('password_max_length', 'tank_auth'), + 'size' => 30, +); +?> +uri->uri_string()); ?> + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/application/views/auth/send_again_form.php b/application/views/auth/send_again_form.php new file mode 100644 index 0000000..024eedf --- /dev/null +++ b/application/views/auth/send_again_form.php @@ -0,0 +1,19 @@ + 'email', + 'id' => 'email', + 'value' => set_value('email'), + 'maxlength' => 80, + 'size' => 30, +); +?> +uri->uri_string()); ?> + + + + + + +
+ + \ No newline at end of file diff --git a/application/views/auth/unregister_form.php b/application/views/auth/unregister_form.php new file mode 100644 index 0000000..bfd5ea3 --- /dev/null +++ b/application/views/auth/unregister_form.php @@ -0,0 +1,17 @@ + 'password', + 'id' => 'password', + 'size' => 30, +); +?> +uri->uri_string()); ?> + + + + + + +
+ + \ No newline at end of file diff --git a/application/views/email/activate-html.php b/application/views/email/activate-html.php new file mode 100644 index 0000000..2d743c3 --- /dev/null +++ b/application/views/email/activate-html.php @@ -0,0 +1,34 @@ + + +Welcome to <?php echo $site_name; ?>! + +
+ + + + + +
+

Welcome to !

+Thanks for joining . We listed your sign in details below, make sure you keep them safe.
+To verify your email address, please follow this link:
+
+Finish your registration...
+
+Link doesn't work? Copy the following link to your browser address bar:
+
+
+Please verify your email within hours, otherwise your registration will become invalid and you will have to register again.
+
+
+ 0) { ?>Your username:
+Your email address:
+Your password:
+
+
+Have fun!
+The Team +
+
+ + \ No newline at end of file diff --git a/application/views/email/activate-txt.php b/application/views/email/activate-txt.php new file mode 100644 index 0000000..dfa8d58 --- /dev/null +++ b/application/views/email/activate-txt.php @@ -0,0 +1,24 @@ +Welcome to , + +Thanks for joining . We listed your sign in details below, make sure you keep them safe. +To verify your email address, please follow this link: + + + + +Please verify your email within hours, otherwise your registration will become invalid and you will have to register again. + 0) { ?> + +Your username: + + +Your email address: + + +Your password: + + + + +Have fun! +The Team \ No newline at end of file diff --git a/application/views/email/change_email-html.php b/application/views/email/change_email-html.php new file mode 100644 index 0000000..224b6c6 --- /dev/null +++ b/application/views/email/change_email-html.php @@ -0,0 +1,33 @@ + + +Your new email address on <?php echo $site_name; ?> + +
+ + + + + +
+

Your new email address on

+You have changed your email address for .
+Follow this link to confirm your new email address:
+
+Confirm your new email
+
+Link doesn't work? Copy the following link to your browser address bar:
+
+
+
+Your email address:
+
+
+You received this email, because it was requested by a user. If you have received this by mistake, please DO NOT click the confirmation link, and simply delete this email. After a short time, the request will be removed from the system.
+
+
+Thank you,
+The Team +
+
+ + \ No newline at end of file diff --git a/application/views/email/change_email-txt.php b/application/views/email/change_email-txt.php new file mode 100644 index 0000000..db8c0cc --- /dev/null +++ b/application/views/email/change_email-txt.php @@ -0,0 +1,16 @@ +Hi 0) { ?> , + +You have changed your email address for . +Follow this link to confirm your new email address: + + + + +Your new email: + + +You received this email, because it was requested by a user. If you have received this by mistake, please DO NOT click the confirmation link, and simply delete this email. After a short time, the request will be removed from the system. + + +Thank you, +The Team \ No newline at end of file diff --git a/application/views/email/forgot_password-html.php b/application/views/email/forgot_password-html.php new file mode 100644 index 0000000..6cbdcc5 --- /dev/null +++ b/application/views/email/forgot_password-html.php @@ -0,0 +1,30 @@ + + +Create a new password on <?php echo $site_name; ?> + +
+ + + + + +
+

Create a new password

+Forgot your password, huh? No big deal.
+To create a new password, just follow this link:
+
+Create a new password
+
+Link doesn't work? Copy the following link to your browser address bar:
+
+
+
+You received this email, because it was requested by a user. This is part of the procedure to create a new password on the system. If you DID NOT request a new password then please ignore this email and your password will remain the same.
+
+
+Thank you,
+The Team +
+
+ + \ No newline at end of file diff --git a/application/views/email/forgot_password-txt.php b/application/views/email/forgot_password-txt.php new file mode 100644 index 0000000..5886f77 --- /dev/null +++ b/application/views/email/forgot_password-txt.php @@ -0,0 +1,13 @@ +Hi 0) { ?> , + +Forgot your password, huh? No big deal. +To create a new password, just follow this link: + + + + +You received this email, because it was requested by a user. This is part of the procedure to create a new password on the system. If you DID NOT request a new password then please ignore this email and your password will remain the same. + + +Thank you, +The Team \ No newline at end of file diff --git a/application/views/email/reset_password-html.php b/application/views/email/reset_password-html.php new file mode 100644 index 0000000..ea8bf38 --- /dev/null +++ b/application/views/email/reset_password-html.php @@ -0,0 +1,26 @@ + + +Your new password on <?php echo $site_name; ?> + +
+ + + + + +
+

Your new password on

+You have changed your password.
+Please, keep it in your records so you don't forget it.
+
+ 0) { ?>Your username:
+Your email address:
+
*/ ?> +
+
+Thank you,
+The Team +
+
+ + \ No newline at end of file diff --git a/application/views/email/reset_password-txt.php b/application/views/email/reset_password-txt.php new file mode 100644 index 0000000..83c958c --- /dev/null +++ b/application/views/email/reset_password-txt.php @@ -0,0 +1,17 @@ +Hi 0) { ?> , + +You have changed your password. +Please, keep it in your records so you don't forget it. + 0) { ?> + +Your username: + + +Your email address: + + + +*/ ?> + +Thank you, +The Team \ No newline at end of file diff --git a/application/views/email/welcome-html.php b/application/views/email/welcome-html.php new file mode 100644 index 0000000..a78a12b --- /dev/null +++ b/application/views/email/welcome-html.php @@ -0,0 +1,32 @@ + + +Welcome to <?php echo $site_name; ?>! + +
+ + + + + +
+

Welcome to !

+Thanks for joining . We listed your sign in details below, make sure you keep them safe.
+To open your homepage, please follow this link:
+
+Go to now!
+
+Link doesn't work? Copy the following link to your browser address bar:
+
+
+
+ 0) { ?>Your username:
+Your email address:
+
*/ ?> +
+
+Have fun!
+The Team +
+
+ + \ No newline at end of file diff --git a/application/views/email/welcome-txt.php b/application/views/email/welcome-txt.php new file mode 100644 index 0000000..51949ee --- /dev/null +++ b/application/views/email/welcome-txt.php @@ -0,0 +1,20 @@ +Welcome to , + +Thanks for joining . We listed your sign in details below. Make sure you keep them safe. +Follow this link to login on the site: + + + + 0) { ?> + +Your username: + + +Your email address: + + + +*/ ?> + +Have fun! +The Team \ No newline at end of file diff --git a/application/views/welcome.php b/application/views/welcome.php new file mode 100644 index 0000000..0121fdb --- /dev/null +++ b/application/views/welcome.php @@ -0,0 +1 @@ +Hi, ! You are logged in now. \ No newline at end of file diff --git a/htdocs/captcha/fonts/.htaccess b/htdocs/captcha/fonts/.htaccess new file mode 100644 index 0000000..6224851 --- /dev/null +++ b/htdocs/captcha/fonts/.htaccess @@ -0,0 +1,4 @@ + +order allow,deny +deny from all + \ No newline at end of file diff --git a/htdocs/captcha/fonts/1.ttf b/htdocs/captcha/fonts/1.ttf new file mode 100644 index 0000000..07f5e7f Binary files /dev/null and b/htdocs/captcha/fonts/1.ttf differ diff --git a/htdocs/captcha/fonts/2.ttf b/htdocs/captcha/fonts/2.ttf new file mode 100644 index 0000000..a21de64 Binary files /dev/null and b/htdocs/captcha/fonts/2.ttf differ diff --git a/htdocs/captcha/fonts/3.ttf b/htdocs/captcha/fonts/3.ttf new file mode 100644 index 0000000..c9b5f0c Binary files /dev/null and b/htdocs/captcha/fonts/3.ttf differ diff --git a/htdocs/captcha/fonts/4.ttf b/htdocs/captcha/fonts/4.ttf new file mode 100644 index 0000000..c5e0933 Binary files /dev/null and b/htdocs/captcha/fonts/4.ttf differ diff --git a/htdocs/captcha/fonts/5.ttf b/htdocs/captcha/fonts/5.ttf new file mode 100644 index 0000000..c3cea1d Binary files /dev/null and b/htdocs/captcha/fonts/5.ttf differ diff --git a/htdocs/captcha/fonts/index.html b/htdocs/captcha/fonts/index.html new file mode 100644 index 0000000..e69de29 diff --git a/htdocs/captcha/index.html b/htdocs/captcha/index.html new file mode 100644 index 0000000..e69de29