Customizing SMF > SMF Coding Discussion

[WIP/BETA] EU cookie law

<< < (4/50) > >>

CircleDock:
How to make Emanuele's Modification only affect visitors from within the EU

As it stands, Emanuele's modification will prompt all visitors to accept cookies, regardless of where they are located in the world. This may not be desirable and could be irritable for non-EU members.  There is a way around this but you will need to have Spuds' geoIP (IP to Location) modification installed as the method presented here makes use of that modification's database tables.

We will be editing just one file - Subs-EclWarning.php - which should be in your Sources directory.

Search for:

--- Code: ---    global $cookiename, $modSettings;

    static $storeCookies;

    if (isset($storeCookies))
        return $storeCookies;

--- End code ---

Replace with:

--- Code: ---    global $cookiename, $modSettings;

    static $storeCookies;
    static $inEU;           // True if visitor's Point of Presence is within a EU member state

    // Have we checked this visitor's Point of Presence?
    if (!isset($inEU))
        $inEU = ecl_IsInEU();
    // If he's not within the EU, he can have cookies without giving permission
    if (!$inEU)
        $storeCookies = true;

    if (isset($storeCookies))
        return $storeCookies;

--- End code ---

Immediately above the function ecl_authorized_cookies(), add the following code:

--- Code: ---//
// Convert an IPv4 Address to a long integer. Adapted from a similarly-named
// geoIP function and included to save loading the whole of geoIP.
//
// Parameter:   $ip_Addr       An IPv4 Address in dotted notation
//
// Returns:     >0             IT Address as long integer
//              0              No IP Address or badly-formed IP Address
//
function ecl_geo_dot2long($ip_addr)
{
    if (empty($ip_addr))
return 0;
    elseif (preg_match('~\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}~', $ip_addr, $dummy))
{
        $ips = explode('.', $ip_addr);
        return ($ips[3] + $ips[2] * 256 + $ips[1] * 256 * 256 + $ips[0] * 256 * 256 * 256);
    }
else
return 0;
}

//
// Master Function to determine whether an IP Address points to a location within the European Union.
// If the geoIP modification is available and the visitor's IP Address is IPv4, then we use the geoIP
// tables. IPv6 Addresses currently not catered for.
//
// Returns     true     If the ECL modification should prompt for permission to store cookies.
//
function ecl_IsInEU()
{
    global $sourcedir;
   
  if (!isset($_SERVER['REMOTE_ADDR']))
    {
      return true;
    }
 
  if ((file_exists($sourcedir . 'geoIP.php')) && (strpos($_SERVER['REMOTE_ADDR'],':') === false))
  {
    return ecl_geoIP($_SERVER['REMOTE_ADDR']);
  }

// Add code here if you want to check IPv6 Addresses (not currently catered for).
  return true;
}

//
// This function checks the geoIP database tables to determine whether a given
// IPv4 Address is located within the EU. Uses code from geoIP modification.
//
// Parameter:   $ipAddress      A properly-formed IPv4 Address
//
// Returns:     true            IP Address is within the EU
//              false           IP Address not within the EU
//
function ecl_geoIP($ipAddress)
{
    global $smcFunc;
   
    $euNations = array('AD', 'AT', 'BE', 'BG', 'CY', 'CZ',      // Andorra (Sain), Austria, Belgium, Bulgaria, Cyprus, Czech Rep,
                       'DE', 'DK', 'EE', 'ES', 'EU', 'FI',      // Germany, Denmark, Estonia, Spain, (Europe), Finland,
                       'FR', 'FX', 'GB', 'GG', 'GR', 'HU',      // France, France (Metropolitain), United Kingdom, Guernsey, Greece, Hungary,
                       'IE', 'IM', 'IT', 'JE', 'LI', 'LT',      // Ireland, Isle of Man, Italy, Jersey, Lithuania, Lichtenstein,
                       'LU', 'LV', 'MC', 'MT', 'PL', 'PT',      // Luxemburg, Latvia, Monaco, Malta, Poland, Portugal,
                       'RO', 'SE', 'SI', 'SK', 'SM', 'VA',      // Romania, Sweden, Slovenia, Slovakia, San Marino, Vatican City             
    );
   
    $ip = $ipAddress;
    // We have an IPv4 address, convert to a long integer
    if (strpos($ip,'.'))
        $ipl = ecl_geo_dot2long($ip);

    // No declared IP Address, must be a dodgy user. Assume he's in Europe!
    if ($ipl === 0)
        return true;
       
    // Localhost is assumed not to be within the EU, though.
    if ($ipl == 2130706433)
        return false;
   
    // Query the geoIP database       
    $request = $smcFunc['db_query']('', '
SELECT ip.start, ip.end,
gc.cc, gc.cn as country
FROM {db_prefix}geoip_ip as ip
LEFT JOIN {db_prefix}geoip_countries AS gc ON (ip.locid = gc.ci)
WHERE ip.end >= {int:ip}
ORDER BY ip.end ASC
LIMIT 1',
array(
'ip' => (int) $ipl
)
);

    // Sanitise the result. Assume the visitor is within the EU.
$row = $smcFunc['db_fetch_assoc']($request);
    $cc = 'EU';
    // If the IP Address is not contained within a range, the next higher value will be returned.
    if ($row['start'] <= $ipl && $row['end'] >= $ipl)
        $cc = $row['cc'];
    $smcFunc['db_free_result']($request);   
   
    //  We have the two-letter country code, does it match?
    $res = array_search($cc, $euNations);
    if ($res === false)
            return false;
    return true;
}

--- End code ---

That's it folks!

A caveat and other considerations:

* This addition does not currently cater for checking the location of IPv6 Addresses and assumes any IPv6 address is within the EU.
* As other countries (eg the US, Australia, New Zealand etc) enact similar cookie laws, their two-letter country codes can be added to the array $euNations
* You can replace the database query with a cURL request to an external IP-to-location server.
* Note that some EU member states have more than one identifiable country code (eg the UK includes 'GB', 'GG', 'IM' and "JE').
* Note that I'm only testing for the presence of the geoIP modification (and hence its database), its code is not loaded and executed by this modification. If you have installed geoIP but have not updated its database, then you will need to do this.
Enjoy!

emanuele:
Honestly geo location is not at all accurate, so I would strictly avoid it.

CircleDock:

--- Quote from: emanuele on May 03, 2012, 01:07:08 PM ---Honestly geo location is not at all accurate, so I would strictly avoid it.

--- End quote ---
Your point is well taken but MaxMind, whose database this uses, claims it to be well over 98% accurate at the country level, which is all we're interested in. The changes I've suggested do err on the side of caution and assume the visitor is within the EU unless their IP Address shows otherwise.

There's enough hostility towards this law as it is and some EU-based Admins might well be hesitant to implement any solution that might antagonize their non-EU members and visitors.

Given that many UK Government sites employ Geo-Location, as does all the UK's television broadcasters, Amazon etc., I can not see any reason why the ICO should object or come down heavily against a Forum where its use of Geo-Location has produced an erroneous result.

:
The ICO does not itself use country based geo-location for the purposes of cookie detection and I can't see them taking it well if there is a complaint because it seems to be 'how can we avoid having to do this' which is a sign of bad faith compliance.

Geo-location where it can be used to benefit the user's experience is encouraged, or where it is required for legal compliance (e.g. content that is only licensed in certain places) but to avoid compliance with a law, is not something I'd want to argue.

emanuele:

--- Quote from: CircleDock on May 04, 2012, 12:48:44 PM ---Your point is well taken but MaxMind, whose database this uses, claims it to be well over 98% accurate at the country level, which is all we're interested in.

--- End quote ---
That would leave you a 1% (well, probably less) of probability that an EU-based user will not be recognized as such.
If you are lucky enough within this 1% you will not get anyone that will sue you (or report to the competent authority).

Feel free to post it, no problem, I (personally) would not include it in a package. ;)

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version