xref: /web-master/scripts/ip-to-country (revision 0e511803)
1<?php /* vim: set noet ts=4 sw=4 ft=php: : */
2
3// This script will only work if included from "update-backend"!
4
5// Fetch ip-to-country.com data and index database
6function fetch_ip_to_country($root)
7{
8    // Files used / affected
9    $ipdbt   = "ip-to-country.db~";
10    $ipdbc   = "ip-to-country.dbr~";
11    $ipdb    = "$root/backend/ip-to-country.db";
12    $ipidx   = "$root/backend/ip-to-country.idx";
13
14    // Create one index entry for at least
15    // every '$indexby'th record in the db
16    $indexby = 10000000;
17
18    // Get last modified date of ip-to-country data
19    if (file_exists($ipdb)) { $lastmod = filemtime($ipdb); }
20    else { $lastmod = 0; }
21
22    // Fetch data from server to local temporary file
23    $succ = fetch_into_file("http://www.directi.com/iptocountry/" .
24                            "?site=php.net&dbformat=1&lastupdate=" .
25                            $lastmod, $ipdbt);
26
27    // No success in getting the DB
28    if (!$succ) { return FALSE; }
29
30    /*
31     The following code checks the structure of the file
32     received. If this is an updated data file, then it
33     should start with a given line and end with a given line,
34     and all lines inside should be 24 bytes long with a
35     specific format. If the data received is not in this
36     format, then we reject to replace the old file with this.
37    */
38
39    // Open temporary database for reading
40    $ipdbf = @fopen($ipdbt, "r");
41    if (!$ipdbf) { die("Unable to open '$ipdbt' for reading"); }
42
43    // Open local rewritten database for writing
44    $ipdbcf = @fopen($ipdbc, "w");
45    if (!$ipdbcf) { die("Unable to open '$ipdbc' for writing"); }
46
47    // Initial state is the first line and
48    // the file is expected to be ok
49    $state = 'firstline'; $fileok = TRUE;
50
51    // While we can read a line from the file
52    while (!feof($ipdbf) && ($line = fgets($ipdbf))) {
53
54        // Check the input depending on the state
55        switch ($state) {
56
57            // First line is defined, and after it we
58            // switch to the 'data' state
59            case 'firstline' :
60                if (trim($line) != "#Directi IP-to-Country Db: Start") {
61                    $fileok = FALSE;
62                    break 2;
63                } else { $state = 'data'; }
64                break;
65
66            // In the data state we either get the last line,
67            // or we get a data line containing 24 bytes, in
68            // which case we write it out to the local rewritten
69            // database
70            case 'data' :
71                if (trim($line) == "#Directi IP-to-Country Db: End") {
72                    $state = 'lastline';
73                } elseif (!preg_match("!\\d{20}\\w{3}\n!", $line)) {
74                    $fileok = FALSE;
75                    break 2;
76                } else {
77                    fwrite($ipdbcf, $line);
78                }
79                break;
80
81            // We should not receive any data after the last
82            // line, only whitespace is allowed
83            case 'lastline' :
84                if (strlen(trim($line)) > 0) {
85                    $fileok = FALSE;
86                    break 2;
87                }
88                break;
89        }
90    }
91    fclose($ipdbf);
92    fclose($ipdbcf);
93
94    // Remove the temp file. If the data is ok, move
95    // it to the destination, or else delete that too
96    unlink($ipdbt);
97    if (file_exists($ipdbc)) {
98        if (!$fileok) {
99            unlink($ipdbc);
100        } else {
101            rename($ipdbc, $ipdb);
102            create_ip_to_country_index($ipdb, $ipidx, $indexby);
103        }
104    }
105}
106
107// Create local index for ip-to-country.com data
108function create_ip_to_country_index($ipdb, $ipidx, $indexby)
109{
110    // Last indexed number and last record number
111    $lastidx = $recnum = 0;
112
113    // We store the index in a PHP array temporarily
114    $idx_list = [$indexby, "0,0"];
115
116    // Open database for reading
117    $ipdbf = fopen($ipdb, "r");
118
119    // Return with error in case of we cannot open the db
120    if (!$ipdbf) { die("Unable to open '$ipdb' for reading"); }
121
122    // While we can read the file
123    while (!feof($ipdbf)) {
124
125        // Get one record
126        $record = fread($ipdbf, 24);
127
128        // Unable to read a record and not at end => error
129        if (strlen($record) != 24 && !feof($ipdbf)) {
130            die("Incorrect ip-to-country database format");
131        }
132
133        // This is a new record
134        $recnum++;
135
136        // Get the start of the range for this record
137        $range_start = (float) substr($record, 0, 10);
138
139        // If this range starts a new step with our granularity,
140        // add a new element to the index array
141        if (intval($range_start / $indexby) > $lastidx) {
142            $lastidx = intval($range_start / $indexby);
143            $idx_list[] = "$lastidx,$recnum";
144        }
145    }
146
147    // Close the database file
148    fclose($ipdbf);
149
150    // Write out index to file
151    $ipidxf = fopen($ipidx, "w");
152    if (!$ipidxf) { die("Unable to open '$ipidx' for writing"); }
153    fwrite($ipidxf, join("\n", $idx_list));
154    fclose($ipidxf);
155}
156