You are here

ip2country.module in IP-based Determination of a Visitor's Country 6

Same filename and directory in other branches
  1. 8 ip2country.module
  2. 7 ip2country.module

Determination of user's Country based on IP.

This module uses the IP Address that a user is connected from to deduce the Country where the user is located. This method is not foolproof, because a user may connect through an anonymizing proxy, or may be in an unusual case, such as getting service from a neighboring country, or using an IP block leased from a company in another country. Additionally, users accessing a server on a local network may be using an IP that is not assigned to any country (e.g. 192.168.x.x).

Country determination occurs upon user login. If a country can be determined from the IP address, the ISO 3166 2-character country code is stored in the Drupal $user object as $user->country_iso_code_2. If no country can be determined, this member is left unset.

The database used is maintained by ARIN, the American Registry for Internet Numbers (http://www.arin.net/about_us/index.html), which is one of the 5 official Regional Internet Registries (RIR) responsible for assigning IP addresses. The claim is the database is 98% accurate, with most of the problems coming from users in less-developed countries. Regardless, there's no more-authoritative source of this information.

@author Tim Rohaly. <http://drupal.org/user/202830>

File

ip2country.module
View source
<?php

/**
 * @file
 * Determination of user's Country based on IP.
 *
 * This module uses the IP Address that a user is connected from to deduce
 * the Country where the user is located. This method is not foolproof,
 * because a user may connect through an anonymizing proxy, or may be in
 * an unusual case, such as getting service from a neighboring country,
 * or using an IP block leased from a company in another country.
 * Additionally, users accessing a server on a local network may be using
 * an IP that is not assigned to any country (e.g. 192.168.x.x).
 *
 * Country determination occurs upon user login. If a country can be
 * determined from the IP address, the ISO 3166 2-character country code
 * is stored in the Drupal $user object as $user->country_iso_code_2.
 * If no country can be determined, this member is left unset.
 *
 * The database used is maintained by ARIN, the American Registry for
 * Internet Numbers (http://www.arin.net/about_us/index.html), which is
 * one of the 5 official Regional Internet Registries (RIR) responsible
 * for assigning IP addresses. The claim is the database is 98% accurate,
 * with most of the problems coming from users in less-developed countries.
 * Regardless, there's no more-authoritative source of this information.
 *
 * @author Tim Rohaly.    <http://drupal.org/user/202830>
 */

/******************************************************************************
 * Drupal Hooks                                                               *
 ******************************************************************************/

/**
 * Implements hook_help().
 */
function ip2country_help($path, $arg) {
  switch ($path) {
    case 'admin/help#ip2country':
      return t('Determines the Country where the user is located based on the IP address used.');
      break;
    case 'admin/settings/ip2country':
      return t('Configuration settings for the ip2country module.');
      break;
  }
}

/**
 * Implements hook_perm().
 */
function ip2country_perm() {
  return array(
    'administer ip2country',
  );
}

/**
 * Implements hook_cron().
 *
 * Updates the IP to Country database automatically on a periodic
 * basis. Default period is 1 week.
 */
function ip2country_cron() {

  // Utility functions for fetching and loading IP/Country DB from RIR.
  module_load_include('inc', 'ip2country');

  // Automatic database updates are disabled when $update_interval == 0.
  $update_interval = variable_get('ip2country_update_interval', 604800);
  if ($update_interval && variable_get('ip2country_last_update', 0) <= time() - $update_interval) {
    $status = ip2country_update_database(variable_get('ip2country_rir', 'arin'));

    // Log to watchdog if requested.
    if (variable_get('ip2country_watchdog', TRUE)) {
      if ($status != FALSE) {

        // Success.
        watchdog('ip2country', 'Database updated from @registry server. Table contains @rows rows.', array(
          '@registry' => drupal_strtoupper(variable_get('ip2country_rir', 'arin')),
          '@rows' => $status,
        ), WATCHDOG_NOTICE);
      }
      else {

        // Failure.
        watchdog('ip2country', 'Database update from @registry server FAILED.', array(
          '@registry' => drupal_strtoupper(variable_get('ip2country_rir', 'arin')),
        ), WATCHDOG_WARNING);
      }
    }
  }
}

/**
 * Implements hook_menu().
 */
function ip2country_menu() {
  $items = array();
  $items['admin/settings/ip2country'] = array(
    'title' => 'IP to Country settings',
    'description' => 'Settings for determining user location from IP address.',
    'access arguments' => array(
      'administer ip2country',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'ip2country_admin_settings',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'ip2country.admin.inc',
  );
  $items['admin/settings/ip2country/update'] = array(
    'title' => 'Update database',
    'access arguments' => array(
      'administer ip2country',
    ),
    'page callback' => '_ip2country_update',
    'type' => MENU_CALLBACK,
    'file' => 'ip2country.admin.inc',
  );
  $items['admin/settings/ip2country/lookup'] = array(
    'title' => 'Lookup IP address in database',
    'access arguments' => array(
      'administer ip2country',
    ),
    'page callback' => '_ip2country_lookup',
    'type' => MENU_CALLBACK,
    'file' => 'ip2country.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_user().
 *
 * Detects IP and determines country upon user login.
 */
function ip2country_user($op, &$edit, &$account, $category = NULL) {
  switch ($op) {
    case 'login':

      // Successful login. First determine user's country based on IP.
      $ip = ip_address();
      $country_code = ip2country_get_country($ip);

      // Now check to see if this user has "administer ip2country" permission
      // and if debug mode set. If both are TRUE, use debug information
      // instead of real information.
      if (user_access('administer ip2country') && variable_get('ip2country_debug', FALSE)) {
        $type = variable_get('ip2country_test_type', 0);
        if ($type == 0) {

          // Debug Country entered.
          $country_code = variable_get('ip2country_test_country', 'US');
        }
        else {

          // Debug IP entered.
          $ip = variable_get('ip2country_test_ip_address', $ip);
          $country_code = ip2country_get_country($ip);
        }
        drupal_set_message(t('Using DEBUG value for Country - @country', array(
          '@country' => $country_code,
        )));
      }

      // Finally, save country, if it has been determined.
      if ($country_code) {

        // Store the ISO country code in the $user object.
        user_save($account, array(
          'country_iso_code_2' => $country_code,
        ));
      }
      break;
  }
}

/******************************************************************************
 * Module Functions                                                           *
 ******************************************************************************/

/**
 * Gets the ISO 3166 2-character country Code from the IP address.
 *
 * @param $ip_address
 *   IP address either as a dotted quad string (e.g. "127.0.0.1") or
 *   as a 32-bit unsigned long integer.
 *
 * @return
 *   FALSE if the lookup failed to find a country for this IP.
 */
function ip2country_get_country($ip_address) {
  $ipl = ip2long($ip_address);
  if (is_long($ip_address)) {
    $ipl = $ip_address;
  }

  // Locate IP within range.
  $sql = "SELECT country FROM {ip2country}\n          WHERE (%d >= ip_range_first AND %d <= ip_range_last) LIMIT 1";
  $country_code = db_result(db_query($sql, $ipl, $ipl));
  return $country_code;
}

/**
 * Gets the total count of IP ranges in database.
 */
function ip2country_get_count() {
  $sql = "SELECT COUNT(*) FROM {ip2country}";
  $count = db_result(db_query($sql));
  return (int) $count;
}

/******************************************************************************
 * Ubercart Integration                                                       *
 ******************************************************************************/

/**
 * Implements hook_form_[form_id]_alter().
 *
 * Alters Ubercart's uc_cart_checkout_form() to use ip2country's country
 * determination as the default billing and delivery country. If the user's
 * country hasn't been determined, the store country will be used instead.
 */
function ip2country_form_uc_cart_checkout_form_alter(&$form, &$form_state) {
  global $user;
  if (isset($user->country_iso_code_2)) {
    if (module_exists('uc_store')) {
      $country_id = db_result(db_query("SELECT country_id from {uc_countries} WHERE country_iso_code_2 = '%s'", $user->country_iso_code_2));
      if (!$country_id) {
        $country_id = uc_store_default_country();
      }
      $form['panes']['billing']['billing_country']['#default_value'] = $form['panes']['delivery']['delivery_country']['#default_value'] = $country_id;
    }
  }
}

/**
 * Implements hook_form_[form_id]_alter().
 *
 * Alters Ubercart's uc_cart_pane_quotes() form to use ip2country's country
 * determination as the default billing and delivery country. If the user's
 * country hasn't been determined, the store country will be used instead.
 */
function ip2country_form_uc_cart_pane_quotes_alter(&$form, &$form_state) {
  global $user;
  if (isset($user->country_iso_code_2)) {
    if (module_exists('uc_store')) {
      $country_id = db_result(db_query("SELECT country_id from {uc_countries} WHERE country_iso_code_2 = '%s'", $user->country_iso_code_2));
      if (!$country_id) {
        $country_id = uc_store_default_country();
      }
      $form['delivery_country']['#default_value'] = $country_id;
    }
  }
}

Functions

Namesort descending Description
ip2country_cron Implements hook_cron().
ip2country_form_uc_cart_checkout_form_alter Implements hook_form_[form_id]_alter().
ip2country_form_uc_cart_pane_quotes_alter Implements hook_form_[form_id]_alter().
ip2country_get_count Gets the total count of IP ranges in database.
ip2country_get_country Gets the ISO 3166 2-character country Code from the IP address.
ip2country_help Implements hook_help().
ip2country_menu Implements hook_menu().
ip2country_perm Implements hook_perm().
ip2country_user Implements hook_user().