diff --git a/geocoder.admin.inc b/geocoder.admin.inc index c4ca49b..734e4e7 100644 --- a/geocoder.admin.inc +++ b/geocoder.admin.inc @@ -34,6 +34,67 @@ function geocoder_admin_settings($form, &$form_state) { '#required' => FALSE, ); + $form['geocoder_google_auth_method'] = array( + '#type' => 'select', + '#title' => t('Google API Authorization Method'), + '#description' => t("If your website runs on shared hosting, you'll want to authenticate requests to the Google Geocoding API to reduce the likelihood of being rate limited (2500 requests per day / 5 requests per second). Alternatively, Google Maps for Work customers may use their Client ID and Signing Key to authenticate."), + '#default_value' => variable_get('geocoder_google_auth_method', GEOCODER_GOOGLE_AUTH_NONE), + '#options' => array( + GEOCODER_GOOGLE_AUTH_NONE => 'None', + GEOCODER_GOOGLE_AUTH_KEY => 'API Key (free)', + GEOCODER_GOOGLE_AUTH_WORK => 'Google Maps API for Work', + ), + ); + + $form['geocoder_apikey_google'] = array( + '#type' => 'textfield', + '#title' => t('Google Maps API Key'), + '#description' => t('Obtain a free Google Geocoding API Key at @link', array( + '@link' => 'https://developers.google.com/maps/documentation/geocoding/#api_key', + )), + '#default_value' => empty($geocoder_settings['geocoder_apikey_google']) ? '' : $geocoder_settings['geocoder_apikey_google'], + '#required' => FALSE, + '#states' => array( + 'visible' => array( + ':input[name="geocoder_google_auth_method"]' => array('value' => GEOCODER_GOOGLE_AUTH_KEY), + ), + ), + ); + + $form['geocoder_google_client_id'] = array( + '#type' => 'textfield', + '#title' => t('Google Maps API for Work: Client ID'), + '#description' => t('For more information, visit @link', array( + '@link' => 'https://developers.google.com/maps/documentation/business/webservices/auth#business-specific_parameters', + )), + '#default_value' => variable_get('geocoder_google_client_id'), + '#required' => FALSE, + '#states' => array( + 'visible' => array( + ':input[name="geocoder_google_auth_method"]' => array( + 'value' => GEOCODER_GOOGLE_AUTH_WORK, + ), + ), + ), + ); + + $form['geocoder_google_private_key'] = array( + '#type' => 'textfield', + '#title' => t('Google Maps API for Work: Private/Signing Key'), + '#description' => t('For more information, visit @link', array( + '@link' => 'https://developers.google.com/maps/documentation/business/webservices/auth#how_do_i_get_my_signing_key', + )), + '#default_value' => variable_get('geocoder_google_private_key'), + '#required' => FALSE, + '#states' => array( + 'visible' => array( + ':input[name="geocoder_google_auth_method"]' => array( + 'value' => GEOCODER_GOOGLE_AUTH_WORK, + ), + ), + ), + ); + $form['geocoder_google_delay'] = array( '#type' => 'textfield', '#title' => t('Delay between Google geocoding requests (in milliseconds)'), @@ -57,5 +118,6 @@ function geocoder_admin_settings_submit($form, &$form_state) { $geocoder_settings['geocoder_apikey_yahoo'] = trim($form_state['values']['geocoder_apikey_yahoo']); $geocoder_settings['geocoder_apikey_yandex'] = trim($form_state['values']['geocoder_apikey_yandex']); $geocoder_settings['geocoder_apikey_bing'] = trim($form_state['values']['geocoder_apikey_bing']); + $geocoder_settings['geocoder_apikey_google'] = trim($form_state['values']['geocoder_apikey_google']); variable_set("geocoder_settings", $geocoder_settings); } diff --git a/geocoder.module b/geocoder.module index 6d6a0c2..0f225bd 100644 --- a/geocoder.module +++ b/geocoder.module @@ -8,6 +8,10 @@ include_once('geocoder.widget.inc'); include_once('geocoder.services.inc'); +define('GEOCODER_GOOGLE_AUTH_NONE', 1); +define('GEOCODER_GOOGLE_AUTH_KEY', 2); +define('GEOCODER_GOOGLE_AUTH_WORK', 3); + /** * The Geocoder API call. * diff --git a/plugins/geocoder_handler/google.inc b/plugins/geocoder_handler/google.inc index ee7a6e1..5305934 100644 --- a/plugins/geocoder_handler/google.inc +++ b/plugins/geocoder_handler/google.inc @@ -52,7 +52,54 @@ function geocoder_google($address, $options = array()) { $query['components'] = $options['biasing']['components']; } - $url = url("http://maps.googleapis.com/maps/api/geocode/json", array('query' => $query)); + // Add Google API authentication parameters. + switch (variable_get('geocoder_google_auth_method')) { + + // Google Geocoding API Key. + case GEOCODER_GOOGLE_AUTH_KEY: + $geocoder_settings = variable_get("geocoder_settings", array()); + if (!empty($geocoder_settings['geocoder_apikey_google'])) { + $query['key'] = $geocoder_settings['geocoder_apikey_google']; + } + break; + + // Google Maps for Work signature. + case GEOCODER_GOOGLE_AUTH_WORK: + $client_id = variable_get('geocoder_google_client_id', FALSE); + $private_key = variable_get('geocoder_google_private_key', FALSE); + if (!empty($client_id) && !empty($private_key)) { + $google_maps_for_work = TRUE; + $query['client'] = $client_id; + } + break; + } + + // Build the URL. + $url = url("https://maps.googleapis.com/maps/api/geocode/json", array( + 'query' => $query, + )); + + // If authenticating via Google Maps for Work, add signature. + // @see https://developers.google.com/maps/documentation/business/webservices/auth#generating_valid_signatures + if (!empty($google_maps_for_work)) { + + // Strip off the protocol and host for signing. + $parsed = parse_url($url); + $url_to_sign = $parsed['path'] . '?' . $parsed['query']; + + // Decode the key in a URL-safe way (RFC 4648 Section 5). + // @see https://www.ietf.org/rfc/rfc4648.txt + $decoded_key = base64_decode(strtr($private_key, '-_', '+/')); + + // Use HMAC SHA1 to sign the URL with the decoded key. + $signature = hash_hmac('sha1', $url_to_sign, $decoded_key, TRUE); + + // Append the signature to the URL's query parameters. Do this manually to + // avoid URL encoding. + $url .= '&signature=' . strtr(base64_encode($signature), '+/', '-_'); + } + + // Send the request. $result = drupal_http_request($url); $delay_trigger = TRUE;