commit be58a50cb9bea20a26f78b5b684aaa68de9d4586
Author: Assos Assos <assos+forge@centrale-marseille.fr>
Date:   Thu Mar 23 00:06:04 2017 +0100

    Weekly update of contrib modules

diff --git a/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.inc b/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.inc
index fa835ecb..c0586ac2 100644
--- a/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.inc
+++ b/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.inc
@@ -659,6 +659,14 @@ function ldap_authentication_test_credentials($auth_conf, $sso_login, $authname,
         */
       $credentials_pass = (boolean)($ldap_user);
     }
+    elseif ($ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_USER) {
+       /**
+        * With user bind method, the only way we can reach this part of the
+        * code is when the pw has already been checked and $ldap_user could be
+        * loaded, so we're good to go.
+        */
+      $credentials_pass = true;
+    }
     else {
       $credentials_pass = ($ldap_server->bind($ldap_user['dn'], $password, FALSE) == LDAP_SUCCESS);
     }
diff --git a/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.info b/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.info
index 610261fc..cbead42c 100644
--- a/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.info
+++ b/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.info
@@ -14,9 +14,9 @@ files[] = ldap_authentication.theme.inc
 files[] = ldap_authentication.admin.inc
 files[] = tests/ldap_authentication.test
 
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.module b/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.module
index d60aa39e..3b1409cc 100644
--- a/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.module
+++ b/drupal7/sites/all/modules/ldap/ldap_authentication/ldap_authentication.module
@@ -212,7 +212,7 @@ function _ldap_authentication_verify_user_profile() {
       );
     }
   }
-  return TRUE;
+  return $result;
 }
 
 /**
@@ -335,7 +335,7 @@ function ldap_authentication_show_reset_pwd($user = NULL) {
   $auth_conf = ldap_authentication_get_valid_conf();
   // Hide user/password form if ldap authentication is required and deny access
   // to users without ldap authorizations is enabled
-  if ($user->uid == 1 || $auth_conf || (current_path() == 'user/password' && $auth_conf->authenticationMode != LDAP_AUTHENTICATION_EXCLUSIVE)) {
+  if ($user->uid == 1 || !$auth_conf || (current_path() == 'user/password' && $auth_conf->authenticationMode != LDAP_AUTHENTICATION_EXCLUSIVE)) {
     return TRUE;
    // always show at user/passwordurl. otherwise user 1 will not be able to reset password.
   }
diff --git a/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization.info b/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization.info
index a987d7f2..bdaf0ff7 100644
--- a/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization.info
+++ b/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization.info
@@ -17,9 +17,9 @@ files[] = tests/BasicTests.test
 files[] = tests/Og2Tests.test
 configure = admin/config/people/ldap/authorization
 
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_drupal_role/ldap_authorization_drupal_role.info b/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_drupal_role/ldap_authorization_drupal_role.info
index f49df6c6..52727904 100644
--- a/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_drupal_role/ldap_authorization_drupal_role.info
+++ b/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_drupal_role/ldap_authorization_drupal_role.info
@@ -9,9 +9,9 @@ files[] = LdapAuthorizationConsumerRole.class.php
 files[] = ldap_authorization_drupal_role.module
 files[] = ldap_authorization_drupal_role.inc
 
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_og/ldap_authorization_og.info b/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_og/ldap_authorization_og.info
index 394d1ab6..5e87dd8a 100644
--- a/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_og/ldap_authorization_og.info
+++ b/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_og/ldap_authorization_og.info
@@ -13,9 +13,9 @@ files[] = ldap_authorization_og.inc
 
 core = "7.x"
 
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_og/ldap_authorization_og.module b/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_og/ldap_authorization_og.module
index 45b742a3..682f201b 100644
--- a/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_og/ldap_authorization_og.module
+++ b/drupal7/sites/all/modules/ldap/ldap_authorization/ldap_authorization_og/ldap_authorization_og.module
@@ -141,9 +141,9 @@ function ldap_authorization_og1_get_group($value, $value_type = 'group_name', $r
 /**
  * Generic function to convert between query values and organic groups structures and attributes
  *
- * @param mixed $value signifies query value e.g. 'bakers', 7 etc.
- * @param mixed $value_type signifies query type e.g. 'group_name', 'gid', etc.
- * @param string $return signifying return type. e.g.  'object', 'label', 'name', 'gid'
+ * @param mixed $entity_type signifies query value e.g. 'bakers', 7 etc.
+ * @param mixed $group_name signifies query type e.g. 'group_name', 'gid', etc.
+ *
  * @return mixed organic group object, gid, label, etc.
  */
 function ldap_authorization_og2_get_group_from_name($entity_type, $group_name) {
diff --git a/drupal7/sites/all/modules/ldap/ldap_feeds/FeedsLdapEntryParser.inc b/drupal7/sites/all/modules/ldap/ldap_feeds/FeedsLdapEntryParser.inc
index 0a437bc2..0b11b0ae 100644
--- a/drupal7/sites/all/modules/ldap/ldap_feeds/FeedsLdapEntryParser.inc
+++ b/drupal7/sites/all/modules/ldap/ldap_feeds/FeedsLdapEntryParser.inc
@@ -44,7 +44,7 @@ class FeedsLdapEntryParser extends FeedsParser {
           }
         }
         else {
-          $parsed_item[$source] = ' ';
+          $parsed_item[$source] = '';
         }
       }
       $parsed_items[] = $parsed_item;
diff --git a/drupal7/sites/all/modules/ldap/ldap_feeds/ldap_feeds.info b/drupal7/sites/all/modules/ldap/ldap_feeds/ldap_feeds.info
index 0407c9da..e8bc244f 100644
--- a/drupal7/sites/all/modules/ldap/ldap_feeds/ldap_feeds.info
+++ b/drupal7/sites/all/modules/ldap/ldap_feeds/ldap_feeds.info
@@ -12,9 +12,9 @@ configure = admin/structure/feeds
 core = 7.x
 php = 5.2
 
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_help/ldap_help.info b/drupal7/sites/all/modules/ldap/ldap_help/ldap_help.info
index 2866e076..fd82e0d3 100644
--- a/drupal7/sites/all/modules/ldap/ldap_help/ldap_help.info
+++ b/drupal7/sites/all/modules/ldap/ldap_help/ldap_help.info
@@ -5,9 +5,9 @@ core = 7.x
 
 dependencies[] = ldap_servers
 dependencies[] = ldap_test
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_query/ldap_query.admin.inc b/drupal7/sites/all/modules/ldap/ldap_query/ldap_query.admin.inc
index 6f7f227c..5503af84 100644
--- a/drupal7/sites/all/modules/ldap/ldap_query/ldap_query.admin.inc
+++ b/drupal7/sites/all/modules/ldap/ldap_query/ldap_query.admin.inc
@@ -105,8 +105,8 @@ function ldap_query_admin_form_submit($form, &$form_state) {
    // }
   }
   else {
-    form_set_error($server->errorName(), $server->errorMsg());
-    $server->clearError();
+    form_set_error($query->errorName(), $query->errorMsg());
+    $query->clearError();
   }
   ldap_query_cache_clear();
 }
diff --git a/drupal7/sites/all/modules/ldap/ldap_query/ldap_query.info b/drupal7/sites/all/modules/ldap/ldap_query/ldap_query.info
index 8c3a3f68..549a33d6 100644
--- a/drupal7/sites/all/modules/ldap/ldap_query/ldap_query.info
+++ b/drupal7/sites/all/modules/ldap/ldap_query/ldap_query.info
@@ -17,9 +17,9 @@ files[] = tests/ldap_query.test
 
 configure = admin/config/people/ldap/query
 
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_servers/LdapServer.class.php b/drupal7/sites/all/modules/ldap/ldap_servers/LdapServer.class.php
index a07205bf..97d9abcd 100644
--- a/drupal7/sites/all/modules/ldap/ldap_servers/LdapServer.class.php
+++ b/drupal7/sites/all/modules/ldap/ldap_servers/LdapServer.class.php
@@ -327,7 +327,7 @@ class LdapServer {
       return LDAP_CONNECT_ERROR;
     }
 
-    if ($anon_bind !== FALSE && $userdn === NULL && $pass === NULL && $this->bind_method == LDAP_SERVERS_BIND_METHOD_ANON) {
+    if ($anon_bind === FALSE && $userdn === NULL && $pass === NULL && $this->bind_method == LDAP_SERVERS_BIND_METHOD_ANON) {
       $anon_bind = TRUE;
     }
     if ($anon_bind === TRUE) {
@@ -463,11 +463,15 @@ class LdapServer {
       return FALSE;
     }
 
+    if (!empty($attributes['unicodePwd']) && ($this->ldap_type == 'ad')) {
+      $attributes['unicodePwd'] = ldap_servers_convert_password_for_active_directory_unicodePwd($attributes['unicodePwd']);
+    }
+
     $result = @ldap_add($this->connection, $dn, $attributes);
     if (!$result) {
       $error = "LDAP Server ldap_add(%dn) Error Server ID = %sid, LDAP Err No: %ldap_errno LDAP Err Message: %ldap_err2str ";
       $tokens = array('%dn' => $dn, '%sid' => $this->sid, '%ldap_errno' => ldap_errno($this->connection), '%ldap_err2str' => ldap_err2str(ldap_errno($this->connection)));
-      debug(t($error, $tokens));
+      //debug(t($error, $tokens));
       watchdog('ldap_server', $error, $tokens, WATCHDOG_ERROR);
     }
 
@@ -549,6 +553,11 @@ class LdapServer {
         $old_attributes =  $entries[0];
       }
     }
+
+    if (!empty($attributes['unicodePwd']) && ($this->ldap_type == 'ad')) {
+      $attributes['unicodePwd'] = ldap_servers_convert_password_for_active_directory_unicodePwd($attributes['unicodePwd']);
+    }
+
     $attributes = $this->removeUnchangedAttributes($attributes, $old_attributes);
 
     foreach ($attributes as $key => $cur_val) {
@@ -1139,7 +1148,12 @@ class LdapServer {
         && isset($ldap_entry[$this->unique_persistent_attr][0])
         && is_scalar($ldap_entry[$this->unique_persistent_attr][0])
         ) {
-      $puid = $ldap_entry[$this->unique_persistent_attr][0];
+      if (is_array($ldap_entry[$this->unique_persistent_attr])) {
+        $puid = $ldap_entry[$this->unique_persistent_attr][0];
+      }
+      else {
+        $puid = $ldap_entry[$this->unique_persistent_attr];
+      }
       return ($this->unique_persistent_attr_binary) ? ldap_servers_binary($puid) : $puid;
     }
     else {
diff --git a/drupal7/sites/all/modules/ldap/ldap_servers/LdapServerAdmin.class.php b/drupal7/sites/all/modules/ldap/ldap_servers/LdapServerAdmin.class.php
index be8c2e75..3490f8f0 100644
--- a/drupal7/sites/all/modules/ldap/ldap_servers/LdapServerAdmin.class.php
+++ b/drupal7/sites/all/modules/ldap/ldap_servers/LdapServerAdmin.class.php
@@ -691,6 +691,7 @@ public function drupalFormSubmit($op, $values) {
           'size' => 'small',
           'not null' => FALSE,
           'default' => 0,
+          'boolean' => FALSE,
         ),
       ),
 
@@ -877,8 +878,12 @@ public function drupalFormSubmit($op, $values) {
         'form' => array(
           'fieldset' => 'users',
           '#type' => 'checkbox',
-          '#title' => t('Does PUID hold a binary value?'),
-          '#description' => t(''),
+          '#title' => t('Does the <em>Persistent and Unique User ID
+            Attribute</em> hold a binary value?'),
+          '#description' => t('You need to set this if you are using a binary
+             attribute such as objectSid in ActiveDirectory for the PUID.<br>
+             If you don\'t want this consider switching to another attribute,
+             such as samaccountname.'),
         ),
         'schema' => array(
           'type' => 'int',
diff --git a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.drush.inc b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.drush.inc
new file mode 100644
index 00000000..1527af0f
--- /dev/null
+++ b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.drush.inc
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ *   LDAP module drush integration.
+ */
+
+/**
+ * Implements hook_drush_command().
+ *
+ * @return
+ *   An associative array describing your command(s).
+ *
+ * @see drush_parse_command()
+ */
+function ldap_servers_drush_command() {
+  $items['ldap-servers-set-password'] = array(
+    'description' => 'Set the password of an otherwise already configured LDAP server.',
+    'aliases' => array('lssp'),
+    'arguments' => array(
+      'ldap_sid' => 'The configured LDAP server ID.'
+    ),
+    'options' => array(
+      'password' => 'The password to set for the server.'
+    ),
+    'examples' => array(
+      'drush lssp LDAP_SERVER_ID --password="PASSWORD"' => 'Sets the password for LDAP_SERVER_ID. Replace LDAP_SERVER_ID with the "Machine name for this configuration." of
+        the server found at /admin/config/people/ldap/servers, and replace PASSWORD with your password.',
+    ),
+  );
+  return $items;
+}
+
+/**
+ * Callback for the ldap-servers-set-password command.
+ *
+ * @param string $ldap_sid The server ID for which to set the password.
+ * @option string --password Used to provide the password via an option in the Drush command.
+ *
+ * @return
+ *   Error or success message.
+ */
+function drush_ldap_servers_set_password($ldap_sid = NULL) {
+  // Check for the argument.
+  if (!isset($ldap_sid)) {
+    return drush_set_error(t('The server ID was not included as an argument. Use the "Machine name for this server configuration." found on the edit screen for that server.'));
+  }
+  // Instantiate the server configuration with the provided sid.
+  ldap_servers_module_load_include('php', 'ldap_servers', 'LdapServerAdmin.class');
+  $ldap_server = new LdapServerAdmin($ldap_sid);
+  if (isset($ldap_server)) {
+    // Retrieves the password from the --password option set in the drush command.
+    $ldap_server->bindpw = drush_get_option('password');
+    if (!isset($ldap_server->bindpw)) {
+      return drupal_set_message(dt('No password was provided for @ldap_sid. A password has not been set.', array('@ldap_sid' => $ldap_sid)));
+    }
+    // Save the server configuration with the password.
+    $ldap_server->save('edit');
+    // Notify of success.
+    return drupal_set_message(dt('Password for @ldap_sid has been set.', array('@ldap_sid' => $ldap_sid)));
+  }
+  // Provided server ID does not match any of the existing server IDs.
+  return drush_set_error(dt('@ldap_sid does not match the server ID of any configured servers.  Use the "Machine name for this server configuration." found on the edit screen for that server.',
+    array('@ldap_sid' => $ldap_sid)));
+}
diff --git a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.encryption.inc b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.encryption.inc
index 3e65c985..f3c34f62 100644
--- a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.encryption.inc
+++ b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.encryption.inc
@@ -173,14 +173,17 @@ function _ldap_servers_decrypt($cipher_txt, $enc_type = NULL) {
         $key = substr($key, 0, $key_length);
         // Determine the algorithm IV.
         $ivsize = mcrypt_enc_get_iv_size($td);
-        // Split apart IV and text.
-        $iv = substr($cipher_txt, 0, $ivsize);
-        $cipher_txt = substr($cipher_txt, $ivsize);
-        // If the IV exists, decrypt the text.
-        if ($iv) {
-          mcrypt_generic_init($td, $key, $iv);
-          $clear_txt = mdecrypt_generic($td, $cipher_txt);
-          mcrypt_generic_deinit($td);
+        // Process if the decoded cipher text is sufficient.
+        if (strlen($cipher_txt) > $ivsize) {
+          // Split apart IV and text.
+          $iv = substr($cipher_txt, 0, $ivsize);
+          $cipher_txt = substr($cipher_txt, $ivsize);
+          // If the IV exists, decrypt the text.
+          if ($iv) {
+            mcrypt_generic_init($td, $key, $iv);
+            $clear_txt = mdecrypt_generic($td, $cipher_txt);
+            mcrypt_generic_deinit($td);
+          }
         }
         // Close the module.
         mcrypt_module_close($td);
diff --git a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.functions.inc b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.functions.inc
index 928d3e63..625fa738 100644
--- a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.functions.inc
+++ b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.functions.inc
@@ -47,6 +47,28 @@ function ldap_password_modify($userdn, $new_password, $ldap_server) {
   return $status;
 }
 
+/**
+ * Converts a password to the format that Active Directory supports (for the 
+ * purpose of changing or setting).  Note that AD needs the field to be called
+ * unicodePwd (as opposed to userPassword)
+ * 
+ * @param string $password
+ *    The password that is being formatted for Active Directory unicodePwd field
+ * @return string
+ *    $password surrounded with quotes and in UTF-16LE encoding
+ */
+function ldap_servers_convert_password_for_active_directory_unicodePwd($password) {
+  // This function can be called with $attributes['unicodePwd'] as an array.
+  if (!is_array($password)) {
+    return mb_convert_encoding("\"{$password}\"", "UTF-16LE");
+  }
+  else {
+    // Presumably there is no use case for there being more than one password in
+    // the $attributes array, hence it will be at index 0 and we return in kind.
+    return array(mb_convert_encoding("\"{$password[0]}\"", "UTF-16LE"));
+  }
+}
+
 /**
  *
  *  this attempts to find bad dns, but should only be used as warnings
@@ -226,6 +248,9 @@ function ldap_server_massage_text($value, $value_type, $context) {
         if ($is_scalar) {
             $values = array($values);
         }
+        if ($values === NULL) {
+          return NULL;
+        }
 
        foreach ($values as $key => $val) {
             // Escaping of filter meta characters
diff --git a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.info b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.info
index 419d6047..4b3734ec 100644
--- a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.info
+++ b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.info
@@ -17,9 +17,9 @@ files[] = ldap_servers.theme.inc
 files[] = tests/ldap_servers.test
 configure = admin/config/people/ldap/servers
 
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.install b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.install
index 24f6cb93..2bffd414 100644
--- a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.install
+++ b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.install
@@ -84,7 +84,14 @@ function ldap_servers_requirements($phase) {
         try {
           $connect_result = $server->connect();
           if ($connect_result == LDAP_SUCCESS) {
-            $bind_result = $server->bind();
+            if ($server->bind_method == LDAP_SERVERS_BIND_METHOD_USER) {
+              // Check bind anonymously if set to bind via user credentials.
+              // The user has already logged in and so their is no dn or password
+              // to check since LDAP doesn't store passwords.
+              $bind_result = $server->bind(NULL, NULL, TRUE);
+            } else {
+              $bind_result = $server->bind();
+            }
             if ($bind_result == LDAP_SUCCESS) {
               $requirements[$server_req_key]['value'] = $t('Connection: Success, Bind: Success', $server_tokens);
               $requirements[$server_req_key]['severity'] = REQUIREMENT_OK;
diff --git a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.module b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.module
index 903620a0..d34fa513 100644
--- a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.module
+++ b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.module
@@ -320,12 +320,15 @@ function ldap_servers_theme() {
       if (!isset($attributes[$ldap_server->mail_attr])) {
         $attributes[$ldap_server->mail_attr] = ldap_servers_set_attribute_map();
       }
-      if (!isset($attributes[$ldap_server->picture_attr])) {
+      if ($ldap_server->picture_attr && !isset($attributes[$ldap_server->picture_attr])) {
         $attributes[$ldap_server->picture_attr] = ldap_servers_set_attribute_map();
       }
       if ($ldap_server->unique_persistent_attr && !isset($attributes[$ldap_server->unique_persistent_attr])) {
         $attributes[$ldap_server->unique_persistent_attr] = ldap_servers_set_attribute_map();
       }
+      if ($ldap_server->groupUserMembershipsAttr && !isset($attributes[$ldap_server->groupUserMembershipsAttr])) {
+        $attributes[$ldap_server->groupUserMembershipsAttr] = ldap_servers_set_attribute_map();
+      }
       if ($ldap_server->user_dn_expression) {
         ldap_servers_token_extract_attributes($attributes, $ldap_server->user_dn_expression, TRUE);
       }
diff --git a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.test_form.inc b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.test_form.inc
index e0f9640a..a8756333 100644
--- a/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.test_form.inc
+++ b/drupal7/sites/all/modules/ldap/ldap_servers/ldap_servers.test_form.inc
@@ -277,7 +277,9 @@ function ldap_servers_test_form_submit($form, &$form_state) {
     $group_dn = $values['grp_test_grp_dn'];
 
     $result = @ldap_read($ldap_server->connection, $group_dn, 'objectClass=*');
-    $group_entry = ldap_get_entries($ldap_server->connection, $result);
+    if (is_array($result)) {
+      $group_entry = ldap_get_entries($ldap_server->connection, $result);
+    }
     $user = isset($values['testing_drupal_username']) ? $values['testing_drupal_username'] : NULL;
 
     foreach (array(FALSE, TRUE) as $nested) { //FALSE
@@ -347,7 +349,7 @@ function ldap_servers_test_form_submit($form, &$form_state) {
     $form_state['ldap_server_test_data']['ldap_user'] = $ldap_user;
   }
 
-  if ( isset($group_entry) ) {
+  if (isset($group_entry) ) {
     $form_state['ldap_server_test_data']['group_entry'] = $group_entry;
   }
   
diff --git a/drupal7/sites/all/modules/ldap/ldap_sso/ldap_sso.info b/drupal7/sites/all/modules/ldap/ldap_sso/ldap_sso.info
index 4bd2c493..bd9efbd3 100644
--- a/drupal7/sites/all/modules/ldap/ldap_sso/ldap_sso.info
+++ b/drupal7/sites/all/modules/ldap/ldap_sso/ldap_sso.info
@@ -6,9 +6,9 @@ dependencies[] = ldap_authentication
 core = 7.x
 configure = admin/config/people/ldap/authentication
 
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_test/ldap_test.info b/drupal7/sites/all/modules/ldap/ldap_test/ldap_test.info
index feedf4ad..2e9d8d3b 100644
--- a/drupal7/sites/all/modules/ldap/ldap_test/ldap_test.info
+++ b/drupal7/sites/all/modules/ldap/ldap_test/ldap_test.info
@@ -18,9 +18,9 @@ files[] = LdapServerTest.class.php
 files[] = LdapTestFunctions.class.php
 files[] = LdapUserTestCase.class.php
 
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_user/LdapUserConf.class.php b/drupal7/sites/all/modules/ldap/ldap_user/LdapUserConf.class.php
index 2bfbef8d..0dac10d9 100644
--- a/drupal7/sites/all/modules/ldap/ldap_user/LdapUserConf.class.php
+++ b/drupal7/sites/all/modules/ldap/ldap_user/LdapUserConf.class.php
@@ -509,7 +509,7 @@ class LdapUserConf {
     if (is_scalar($account)) {
       $username = $account;
       $account = new stdClass();
-      $acount->name = $username;
+      $account->name = $username;
     }
 
     list($account, $user_entity) = ldap_user_load_user_acct_and_entity($account->name);
@@ -867,9 +867,8 @@ class LdapUserConf {
     // determine server that is associated with user
 
     $boolean_result = FALSE;
-    $language = ($account->language) ? $account->language : LANGUAGE_NONE;
-    if (isset($account->ldap_user_prov_entries[$language][0])) {
-      foreach ($account->ldap_user_prov_entries[$language] as $i => $field_instance) {
+    if (isset($account->ldap_user_prov_entries[LANGUAGE_NONE][0])) {
+      foreach ($account->ldap_user_prov_entries[LANGUAGE_NONE] as $i => $field_instance) {
         $parts = explode('|', $field_instance['value']);
         if (count($parts) == 2) {
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_user/LdapUserConfAdmin.class.php b/drupal7/sites/all/modules/ldap/ldap_user/LdapUserConfAdmin.class.php
index 6b7c8f0e..bd984fec 100644
--- a/drupal7/sites/all/modules/ldap/ldap_user/LdapUserConfAdmin.class.php
+++ b/drupal7/sites/all/modules/ldap/ldap_user/LdapUserConfAdmin.class.php
@@ -998,23 +998,7 @@ EOT;
 
     );
 
-    $values['orphanedDrupalAcctBehaviorDescription'] = '<ul>' .
-       '<li style="color: red">' . t('This is a new feature as of 11/7/2012!
-        It is highly recommended to use the "Perform no action,
-        but email list of orphaned accounts" for some time before using the
-        "Disable the account" options.') . '</li>' .
-
-      '<li>' . t('These actions will only occur if the query to server is successful
-      and does not return a user entry.  If the ldap server is down or otherwise
-      cannot be connected to, no actions
-      will be performed.') . '</li>' .
-
-      '<li>' .
-      t('When initially using this option, its best to simply check email and
-        delete the accounts by hand.  When you are happy with the
-      behavior, switch to one of the automated options.') . '</li>' .
-
-      '</ul>';
+    $values['orphanedDrupalAcctBehaviorDescription'] = t('It is highly recommended to use the "Perform no action, but email list of orphaned accounts" for some time before considering switching to "Disable the account" options.');
 
 
     $values['manualAccountConflictOptions'] =  array(
diff --git a/drupal7/sites/all/modules/ldap/ldap_user/ldap_user.info b/drupal7/sites/all/modules/ldap/ldap_user/ldap_user.info
index 8c42525c..a69cecb4 100644
--- a/drupal7/sites/all/modules/ldap/ldap_user/ldap_user.info
+++ b/drupal7/sites/all/modules/ldap/ldap_user/ldap_user.info
@@ -23,9 +23,9 @@ files[] = tests/ldap_user.test
 stylesheets[all][] = ldap_user.css
 configure = admin/config/people/ldap/user
 
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/ldap/ldap_user/ldap_user.module b/drupal7/sites/all/modules/ldap/ldap_user/ldap_user.module
index 3dbf733c..8cca143a 100644
--- a/drupal7/sites/all/modules/ldap/ldap_user/ldap_user.module
+++ b/drupal7/sites/all/modules/ldap/ldap_user/ldap_user.module
@@ -228,8 +228,12 @@ function ldap_user_ldap_attributes_needed_alter(&$attributes, $params) {
       case 'all':
         $attributes[$ldap_server->user_attr] = ldap_servers_set_attribute_map(@$attributes[$ldap_server->user_attr]); // array($ldap_server->user_attr, 0, NULL);
         $attributes[$ldap_server->mail_attr] = ldap_servers_set_attribute_map(@$attributes[$ldap_server->mail_attr]);
-        $attributes[$ldap_server->picture_attr] = ldap_servers_set_attribute_map(@$attributes[$ldap_server->picture_attr]);
-        $attributes[$ldap_server->unique_persistent_attr] = ldap_servers_set_attribute_map(@$attributes[$ldap_server->unique_persistent_attr]);
+        if ($ldap_server->picture_attr) {
+          $attributes[$ldap_server->picture_attr] = ldap_servers_set_attribute_map(@$attributes[$ldap_server->picture_attr]);
+        }
+        if ($ldap_server->unique_persistent_attr) {
+          $attributes[$ldap_server->unique_persistent_attr] = ldap_servers_set_attribute_map(@$attributes[$ldap_server->unique_persistent_attr]);
+        }
         if ($ldap_server->mail_template) {
           ldap_servers_token_extract_attributes($attributes,  $ldap_server->mail_template);
         }
@@ -491,6 +495,15 @@ function ldap_user_form_password_policy_password_tab_alter(&$form, &$form_state)
   array_unshift($form['#validate'], 'ldap_user_grab_password_validate');
 }
 
+/**
+* Implements hook_form_FORM_ID_alter(). for user-pass-reset form. Useful for
+* sites where this is the form ID for a user to intially set their password
+* (user clicks an emailed registration link, is prompted to set their password).
+*/
+function ldap_user_form_user_pass_reset_alter(&$form, &$form_state) {
+  array_unshift($form['#validate'], 'ldap_user_grab_password_validate');
+}
+
 /**
  * store password from logon forms in ldap_user_ldap_provision_pwd static variable
  * for use in provisioning to ldap
@@ -763,10 +776,8 @@ function ldap_user_provision_to_drupal($ldap_user, $user_edit = array()) {
  *   and protect unencrypted user password from other modules
  *
  *   @param enum string $action 'get' | 'set'
- *   @param string | FALE $value as user entered password
+ *   @param string | FALSE $value as user entered password
  */
-
-
 function ldap_user_ldap_provision_pwd($action, $value = NULL, $reset = FALSE) {
 
   //$calling_function = FALSE;
diff --git a/drupal7/sites/all/modules/ldap/ldap_views/ldap_views.info b/drupal7/sites/all/modules/ldap/ldap_views/ldap_views.info
index ba514fbb..ff917551 100644
--- a/drupal7/sites/all/modules/ldap/ldap_views/ldap_views.info
+++ b/drupal7/sites/all/modules/ldap/ldap_views/ldap_views.info
@@ -21,9 +21,9 @@ files[] = handlers/ldap_views_handler_filter.inc
 files[] = handlers/ldap_views_handler_filter_attribute.inc
 files[] = handlers/ldap_views_handler_sort.inc
 files[] = handlers/ldap_views_handler_sort_attribute.inc
-; Information added by Drupal.org packaging script on 2017-02-15
-version = "7.x-2.0-beta12"
+; Information added by Drupal.org packaging script on 2017-03-18
+version = "7.x-2.0"
 core = "7.x"
 project = "ldap"
-datestamp = "1487199794"
+datestamp = "1489858388"
 
diff --git a/drupal7/sites/all/modules/recaptcha/README.txt b/drupal7/sites/all/modules/recaptcha/README.txt
index 56b7d187..f09d2807 100644
--- a/drupal7/sites/all/modules/recaptcha/README.txt
+++ b/drupal7/sites/all/modules/recaptcha/README.txt
@@ -6,14 +6,13 @@ improve the CAPTCHA system and protect email addresses. For
 more information on what reCAPTCHA is, please visit:
     https://www.google.com/recaptcha
 
+This version of the module uses the new Google No CAPTCHA reCAPTCHA API.
 
 DEPENDENCIES
 ------------
 
-* reCAPTCHA depends on the CAPTCHA module.
+* reCAPTCHA module depends on the CAPTCHA module.
   https://drupal.org/project/captcha
-* Some people have found that they also need to use jQuery Update module.
-  https://drupal.org/project/jquery_update
 
 
 CONFIGURATION
@@ -26,79 +25,28 @@ CONFIGURATION
    administration page available at:
        admin/config/people/captcha/recaptcha
 
-3. Register for a public and private reCAPTCHA key at:
+3. Register your web site at
        https://www.google.com/recaptcha/admin/create
 
-4. Input the keys into the reCAPTCHA settings. The rest of
-   the settings should be fine as their defaults.
+4. Input the site and private keys into the reCAPTCHA settings.
 
 5. Visit the Captcha administration page and set where you
    want the reCAPTCHA form to be presented:
        admin/config/people/captcha
 
+KNOWN ISSUES
+------------
 
-MAILHIDE INPUT FORMAT
----------------------
-
-The reCAPTCHA module also comes with an input format to
-protect email addresses. This, of course, is optional to
-use and is only there if you want it. The following is how
-you use that input filter:
-
-1. Enable the reCAPTCHA Mailhide module:
-       admin/modules
-
-2. Head over to your text format settings:
-       admin/config/content/formats
-
-3. Edit your default input format and add the reCAPTCHA
-   Mailhide filter.
-
-4. Click on the Configure tab and put in a public and
-   private Mailhide key obtained from:
-       https://www.google.com/recaptcha/mailhide/apikey
-
-5. Use the Rearrange tab to rearrange the weight of the
-   filter depending on what filters already exist.  Make
-   sure it is before the URL Filter.
-
-Note: You will require the installation of the mcrypt
-      PHP module in your web server for Mailhide to work:
-         http://php.net/manual/en/ref.mcrypt.php
-
-
-MULTI-DOMAIN SUPPORT
---------------------
-
-Since reCAPTCHA uses API keys that are unique to each
-domain, if you're using a multi-domain system using the
-same database, the reCAPTCHA module won't work when
-querying the reCAPTCHA web service.  If you put the
-following into your sites/mysite/settings.php file for
-each domain, it will override the API key values and make
-it so multi-domain systems are capable.
-
-  $conf = array(
-    'recaptcha_public_key' =>  'my other public key',
-    'recaptcha_private_key' =>  'my other private key',
-  );
-
-
-CUSTOM RECAPTCHA THEME
-----------------------
-
-You can create a custom reCAPTCHA theme widget by setting
-the theme of the reCAPTCHA form to "custom" in the
-reCAPTCHA administration page.  This will output a custom
-form that is themeable through the theme function:
-  theme_recaptcha_custom_widget().
-
-If you don't implement this function, it is still quite
-easily customizable through manipulating the CSS.
-
-For more information on this, visit:
-https://developers.google.com/recaptcha/docs/customization
+- The PHP setting 'arg_separator.output' set by Drupal core causes conflicts
+  with the Google reCAPTCHA library. This setting is and was never used by
+  Drupal core, but still exist in "settings.php" and need to be removed.
+    
+  See https://www.drupal.org/node/2476237 for more information.
 
+- cURL requests fail because of outdated root certificate. The reCAPTCHA module
+  may not able to connect to Google servers and fails to verify the answer.
+  
+  See https://www.drupal.org/node/2481341 for more information.
 
 THANK YOU
 ---------
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/LICENSE b/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/LICENSE
deleted file mode 100644
index b612f71f..00000000
--- a/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net
-AUTHORS:
-  Mike Crawford
-  Ben Maurer
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/README b/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/README
deleted file mode 100644
index 21f1a276..00000000
--- a/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/README
+++ /dev/null
@@ -1,7 +0,0 @@
-reCAPTCHA README
-================
-
-The reCAPTCHA PHP Lirary helps you use the reCAPTCHA API. Documentation
-for this library can be found at
-
-	http://recaptcha.net/plugins/php
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/example-captcha.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/example-captcha.php
deleted file mode 100644
index f6f7fa03..00000000
--- a/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/example-captcha.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<html>
-  <body>
-    <form action="" method="post">
-<?php
-
-require_once('recaptchalib.php');
-
-// Get a key from https://www.google.com/recaptcha/admin/create
-$publickey = "";
-$privatekey = "";
-
-# the response from reCAPTCHA
-$resp = null;
-# the error code from reCAPTCHA, if any
-$error = null;
-
-# was there a reCAPTCHA response?
-if ($_POST["recaptcha_response_field"]) {
-        $resp = recaptcha_check_answer ($privatekey,
-                                        $_SERVER["REMOTE_ADDR"],
-                                        $_POST["recaptcha_challenge_field"],
-                                        $_POST["recaptcha_response_field"]);
-
-        if ($resp->is_valid) {
-                echo "You got it!";
-        } else {
-                # set the error code so that we can display it
-                $error = $resp->error;
-        }
-}
-echo recaptcha_get_html($publickey, $error);
-?>
-    <br/>
-    <input type="submit" value="submit" />
-    </form>
-  </body>
-</html>
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/example-mailhide.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/example-mailhide.php
deleted file mode 100644
index d08622c7..00000000
--- a/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/example-mailhide.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<html><body>
-<?
-require_once ("recaptchalib.php");
-
-// get a key at http://www.google.com/recaptcha/mailhide/apikey
-$mailhide_pubkey = '';
-$mailhide_privkey = '';
-
-?>
-
-The Mailhide version of example@example.com is
-<? echo recaptcha_mailhide_html ($mailhide_pubkey, $mailhide_privkey, "example@example.com"); ?>. <br>
-
-The url for the email is:
-<? echo recaptcha_mailhide_url ($mailhide_pubkey, $mailhide_privkey, "example@example.com"); ?> <br>
-
-</body></html>
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/recaptchalib.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/recaptchalib.php
deleted file mode 100644
index 32c4f4d7..00000000
--- a/drupal7/sites/all/modules/recaptcha/recaptcha-php-1.11/recaptchalib.php
+++ /dev/null
@@ -1,277 +0,0 @@
-<?php
-/*
- * This is a PHP library that handles calling reCAPTCHA.
- *    - Documentation and latest version
- *          http://recaptcha.net/plugins/php/
- *    - Get a reCAPTCHA API Key
- *          https://www.google.com/recaptcha/admin/create
- *    - Discussion group
- *          http://groups.google.com/group/recaptcha
- *
- * Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net
- * AUTHORS:
- *   Mike Crawford
- *   Ben Maurer
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-/**
- * The reCAPTCHA server URL's
- */
-define("RECAPTCHA_API_SERVER", "http://www.google.com/recaptcha/api");
-define("RECAPTCHA_API_SECURE_SERVER", "https://www.google.com/recaptcha/api");
-define("RECAPTCHA_VERIFY_SERVER", "www.google.com");
-
-/**
- * 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 = _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 <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
-	}
-	
-	if ($use_ssl) {
-                $server = RECAPTCHA_API_SECURE_SERVER;
-        } else {
-                $server = RECAPTCHA_API_SERVER;
-        }
-
-        $errorpart = "";
-        if ($error) {
-           $errorpart = "&amp;error=" . $error;
-        }
-        return '<script type="text/javascript" src="'. $server . '/challenge?k=' . $pubkey . $errorpart . '"></script>
-
-	<noscript>
-  		<iframe src="'. $server . '/noscript?k=' . $pubkey . $errorpart . '" height="300" width="500" frameborder="0"></iframe><br/>
-  		<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
-  		<input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>
-	</noscript>';
-}
-
-
-
-
-/**
- * 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 <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
-	}
-
-	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 <a href='http://www.google.com/recaptcha/mailhide/apikey'>http://www.google.com/recaptcha/mailhide/apikey</a>");
-	}
-	
-
-	$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]) . "<a href='" . htmlentities ($url) .
-		"' onclick=\"window.open('" . htmlentities ($url) . "', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;\" title=\"Reveal this e-mail address\">...</a>@" . htmlentities ($emailparts [1]);
-
-}
-
-
-?>
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/CONTRIBUTING.md b/drupal7/sites/all/modules/recaptcha/recaptcha-php/CONTRIBUTING.md
new file mode 100644
index 00000000..1ba85392
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/CONTRIBUTING.md
@@ -0,0 +1,24 @@
+Want to contribute? Great! First, read this page (including the small print at the end).
+
+### Before you contribute
+Before we can use your code, you must sign the
+[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1)
+(CLA), which you can do online. The CLA is necessary mainly because you own the
+copyright to your changes, even after your contribution becomes part of our
+codebase, so we need your permission to use and distribute your code. We also
+need to be sure of various other things—for instance that you'll tell us if you
+know that your code infringes on other people's patents. You don't have to sign
+the CLA until after you've submitted your code for review and a member has
+approved it, but you must do it before we can put your code into our codebase.
+Before you start working on a larger contribution, you should get in touch with
+us first through the issue tracker with your idea so that we can help out and
+possibly guide you. Coordinating up front makes it much easier to avoid
+frustration later on.
+
+### Code reviews
+All submissions, including submissions by project members, require review. We
+use Github pull requests for this purpose.
+
+### The small print
+Contributions made by corporations are covered by a different agreement than
+the one above, the Software Grant and Corporate Contributor License Agreement.
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/LICENSE b/drupal7/sites/all/modules/recaptcha/recaptcha-php/LICENSE
new file mode 100644
index 00000000..f6412328
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/LICENSE
@@ -0,0 +1,29 @@
+Copyright 2014, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+    * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/README.md b/drupal7/sites/all/modules/recaptcha/recaptcha-php/README.md
new file mode 100644
index 00000000..6e81a8ed
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/README.md
@@ -0,0 +1,113 @@
+# reCAPTCHA PHP client library
+
+[![Build Status](https://travis-ci.org/google/recaptcha.svg)](https://travis-ci.org/google/recaptcha)
+[![Latest Stable Version](https://poser.pugx.org/google/recaptcha/v/stable.svg)](https://packagist.org/packages/google/recaptcha)
+[![Total Downloads](https://poser.pugx.org/google/recaptcha/downloads.svg)](https://packagist.org/packages/google/recaptcha)
+
+* Project page: http://www.google.com/recaptcha/
+* Repository: https://github.com/google/recaptcha
+* Version: 1.1.2
+* License: BSD, see [LICENSE](LICENSE)
+
+## Description
+
+reCAPTCHA is a free CAPTCHA service that protect websites from spam and abuse.
+This is Google authored code that provides plugins for third-party integration
+with reCAPTCHA.
+
+## Installation
+
+### Composer (Recommended)
+
+[Composer](https://getcomposer.org/) is a widely used dependency manager for PHP
+packages. This reCAPTCHA client is available on Packagist as
+[`google/recaptcha`](https://packagist.org/packages/google/recaptcha) and can be
+installed either by running the `composer require` command or adding the library
+to your `composer.json`. To enable Composer for you project, refer to the
+project's [Getting Started](https://getcomposer.org/doc/00-intro.md)
+documentation.
+
+To add this dependency using the command, run the following from within your
+project directory:
+```
+composer require google/recaptcha "~1.1"
+```
+
+Alternatively, add the dependency directly to your `composer.json` file:
+```json
+"require": {
+    "google/recaptcha": "~1.1"
+}
+```
+
+### Direct download (no Composer)
+
+If you wish to install the library manually (i.e. without Composer), then you
+can use the links on the main project page to either clone the repo or download
+the [ZIP file](https://github.com/google/recaptcha/archive/master.zip). For
+convenience, an autoloader script is provided in `src/autoload.php` which you
+can require into your script instead of Composer's `vendor/autoload.php`. For
+example:
+
+```php
+require('/path/to/recaptcha/src/autoload.php');
+$recaptcha = new \ReCaptcha\ReCaptcha($secret);
+```
+
+The classes in the project are structured according to the
+[PSR-4](http://www.php-fig.org/psr/psr-4/) standard, so you may of course also
+use your own autoloader or require the needed files directly in your code.
+
+### Development install
+
+If you would like to contribute to this project or run the unit tests on within
+your own environment you will need to install the development dependencies, in
+this case that means [PHPUnit](https://phpunit.de/). If you clone the repo and
+run `composer install` from within the repo, this will also grab PHPUnit and all
+its dependencies for you. If you only need the autoloader installed, then you
+can always specify to Composer not to run in development mode, e.g. `composer
+install --no-dev`.
+
+*Note:* These dependencies are only required for development, there's no
+requirement for them to be included in your production code.
+
+## Usage
+
+First, register keys for your site at https://www.google.com/recaptcha/admin
+
+When your app receives a form submission containing the `g-recaptcha-response`
+field, you can verify it using:
+```php
+<?php
+$recaptcha = new \ReCaptcha\ReCaptcha($secret);
+$resp = $recaptcha->verify($gRecaptchaResponse, $remoteIp);
+if ($resp->isSuccess()) {
+    // verified!
+} else {
+    $errors = $resp->getErrorCodes();
+}
+```
+
+You can see an end-to-end working example in
+[examples/example-captcha.php](examples/example-captcha.php)
+
+## Upgrading
+
+### From 1.0.0
+
+The previous version of this client is still available on the `1.0.0` tag [in
+this repo](https://github.com/google/recaptcha/tree/1.0.0) but it is purely for
+reference and will not receive any updates.
+
+The major changes in 1.1.0 are:
+* installation now via Composer;
+* class loading also via Composer;
+* classes now namespaced;
+* old method call was `$rc->verifyResponse($remoteIp, $response)`, new call is
+  `$rc->verify($response, $remoteIp)`
+
+## Contributing
+
+We accept contributions via GitHub Pull Requests, but all contributors need to
+be covered by the standard Google Contributor License Agreement. You can find
+instructions for this in [CONTRIBUTING](CONTRIBUTING.md)
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/composer.json b/drupal7/sites/all/modules/recaptcha/recaptcha-php/composer.json
new file mode 100644
index 00000000..63b751c8
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/composer.json
@@ -0,0 +1,28 @@
+{
+    "name": "google/recaptcha",
+    "description": "Client library for reCAPTCHA, a free service that protect websites from spam and abuse.",
+    "type": "library",
+    "keywords": ["recaptcha", "captcha", "spam", "abuse"],
+    "homepage": "http://www.google.com/recaptcha/",
+    "license": "BSD-3-Clause",
+    "support": {
+        "forum": "https://groups.google.com/forum/#!forum/recaptcha",
+        "source": "https://github.com/google/recaptcha"
+    },
+    "require": {
+        "php": ">=5.3.2"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "4.5.*"
+    },
+    "autoload": {
+        "psr-4": {
+            "ReCaptcha\\": "src/ReCaptcha"
+        }
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "1.1.x-dev"
+        }
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/examples/example-captcha.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/examples/example-captcha.php
new file mode 100644
index 00000000..8875171b
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/examples/example-captcha.php
@@ -0,0 +1,130 @@
+<?php
+/**
+ * Working sample code to accompany the library. The instructions here assume
+ * you've just cloned the repo. If you've installed via composer, you will want
+ * to adjust the path to the autoloader.
+ *
+ * 1. Run the server. For example, under Linux you can probably use:
+ * /usr/bin/php -S "localhost:8000" "examples/example-captcha.php"
+ * 2. Point your browser at http://localhost:8000
+ * 3. Follow the instructions
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+// Initiate the autoloader. The file should be generated by Composer.
+// You will provide your own autoloader or require the files directly if you did
+// not install via Composer.
+require_once __DIR__ . '/../vendor/autoload.php';
+
+// Register API keys at https://www.google.com/recaptcha/admin
+$siteKey = '';
+$secret = '';
+
+// reCAPTCHA supported 40+ languages listed here: https://developers.google.com/recaptcha/docs/language
+$lang = 'en';
+?>
+<html>
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+        <title>reCAPTCHA Example</title>
+        <link rel="shortcut icon" href="//www.gstatic.com/recaptcha/admin/favicon.ico" type="image/x-icon"/>
+        <style type="text/css">
+            body {
+                margin: 1em 5em 0 5em;
+                font-family: sans-serif;
+            }
+            fieldset {
+                display: inline;
+                padding: 1em;
+            }
+        </style>
+    </head>
+    <body>
+        <h1>reCAPTCHA Example</h1>
+        <?php if ($siteKey === '' || $secret === ''): ?>
+            <h2>Add your keys</h2>
+            <p>If you do not have keys already then visit <tt>
+            <a href = "https://www.google.com/recaptcha/admin">
+                https://www.google.com/recaptcha/admin</a></tt> to generate them.
+        Edit this file and set the respective keys in <tt>$siteKey</tt> and
+        <tt>$secret</tt>. Reload the page after this.</p>
+    <?php
+elseif (isset($_POST['g-recaptcha-response'])):
+    // The POST data here is unfiltered because this is an example.
+    // In production, *always* sanitise and validate your input'
+    ?>
+    <h2><tt>POST</tt> data</h2>
+    <tt><pre><?php var_export($_POST); ?></pre></tt>
+    <?php
+// If the form submission includes the "g-captcha-response" field
+// Create an instance of the service using your secret
+    $recaptcha = new \ReCaptcha\ReCaptcha($secret);
+
+// If file_get_contents() is locked down on your PHP installation to disallow
+// its use with URLs, then you can use the alternative request method instead.
+// This makes use of fsockopen() instead.
+//  $recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\SocketPost());
+
+// Make the call to verify the response and also pass the user's IP address
+    $resp = $recaptcha->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']);
+
+    if ($resp->isSuccess()):
+// If the response is a success, that's it!
+        ?>
+        <h2>Success!</h2>
+        <p>That's it. Everything is working. Go integrate this into your real project.</p>
+        <p><a href="/">Try again</a></p>
+        <?php
+    else:
+// If it's not successfull, then one or more error codes will be returned.
+        ?>
+        <h2>Something went wrong</h2>
+        <p>The following error was returned: <?php
+            foreach ($resp->getErrorCodes() as $code) {
+                echo '<tt>' , $code , '</tt> ';
+            }
+            ?></p>
+        <p>Check the error code reference at <tt><a href="https://developers.google.com/recaptcha/docs/verify#error-code-reference">https://developers.google.com/recaptcha/docs/verify#error-code-reference</a></tt>.
+        <p><strong>Note:</strong> Error code <tt>missing-input-response</tt> may mean the user just didn't complete the reCAPTCHA.</p>
+        <p><a href="/">Try again</a></p>
+    <?php
+    endif;
+else:
+// Add the g-recaptcha tag to the form you want to include the reCAPTCHA element
+    ?>
+    <p>Complete the reCAPTCHA then submit the form.</p>
+    <form action="/" method="post">
+        <fieldset>
+            <legend>An example form</legend>
+            <p>Example input A: <input type="text" name="ex-a" value="foo"></p>
+            <p>Example input B: <input type="text" name="ex-b" value="bar"></p>
+
+            <div class="g-recaptcha" data-sitekey="<?php echo $siteKey; ?>"></div>
+            <script type="text/javascript"
+                    src="https://www.google.com/recaptcha/api.js?hl=<?php echo $lang; ?>">
+            </script>
+            <p><input type="submit" value="Submit" /></p>
+        </fieldset>
+    </form>
+<?php endif; ?>
+</body>
+</html>
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/phpunit.xml.dist b/drupal7/sites/all/modules/recaptcha/recaptcha-php/phpunit.xml.dist
new file mode 100644
index 00000000..43766788
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/phpunit.xml.dist
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.4/phpunit.xsd"
+         colors="true"
+         verbose="true"
+         bootstrap="src/autoload.php">
+    <testsuites>
+        <testsuite name="reCAPTCHA Test Suite">
+            <directory>tests/ReCaptcha/</directory>
+        </testsuite>
+    </testsuites>
+    <filter>
+        <whitelist>
+            <directory suffix=".php">src/ReCaptcha/</directory>
+        </whitelist>
+    </filter>
+</phpunit>
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/ReCaptcha.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/ReCaptcha.php
new file mode 100644
index 00000000..e2f7c347
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/ReCaptcha.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha;
+
+/**
+ * reCAPTCHA client.
+ */
+class ReCaptcha
+{
+    /**
+     * Version of this client library.
+     * @const string
+     */
+    const VERSION = 'php_1.1.2';
+
+    /**
+     * Shared secret for the site.
+     * @var type string
+     */
+    private $secret;
+
+    /**
+     * Method used to communicate  with service. Defaults to POST request.
+     * @var RequestMethod
+     */
+    private $requestMethod;
+
+    /**
+     * Create a configured instance to use the reCAPTCHA service.
+     *
+     * @param string $secret shared secret between site and reCAPTCHA server.
+     * @param RequestMethod $requestMethod method used to send the request. Defaults to POST.
+     */
+    public function __construct($secret, RequestMethod $requestMethod = null)
+    {
+        if (empty($secret)) {
+            throw new \RuntimeException('No secret provided');
+        }
+
+        if (!is_string($secret)) {
+            throw new \RuntimeException('The provided secret must be a string');
+        }
+
+        $this->secret = $secret;
+
+        if (!is_null($requestMethod)) {
+            $this->requestMethod = $requestMethod;
+        } else {
+            $this->requestMethod = new RequestMethod\Post();
+        }
+    }
+
+    /**
+     * Calls the reCAPTCHA siteverify API to verify whether the user passes
+     * CAPTCHA test.
+     *
+     * @param string $response The value of 'g-recaptcha-response' in the submitted form.
+     * @param string $remoteIp The end user's IP address.
+     * @return Response Response from the service.
+     */
+    public function verify($response, $remoteIp = null)
+    {
+        // Discard empty solution submissions
+        if (empty($response)) {
+            $recaptchaResponse = new Response(false, array('missing-input-response'));
+            return $recaptchaResponse;
+        }
+
+        $params = new RequestParameters($this->secret, $response, $remoteIp, self::VERSION);
+        $rawResponse = $this->requestMethod->submit($params);
+        return Response::fromJson($rawResponse);
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod.php
new file mode 100644
index 00000000..fc4dde59
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha;
+
+/**
+ * Method used to send the request to the service.
+ */
+interface RequestMethod
+{
+
+    /**
+     * Submit the request with the specified parameters.
+     *
+     * @param RequestParameters $params Request parameters
+     * @return string Body of the reCAPTCHA response
+     */
+    public function submit(RequestParameters $params);
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/Curl.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/Curl.php
new file mode 100644
index 00000000..20ee5309
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/Curl.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha\RequestMethod;
+
+/**
+ * Convenience wrapper around the cURL functions to allow mocking.
+ */
+class Curl
+{
+
+    /**
+     * @see http://php.net/curl_init
+     * @param string $url
+     * @return resource cURL handle
+     */
+    public function init($url = null)
+    {
+        return curl_init($url);
+    }
+
+    /**
+     * @see http://php.net/curl_setopt_array
+     * @param resource $ch
+     * @param array $options
+     * @return bool
+     */
+    public function setoptArray($ch, array $options)
+    {
+        return curl_setopt_array($ch, $options);
+    }
+
+    /**
+     * @see http://php.net/curl_exec
+     * @param resource $ch
+     * @return mixed
+     */
+    public function exec($ch)
+    {
+        return curl_exec($ch);
+    }
+
+    /**
+     * @see http://php.net/curl_close
+     * @param resource $ch
+     */
+    public function close($ch)
+    {
+        curl_close($ch);
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/CurlPost.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/CurlPost.php
new file mode 100644
index 00000000..e06d1cc8
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/CurlPost.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha\RequestMethod;
+
+use ReCaptcha\RequestMethod;
+use ReCaptcha\RequestParameters;
+
+/**
+ * Sends cURL request to the reCAPTCHA service.
+ * Note: this requires the cURL extension to be enabled in PHP
+ * @see http://php.net/manual/en/book.curl.php
+ */
+class CurlPost implements RequestMethod
+{
+    /**
+     * URL to which requests are sent via cURL.
+     * @const string
+     */
+    const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
+
+    /**
+     * Curl connection to the reCAPTCHA service
+     * @var Curl
+     */
+    private $curl;
+
+    public function __construct(Curl $curl = null)
+    {
+        if (!is_null($curl)) {
+            $this->curl = $curl;
+        } else {
+            $this->curl = new Curl();
+        }
+    }
+
+    /**
+     * Submit the cURL request with the specified parameters.
+     *
+     * @param RequestParameters $params Request parameters
+     * @return string Body of the reCAPTCHA response
+     */
+    public function submit(RequestParameters $params)
+    {
+        $handle = $this->curl->init(self::SITE_VERIFY_URL);
+
+        $options = array(
+            CURLOPT_POST => true,
+            CURLOPT_POSTFIELDS => $params->toQueryString(),
+            CURLOPT_HTTPHEADER => array(
+                'Content-Type: application/x-www-form-urlencoded'
+            ),
+            CURLINFO_HEADER_OUT => false,
+            CURLOPT_HEADER => false,
+            CURLOPT_RETURNTRANSFER => true,
+            CURLOPT_SSL_VERIFYPEER => true
+        );
+        $this->curl->setoptArray($handle, $options);
+
+        $response = $this->curl->exec($handle);
+        $this->curl->close($handle);
+
+        return $response;
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/Post.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/Post.php
new file mode 100644
index 00000000..7770d908
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/Post.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha\RequestMethod;
+
+use ReCaptcha\RequestMethod;
+use ReCaptcha\RequestParameters;
+
+/**
+ * Sends POST requests to the reCAPTCHA service.
+ */
+class Post implements RequestMethod
+{
+    /**
+     * URL to which requests are POSTed.
+     * @const string
+     */
+    const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
+
+    /**
+     * Submit the POST request with the specified parameters.
+     *
+     * @param RequestParameters $params Request parameters
+     * @return string Body of the reCAPTCHA response
+     */
+    public function submit(RequestParameters $params)
+    {
+        /**
+         * PHP 5.6.0 changed the way you specify the peer name for SSL context options.
+         * Using "CN_name" will still work, but it will raise deprecated errors.
+         */
+        $peer_key = version_compare(PHP_VERSION, '5.6.0', '<') ? 'CN_name' : 'peer_name';
+        $options = array(
+            'http' => array(
+                'header' => "Content-type: application/x-www-form-urlencoded\r\n",
+                'method' => 'POST',
+                'content' => $params->toQueryString(),
+                // Force the peer to validate (not needed in 5.6.0+, but still works
+                'verify_peer' => true,
+                // Force the peer validation to use www.google.com
+                $peer_key => 'www.google.com',
+            ),
+        );
+        $context = stream_context_create($options);
+        return file_get_contents(self::SITE_VERIFY_URL, false, $context);
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/Socket.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/Socket.php
new file mode 100644
index 00000000..d3c87922
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/Socket.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha\RequestMethod;
+
+/**
+ * Convenience wrapper around native socket and file functions to allow for
+ * mocking.
+ */
+class Socket
+{
+    private $handle = null;
+
+    /**
+     * fsockopen
+     * 
+     * @see http://php.net/fsockopen
+     * @param string $hostname
+     * @param int $port
+     * @param int $errno
+     * @param string $errstr
+     * @param float $timeout
+     * @return resource
+     */
+    public function fsockopen($hostname, $port = -1, &$errno = 0, &$errstr = '', $timeout = null)
+    {
+        $this->handle = fsockopen($hostname, $port, $errno, $errstr, (is_null($timeout) ? ini_get("default_socket_timeout") : $timeout));
+
+        if ($this->handle != false && $errno === 0 && $errstr === '') {
+            return $this->handle;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * fwrite
+     * 
+     * @see http://php.net/fwrite
+     * @param string $string
+     * @param int $length
+     * @return int | bool
+     */
+    public function fwrite($string, $length = null)
+    {
+        return fwrite($this->handle, $string, (is_null($length) ? strlen($string) : $length));
+    }
+
+    /**
+     * fgets
+     * 
+     * @see http://php.net/fgets
+     * @param int $length
+     * @return string
+     */
+    public function fgets($length = null)
+    {
+        return fgets($this->handle, $length);
+    }
+
+    /**
+     * feof
+     * 
+     * @see http://php.net/feof
+     * @return bool
+     */
+    public function feof()
+    {
+        return feof($this->handle);
+    }
+
+    /**
+     * fclose
+     * 
+     * @see http://php.net/fclose
+     * @return bool
+     */
+    public function fclose()
+    {
+        return fclose($this->handle);
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/SocketPost.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/SocketPost.php
new file mode 100644
index 00000000..47541215
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestMethod/SocketPost.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha\RequestMethod;
+
+use ReCaptcha\RequestMethod;
+use ReCaptcha\RequestParameters;
+
+/**
+ * Sends a POST request to the reCAPTCHA service, but makes use of fsockopen()
+ * instead of get_file_contents(). This is to account for people who may be on
+ * servers where allow_furl_open is disabled.
+ */
+class SocketPost implements RequestMethod
+{
+    /**
+     * reCAPTCHA service host.
+     * @const string
+     */
+    const RECAPTCHA_HOST = 'www.google.com';
+
+    /**
+     * @const string reCAPTCHA service path
+     */
+    const SITE_VERIFY_PATH = '/recaptcha/api/siteverify';
+
+    /**
+     * @const string Bad request error
+     */
+    const BAD_REQUEST = '{"success": false, "error-codes": ["invalid-request"]}';
+
+    /**
+     * @const string Bad response error
+     */
+    const BAD_RESPONSE = '{"success": false, "error-codes": ["invalid-response"]}';
+
+    /**
+     * Socket to the reCAPTCHA service
+     * @var Socket
+     */
+    private $socket;
+
+    /**
+     * Constructor
+     *
+     * @param \ReCaptcha\RequestMethod\Socket $socket optional socket, injectable for testing
+     */
+    public function __construct(Socket $socket = null)
+    {
+        if (!is_null($socket)) {
+            $this->socket = $socket;
+        } else {
+            $this->socket = new Socket();
+        }
+    }
+
+    /**
+     * Submit the POST request with the specified parameters.
+     *
+     * @param RequestParameters $params Request parameters
+     * @return string Body of the reCAPTCHA response
+     */
+    public function submit(RequestParameters $params)
+    {
+        $errno = 0;
+        $errstr = '';
+
+        if (false === $this->socket->fsockopen('ssl://' . self::RECAPTCHA_HOST, 443, $errno, $errstr, 30)) {
+            return self::BAD_REQUEST;
+        }
+
+        $content = $params->toQueryString();
+
+        $request = "POST " . self::SITE_VERIFY_PATH . " HTTP/1.1\r\n";
+        $request .= "Host: " . self::RECAPTCHA_HOST . "\r\n";
+        $request .= "Content-Type: application/x-www-form-urlencoded\r\n";
+        $request .= "Content-length: " . strlen($content) . "\r\n";
+        $request .= "Connection: close\r\n\r\n";
+        $request .= $content . "\r\n\r\n";
+
+        $this->socket->fwrite($request);
+        $response = '';
+
+        while (!$this->socket->feof()) {
+            $response .= $this->socket->fgets(4096);
+        }
+
+        $this->socket->fclose();
+
+        if (0 !== strpos($response, 'HTTP/1.1 200 OK')) {
+            return self::BAD_RESPONSE;
+        }
+
+        $parts = preg_split("#\n\s*\n#Uis", $response);
+
+        return $parts[1];
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestParameters.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestParameters.php
new file mode 100644
index 00000000..cb66f26c
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/RequestParameters.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha;
+
+/**
+ * Stores and formats the parameters for the request to the reCAPTCHA service.
+ */
+class RequestParameters
+{
+    /**
+     * Site secret.
+     * @var string
+     */
+    private $secret;
+
+    /**
+     * Form response.
+     * @var string
+     */
+    private $response;
+
+    /**
+     * Remote user's IP address.
+     * @var string
+     */
+    private $remoteIp;
+
+    /**
+     * Client version.
+     * @var string
+     */
+    private $version;
+
+    /**
+     * Initialise parameters.
+     *
+     * @param string $secret Site secret.
+     * @param string $response Value from g-captcha-response form field.
+     * @param string $remoteIp User's IP address.
+     * @param string $version Version of this client library.
+     */
+    public function __construct($secret, $response, $remoteIp = null, $version = null)
+    {
+        $this->secret = $secret;
+        $this->response = $response;
+        $this->remoteIp = $remoteIp;
+        $this->version = $version;
+    }
+
+    /**
+     * Array representation.
+     *
+     * @return array Array formatted parameters.
+     */
+    public function toArray()
+    {
+        $params = array('secret' => $this->secret, 'response' => $this->response);
+
+        if (!is_null($this->remoteIp)) {
+            $params['remoteip'] = $this->remoteIp;
+        }
+
+        if (!is_null($this->version)) {
+            $params['version'] = $this->version;
+        }
+
+        return $params;
+    }
+
+    /**
+     * Query string representation for HTTP request.
+     *
+     * @return string Query string formatted parameters.
+     */
+    public function toQueryString()
+    {
+        return http_build_query($this->toArray(), '', '&');
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/Response.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/Response.php
new file mode 100644
index 00000000..d2d8a8bf
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/ReCaptcha/Response.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha;
+
+/**
+ * The response returned from the service.
+ */
+class Response
+{
+    /**
+     * Succes or failure.
+     * @var boolean
+     */
+    private $success = false;
+
+    /**
+     * Error code strings.
+     * @var array
+     */
+    private $errorCodes = array();
+
+    /**
+     * Build the response from the expected JSON returned by the service.
+     *
+     * @param string $json
+     * @return \ReCaptcha\Response
+     */
+    public static function fromJson($json)
+    {
+        $responseData = json_decode($json, true);
+
+        if (!$responseData) {
+            return new Response(false, array('invalid-json'));
+        }
+
+        if (isset($responseData['success']) && $responseData['success'] == true) {
+            return new Response(true);
+        }
+
+        if (isset($responseData['error-codes']) && is_array($responseData['error-codes'])) {
+            return new Response(false, $responseData['error-codes']);
+        }
+
+        return new Response(false);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param boolean $success
+     * @param array $errorCodes
+     */
+    public function __construct($success, array $errorCodes = array())
+    {
+        $this->success = $success;
+        $this->errorCodes = $errorCodes;
+    }
+
+    /**
+     * Is success?
+     *
+     * @return boolean
+     */
+    public function isSuccess()
+    {
+        return $this->success;
+    }
+
+    /**
+     * Get error codes.
+     *
+     * @return array
+     */
+    public function getErrorCodes()
+    {
+        return $this->errorCodes;
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/autoload.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/autoload.php
new file mode 100644
index 00000000..a53cbd78
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/src/autoload.php
@@ -0,0 +1,38 @@
+<?php
+
+/* An autoloader for ReCaptcha\Foo classes. This should be require()d
+ * by the user before attempting to instantiate any of the ReCaptcha
+ * classes.
+ */
+
+spl_autoload_register(function ($class) {
+    if (substr($class, 0, 10) !== 'ReCaptcha\\') {
+      /* If the class does not lie under the "ReCaptcha" namespace,
+       * then we can exit immediately.
+       */
+      return;
+    }
+
+    /* All of the classes have names like "ReCaptcha\Foo", so we need
+     * to replace the backslashes with frontslashes if we want the
+     * name to map directly to a location in the filesystem.
+     */
+    $class = str_replace('\\', '/', $class);
+
+    /* First, check under the current directory. It is important that
+     * we look here first, so that we don't waste time searching for
+     * test classes in the common case.
+     */
+    $path = dirname(__FILE__).'/'.$class.'.php';
+    if (is_readable($path)) {
+        require_once $path;
+    }
+
+    /* If we didn't find what we're looking for already, maybe it's
+     * a test class?
+     */
+    $path = dirname(__FILE__).'/../tests/'.$class.'.php';
+    if (is_readable($path)) {
+        require_once $path;
+    }
+});
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/ReCaptchaTest.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/ReCaptchaTest.php
new file mode 100644
index 00000000..dbbfc992
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/ReCaptchaTest.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha;
+
+class ReCaptchaTest extends \PHPUnit_Framework_TestCase
+{
+
+    /**
+     * @expectedException \RuntimeException
+     * @dataProvider invalidSecretProvider
+     */
+    public function testExceptionThrownOnInvalidSecret($invalid)
+    {
+        $rc = new ReCaptcha($invalid);
+    }
+
+    public function invalidSecretProvider()
+    {
+        return array(
+            array(''),
+            array(null),
+            array(0),
+            array(new \stdClass()),
+            array(array()),
+        );
+    }
+
+    public function testVerifyReturnsErrorOnMissingResponse()
+    {
+        $rc = new ReCaptcha('secret');
+        $response = $rc->verify('');
+        $this->assertFalse($response->isSuccess());
+        $this->assertEquals(array('missing-input-response'), $response->getErrorCodes());
+    }
+
+    public function testVerifyReturnsResponse()
+    {
+        $method = $this->getMock('\\ReCaptcha\\RequestMethod', array('submit'));
+        $method->expects($this->once())
+                ->method('submit')
+                ->with($this->callback(function ($params) {
+
+                            return true;
+                        }))
+                ->will($this->returnValue('{"success": true}'));
+        ;
+        $rc = new ReCaptcha('secret', $method);
+        $response = $rc->verify('response');
+        $this->assertTrue($response->isSuccess());
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestMethod/CurlPostTest.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestMethod/CurlPostTest.php
new file mode 100644
index 00000000..892e207c
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestMethod/CurlPostTest.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha\RequestMethod;
+
+use \ReCaptcha\RequestParameters;
+
+class CurlPostTest extends \PHPUnit_Framework_TestCase
+{
+
+    protected function setUp()
+    {
+        if (!extension_loaded('curl')) {
+            $this->markTestSkipped(
+                    'The cURL extension is not available.'
+            );
+        }
+    }
+
+    public function testSubmit()
+    {
+        $curl = $this->getMock('\\ReCaptcha\\RequestMethod\\Curl',
+                array('init', 'setoptArray', 'exec', 'close'));
+        $curl->expects($this->once())
+                ->method('init')
+                ->willReturn(new \stdClass);
+        $curl->expects($this->once())
+                ->method('setoptArray')
+                ->willReturn(true);
+        $curl->expects($this->once())
+                ->method('exec')
+                ->willReturn('RESPONSEBODY');
+        $curl->expects($this->once())
+                ->method('close');
+
+        $pc = new CurlPost($curl);
+        $response = $pc->submit(new RequestParameters("secret", "response"));
+        $this->assertEquals('RESPONSEBODY', $response);
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestMethod/PostTest.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestMethod/PostTest.php
new file mode 100644
index 00000000..2eaabb5c
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestMethod/PostTest.php
@@ -0,0 +1,118 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha\RequestMethod;
+
+use ReCaptcha\RequestParameters;
+
+class PostTest extends \PHPUnit_Framework_TestCase
+{
+    public static $assert = null;
+    protected $parameters = null;
+    protected $runcount = 0;
+
+    public function setUp()
+    {
+        $this->parameters = new RequestParameters("secret", "response", "remoteip", "version");
+    }
+
+    public function tearDown()
+    {
+        self::$assert = null;
+    }
+
+    public function testHTTPContextOptions()
+    {
+        $req = new Post();
+        self::$assert = array($this, "httpContextOptionsCallback");
+        $req->submit($this->parameters);
+        $this->assertEquals(1, $this->runcount, "The assertion was ran");
+    }
+
+    public function testSSLContextOptions()
+    {
+        $req = new Post();
+        self::$assert = array($this, "sslContextOptionsCallback");
+        $req->submit($this->parameters);
+        $this->assertEquals(1, $this->runcount, "The assertion was ran");
+    }
+
+    public function httpContextOptionsCallback(array $args)
+    {
+        $this->runcount++;
+        $this->assertCommonOptions($args);
+
+        $options = stream_context_get_options($args[2]);
+        $this->assertArrayHasKey('http', $options);
+
+        $this->assertArrayHasKey('method', $options['http']);
+        $this->assertEquals("POST", $options['http']['method']);
+
+        $this->assertArrayHasKey('content', $options['http']);
+        $this->assertEquals($this->parameters->toQueryString(), $options['http']['content']);
+
+        $this->assertArrayHasKey('header', $options['http']);
+        $headers = array(
+            "Content-type: application/x-www-form-urlencoded",
+        );
+        foreach ($headers as $header) {
+            $this->assertContains($header, $options['http']['header']);
+        }
+    }
+
+    public function sslContextOptionsCallback(array $args)
+    {
+        $this->runcount++;
+        $this->assertCommonOptions($args);
+
+        $options = stream_context_get_options($args[2]);
+        $this->assertArrayHasKey('http', $options);
+        $this->assertArrayHasKey('verify_peer', $options['http']);
+        $this->assertTrue($options['http']['verify_peer']);
+
+        $key = version_compare(PHP_VERSION, "5.6.0", "<") ? "CN_name" : "peer_name";
+
+        $this->assertArrayHasKey($key, $options['http']);
+        $this->assertEquals("www.google.com", $options['http'][$key]);
+    }
+
+    protected function assertCommonOptions(array $args)
+    {
+        $this->assertCount(3, $args);
+        $this->assertStringStartsWith("https://www.google.com/", $args[0]);
+        $this->assertFalse($args[1]);
+        $this->assertTrue(is_resource($args[2]), "The context options should be a resource");
+    }
+}
+
+function file_get_contents()
+{
+    if (PostTest::$assert) {
+        return call_user_func(PostTest::$assert, func_get_args());
+    }
+    // Since we can't represent maxlen in userland...
+    return call_user_func_array('file_get_contents', func_get_args());
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestMethod/SocketPostTest.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestMethod/SocketPostTest.php
new file mode 100644
index 00000000..10eb7c28
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestMethod/SocketPostTest.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha\RequestMethod;
+
+use ReCaptcha\RequestParameters;
+
+class SocketPostTest extends \PHPUnit_Framework_TestCase
+{
+
+    public function testSubmitSuccess()
+    {
+        $socket = $this->getMock('\\ReCaptcha\\RequestMethod\\Socket', array('fsockopen', 'fwrite', 'fgets', 'feof', 'fclose'));
+        $socket->expects($this->once())
+                ->method('fsockopen')
+                ->willReturn(true);
+        $socket->expects($this->once())
+                ->method('fwrite');
+        $socket->expects($this->once())
+                ->method('fgets')
+                ->willReturn("HTTP/1.1 200 OK\n\nRESPONSEBODY");
+        $socket->expects($this->exactly(2))
+                ->method('feof')
+                ->will($this->onConsecutiveCalls(false, true));
+        $socket->expects($this->once())
+                ->method('fclose')
+                ->willReturn(true);
+
+        $ps = new SocketPost($socket);
+        $response = $ps->submit(new RequestParameters("secret", "response", "remoteip", "version"));
+        $this->assertEquals('RESPONSEBODY', $response);
+    }
+
+    public function testSubmitBadResponse()
+    {
+        $socket = $this->getMock('\\ReCaptcha\\RequestMethod\\Socket', array('fsockopen', 'fwrite', 'fgets', 'feof', 'fclose'));
+        $socket->expects($this->once())
+                ->method('fsockopen')
+                ->willReturn(true);
+        $socket->expects($this->once())
+                ->method('fwrite');
+        $socket->expects($this->once())
+                ->method('fgets')
+                ->willReturn("HTTP/1.1 500 NOPEn\\nBOBBINS");
+        $socket->expects($this->exactly(2))
+                ->method('feof')
+                ->will($this->onConsecutiveCalls(false, true));
+        $socket->expects($this->once())
+                ->method('fclose')
+                ->willReturn(true);
+
+        $ps = new SocketPost($socket);
+        $response = $ps->submit(new RequestParameters("secret", "response", "remoteip", "version"));
+        $this->assertEquals(SocketPost::BAD_RESPONSE, $response);
+    }
+
+    public function testSubmitBadRequest()
+    {
+        $socket = $this->getMock('\\ReCaptcha\\RequestMethod\\Socket', array('fsockopen'));
+        $socket->expects($this->once())
+                ->method('fsockopen')
+                ->willReturn(false);
+        $ps = new SocketPost($socket);
+        $response = $ps->submit(new RequestParameters("secret", "response", "remoteip", "version"));
+        $this->assertEquals(SocketPost::BAD_REQUEST, $response);
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestParametersTest.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestParametersTest.php
new file mode 100644
index 00000000..adc601b4
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/RequestParametersTest.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha;
+
+class RequestParametersTest extends \PHPUnit_Framework_TestCase
+{
+
+    public function provideValidData()
+    {
+        return array(
+            array('SECRET', 'RESPONSE', 'REMOTEIP', 'VERSION',
+                array('secret' => 'SECRET', 'response' => 'RESPONSE', 'remoteip' => 'REMOTEIP', 'version' => 'VERSION'),
+                'secret=SECRET&response=RESPONSE&remoteip=REMOTEIP&version=VERSION'),
+            array('SECRET', 'RESPONSE', null, null,
+                array('secret' => 'SECRET', 'response' => 'RESPONSE'),
+                'secret=SECRET&response=RESPONSE'),
+        );
+    }
+
+    /**
+     * @dataProvider provideValidData
+     */
+    public function testToArray($secret, $response, $remoteIp, $version, $expectedArray, $expectedQuery)
+    {
+        $params = new RequestParameters($secret, $response, $remoteIp, $version);
+        $this->assertEquals($params->toArray(), $expectedArray);
+    }
+
+    /**
+     * @dataProvider provideValidData
+     */
+    public function testToQueryString($secret, $response, $remoteIp, $version, $expectedArray, $expectedQuery)
+    {
+        $params = new RequestParameters($secret, $response, $remoteIp, $version);
+        $this->assertEquals($params->toQueryString(), $expectedQuery);
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/ResponseTest.php b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/ResponseTest.php
new file mode 100644
index 00000000..0e7eac93
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-php/tests/ReCaptcha/ResponseTest.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * This is a PHP library that handles calling reCAPTCHA.
+ *
+ * @copyright Copyright (c) 2015, Google Inc.
+ * @link      http://www.google.com/recaptcha
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace ReCaptcha;
+
+class ResponseTest extends \PHPUnit_Framework_TestCase
+{
+
+    /**
+     * @dataProvider provideJson
+     */
+    public function testFromJson($json, $success, $errorCodes)
+    {
+        $response = Response::fromJson($json);
+        $this->assertEquals($success, $response->isSuccess());
+        $this->assertEquals($errorCodes, $response->getErrorCodes());
+    }
+
+    public function provideJson()
+    {
+        return array(
+            array('{"success": true}', true, array()),
+            array('{"success": false, "error-codes": ["test"]}', false, array('test')),
+            array('{"success": true, "error-codes": ["test"]}', true, array()),
+            array('{"success": false}', false, array()),
+            array('BAD JSON', false, array('invalid-json')),
+        );
+    }
+
+    public function testIsSuccess()
+    {
+        $response = new Response(true);
+        $this->assertTrue($response->isSuccess());
+
+        $response = new Response(false);
+        $this->assertFalse($response->isSuccess());
+    }
+
+    public function testGetErrorCodes()
+    {
+        $errorCodes = array('test');
+        $response = new Response(true, $errorCodes);
+        $this->assertEquals($errorCodes, $response->getErrorCodes());
+    }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha-widget-noscript.tpl.php b/drupal7/sites/all/modules/recaptcha/recaptcha-widget-noscript.tpl.php
new file mode 100644
index 00000000..dc542768
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha-widget-noscript.tpl.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file recaptcha-widget-noscript.tpl.php
+ * Default theme implementation to present the reCAPTCHA noscript code.
+ *
+ * Available variables:
+ * - $sitekey: Google web service site key.
+ * - $language: Current site language code.
+ * - $url: Google web service API url.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_recaptcha_widget_noscript()
+ */
+?>
+<noscript>
+  <div style="width: 302px; height: 352px;">
+    <div style="width: 302px; height: 352px; position: relative;">
+      <div style="width: 302px; height: 352px; position: absolute;">
+        <iframe src="<?php print $url; ?>" frameborder="0" scrolling="no" style="width: 302px; height:352px; border-style: none;"></iframe>
+      </div>
+      <div style="width: 250px; height: 80px; position: absolute; border-style: none; bottom: 21px; left: 25px; margin: 0px; padding: 0px; right: 25px;">
+        <textarea id="g-recaptcha-response" name="g-recaptcha-response" class="g-recaptcha-response" style="width: 250px; height: 80px; border: 1px solid #c1c1c1; margin: 0px; padding: 0px; resize: none;" value=""></textarea>
+      </div>
+    </div>
+  </div>
+</noscript>
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha.admin.inc b/drupal7/sites/all/modules/recaptcha/recaptcha.admin.inc
index c0ecf912..bd2dc23a 100644
--- a/drupal7/sites/all/modules/recaptcha/recaptcha.admin.inc
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha.admin.inc
@@ -2,82 +2,83 @@
 
 /**
  * @file
- * Provides the reCAPTCHA administration settings.
+ * Provides the Google No CAPTCHA administration settings.
  */
 
 /**
- * Form callback; administrative settings for reCaptcha.
+ * Form callback; administrative settings for Google No CAPTCHA.
  */
 function recaptcha_admin_settings() {
-  // Load the recaptcha library. Error if library does not load.
-  if (!_recaptcha_load_library()) {
-    drupal_set_message(t('Error loading recaptchalib.'), 'error');
-    return FALSE;
-  }
-
-  $form = array();
-  $form['recaptcha_public_key'] = array(
+  $form['recaptcha_general_settings'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('General settings'),
+  );
+  $form['recaptcha_general_settings']['recaptcha_site_key'] = array(
     '#type' => 'textfield',
-    '#title' => t('Public Key'),
-    '#default_value' => variable_get('recaptcha_public_key', ''),
+    '#title' => t('Site key'),
+    '#default_value' => variable_get('recaptcha_site_key', ''),
     '#maxlength' => 40,
-    '#description' => t('The public key given to you when you <a href="@url" target="_blank">register for reCAPTCHA</a>.', array('@url' => url(recaptcha_get_signup_url($_SERVER['SERVER_NAME'], variable_get('site_name', ''))))),
+    '#description' => t('The site key given to you when you <a href="@url">register for reCAPTCHA</a>.', array('@url' => 'http://www.google.com/recaptcha/admin')),
     '#required' => TRUE,
   );
-  $form['recaptcha_private_key'] = array(
+  $form['recaptcha_general_settings']['recaptcha_secret_key'] = array(
     '#type' => 'textfield',
-    '#title' => t('Private Key'),
-    '#default_value' => variable_get('recaptcha_private_key', ''),
+    '#title' => t('Secret key'),
+    '#default_value' => variable_get('recaptcha_secret_key', ''),
     '#maxlength' => 40,
-    '#description' => t('The private key given to you when you <a href="@url" target="_blank">register for reCAPTCHA</a>.', array('@url' => url(recaptcha_get_signup_url($_SERVER['SERVER_NAME'], variable_get('site_name', ''))))),
+    '#description' => t('The secret key given to you when you <a href="@url">register for reCAPTCHA</a>.', array('@url' => 'http://www.google.com/recaptcha/admin')),
     '#required' => TRUE,
   );
-  $form['recaptcha_server_status_check_interval'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Captcha server check interval'),
-    '#default_value' => variable_get('recaptcha_server_status_check_interval', 5),
-    '#description' => t('Number of minutes to cache reCAPTCHA server status.'),
-    '#size' => 10,
-  );
-  $form['recaptcha_ajax_api'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('AJAX API'),
-    '#default_value' => variable_get('recaptcha_ajax_api', FALSE),
-    '#description' => t('Use the AJAX API to display reCAPTCHA.'),
-  );
-  $form['recaptcha_nocookies'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Disable Client-Side Cookies'),
-    '#default_value' => variable_get('recaptcha_nocookies', FALSE),
-    '#description' => t('Add flag to disable third-party cookies set by reCAPTCHA.'),
-  );
-  $form['recaptcha_theme_settings'] = array(
+
+  $form['recaptcha_widget_settings'] = array(
     '#type' => 'fieldset',
-    '#title' => t('Theme Settings'),
+    '#title' => t('Widget settings'),
     '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
+    '#collapsed' => FALSE,
   );
-  $form['recaptcha_theme_settings']['recaptcha_theme'] = array(
+  $form['recaptcha_widget_settings']['recaptcha_theme'] = array(
     '#type' => 'select',
     '#title' => t('Theme'),
     '#description' => t('Defines which theme to use for reCAPTCHA.'),
     '#options' => array(
-      'red' => t('Red'),
-      'white' => t('White'),
-      'blackglass' => t('Black Glass'),
-      'clean' => t('Clean'),
-      'custom' => t('Custom'),
+      'light' => t('Light (default)'),
+      'dark' => t('Dark'),
     ),
-    '#default_value' => variable_get('recaptcha_theme', 'red'),
-    '#required' => TRUE,
+    '#default_value' => variable_get('recaptcha_theme', 'light'),
   );
-  $form['recaptcha_theme_settings']['recaptcha_tabindex'] = array(
+  $form['recaptcha_widget_settings']['recaptcha_type'] = array(
+    '#type' => 'select',
+    '#title' => t('Type'),
+    '#description' => t('The type of CAPTCHA to serve.'),
+    '#options' => array(
+      'image' => t('Image (default)'),
+      'audio' => t('Audio'),
+    ),
+    '#default_value' => variable_get('recaptcha_type', 'image'),
+  );
+  $form['recaptcha_widget_settings']['recaptcha_size'] = array(
+    '#default_value' => variable_get('recaptcha_size', ''),
+    '#description' => t('The size of CAPTCHA to serve.'),
+    '#options' => array(
+      '' => t('Normal (default)'),
+      'compact' => t('Compact'),
+    ),
+    '#title' => t('Size'),
+    '#type' => 'select',
+  );
+  $form['recaptcha_widget_settings']['recaptcha_tabindex'] = array(
     '#type' => 'textfield',
-    '#title' => t('Tab Index'),
-    '#description' => t('Sets a <a href="@tabindex" target="_blank">tabindex</a> for the reCAPTCHA text box. If other elements in the form use a tabindex, this should be set so that navigation is easier for the user.', array('@tabindex' => 'http://www.w3.org/TR/html4/interact/forms.html#adef-tabindex')),
-    '#default_value' => variable_get('recaptcha_tabindex', ''),
+    '#title' => t('Tabindex'),
+    '#description' => t('Set the <a href="@tabindex">tabindex</a> of the widget and challenge (Default = 0). If other elements in your page use tabindex, it should be set to make user navigation easier.', array('@tabindex' => 'http://www.w3.org/TR/html4/interact/forms.html#adef-tabindex')),
+    '#default_value' => variable_get('recaptcha_tabindex', 0),
     '#size' => 4,
   );
+  $form['recaptcha_widget_settings']['recaptcha_noscript'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable fallback for browsers with JavaScript disabled'),
+    '#default_value' => variable_get('recaptcha_noscript', 0),
+    '#description' => t('If JavaScript is a requirement for your site, you should <strong>not</strong> enable this feature. With this enabled, a compatibility layer will be added to the captcha to support non-js users.'),
+  );
 
   return system_settings_form($form);
 }
@@ -88,13 +89,8 @@ function recaptcha_admin_settings() {
  * @see recaptcha_admin_settings()
  */
 function recaptcha_admin_settings_validate($form, &$form_state) {
-  // Clear the page cache if Ajax API has been disabled.
-  if (empty($form_state['values']['recaptcha_ajax_api']) && $form['recaptcha_ajax_api']['#default_value']) {
-    cache_clear_all(NULL, 'cache_page');
-  }
-
   $tabindex = $form_state['values']['recaptcha_tabindex'];
-  if (!empty($tabindex) && !is_numeric($tabindex)) {
-    form_set_error('recaptcha_tabindex', t('The Tab Index must be an integer.'));
+  if (!is_numeric($tabindex)) {
+    form_set_error('recaptcha_tabindex', t('The tabindex must be an integer.'));
   }
 }
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha.info b/drupal7/sites/all/modules/recaptcha/recaptcha.info
index bb242b0d..608866b2 100644
--- a/drupal7/sites/all/modules/recaptcha/recaptcha.info
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha.info
@@ -1,16 +1,24 @@
 name = reCAPTCHA
-description = Uses the reCAPTCHA web service to improve the CAPTCHA system.
+description = Protect your website from spam and abuse while letting real people pass through with ease. 
 core = 7.x
 package = Spam control
 dependencies[] = captcha
-files[] = tests/recaptcha_base.test
-files[] = tests/recaptcha_ui.test
-files[] = tests/recaptcha_server_status.test
 configure = admin/config/people/captcha/recaptcha
+php = 5.3.2
 
-; Information added by Drupal.org packaging script on 2017-01-06
-version = "7.x-1.14"
+files[] = recaptcha.test
+files[] = recaptcha-php/src/ReCaptcha/ReCaptcha.php
+files[] = recaptcha-php/src/ReCaptcha/RequestMethod.php
+files[] = recaptcha-php/src/ReCaptcha/RequestParameters.php
+files[] = recaptcha-php/src/ReCaptcha/Response.php
+files[] = recaptcha-php/src/ReCaptcha/RequestMethod/Post.php
+files[] = recaptcha-php/src/ReCaptcha/RequestMethod/Socket.php
+files[] = recaptcha-php/src/ReCaptcha/RequestMethod/SocketPost.php
+files[] = src/ReCaptcha/RequestMethod/Drupal7Post.php
+
+; Information added by Drupal.org packaging script on 2016-07-23
+version = "7.x-2.2"
 core = "7.x"
 project = "recaptcha"
-datestamp = "1483730050"
+datestamp = "1469287440"
 
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha.install b/drupal7/sites/all/modules/recaptcha/recaptcha.install
index 8ce84be5..bd2cc6e0 100644
--- a/drupal7/sites/all/modules/recaptcha/recaptcha.install
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha.install
@@ -2,36 +2,102 @@
 
 /**
  * @file
- * Provides install, updated, and uninstall functions for recaptcha.
+ * Installation file for reCAPTCHA module.
  */
 
 /**
  * Implements hook_uninstall().
  */
 function recaptcha_uninstall() {
-  // Delete all the recaptcha variables and then clear the variable cache.
+  variable_del('recaptcha_site_key');
+  variable_del('recaptcha_secret_key');
+  variable_del('recaptcha_theme');
+  variable_del('recaptcha_type');
+  variable_del('recaptcha_size');
+  variable_del('recaptcha_tabindex');
+  variable_del('recaptcha_noscript');
+}
+
+/**
+ * Update variables from 7.x-1.x.
+ */
+function recaptcha_update_7200() {
+  // Migrate the public key to site key.
+  // Upgrade from 6.x-2.x to 7.x-2.x
+  $site_key = variable_get('recaptcha_site_key', '');
+  // Upgrade from 7.x-1.x to 7.x-2.x
+  $public_key = variable_get('recaptcha_public_key', $site_key);
+  variable_set('recaptcha_site_key', $public_key);
+  variable_del('recaptcha_public_key');
+
+  // Upgrade from 6.x-2.x to 7.x-2.x
+  $secret_key = variable_get('recaptcha_secret_key', '');
+  // Upgrade from 7.x-1.x to 7.x-2.x
+  $private_key = variable_get('recaptcha_private_key', $secret_key);
+  variable_set('recaptcha_secret_key', $private_key);
+  variable_del('recaptcha_private_key');
+
+  // Migrate previous 1.x themes to 2.x as good as possible.
+  $recaptcha_themes = array(
+    'red' => 'light',
+    'white' => 'light',
+    'blackglass' => 'dark',
+    'clean' => 'light',
+    'custom' => 'light',
+  );
+  // Upgrade from 6.x-2.x to 7.x-2.x
+  $recaptcha_themes += array(
+    'light' => 'light',
+    'dark' => 'dark',
+  );
+  $recaptcha_theme = variable_get('recaptcha_theme', 'light');
+  variable_set('recaptcha_theme', $recaptcha_themes[$recaptcha_theme]);
+
+  // Delete obsolete variables.
+  variable_del('recaptcha_ajax_api');
+  variable_del('recaptcha_nocookies');
+  variable_del('recaptcha_server_status_check_interval');
+
+  // Remove stale variables not cleaned up properly in previous releases.
+  variable_del('recaptcha_api_server');
+  variable_del('recaptcha_api_secure_server');
+  variable_del('recaptcha_secure_connection');
+  variable_del('recaptcha_verify_server');
+
+  // Mailhide module is not available, delete obsolete variables:
+  // - recaptcha_mailhide_public_key
+  // - recaptcha_mailhide_public_key_*
+  // - recaptcha_mailhide_private_key
+  // - recaptcha_mailhide_private_key_*
   db_delete('variable')
-    ->condition('name', 'recaptcha_%', 'LIKE')
+    ->condition('name', 'recaptcha_mailhide_%', 'LIKE')
     ->execute();
   cache_clear_all('variables', 'cache_bootstrap');
+
+  return t('Migrated settings. You may need to create a new site key and secret key if it does not work.');
 }
 
 /**
- * Implements hook_update_N().
+ * Default empty tabindex to 0.
  */
+function recaptcha_update_7201() {
+  // Change an empty tabindex to value 0 for consistency with D8.
+  $recaptcha_tabindex = variable_get('recaptcha_tabindex', '');
+  if ($recaptcha_tabindex == '') {
+    variable_set('recaptcha_tabindex', 0);
+  }
+
+  return t('Empty tabindex has been upgraded to 0.');
+}
 
 /**
- * Issue #2473329: Remove obsolete variables.
+ * Remove recaptcha_mailhide 7.x-1.x from system table.
  */
-function recaptcha_update_7100() {
-  $variables = array(
-    'recaptcha_secure_connection',
-    'recaptcha_api_server',
-    'recaptcha_api_secure_server',
-    'recaptcha_verify_server',
-  );
-  foreach ($variables as $variable) {
-    variable_del($variable);
-  }
-  return t('Removed obsolete variables.');
+function recaptcha_update_7202() {
+  // #2487215: Drupal 7.50+ requires system table cleanup.
+  db_delete('system')
+    ->condition('name', 'recaptcha_mailhide')
+    ->execute();
+
+  return t('Removed recaptcha_mailhide module from database.');
 }
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha.js b/drupal7/sites/all/modules/recaptcha/recaptcha.js
deleted file mode 100644
index 4be3cd40..00000000
--- a/drupal7/sites/all/modules/recaptcha/recaptcha.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * @file
- * Drupal JavaScript behaviors for recaptcha.
- */
-
-(function ($) {
-  Drupal.behaviors.recaptcha = {
-    attach: function (context) {
-      Recaptcha.create(Drupal.settings.recaptcha.public_key, Drupal.settings.recaptcha.container, {theme: Drupal.settings.recaptcha.theme});
-    },
-    detach: function (context) {
-    }
-  };
-}(jQuery));
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha.module b/drupal7/sites/all/modules/recaptcha/recaptcha.module
index fe4fd7dd..1e65aab4 100644
--- a/drupal7/sites/all/modules/recaptcha/recaptcha.module
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha.module
@@ -2,53 +2,54 @@
 
 /**
  * @file
- * Uses the reCAPTCHA web service to improve the CAPTCHA system.
+ * Verifies if user is a human without necessity to solve a CAPTCHA.
  */
 
+require_once dirname(__FILE__) . '/recaptcha-php/src/ReCaptcha/ReCaptcha.php';
+require_once dirname(__FILE__) . '/recaptcha-php/src/ReCaptcha/RequestMethod.php';
+require_once dirname(__FILE__) . '/recaptcha-php/src/ReCaptcha/RequestParameters.php';
+require_once dirname(__FILE__) . '/recaptcha-php/src/ReCaptcha/Response.php';
+require_once dirname(__FILE__) . '/src/ReCaptcha/RequestMethod/Drupal7Post.php';
+
 /**
  * Implements hook_help().
  */
 function recaptcha_help($path, $arg) {
-  $output = '';
-
   switch ($path) {
-    case 'admin/modules#name':
-      $output .= t('reCAPTCHA');
-      break;
-
-    case 'admin/modules#description':
-    case 'admin/user/captcha/recaptcha':
-      $output .= t('Uses the <a href="@url" target="_blank">reCAPTCHA</a> web service to improve the CAPTCHA system and protect email addresses.', array('@url' => url('https://www.google.com/recaptcha')));
-      break;
-
-    case 'admin/help#recaptcha':
-      $output .= '<p>' .
-        t('Uses the reCAPTCHA web service to improve the CAPTCHA module and protect email addresses. For more information on what reCAPTCHA is, visit <a href="@url" target="_blank">the official website</a>.', array('@url' => url('https://www.google.com/recaptcha'))) .
-        '</p><h3>' .
-        t('Configuration') .
-        '</h3><p>' .
-        t('The settings associated with reCAPTCHA can be found in the <a href="@recaptchatab">reCAPTCHA tab</a>, in the <a href="@captchasettings">CAPTCHA settings</a>. You must set your public and private reCAPTCHA keys in order to use the module. Once the public and private keys are set, visit the <a href="@captchasettings">CAPTCHA settings</a>, where you can choose where reCAPTCHA should be displayed.', array('@recaptchatab' => url('admin/user/captcha/recaptcha'), '@captchasettings' => url('admin/user/captcha'))) .
-        '</p>';
-      break;
+    case 'admin/config/people/captcha/recaptcha':
+      return t('Google <a href="@url">reCAPTCHA</a> is a free service to protect your website from spam and abuse. reCAPTCHA uses an advanced risk analysis engine and adaptive CAPTCHAs to keep automated software from engaging in abusive activities on your site. It does this while letting your valid users pass through with ease.', array('@url' => 'https://www.google.com/recaptcha'));
   }
+}
 
-  return $output;
+/**
+ * Implements hook_theme().
+ */
+function recaptcha_theme() {
+  return array(
+    'recaptcha_widget_noscript' => array(
+      'variables' => array(
+        'widget' => NULL,
+      ),
+      'template' => 'recaptcha-widget-noscript',
+    ),
+  );
 }
 
 /**
  * Implements hook_menu().
  */
 function recaptcha_menu() {
-  $items = array();
   $items['admin/config/people/captcha/recaptcha'] = array(
     'title' => 'reCAPTCHA',
-    'description' => 'Administer the reCAPTCHA web service.',
+    'description' => 'Administer the Google No CAPTCHA reCAPTCHA web service.',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('recaptcha_admin_settings'),
     'access arguments' => array('administer recaptcha'),
     'type' => MENU_LOCAL_TASK,
     'file' => 'recaptcha.admin.inc',
+    'weight' => 1,
   );
+
   return $items;
 }
 
@@ -58,8 +59,8 @@ function recaptcha_menu() {
 function recaptcha_permission() {
   return array(
     'administer recaptcha' => array(
-      'title' => t('reCaptcha Administration'),
-      'description' => t('Administer reCaptcha settings'),
+      'title' => t('Administer reCAPTCHA'),
+      'description' => t('Administer reCAPTCHA settings'),
     ),
   );
 }
@@ -68,6 +69,8 @@ function recaptcha_permission() {
  * Implements hook_captcha().
  */
 function recaptcha_captcha($op, $captcha_type = '') {
+  global $language;
+
   switch ($op) {
     case 'list':
       return array('reCAPTCHA');
@@ -75,94 +78,60 @@ function recaptcha_captcha($op, $captcha_type = '') {
     case 'generate':
       $captcha = array();
       if ($captcha_type == 'reCAPTCHA') {
-        // Retrieve configuration variables.
-        $recaptcha_theme = variable_get('recaptcha_theme', 'red');
-        $recaptcha_tabindex = variable_get('recaptcha_tabindex', NULL);
-        $recaptcha_public_key = variable_get('recaptcha_public_key', FALSE);
-        $recaptcha_ajax_api = variable_get('recaptcha_ajax_api', FALSE);
-
-        // Test if reCAPTCHA can be used, falling back to Math if it is not
-        // configured, the library won't load, or the server is down.
-        if (!$recaptcha_public_key || !_recaptcha_load_library() || !_recaptcha_server_is_up()) {
-          return captcha_captcha('generate', 'Math');
-        }
-
-        if ($recaptcha_ajax_api) {
-          // By default CAPTCHA turns off page caching on any page where a
-          // CAPTCHA challenge appears. If recaptcha is using AJAX API, set
-          // caching back to its old state as stored in DB.
-          global $conf;
-          $conf['cache'] = unserialize(db_query("SELECT value FROM {variable} WHERE name = 'cache'")->fetchField());
-        }
-
-        $recaptcha_options = array(
-          'theme' => $recaptcha_theme,
-        );
-
-        // Localization support.
-        global $language;
-        if (isset($language->language)) {
-          // reCAPTCHA uses two-character language codes, so 'pt-br' must be
-          // passed as 'pt'.
-          $recaptcha_options['lang'] = drupal_substr($language->language, 0, 2);
-        }
-
-        // Construct the Javascript, but only display it once.
-        static $_recaptcha_jsadded = FALSE;
-        if ($_recaptcha_jsadded == FALSE && $recaptcha_ajax_api == FALSE) {
-          $_recaptcha_jsadded = TRUE;
-
-          // Add support to display the custom theme.
-          if ($recaptcha_theme == 'custom') {
-            $recaptcha_options['custom_theme_widget'] = 'recaptcha_custom_theme_widget';
-          }
+        $recaptcha_site_key = variable_get('recaptcha_site_key', '');
+        $recaptcha_secret_key = variable_get('recaptcha_secret_key', '');
+
+        if (!empty($recaptcha_site_key) && !empty($recaptcha_secret_key)) {
+          // Build the reCAPTCHA captcha form if site_key and secret_key are
+          // configured. Captcha requires TRUE to be returned in solution.
+          $captcha['solution'] = TRUE;
+          $captcha['captcha_validate'] = 'recaptcha_captcha_validation';
+          $captcha['form']['captcha_response'] = array(
+            '#type' => 'hidden',
+            '#value' => 'Google no captcha',
+          );
 
-          // Set the default tab index.
-          if (!empty($recaptcha_tabindex)) {
-            $recaptcha_options['tabindex'] = $recaptcha_tabindex;
+          $noscript = '';
+          if (variable_get('recaptcha_noscript', 0)) {
+            $variables = array(
+              'sitekey' => $recaptcha_site_key,
+              'language' => $language->language,
+            );
+            $noscript = theme('recaptcha_widget_noscript', array('widget' => $variables));
           }
-          drupal_add_js('var RecaptchaOptions = ' . drupal_json_encode($recaptcha_options) . ';', array('type' => 'inline'));
-        }
 
-        // Create the form. Captcha requires TRUE to be returned in solution.
-        $captcha['solution'] = TRUE;
-        $captcha['captcha_validate'] = 'recaptcha_captcha_validation';
-
-        // If 'Disable Client-Side Cookies' is set, then add query string to
-        // end of the public key string before passing to recaptchalib.
-        if (variable_get('recaptcha_nocookies', FALSE)) {
-          $recaptcha_public_key .= '&nocookie=1';
-        }
+          $attributes = array(
+            'class' => 'g-recaptcha',
+            'data-sitekey' => $recaptcha_site_key,
+            'data-theme' => variable_get('recaptcha_theme', 'light'),
+            'data-type' => variable_get('recaptcha_type', 'image'),
+            'data-size' => variable_get('recaptcha_size', ''),
+            'data-tabindex' => variable_get('recaptcha_tabindex', 0),
+          );
+          // Filter out empty tabindex/size.
+          $attributes = array_filter($attributes);
 
-        $captcha['form']['captcha_response'] = array(
-          '#type' => 'hidden',
-          '#value' => 'reCAPTCHA',
-        );
+          $captcha['form']['recaptcha_widget'] = array(
+            '#markup' => '<div' . drupal_attributes($attributes) . '></div>',
+            '#suffix' => $noscript,
+          );
 
-        // Expose the form, either straight HTML, or using the AJAX API.
-        // Build the custom theme HTML if necessary.
-        $recaptcha_custom_html = ($recaptcha_theme == 'custom') ? theme('recaptcha_custom_widget') : '';
-        if ($recaptcha_ajax_api == FALSE) {
-          // Only generate recaptcha_get_html() if we're not using the AJAX API.
-          $html = recaptcha_get_html($recaptcha_public_key, NULL, TRUE);
-          $captcha['form']['captcha_form'] = array(
-            '#type' => 'item',
-            '#markup' => (!empty($recaptcha_custom_html) ? '<div id="recaptcha_custom_theme_widget">' . $recaptcha_custom_html . '</div>' : '') . $html,
+          // @todo: #1664602: D7 does not yet support "async" in drupal_add_js().
+          // drupal_add_js(url('https://www.google.com/recaptcha/api.js', array('query' => array('hl' => $language->language), 'absolute' => TRUE)), array('defer' => TRUE, 'async' => TRUE, 'type' => 'external'));
+          $data = array(
+            '#tag' => 'script',
+            '#value' => '',
+            '#attributes' => array(
+              'src' => url('https://www.google.com/recaptcha/api.js', array('query' => array('hl' => $language->language), 'absolute' => TRUE)),
+              'async' => 'async',
+              'defer' => 'defer',
+            ),
           );
+          drupal_add_html_head($data, 'recaptcha_api');
         }
         else {
-          $captcha['form']['captcha_form'] = array(
-            '#type' => 'item',
-            // Create the destination container, inserting any custom theme HTML
-            // necessary ($recaptcha_custom_html will be empty if we are not
-            // using custom theme).
-            '#markup' => '<div id="recaptcha_ajax_api_container">' . $recaptcha_custom_html . '</div>',
-          );
-          drupal_add_js('https://www.google.com/recaptcha/api/js/recaptcha_ajax.js', array('type' => 'external'));
-          $recaptcha_options['public_key'] = $recaptcha_public_key;
-          $recaptcha_options['container'] = 'recaptcha_ajax_api_container';
-          drupal_add_js(array('recaptcha' => $recaptcha_options), 'setting');
-          drupal_add_js(drupal_get_path('module', 'recaptcha') . '/recaptcha.js');
+          // Fallback to Math captcha as reCAPTCHA is not configured.
+          $captcha = captcha_captcha('generate', 'Math');
         }
       }
       return $captcha;
@@ -170,114 +139,53 @@ function recaptcha_captcha($op, $captcha_type = '') {
 }
 
 /**
- * Test whether the reCAPTCHA server is up.
- *
- * @return bool
- *   TRUE if the reCAPTCHA server is up, FALSE otherwise.
+ * CAPTCHA Callback; Validates the reCAPTCHA code.
  */
-function _recaptcha_test_recaptcha_server() {
-  $response = drupal_http_request('https://' . RECAPTCHA_VERIFY_SERVER);
-  if (empty($response->error)) {
-    return TRUE;
-  }
-  else {
-    watchdog('reCAPTCHA', 'Unable to connect with the reCAPTCHA server (@server): @errno: @errstr', array(
-      '@server' => RECAPTCHA_VERIFY_SERVER,
-      '@errno' => $response->code,
-      '@errstr' => $response->error,
-    ), WATCHDOG_ERROR);
+function recaptcha_captcha_validation($solution, $response, $element, $form_state) {
+  $recaptcha_secret_key = variable_get('recaptcha_secret_key', '');
+  if (empty($_POST['g-recaptcha-response']) || empty($recaptcha_secret_key)) {
     return FALSE;
   }
-}
 
-/**
- * Return the cached output of _recaptcha_test_recaptcha_server().
- *
- * @return bool
- *   TRUE if the reCAPTCHA server was up last time it was checked, FALSE
- *   otherwise.
- */
-function _recaptcha_server_is_up() {
-  static $server_status;
-  // Use static cache value, if available.
-  if (isset($server_status)) {
-    return $server_status;
-  }
-  // Check if the status is cached already.
-  $status_cache = cache_get('recaptcha_server_status');
-  if ($status_cache !== FALSE) {
-    $server_status = (bool) $status_cache;
-    return $server_status;
-  }
-  // Status is not cached. Check if server is up.
-  $server_status = _recaptcha_test_recaptcha_server();
+  // Use drupal_http_request() to circumvent all issues with the Google library.
+  $recaptcha = new \ReCaptcha\ReCaptcha($recaptcha_secret_key, new \ReCaptcha\RequestMethod\Drupal7Post());
 
-  // Set to 5 minutes, if interval is not set.
-  $cache_period = variable_get('recaptcha_server_status_check_interval', 5);
-  // Save it as integer as cache_get returns FALSE, if not found.
-  cache_set('recaptcha_server_status', (int) $server_status, 'cache', time() + ($cache_period * 60));
-  return $server_status;
-}
+  $resp = $recaptcha->verify(
+    $_POST['g-recaptcha-response'],
+    ip_address()
+  );
 
-/**
- * CAPTCHA Callback; Validates the reCAPTCHA code.
- */
-function recaptcha_captcha_validation($solution = NULL, $response = NULL) {
-  global $user;
-  $recaptcha_private_key = variable_get('recaptcha_private_key', FALSE);
-  $ip_address = ip_address();
-  if ($recaptcha_private_key && $ip_address && $response === 'reCAPTCHA' && !empty($_POST['recaptcha_challenge_field']) && !empty($_POST['recaptcha_response_field']) && _recaptcha_server_is_up()) {
-    $resp = recaptcha_check_answer(
-      $recaptcha_private_key,
-      $ip_address,
-      check_plain($_POST['recaptcha_challenge_field']),
-      check_plain($_POST['recaptcha_response_field'])
+  if ($resp->isSuccess()) {
+    // Verified!
+    return TRUE;
+  }
+  else {
+    // Error code reference, https://developers.google.com/recaptcha/docs/verify
+    $error_codes = array(
+      'missing-input-secret' => t('The secret parameter is missing.'),
+      'invalid-input-secret' => t('The secret parameter is invalid or malformed.'),
+      'missing-input-response' => t('The response parameter is missing.'),
+      'invalid-input-response' => t('The response parameter is invalid or malformed.'),
+      'invalid-json' => t('The json response is invalid or malformed.'),
+      'unknown' => t('Unknown error.'),
     );
-    return $resp->is_valid;
+    foreach ($resp->getErrorCodes() as $code) {
+      if (!isset($error_codes[$code])) {
+        $code = 'unknown';
+      }
+      watchdog('reCAPTCHA web service', '@error', array('@error' => $error_codes[$code]), WATCHDOG_ERROR);
+    }
   }
   return FALSE;
 }
 
 /**
- * Implements hook_theme().
- */
-function recaptcha_theme() {
-  return array(
-    'recaptcha_custom_widget' => array(
-      'arguments' => array(),
-    ),
-  );
-}
-
-/**
- * Theme function: creates the custom themed recaptcha widget.
+ * Process variables for recaptcha-widget-noscript.tpl.php.
  *
- * @ingroup themeable
- */
-function theme_recaptcha_custom_widget() {
-  $recaptcha_only_if_incorrect_sol = t('Incorrect please try again');
-  $recaptcha_only_if_image_enter = t('Enter the words above:');
-  $recaptcha_only_if_audio_enter = t('Enter the words you hear:');
-  $recaptcha_get_another_captcha = t('Get another CAPTCHA');
-  $recaptcha_only_if_image = t('Get an audio CAPTCHA');
-  $recaptcha_only_if_audio = t('Get an image CAPTCHA');
-  $help = t('Help');
-  return <<<EOT
-    <div id="recaptcha_image"></div>
-    <div class="recaptcha_only_if_incorrect_sol" style="color:red">$recaptcha_only_if_incorrect_sol</div>
-    <span class="recaptcha_only_if_image">$recaptcha_only_if_image_enter</span>
-    <span class="recaptcha_only_if_audio">$recaptcha_only_if_audio_enter</span>
-    <input type="text" id="recaptcha_response_field" name="recaptcha_response_field" />
-    <div class="recaptcha_get_another_captcha"><a href="javascript:Recaptcha.reload()">$recaptcha_get_another_captcha</a></div>
-    <div class="recaptcha_only_if_image"><a href="javascript:Recaptcha.switch_type('audio')">$recaptcha_only_if_image</a></div>
-    <div class="recaptcha_only_if_audio"><a href="javascript:Recaptcha.switch_type('image')">$recaptcha_only_if_audio</a></div>
-    <div class="recaptcha_help"><a href="javascript:Recaptcha.showhelp()">$help</a></div>
-EOT;
-}
-
-/**
- * Load the recaptcha library.
+ * @see recaptcha-widget-noscript.tpl.php
  */
-function _recaptcha_load_library() {
-  return module_load_include('php', 'recaptcha', 'recaptcha-php-1.11/recaptchalib');
+function template_preprocess_recaptcha_widget_noscript(&$variables) {
+  $variables['sitekey']  = check_plain($variables['widget']['sitekey']);
+  $variables['language'] = check_plain($variables['widget']['language']);
+  $variables['url']      = check_url(url('https://www.google.com/recaptcha/api/fallback', array('query' => array('k' => $variables['widget']['sitekey'], 'hl' => $variables['widget']['language']), 'absolute' => TRUE)));
 }
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha.test b/drupal7/sites/all/modules/recaptcha/recaptcha.test
new file mode 100644
index 00000000..4afedf74
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/recaptcha.test
@@ -0,0 +1,179 @@
+<?php
+
+/**
+ * @file
+ * Tests for reCAPTCHA module.
+ */
+class ReCaptchaBasicTest extends DrupalWebTestCase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => t('reCAPTCHA basic tests'),
+      'description' => t('Test basic functionality of reCAPTCHA module.'),
+      'group' => t('reCAPTCHA'),
+      'dependencies' => array('captcha'),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp() {
+    parent::setUp('captcha', 'recaptcha');
+    module_load_include('inc', 'captcha');
+
+    // Create a normal user.
+    $permissions = array(
+      'access content',
+    );
+    $this->normal_user = $this->drupalCreateUser($permissions);
+
+    // Create an admin user.
+    $permissions += array(
+      'administer CAPTCHA settings',
+      'skip CAPTCHA',
+      'administer permissions',
+      'administer content types',
+      'administer recaptcha',
+    );
+    $this->admin_user = $this->drupalCreateUser($permissions);
+  }
+
+  /**
+   * Test access to the administration page.
+   */
+  function testReCaptchaAdminAccess() {
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/config/people/captcha/recaptcha');
+    $this->assertNoText(t('Access denied'), 'Admin users should be able to access the reCAPTCHA admin page', 'reCAPTCHA');
+    $this->drupalLogout();
+  }
+
+  /**
+   * Test the reCAPTCHA settings form.
+   */
+  function testReCaptchaAdminSettingsForm() {
+    $this->drupalLogin($this->admin_user);
+
+    $site_key = $this->randomName(40);
+    $secret_key = $this->randomName(40);
+
+    // Check form validation.
+    $edit['recaptcha_site_key'] = '';
+    $edit['recaptcha_secret_key'] = '';
+    $edit['recaptcha_tabindex'] = $this->randomName(2);
+    $this->drupalPost('admin/config/people/captcha/recaptcha', $edit, t('Save configuration'));
+
+    $this->assertRaw(t('Site key field is required.'), '[testReCaptchaConfiguration]: Empty site key detected.');
+    $this->assertRaw(t('Secret key field is required.'), '[testReCaptchaConfiguration]: Empty secret key detected.');
+    $this->assertRaw(t('The tabindex must be an integer.'), '[testReCaptchaConfiguration]: Invalid value for tab index detected.');
+
+    // Save form with valid values.
+    $edit['recaptcha_site_key'] = $site_key;
+    $edit['recaptcha_secret_key'] = $secret_key;
+    $edit['recaptcha_tabindex'] = 0;
+    $this->drupalPost('admin/config/people/captcha/recaptcha', $edit, t('Save configuration'));
+    $this->assertRaw(t('The configuration options have been saved.'), '[testReCaptchaConfiguration]: The configuration options have been saved.');
+
+    $this->assertNoRaw(t('Site key field is required.'), '[testReCaptchaConfiguration]: Site key was not empty.');
+    $this->assertNoRaw(t('Secret key field is required.'), '[testReCaptchaConfiguration]: Secret key was not empty.');
+    $this->assertNoRaw(t('The tabindex must be an integer.'), '[testReCaptchaConfiguration]: Tab index had a valid input.');
+
+    $this->drupalLogout();
+  }
+
+  /**
+   * Testing the protection of the user login form.
+   */
+  function testReCaptchaOnLoginForm() {
+    global $language;
+
+    $site_key = $this->randomName(40);;
+    $secret_key = $this->randomName(40);;
+    $grecaptcha = '<div class="g-recaptcha" data-sitekey="' . $site_key . '" data-theme="light" data-type="image"></div>';
+
+    // Test if login works.
+    $this->drupalLogin($this->normal_user);
+    $this->drupalLogout();
+
+    $this->drupalGet('user');
+    $this->assertNoRaw($grecaptcha, '[testReCaptchaOnLoginForm]: reCAPTCHA is not shown on form.');
+
+    // Enable 'captcha/Math' CAPTCHA on login form.
+    captcha_set_form_id_setting('user_login', 'captcha/Math');
+
+    $this->drupalGet('user');
+    $this->assertNoRaw($grecaptcha, '[testReCaptchaOnLoginForm]: reCAPTCHA is not shown on form.');
+
+    // Enable 'recaptcha/reCAPTCHA' on login form.
+    captcha_set_form_id_setting('user_login', 'recaptcha/reCAPTCHA');
+    $result = captcha_get_form_id_setting('user_login');
+    $this->assertNotNull($result, 'A configuration has been found for CAPTCHA point: user_login', 'reCAPTCHA');
+    $this->assertEqual($result->module, 'recaptcha', 'reCAPTCHA module configured for CAPTCHA point: user_login', 'reCAPTCHA');
+    $this->assertEqual($result->captcha_type, 'reCAPTCHA', 'reCAPTCHA type has been configured for CAPTCHA point: user_login', 'reCAPTCHA');
+
+    // Check if a Math CAPTCHA is still shown on the login form. The site key
+    // and security key have not yet configured for reCAPTCHA. The module need
+    // to fall back to math captcha.
+    $this->drupalGet('user');
+    $this->assertRaw(t('Math question'), '[testReCaptchaOnLoginForm]: Math CAPTCHA is shown on form.');
+
+    // Configure site key and security key to show reCAPTCHA and no fall back.
+    variable_set('recaptcha_site_key', $site_key);
+    variable_set('recaptcha_secret_key', $secret_key);
+
+    // Check if there is a reCAPTCHA on the login form.
+    $this->drupalGet('user');
+    $this->assertRaw($grecaptcha, '[testReCaptchaOnLoginForm]: reCAPTCHA is shown on form.');
+    $this->assertRaw('<script src="https://www.google.com/recaptcha/api.js?hl=' . $language->language . '" async="async" defer="defer"></script>', '[testReCaptchaOnLoginForm]: reCAPTCHA is shown on form.');
+    $this->assertNoRaw($grecaptcha . '<noscript>', '[testReCaptchaOnLoginForm]: NoScript code is not enabled for the reCAPTCHA.');
+
+    // Test if the fall back url is properly build and noscript code added.
+    variable_set('recaptcha_noscript', 1);
+    $this->drupalGet('user');
+    $this->assertRaw($grecaptcha . '<noscript>', '[testReCaptchaOnLoginForm]: NoScript for reCAPTCHA is shown on form.');
+    $this->assertRaw('https://www.google.com/recaptcha/api/fallback?k=' . $site_key . '&amp;hl=' . $language->language, '[testReCaptchaOnLoginForm]: Fallback URL with IFRAME has been found.');
+
+    // Check that data-size attribute does not exists.
+    variable_set('recaptcha_size', '');
+    $this->drupalGet('user');
+    $element = $this->xpath('//div[@class=:class and @data-size=:size]', array(':class' => 'g-recaptcha', ':size' => 'small'));
+    $this->assertFalse(!empty($element), 'Tag contains no data-size attribute.');
+
+    // Check that data-size attribute exists.
+    variable_set('recaptcha_size', 'small');
+    $this->drupalGet('user');
+    $element = $this->xpath('//div[@class=:class and @data-size=:size]', array(':class' => 'g-recaptcha', ':size' => 'small'));
+    $this->assertTrue(!empty($element), 'Tag contains data-size attribute and value.');
+
+    // Check that data-tabindex attribute does not exists.
+    variable_set('recaptcha_tabindex', 0);
+    $this->drupalGet('user');
+    $element = $this->xpath('//div[@class=:class and @data-tabindex=:index]', array(':class' => 'g-recaptcha', ':index' => 0));
+    $this->assertFalse(!empty($element), 'Tag contains no data-tabindex attribute.');
+
+    // Check that data-tabindex attribute exists.
+    variable_set('recaptcha_tabindex', 5);
+    $this->drupalGet('user');
+    $element = $this->xpath('//div[@class=:class and @data-tabindex=:index]', array(':class' => 'g-recaptcha', ':index' => 5));
+    $this->assertTrue(!empty($element), 'Tag contains data-tabindex attribute and value.');
+
+    // Try to log in, which should fail.
+    $edit['name'] = $this->normal_user->name;
+    $edit['pass'] = $this->normal_user->pass_raw;
+    $edit['captcha_response'] = '?';
+
+    $this->drupalPost('user', $edit, t('Log in'));
+    // Check for error message.
+    $this->assertText(t('The answer you entered for the CAPTCHA was not correct.'), 'CAPTCHA should block user login form', 'reCAPTCHA');
+
+    // And make sure that user is not logged in: check for name and password fields on ?q=user
+    $this->drupalGet('user');
+    $this->assertField('name', t('Username field found.'), 'reCAPTCHA');
+    $this->assertField('pass', t('Password field found.'), 'reCAPTCHA');
+  }
+
+}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha_mailhide/recaptcha_mailhide.info b/drupal7/sites/all/modules/recaptcha/recaptcha_mailhide/recaptcha_mailhide.info
deleted file mode 100644
index 72436403..00000000
--- a/drupal7/sites/all/modules/recaptcha/recaptcha_mailhide/recaptcha_mailhide.info
+++ /dev/null
@@ -1,11 +0,0 @@
-name = reCAPTCHA Mailhide
-description = Uses the reCAPTCHA web service to protect email addresses.
-core = 7.x
-package = Spam control
-
-; Information added by Drupal.org packaging script on 2017-01-06
-version = "7.x-1.14"
-core = "7.x"
-project = "recaptcha"
-datestamp = "1483730050"
-
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha_mailhide/recaptcha_mailhide.install b/drupal7/sites/all/modules/recaptcha/recaptcha_mailhide/recaptcha_mailhide.install
deleted file mode 100644
index c2668453..00000000
--- a/drupal7/sites/all/modules/recaptcha/recaptcha_mailhide/recaptcha_mailhide.install
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * @file
- * Provides install, updated, and uninstall functions for recaptcha_mailhide.
- */
-
-/**
- * Implements hook_update_N().
- */
-
-/**
- * Issue #2473329: Remove obsolete variables.
- */
-function recaptcha_mailhide_update_7100() {
-  // recaptcha_mailhide no longer uses any variables.
-  db_delete('variable')
-    ->condition('name', 'recaptcha_mailhide_%', 'LIKE')
-    ->execute();
-  cache_clear_all('variables', 'cache_bootstrap');
-  return t('Removed obsolete variables.');
-}
diff --git a/drupal7/sites/all/modules/recaptcha/recaptcha_mailhide/recaptcha_mailhide.module b/drupal7/sites/all/modules/recaptcha/recaptcha_mailhide/recaptcha_mailhide.module
deleted file mode 100644
index fcb4f44b..00000000
--- a/drupal7/sites/all/modules/recaptcha/recaptcha_mailhide/recaptcha_mailhide.module
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-
-/**
- * @file
- * Protects email addresses using the reCAPTCHA web service.
- */
-
-/**
- * Implements hook_help().
- */
-function recaptcha_mailhide_help($path, $arg) {
-  $output = '';
-
-  switch ($path) {
-    case 'admin/modules#name':
-      $output .= t('reCAPTCHA');
-      break;
-
-    case 'admin/modules#description':
-      $output .= t('Uses the <a href="@url" target="_blank">reCAPTCHA</a> web service to protect email addresses.', array('@url' => url('https://www.google.com/recaptcha/mailhide/apikey')));
-      break;
-
-    case 'admin/help#recaptcha':
-      $output .= '<p>' .
-        t('Uses the reCAPTCHA web service to protect email addresses. For more information on what reCAPTCHA Mailhide is, visit <a href="@url" target="_blank">the official website</a>.', array('@url' => url('https://www.google.com/recaptcha/mailhide/apikey'))) .
-        '</p><h3>' .
-        t('Configuration') .
-        '</h3><p>' .
-        t('Head over to the <a href="@inputformats">input format settings</a> and add the <a href="@url" target="_blank">reCAPTCHA Mailhide</a> input filter to hide posted emails.', array('@inputformats' => url('admin/settings/filter'), '@url' => url('https://www.google.com/recaptcha/mailhide/apikey'))) .
-        '</p>';
-      break;
-  }
-
-  return $output;
-}
-
-/**
- * Implements hook_filter_info().
- */
-function recaptcha_mailhide_filter_info() {
-  $filters['filter_recaptcha_mailhide'] = array(
-    'title' => t('reCAPTCHA Mailhide'),
-    'description' => _filter_recaptcha_mailhide_tips('', ''),
-    'process callback' => '_filter_recaptcha_mailhide',
-    'settings callback' => '_filter_recaptcha_mailhide_settings',
-    'tips callback' => '_filter_recaptcha_mailhide_tips',
-    'cache' => FALSE,
-  );
-  return $filters;
-}
-
-/**
- * Filter callbacks.
- */
-function _filter_recaptcha_mailhide($text, $filter, $format) {
-  global $_recaptcha_mailhide_public_key, $_recaptcha_mailhide_private_key, $_recaptcha_mailhide_nokey_warn;
-  _recaptcha_mailhide_load_library();
-  $_recaptcha_mailhide_public_key = $filter->settings['recaptcha_mailhide_public_key'];
-  $_recaptcha_mailhide_private_key = $filter->settings['recaptcha_mailhide_private_key'];
-
-  $text = ' ' . $text . ' ';
-  $text = preg_replace_callback("!(<p>|<li>|<br\s*/?>|[ \n\r\t\(])([A-Za-z0-9._-]+@[A-Za-z0-9._+-]+\.[A-Za-z]{2,4})([.,?]?)(?=(</p>|</li>|<br\s*/?>|[ \n\r\t\)]))!i", '_recaptcha_replace', $text);
-  $text = drupal_substr($text, 1, -1);
-
-  unset($_recaptcha_mailhide_public_key);
-  unset($_recaptcha_mailhide_private_key);
-  unset($_recaptcha_mailhide_nokey_warn);
-  return $text;
-}
-
-/**
- *
- */
-function _filter_recaptcha_mailhide_settings($form, &$form_state, $filter, $format, $defaults, $filters) {
-  _recaptcha_mailhide_load_library();
-  $public = isset($filter->settings['recaptcha_mailhide_public_key']) ? $filter->settings['recaptcha_mailhide_public_key'] : '';
-  $private = isset($filter->settings['recaptcha_mailhide_private_key']) ? $filter->settings['recaptcha_mailhide_private_key'] : '';
-  $settings['recaptcha_mailhide_public_key'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Public Key'),
-    '#default_value' => $public,
-    '#maxlength' => 50,
-    '#description' => t('Your public Mailhide key obtained from <a href="@url" target="_blank">reCAPTCHA</a>.', array('@url' => 'https://www.google.com/recaptcha/mailhide/apikey')),
-  );
-  $settings['recaptcha_mailhide_private_key'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Private Key'),
-    '#default_value' => $private,
-    '#maxlength' => 50,
-    '#description' => t('Your private Mailhide key obtained from <a href="@url" target="_blank">reCAPTCHA</a>.', array('@url' => 'https://www.google.com/recaptcha/mailhide/apikey')),
-  );
-  return $settings;
-}
-
-/**
- *
- */
-function _filter_recaptcha_mailhide_tips($filter, $format, $long = FALSE) {
-  return t('E-Mail addresses are hidden with <a href="@url" target="_blank">reCAPTCHA Mailhide</a>.', array('@url' => 'https://www.google.com/recaptcha/mailhide/apikey'));
-}
-
-/**
- * Private reCAPTCHA function to replace an email regex match.
- */
-function _recaptcha_replace($match) {
-  global $_recaptcha_mailhide_public_key, $_recaptcha_mailhide_private_key, $_recaptcha_mailhide_nokey_warn;
-  // If we invoke recaptchalib without setting the keys, it will die(). Instead,
-  // fail gracefully.
-  if (empty($_recaptcha_mailhide_public_key) || empty($_recaptcha_mailhide_private_key) || !function_exists('mcrypt_encrypt')) {
-    if ($_recaptcha_mailhide_nokey_warn != TRUE) {
-      if (!function_exists('mcrypt_encrypt')) {
-        drupal_set_message(t('Addresses cannot be hidden because Mcrypt is not installed.'), 'error');
-      }
-      else {
-        drupal_set_message(t('Addresses cannot be hidden because the administrator has not set the reCAPTCHA Mailhide keys.'), 'error');
-      }
-      $_recaptcha_mailhide_nokey_warn = TRUE;
-    }
-    $email = $match[2];
-  }
-  else {
-    $email = recaptcha_mailhide_html($_recaptcha_mailhide_public_key, $_recaptcha_mailhide_private_key, $match[2]);
-  }
-  return $match[1] . $email . $match[3];
-}
-
-/**
- * Load the recaptcha library.
- */
-function _recaptcha_mailhide_load_library() {
-  module_load_include('php', 'recaptcha', 'recaptcha-php-1.11/recaptchalib');
-}
diff --git a/drupal7/sites/all/modules/recaptcha/src/ReCaptcha/RequestMethod/Drupal7Post.php b/drupal7/sites/all/modules/recaptcha/src/ReCaptcha/RequestMethod/Drupal7Post.php
new file mode 100644
index 00000000..450b7392
--- /dev/null
+++ b/drupal7/sites/all/modules/recaptcha/src/ReCaptcha/RequestMethod/Drupal7Post.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Custom Drupal 7 RequestMehod class for Google reCAPTCHA library.
+ */
+
+namespace ReCaptcha\RequestMethod;
+
+use ReCaptcha\RequestMethod;
+use ReCaptcha\RequestParameters;
+
+/**
+ * Sends POST requests to the reCAPTCHA service.
+ */
+class Drupal7Post implements RequestMethod {
+
+  /**
+   * URL to which requests are POSTed.
+   * @const string
+   */
+  const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
+
+  /**
+   * Submit the POST request with the specified parameters.
+   *
+   * @param RequestParameters $params Request parameters
+   * @return string Body of the reCAPTCHA response
+   */
+  public function submit(RequestParameters $params) {
+
+    $options = array(
+      'headers' => array(
+        'Content-type' => 'application/x-www-form-urlencoded',
+      ),
+      'method' => 'POST',
+      'data' => $params->toQueryString(),
+    );
+    $response = drupal_http_request(self::SITE_VERIFY_URL, $options);
+
+    return isset($response->data) ? $response->data : '';
+  }
+}
diff --git a/drupal7/sites/all/modules/recaptcha/tests/recaptcha_base.test b/drupal7/sites/all/modules/recaptcha/tests/recaptcha_base.test
deleted file mode 100644
index b07471ed..00000000
--- a/drupal7/sites/all/modules/recaptcha/tests/recaptcha_base.test
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains ReCaptcha.
- */
-
-/**
- * Test cases for ReCaptcha module UI.
- */
-abstract class ReCaptchaBaseTestCase extends DrupalWebTestCase {
-
-  /**
-   * User with various administrative permissions.
-   *
-   * @var object Drupal user
-   */
-  protected $admin_user;
-
-  /**
-   * Normal visitor with limited permissions.
-   *
-   * @var object Drupal user;
-   */
-  protected $normal_user;
-
-  /**
-   * Drupal path of the (general) CAPTCHA admin page.
-   */
-  const RECAPTCHA_ADMIN_PATH = 'admin/config/people/captcha/recaptcha';
-
-  /**
-   * Set up some required modules and users.
-   */
-  public function setUp() {
-    parent::setUp('recaptcha');
-
-    // Create a normal user.
-    $permissions = array(
-      'access comments',
-      'post comments',
-      'skip comment approval',
-      'access content',
-      'create page content',
-      'edit own page content',
-    );
-    $this->normal_user = $this->drupalCreateUser($permissions);
-
-    // Create an admin user.
-    $permissions[] = 'administer CAPTCHA settings';
-    $permissions[] = 'skip CAPTCHA';
-    $permissions[] = 'administer permissions';
-    $permissions[] = 'administer content types';
-    $permissions[] = 'administer recaptcha';
-    $this->admin_user = $this->drupalCreateUser($permissions);
-  }
-
-}
diff --git a/drupal7/sites/all/modules/recaptcha/tests/recaptcha_server_status.test b/drupal7/sites/all/modules/recaptcha/tests/recaptcha_server_status.test
deleted file mode 100644
index 660be34d..00000000
--- a/drupal7/sites/all/modules/recaptcha/tests/recaptcha_server_status.test
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains ReCaptcha.
- */
-
-/**
- * Test cases for ReCaptcha module UI.
- */
-class ReCaptchaServerStatusTestCase extends ReCaptchaBaseTestCase {
-
-  /**
-   * Drupal path of the (general) CAPTCHA admin page.
-   */
-  const RECAPTCHA_ADMIN_PATH = 'admin/config/people/captcha/recaptcha';
-
-  /**
-   *
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'ReCaptcha server status cache test',
-      'group' => 'reCAPTCHA',
-      'description' => 'Tests reCaptcha admin UI',
-    );
-  }
-
-  /**
-   *
-   */
-  public function setup() {
-    parent::setUp();
-    include_once drupal_get_path('module', 'recaptcha') . '/recaptcha-php-1.11/recaptchalib.php';
-  }
-
-  /**
-   *
-   */
-  public function testServerStatusCache() {
-    // No cache before status check.
-    $status_cache = cache_get('recaptcha_server_status');
-    $this->assertFalse($status_cache);
-
-    // Call status check to set Cache.
-    _recaptcha_server_is_up();
-
-    $status_cache = cache_get('recaptcha_server_status');
-    $this->assertNotNull($status_cache);
-  }
-
-}
diff --git a/drupal7/sites/all/modules/recaptcha/tests/recaptcha_ui.test b/drupal7/sites/all/modules/recaptcha/tests/recaptcha_ui.test
deleted file mode 100644
index fccc604a..00000000
--- a/drupal7/sites/all/modules/recaptcha/tests/recaptcha_ui.test
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains ReCaptcha.
- */
-
-/**
- * Test cases for ReCaptcha module UI.
- */
-class ReCaptchaAdminUITestCase extends ReCaptchaBaseTestCase {
-
-  /**
-   * Drupal path of the (general) CAPTCHA admin page.
-   */
-  const RECAPTCHA_ADMIN_PATH = 'admin/config/people/captcha/recaptcha';
-
-  /**
-   *
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'ReCaptcha admin UI test',
-      'group' => 'reCAPTCHA',
-      'description' => 'Tests reCaptcha admin UI',
-    );
-  }
-
-  /**
-   * Test access to the admin pages.
-   */
-  public function testAdminAccess() {
-    $this->drupalLogin($this->normal_user);
-    $this->drupalGet(self::RECAPTCHA_ADMIN_PATH);
-    file_put_contents('tmp.simpletest.html', $this->drupalGetContent());
-    $this->assertText(t('Access denied'), 'Normal users should not be able to access the CAPTCHA admin pages', 'CAPTCHA');
-    $this->assertResponse(403);
-
-    $this->drupalLogin($this->admin_user);
-    $this->drupalGet(self::RECAPTCHA_ADMIN_PATH);
-    $this->assertResponse(200);
-    $this->assertField('recaptcha_server_status_check_interval');
-
-    // Update the interval and make sure it get saved properly.
-    $edit = array(
-      'recaptcha_server_status_check_interval' => 20,
-      'recaptcha_public_key' => '6LekT84SAAAAAE0AqDpG5IrtKoyr7OOiE82MtXgJ',
-      'recaptcha_private_key' => '6LekT84SAAAAAE0AqDpG5IrtKoyr7OOiE81MtXgJ',
-    );
-    $this->drupalPost(NULL, $edit, t('Save configuration'));
-    $this->assertText(t('The configuration options have been saved.'));
-    $this->assertFieldByName('recaptcha_server_status_check_interval', 20);
-  }
-
-}
