From 34090190d6eea04fd40b39c07e57e9c76f5920e9 Mon Sep 17 00:00:00 2001 From: Herz Date: Fri, 17 Jun 2016 12:53:59 -0500 Subject: [PATCH] Use recaptcha and validate email --- .gitignore | 4 + ci/application/config/autoload.php | 2 +- ci/application/config/config.php | 5 +- ci/application/config/parser.php | 2 +- ci/application/config/recaptcha.php | 27 +- ci/application/controllers/revista.php | 36 +- ci/application/helpers/custom_helper.php | 70 ++++ ci/application/libraries/Recaptcha.php | 393 +++++----------------- ci/application/views/revista/articulo.php | 5 +- ci/assets/css/biblat.css | 2 + 10 files changed, 215 insertions(+), 331 deletions(-) diff --git a/.gitignore b/.gitignore index 45a0bbc6d..83e5d2b38 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ cache/ .DS_Store +database/* +node_modules/* +.po +logs \ No newline at end of file diff --git a/ci/application/config/autoload.php b/ci/application/config/autoload.php index 53b039608..61bfd3947 100644 --- a/ci/application/config/autoload.php +++ b/ci/application/config/autoload.php @@ -52,7 +52,7 @@ | $autoload['libraries'] = array('database', 'session', 'xmlrpc'); */ -$autoload['libraries'] = array('session', 'template'); +$autoload['libraries'] = array('session', 'template', 'recaptcha'); /* diff --git a/ci/application/config/config.php b/ci/application/config/config.php index ea1671778..856a80947 100644 --- a/ci/application/config/config.php +++ b/ci/application/config/config.php @@ -180,7 +180,7 @@ | your log files will fill up very fast. | */ -$config['log_threshold'] = 1; +$config['log_threshold'] = 0; /* |-------------------------------------------------------------------------- @@ -358,5 +358,8 @@ $config['proxy_ips'] = ''; $config['enable_profiler'] = ENVIRONMENT === "production" ? FALSE : TRUE; +$config['valitation_email'] = ''; + + /* End of file config.php */ /* Location: ./application/config/config.php */ diff --git a/ci/application/config/parser.php b/ci/application/config/parser.php index 11818f6b8..4f2357ee7 100644 --- a/ci/application/config/parser.php +++ b/ci/application/config/parser.php @@ -88,4 +88,4 @@ | */ -$config['parser_assign_refs'] = array('config', 'load', 'session', 'uri', 'input', 'user_agent', 'lang'); \ No newline at end of file +$config['parser_assign_refs'] = array('config', 'load', 'session', 'uri', 'input', 'user_agent', 'lang', 'recaptcha'); \ No newline at end of file diff --git a/ci/application/config/recaptcha.php b/ci/application/config/recaptcha.php index b7ca485ba..a89b76f36 100644 --- a/ci/application/config/recaptcha.php +++ b/ci/application/config/recaptcha.php @@ -1,10 +1,17 @@ - + + */ +$config['recaptcha_sitekey'] = ''; +$config['recaptcha_secretkey'] = ''; +$config['lang'] = "es"; diff --git a/ci/application/controllers/revista.php b/ci/application/controllers/revista.php index c1d0cfd3e..48979fef8 100644 --- a/ci/application/controllers/revista.php +++ b/ci/application/controllers/revista.php @@ -339,16 +339,39 @@ public function articulo($revista='', $articulo='', $mail=''){ } public function solicitudDocumento(){ - $this->output->enable_profiler(false); - if(!empty($_POST['email']) && !empty($_POST['from']) && !empty($_POST['revista']) && !empty($_POST['articulo'])): + $this->output->enable_profiler(FALSE); + $send_email = TRUE; + $result = array( + 'type' => 'error', + 'title' => _('No se pudo enviar la solicitud') + ); + if(empty($_POST['email']) || empty($_POST['from']) || empty($_POST['revista']) || empty($_POST['articulo'])): + $send_email =FALSE; + endif; + + $verify_email = verifyEmail($_POST['email'], $this->config->item('valitation_email'), true); + if($verify_email[0] == 'invalid'): + $send_email =FALSE; + $result['title'] = _('Correo electrónico no valido'); + endif; + + $captcha_answer = $this->input->post('g-recaptcha-response'); + $response = $this->recaptcha->verifyResponse($captcha_answer); + + if(!$response['success']): + $send_email =FALSE; + $result['title'] = _('Verificación incorrecta'); + endif; + + if ($send_email): $biblatDB = $this->load->database('biblat', TRUE); $config['mailtype'] = 'html'; $this->load->library('email'); $this->email->initialize($config); $this->email->from('solicitud@biblat.unam.mx', 'Solicitud Biblat'); $this->email->to('sinfo@dgb.unam.mx'); - //$this->email->to('achwazer@gmail.com'); - //$this->email->cc('anoguez@dgb.unam.mx'); + // $this->email->to('achwazer@gmail.com'); + // $this->email->cc('anoguez@dgb.unam.mx'); $this->email->subject('Solicitud de documento Biblat'); $data = $_POST; $data['fichaDocumento'] = $this->articulo($data['revista'], $data['articulo'], 'true'); @@ -376,11 +399,6 @@ public function solicitudDocumento(){ 'type' => 'success', 'title' => _('La solicitud ha sido enviada') ); - else: - $result = array( - 'type' => 'error', - 'title' => _('No se pudo enviar la solicitud') - ); endif; echo json_encode($result); } diff --git a/ci/application/helpers/custom_helper.php b/ci/application/helpers/custom_helper.php index a1a86cc35..9b7a51e3c 100644 --- a/ci/application/helpers/custom_helper.php +++ b/ci/application/helpers/custom_helper.php @@ -434,3 +434,73 @@ function adjustColorLightenDarken($color_code,$percentage_adjuster = 0) { } endif; +if ( ! function_exists('verifyEmail') ): + + function verifyEmail($toemail, $fromemail, $getdetails = false){ + $email_arr = explode("@", $toemail); + $domain = array_slice($email_arr, -1); + $domain = $domain[0]; + // Trim [ and ] from beginning and end of domain string, respectively + $domain = ltrim($domain, "["); + $domain = rtrim($domain, "]"); + if( "IPv6:" == substr($domain, 0, strlen("IPv6:")) ) { + $domain = substr($domain, strlen("IPv6") + 1); + } + $mxhosts = array(); + if( filter_var($domain, FILTER_VALIDATE_IP) ) + $mx_ip = $domain; + else + getmxrr($domain, $mxhosts, $mxweight); + if(!empty($mxhosts) ) + $mx_ip = $mxhosts[array_search(min($mxweight), $mxhosts)]; + else { + if( filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ) { + $record_a = dns_get_record($domain, DNS_A); + } + elseif( filter_var($domain, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ) { + $record_a = dns_get_record($domain, DNS_AAAA); + } + if( !empty($record_a) ) + $mx_ip = $record_a[0]['ip']; + else { + $result = "invalid"; + $details .= "No suitable MX records found."; + return ( (true == $getdetails) ? array($result, $details) : $result ); + } + } + + $connect = @fsockopen($mx_ip, 25); + if($connect){ + if(preg_match("/^220/i", $out = fgets($connect, 1024))){ + fputs ($connect , "HELO $mx_ip\r\n"); + $out = fgets ($connect, 1024); + $details .= $out."\n"; + + fputs ($connect , "MAIL FROM: <$fromemail>\r\n"); + $from = fgets ($connect, 1024); + $details .= $from."\n"; + fputs ($connect , "RCPT TO: <$toemail>\r\n"); + $to = fgets ($connect, 1024); + $details .= $to."\n"; + fputs ($connect , "QUIT"); + fclose($connect); + if(!preg_match("/^250/i", $from) || !preg_match("/^250/i", $to)){ + $result = "invalid"; + } + else{ + $result = "valid"; + } + } + } + else{ + $result = "invalid"; + $details .= "Could not connect to server"; + } + if($getdetails){ + return array($result, $details); + } + else{ + return $result; + } + } +endif; diff --git a/ci/application/libraries/Recaptcha.php b/ci/application/libraries/Recaptcha.php index 36991a278..7a3888ad7 100644 --- a/ci/application/libraries/Recaptcha.php +++ b/ci/application/libraries/Recaptcha.php @@ -1,338 +1,115 @@ -_ci =& get_instance(); - - //Load the CI Config file for recaptcha - $this->_ci->load->config('recaptcha'); - //load in the values from the config file. - $this->public_key = $this->_ci->config->item('public_key'); - $this->privkey = $this->_ci->config->item('private_key'); - $this->theme = $this->_ci->config->item('recaptcha_theme'); - - //Lets do some basic error handling to see if the configuration is A-OK. - $temp_error_msg = ''; - if ($this->public_key === 'YOUR PUBLIC KEY') - { - $temp_error_msg .= 'You need to set your public key in the config file
'; - } - - if ($this->privkey === 'YOUR PRIVATE KEY') - { - $temp_error_msg .= 'You need to set your private key in the config file
'; - } - - if ($temp_error_msg != '') - { - show_error($temp_error_msg); - } - - } - - /** - * Encodes the given data into a query string format - * @param $data - array of string elements to be encoded - * @return string - encoded request - */ - function recaptcha_qsencode ($data) { - $req = ""; - foreach ( $data as $key => $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 = $this->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'); + $this->ci = & get_instance(); + $this->ci->load->config('recaptcha', TRUE); + if ($this->ci->config->item('recaptcha_secretkey', 'recaptcha') == NULL || $this->ci->config->item('recaptcha_secretkey', 'recaptcha') == "") { + die("To use reCAPTCHA you must get an API key from " . $this->signup_url . ""); } - - 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; - } - - - - - function recaptcha_get_html ($error = null, $use_ssl = false) - { - if ($this->public_key == null || $this->public_key == '') { - die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); + if ($this->ci->config->item('recaptcha_sitekey', 'recaptcha') == NULL || $this->ci->config->item('recaptcha_sitekey', 'recaptcha') == "") { + die("To use reCAPTCHA you must get an API key from " . $this->signup_url . ""); } - - if ($use_ssl) { - $server = self::RECAPTCHA_API_SECURE_SERVER; + $this->_secret = $this->ci->config->item('recaptcha_secretkey', 'recaptcha'); + $this->_sitekey = $this->ci->config->item('recaptcha_sitekey', 'recaptcha'); + if ($this->ci->config->item('lang', 'recaptcha') == NULL || $this->ci->config->item('lang', 'recaptcha') == "") { + $this->_lang = 'en'; } else { - $server = self::RECAPTCHA_API_SERVER; + $this->_lang = $this->ci->config->item('lang', 'recaptcha'); } - - $errorpart = ""; - if ($error) { - $errorpart = "&error=" . $error; - } - return ' - - - '; } - - - - - - /** - * Calls an HTTP POST function to verify if the user's guess was correct - * @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 to convert an array into query string + * @param array $data Array of params + * @return String query string of parameters */ - function recaptcha_check_answer ($remoteip = null, $challenge = null, $response = null, $extra_params = array()) - { - if ($this->privkey == null || $this->privkey == '') { - die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create"); - } - - $remoteip = ($remoteip == null) ? $_SERVER['REMOTE_ADDR'] : $remoteip; - $challenge = ($challenge == null) ? $this->_ci->input->post('recaptcha_challenge_field') : $challenge; - $response = ($response == null) ? $this->_ci->input->post('recaptcha_response_field') : $response; - - 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) { - - $this->is_valid = false; - $this->error = 'incorrect-captcha-sol'; - - } - - $response = $this->recaptcha_http_post (self::RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify", - array ( - 'privatekey' => $this->privkey, - 'remoteip' => $remoteip, - 'challenge' => $challenge, - 'response' => $response - ) + $extra_params - ); - - $answers = explode ("\n", $response [1]); - - - if (trim ($answers [0]) == 'true') { - $this->is_valid = true; - } - else { - $this->is_valid = false; - $this->error = $answers [1]; + private function _encodeQS($data) { + $req = ""; + foreach ($data as $key => $value) { + $req .= $key . '=' . urlencode(stripslashes($value)) . '&'; } - - + return substr($req, 0, strlen($req) - 1); } /** - * 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 + * HTTP GET to communicate with reCAPTCHA server + * @param string $path URL to GET + * @param array $data Array of params + * @return string JSON response from reCAPTCHA server */ - function recaptcha_get_signup_url ($domain = null, $appname = 'Codeigniter') { - return "https://www.google.com/recaptcha/admin/create?" . $this->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=$this->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($email) { - if ($this->public_key == '' || $this->public_key == null || $this->privkey == "" || $this->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*', $this->privkey); - $cryptmail = $this->recaptcha_aes_encrypt ($email, $ky); - - return "http://www.google.com/recaptcha/mailhide/d?k=" . $this->public_key . "&c=" . $this->recaptcha_mailhide_urlbase64 ($cryptmail); + private function _submitHTTPGet($path, $data) { + $req = $this->_encodeQS($data); + $response = file_get_contents($path . $req); + return $response; } /** - * 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 for rendering reCAPTCHA widget into views + * Call this function in your view + * @return string embedded HTML */ - 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; + public function render() { + $return = '
+ '; + return $return; } /** - * 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 for verifying user's input + * @param string $response User's input + * @param string $remoteIp Remote IP you wish to send to reCAPTCHA, if NULL $this->input->ip_address() will be called + * @return array Array of response */ - function recaptcha_mailhide_html($email) { - $emailparts = $this->recaptcha_mailhide_email_parts ($email); - $url = $this->recaptcha_mailhide_url ($this->public_key, $this->privkey, $email); - - return htmlentities($emailparts[0]) . "...@" . htmlentities ($emailparts [1]); - - } - - function checkIfIsValid() - { - if( $this->getIsValid() ) - { - return $this->getIsValid(); - } - else - { - return array( $this->getIsValid(), $this->getError() ); + public function verifyResponse($response, $remoteIp = NULL) { + if ($response == null || strlen($response) == 0) { + // Empty user's input + $return = array( + 'success' => FALSE, + 'error_codes' => 'missing-input' + ); } - } - - function getIsValid() - { - return $this->is_valid; - } - function getError() - { - return $this->error; + $getResponse = $this->_submitHttpGet( + $this->_siteVerifyUrl, array( + 'secret' => $this->_secret, + 'remoteip' => (!is_null($remoteIp)) ? $remoteIp : $this->ci->input->ip_address(), + 'v' => $this->_version, + 'response' => $response + ) + ); + $answers = json_decode($getResponse, TRUE); + + if (trim($answers ['success']) == true) { + // Right captcha! + $return = array( + 'success' => TRUE, + 'error_codes' => '' + ); + } else { + // Wrong captcha! + $return = array( + 'success' => FALSE, + 'error_codes' => $answers['error-codes'] + ); + } + return $return; } - } diff --git a/ci/application/views/revista/articulo.php b/ci/application/views/revista/articulo.php index 572801eb2..790195c1e 100644 --- a/ci/application/views/revista/articulo.php +++ b/ci/application/views/revista/articulo.php @@ -175,7 +175,10 @@
{_('Los documentos originales pueden ser consultados en el Departamento de Información y Servicios Documentales, ubicado en el Anexo de la Dirección General de Bibliotecas (DGB), circuito de la Investigación Científica a un costado del Auditorio Nabor Carrillo, zona de Institutos entre Física y Astronomía. Ciudad Universitaria UNAM.')} {_('Ver mapa')}
{_('Mayores informes: Departamento de Información y Servicios Documentales, Tels. (5255) 5622-3960, 5622-3964, e-mail: sinfo@dgb.unam.mx, Horario: Lunes a viernes (8 a 16 hrs.)')}

-
+
+
{$recaptcha->render()}
+ +
diff --git a/ci/assets/css/biblat.css b/ci/assets/css/biblat.css index cfd2cf0b2..9218183e4 100644 --- a/ci/assets/css/biblat.css +++ b/ci/assets/css/biblat.css @@ -772,3 +772,5 @@ table.articulo{ #floatTable { padding: 0px 20px; } + +.g-recaptcha div { margin-left: auto; margin-right: auto;}