├── .gitignore ├── History.rdoc ├── LICENSE ├── README.rdoc ├── Rakefile ├── bin └── geoip ├── config └── website.yml ├── data └── geoip │ ├── country_code.yml │ ├── country_code3.yml │ ├── country_continent.yml │ ├── country_name.yml │ ├── region.yml │ └── time_zone.yml ├── geoip.gemspec ├── lib └── geoip.rb ├── script ├── destroy ├── generate └── txt2html ├── test ├── csvORG2dat.py ├── organizations.csv ├── organizations.dat ├── test_file.rb ├── test_geoip.rb └── test_helper.rb └── website ├── index.txt ├── javascripts └── rounded_corners_lite.inc.js ├── stylesheets └── screen.css └── template.rhtml /.gitignore: -------------------------------------------------------------------------------- 1 | pkg 2 | rdoc 3 | databases 4 | website/index.html 5 | *_test.rb 6 | external 7 | -------------------------------------------------------------------------------- /History.rdoc: -------------------------------------------------------------------------------- 1 | == 1.2.0 2012-10-17 2 | 3 | * Added support for GeoLiteCityv6 IPv6 database type 4 | 5 | == 0.8.1 2009-06-04 6 | 7 | * Added GeoIP#close method to close the file descriptor 8 | 9 | == 0.8.0 2008-08-29 10 | 11 | * Added Mutex protection around file I/O for thread safety 12 | 13 | == 0.7.0 2008-05-03 14 | 15 | * Added Hez Ronningen's patch for using the ISP lookup data file 16 | 17 | == 0.5.0 2007-10-24 18 | 19 | * 1 major enhancement: 20 | * Initial release 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | [ Note that while the core GeoIP library is licensed under the 2 | LGPL, the libGeoIPUpdate library depends on md5.c and types.h 3 | which are licensed under the GPL. ] 4 | 5 | 6 | GNU LESSER GENERAL PUBLIC LICENSE 7 | Version 2.1, February 1999 8 | 9 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 10 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 11 | Everyone is permitted to copy and distribute verbatim copies 12 | of this license document, but changing it is not allowed. 13 | 14 | [This is the first released version of the Lesser GPL. It also counts 15 | as the successor of the GNU Library Public License, version 2, hence 16 | the version number 2.1.] 17 | 18 | Preamble 19 | 20 | The licenses for most software are designed to take away your 21 | freedom to share and change it. By contrast, the GNU General Public 22 | Licenses are intended to guarantee your freedom to share and change 23 | free software--to make sure the software is free for all its users. 24 | 25 | This license, the Lesser General Public License, applies to some 26 | specially designated software packages--typically libraries--of the 27 | Free Software Foundation and other authors who decide to use it. You 28 | can use it too, but we suggest you first think carefully about whether 29 | this license or the ordinary General Public License is the better 30 | strategy to use in any particular case, based on the explanations below. 31 | 32 | When we speak of free software, we are referring to freedom of use, 33 | not price. Our General Public Licenses are designed to make sure that 34 | you have the freedom to distribute copies of free software (and charge 35 | for this service if you wish); that you receive source code or can get 36 | it if you want it; that you can change the software and use pieces of 37 | it in new free programs; and that you are informed that you can do 38 | these things. 39 | 40 | To protect your rights, we need to make restrictions that forbid 41 | distributors to deny you these rights or to ask you to surrender these 42 | rights. These restrictions translate to certain responsibilities for 43 | you if you distribute copies of the library or if you modify it. 44 | 45 | For example, if you distribute copies of the library, whether gratis 46 | or for a fee, you must give the recipients all the rights that we gave 47 | you. You must make sure that they, too, receive or can get the source 48 | code. If you link other code with the library, you must provide 49 | complete object files to the recipients, so that they can relink them 50 | with the library after making changes to the library and recompiling 51 | it. And you must show them these terms so they know their rights. 52 | 53 | We protect your rights with a two-step method: (1) we copyright the 54 | library, and (2) we offer you this license, which gives you legal 55 | permission to copy, distribute and/or modify the library. 56 | 57 | To protect each distributor, we want to make it very clear that 58 | there is no warranty for the free library. Also, if the library is 59 | modified by someone else and passed on, the recipients should know 60 | that what they have is not the original version, so that the original 61 | author's reputation will not be affected by problems that might be 62 | introduced by others. 63 | 64 | Finally, software patents pose a constant threat to the existence of 65 | any free program. We wish to make sure that a company cannot 66 | effectively restrict the users of a free program by obtaining a 67 | restrictive license from a patent holder. Therefore, we insist that 68 | any patent license obtained for a version of the library must be 69 | consistent with the full freedom of use specified in this license. 70 | 71 | Most GNU software, including some libraries, is covered by the 72 | ordinary GNU General Public License. This license, the GNU Lesser 73 | General Public License, applies to certain designated libraries, and 74 | is quite different from the ordinary General Public License. We use 75 | this license for certain libraries in order to permit linking those 76 | libraries into non-free programs. 77 | 78 | When a program is linked with a library, whether statically or using 79 | a shared library, the combination of the two is legally speaking a 80 | combined work, a derivative of the original library. The ordinary 81 | General Public License therefore permits such linking only if the 82 | entire combination fits its criteria of freedom. The Lesser General 83 | Public License permits more lax criteria for linking other code with 84 | the library. 85 | 86 | We call this license the "Lesser" General Public License because it 87 | does Less to protect the user's freedom than the ordinary General 88 | Public License. It also provides other free software developers Less 89 | of an advantage over competing non-free programs. These disadvantages 90 | are the reason we use the ordinary General Public License for many 91 | libraries. However, the Lesser license provides advantages in certain 92 | special circumstances. 93 | 94 | For example, on rare occasions, there may be a special need to 95 | encourage the widest possible use of a certain library, so that it becomes 96 | a de-facto standard. To achieve this, non-free programs must be 97 | allowed to use the library. A more frequent case is that a free 98 | library does the same job as widely used non-free libraries. In this 99 | case, there is little to gain by limiting the free library to free 100 | software only, so we use the Lesser General Public License. 101 | 102 | In other cases, permission to use a particular library in non-free 103 | programs enables a greater number of people to use a large body of 104 | free software. For example, permission to use the GNU C Library in 105 | non-free programs enables many more people to use the whole GNU 106 | operating system, as well as its variant, the GNU/Linux operating 107 | system. 108 | 109 | Although the Lesser General Public License is Less protective of the 110 | users' freedom, it does ensure that the user of a program that is 111 | linked with the Library has the freedom and the wherewithal to run 112 | that program using a modified version of the Library. 113 | 114 | The precise terms and conditions for copying, distribution and 115 | modification follow. Pay close attention to the difference between a 116 | "work based on the library" and a "work that uses the library". The 117 | former contains code derived from the library, whereas the latter must 118 | be combined with the library in order to run. 119 | 120 | GNU LESSER GENERAL PUBLIC LICENSE 121 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 122 | 123 | 0. This License Agreement applies to any software library or other 124 | program which contains a notice placed by the copyright holder or 125 | other authorized party saying it may be distributed under the terms of 126 | this Lesser General Public License (also called "this License"). 127 | Each licensee is addressed as "you". 128 | 129 | A "library" means a collection of software functions and/or data 130 | prepared so as to be conveniently linked with application programs 131 | (which use some of those functions and data) to form executables. 132 | 133 | The "Library", below, refers to any such software library or work 134 | which has been distributed under these terms. A "work based on the 135 | Library" means either the Library or any derivative work under 136 | copyright law: that is to say, a work containing the Library or a 137 | portion of it, either verbatim or with modifications and/or translated 138 | straightforwardly into another language. (Hereinafter, translation is 139 | included without limitation in the term "modification".) 140 | 141 | "Source code" for a work means the preferred form of the work for 142 | making modifications to it. For a library, complete source code means 143 | all the source code for all modules it contains, plus any associated 144 | interface definition files, plus the scripts used to control compilation 145 | and installation of the library. 146 | 147 | Activities other than copying, distribution and modification are not 148 | covered by this License; they are outside its scope. The act of 149 | running a program using the Library is not restricted, and output from 150 | such a program is covered only if its contents constitute a work based 151 | on the Library (independent of the use of the Library in a tool for 152 | writing it). Whether that is true depends on what the Library does 153 | and what the program that uses the Library does. 154 | 155 | 1. You may copy and distribute verbatim copies of the Library's 156 | complete source code as you receive it, in any medium, provided that 157 | you conspicuously and appropriately publish on each copy an 158 | appropriate copyright notice and disclaimer of warranty; keep intact 159 | all the notices that refer to this License and to the absence of any 160 | warranty; and distribute a copy of this License along with the 161 | Library. 162 | 163 | You may charge a fee for the physical act of transferring a copy, 164 | and you may at your option offer warranty protection in exchange for a 165 | fee. 166 | 167 | 2. You may modify your copy or copies of the Library or any portion 168 | of it, thus forming a work based on the Library, and copy and 169 | distribute such modifications or work under the terms of Section 1 170 | above, provided that you also meet all of these conditions: 171 | 172 | a) The modified work must itself be a software library. 173 | 174 | b) You must cause the files modified to carry prominent notices 175 | stating that you changed the files and the date of any change. 176 | 177 | c) You must cause the whole of the work to be licensed at no 178 | charge to all third parties under the terms of this License. 179 | 180 | d) If a facility in the modified Library refers to a function or a 181 | table of data to be supplied by an application program that uses 182 | the facility, other than as an argument passed when the facility 183 | is invoked, then you must make a good faith effort to ensure that, 184 | in the event an application does not supply such function or 185 | table, the facility still operates, and performs whatever part of 186 | its purpose remains meaningful. 187 | 188 | (For example, a function in a library to compute square roots has 189 | a purpose that is entirely well-defined independent of the 190 | application. Therefore, Subsection 2d requires that any 191 | application-supplied function or table used by this function must 192 | be optional: if the application does not supply it, the square 193 | root function must still compute square roots.) 194 | 195 | These requirements apply to the modified work as a whole. If 196 | identifiable sections of that work are not derived from the Library, 197 | and can be reasonably considered independent and separate works in 198 | themselves, then this License, and its terms, do not apply to those 199 | sections when you distribute them as separate works. But when you 200 | distribute the same sections as part of a whole which is a work based 201 | on the Library, the distribution of the whole must be on the terms of 202 | this License, whose permissions for other licensees extend to the 203 | entire whole, and thus to each and every part regardless of who wrote 204 | it. 205 | 206 | Thus, it is not the intent of this section to claim rights or contest 207 | your rights to work written entirely by you; rather, the intent is to 208 | exercise the right to control the distribution of derivative or 209 | collective works based on the Library. 210 | 211 | In addition, mere aggregation of another work not based on the Library 212 | with the Library (or with a work based on the Library) on a volume of 213 | a storage or distribution medium does not bring the other work under 214 | the scope of this License. 215 | 216 | 3. You may opt to apply the terms of the ordinary GNU General Public 217 | License instead of this License to a given copy of the Library. To do 218 | this, you must alter all the notices that refer to this License, so 219 | that they refer to the ordinary GNU General Public License, version 2, 220 | instead of to this License. (If a newer version than version 2 of the 221 | ordinary GNU General Public License has appeared, then you can specify 222 | that version instead if you wish.) Do not make any other change in 223 | these notices. 224 | 225 | Once this change is made in a given copy, it is irreversible for 226 | that copy, so the ordinary GNU General Public License applies to all 227 | subsequent copies and derivative works made from that copy. 228 | 229 | This option is useful when you wish to copy part of the code of 230 | the Library into a program that is not a library. 231 | 232 | 4. You may copy and distribute the Library (or a portion or 233 | derivative of it, under Section 2) in object code or executable form 234 | under the terms of Sections 1 and 2 above provided that you accompany 235 | it with the complete corresponding machine-readable source code, which 236 | must be distributed under the terms of Sections 1 and 2 above on a 237 | medium customarily used for software interchange. 238 | 239 | If distribution of object code is made by offering access to copy 240 | from a designated place, then offering equivalent access to copy the 241 | source code from the same place satisfies the requirement to 242 | distribute the source code, even though third parties are not 243 | compelled to copy the source along with the object code. 244 | 245 | 5. A program that contains no derivative of any portion of the 246 | Library, but is designed to work with the Library by being compiled or 247 | linked with it, is called a "work that uses the Library". Such a 248 | work, in isolation, is not a derivative work of the Library, and 249 | therefore falls outside the scope of this License. 250 | 251 | However, linking a "work that uses the Library" with the Library 252 | creates an executable that is a derivative of the Library (because it 253 | contains portions of the Library), rather than a "work that uses the 254 | library". The executable is therefore covered by this License. 255 | Section 6 states terms for distribution of such executables. 256 | 257 | When a "work that uses the Library" uses material from a header file 258 | that is part of the Library, the object code for the work may be a 259 | derivative work of the Library even though the source code is not. 260 | Whether this is true is especially significant if the work can be 261 | linked without the Library, or if the work is itself a library. The 262 | threshold for this to be true is not precisely defined by law. 263 | 264 | If such an object file uses only numerical parameters, data 265 | structure layouts and accessors, and small macros and small inline 266 | functions (ten lines or less in length), then the use of the object 267 | file is unrestricted, regardless of whether it is legally a derivative 268 | work. (Executables containing this object code plus portions of the 269 | Library will still fall under Section 6.) 270 | 271 | Otherwise, if the work is a derivative of the Library, you may 272 | distribute the object code for the work under the terms of Section 6. 273 | Any executables containing that work also fall under Section 6, 274 | whether or not they are linked directly with the Library itself. 275 | 276 | 6. As an exception to the Sections above, you may also combine or 277 | link a "work that uses the Library" with the Library to produce a 278 | work containing portions of the Library, and distribute that work 279 | under terms of your choice, provided that the terms permit 280 | modification of the work for the customer's own use and reverse 281 | engineering for debugging such modifications. 282 | 283 | You must give prominent notice with each copy of the work that the 284 | Library is used in it and that the Library and its use are covered by 285 | this License. You must supply a copy of this License. If the work 286 | during execution displays copyright notices, you must include the 287 | copyright notice for the Library among them, as well as a reference 288 | directing the user to the copy of this License. Also, you must do one 289 | of these things: 290 | 291 | a) Accompany the work with the complete corresponding 292 | machine-readable source code for the Library including whatever 293 | changes were used in the work (which must be distributed under 294 | Sections 1 and 2 above); and, if the work is an executable linked 295 | with the Library, with the complete machine-readable "work that 296 | uses the Library", as object code and/or source code, so that the 297 | user can modify the Library and then relink to produce a modified 298 | executable containing the modified Library. (It is understood 299 | that the user who changes the contents of definitions files in the 300 | Library will not necessarily be able to recompile the application 301 | to use the modified definitions.) 302 | 303 | b) Use a suitable shared library mechanism for linking with the 304 | Library. A suitable mechanism is one that (1) uses at run time a 305 | copy of the library already present on the user's computer system, 306 | rather than copying library functions into the executable, and (2) 307 | will operate properly with a modified version of the library, if 308 | the user installs one, as long as the modified version is 309 | interface-compatible with the version that the work was made with. 310 | 311 | c) Accompany the work with a written offer, valid for at 312 | least three years, to give the same user the materials 313 | specified in Subsection 6a, above, for a charge no more 314 | than the cost of performing this distribution. 315 | 316 | d) If distribution of the work is made by offering access to copy 317 | from a designated place, offer equivalent access to copy the above 318 | specified materials from the same place. 319 | 320 | e) Verify that the user has already received a copy of these 321 | materials or that you have already sent this user a copy. 322 | 323 | For an executable, the required form of the "work that uses the 324 | Library" must include any data and utility programs needed for 325 | reproducing the executable from it. However, as a special exception, 326 | the materials to be distributed need not include anything that is 327 | normally distributed (in either source or binary form) with the major 328 | components (compiler, kernel, and so on) of the operating system on 329 | which the executable runs, unless that component itself accompanies 330 | the executable. 331 | 332 | It may happen that this requirement contradicts the license 333 | restrictions of other proprietary libraries that do not normally 334 | accompany the operating system. Such a contradiction means you cannot 335 | use both them and the Library together in an executable that you 336 | distribute. 337 | 338 | 7. You may place library facilities that are a work based on the 339 | Library side-by-side in a single library together with other library 340 | facilities not covered by this License, and distribute such a combined 341 | library, provided that the separate distribution of the work based on 342 | the Library and of the other library facilities is otherwise 343 | permitted, and provided that you do these two things: 344 | 345 | a) Accompany the combined library with a copy of the same work 346 | based on the Library, uncombined with any other library 347 | facilities. This must be distributed under the terms of the 348 | Sections above. 349 | 350 | b) Give prominent notice with the combined library of the fact 351 | that part of it is a work based on the Library, and explaining 352 | where to find the accompanying uncombined form of the same work. 353 | 354 | 8. You may not copy, modify, sublicense, link with, or distribute 355 | the Library except as expressly provided under this License. Any 356 | attempt otherwise to copy, modify, sublicense, link with, or 357 | distribute the Library is void, and will automatically terminate your 358 | rights under this License. However, parties who have received copies, 359 | or rights, from you under this License will not have their licenses 360 | terminated so long as such parties remain in full compliance. 361 | 362 | 9. You are not required to accept this License, since you have not 363 | signed it. However, nothing else grants you permission to modify or 364 | distribute the Library or its derivative works. These actions are 365 | prohibited by law if you do not accept this License. Therefore, by 366 | modifying or distributing the Library (or any work based on the 367 | Library), you indicate your acceptance of this License to do so, and 368 | all its terms and conditions for copying, distributing or modifying 369 | the Library or works based on it. 370 | 371 | 10. Each time you redistribute the Library (or any work based on the 372 | Library), the recipient automatically receives a license from the 373 | original licensor to copy, distribute, link with or modify the Library 374 | subject to these terms and conditions. You may not impose any further 375 | restrictions on the recipients' exercise of the rights granted herein. 376 | You are not responsible for enforcing compliance by third parties with 377 | this License. 378 | 379 | 11. If, as a consequence of a court judgment or allegation of patent 380 | infringement or for any other reason (not limited to patent issues), 381 | conditions are imposed on you (whether by court order, agreement or 382 | otherwise) that contradict the conditions of this License, they do not 383 | excuse you from the conditions of this License. If you cannot 384 | distribute so as to satisfy simultaneously your obligations under this 385 | License and any other pertinent obligations, then as a consequence you 386 | may not distribute the Library at all. For example, if a patent 387 | license would not permit royalty-free redistribution of the Library by 388 | all those who receive copies directly or indirectly through you, then 389 | the only way you could satisfy both it and this License would be to 390 | refrain entirely from distribution of the Library. 391 | 392 | If any portion of this section is held invalid or unenforceable under any 393 | particular circumstance, the balance of the section is intended to apply, 394 | and the section as a whole is intended to apply in other circumstances. 395 | 396 | It is not the purpose of this section to induce you to infringe any 397 | patents or other property right claims or to contest validity of any 398 | such claims; this section has the sole purpose of protecting the 399 | integrity of the free software distribution system which is 400 | implemented by public license practices. Many people have made 401 | generous contributions to the wide range of software distributed 402 | through that system in reliance on consistent application of that 403 | system; it is up to the author/donor to decide if he or she is willing 404 | to distribute software through any other system and a licensee cannot 405 | impose that choice. 406 | 407 | This section is intended to make thoroughly clear what is believed to 408 | be a consequence of the rest of this License. 409 | 410 | 12. If the distribution and/or use of the Library is restricted in 411 | certain countries either by patents or by copyrighted interfaces, the 412 | original copyright holder who places the Library under this License may add 413 | an explicit geographical distribution limitation excluding those countries, 414 | so that distribution is permitted only in or among countries not thus 415 | excluded. In such case, this License incorporates the limitation as if 416 | written in the body of this License. 417 | 418 | 13. The Free Software Foundation may publish revised and/or new 419 | versions of the Lesser General Public License from time to time. 420 | Such new versions will be similar in spirit to the present version, 421 | but may differ in detail to address new problems or concerns. 422 | 423 | Each version is given a distinguishing version number. If the Library 424 | specifies a version number of this License which applies to it and 425 | "any later version", you have the option of following the terms and 426 | conditions either of that version or of any later version published by 427 | the Free Software Foundation. If the Library does not specify a 428 | license version number, you may choose any version ever published by 429 | the Free Software Foundation. 430 | 431 | 14. If you wish to incorporate parts of the Library into other free 432 | programs whose distribution conditions are incompatible with these, 433 | write to the author to ask for permission. For software which is 434 | copyrighted by the Free Software Foundation, write to the Free 435 | Software Foundation; we sometimes make exceptions for this. Our 436 | decision will be guided by the two goals of preserving the free status 437 | of all derivatives of our free software and of promoting the sharing 438 | and reuse of software generally. 439 | 440 | NO WARRANTY 441 | 442 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 443 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 444 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 445 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 446 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 447 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 448 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 449 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 450 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 451 | 452 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 453 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 454 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 455 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 456 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 457 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 458 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 459 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 460 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 461 | DAMAGES. 462 | 463 | END OF TERMS AND CONDITIONS 464 | 465 | How to Apply These Terms to Your New Libraries 466 | 467 | If you develop a new library, and you want it to be of the greatest 468 | possible use to the public, we recommend making it free software that 469 | everyone can redistribute and change. You can do so by permitting 470 | redistribution under these terms (or, alternatively, under the terms of the 471 | ordinary General Public License). 472 | 473 | To apply these terms, attach the following notices to the library. It is 474 | safest to attach them to the start of each source file to most effectively 475 | convey the exclusion of warranty; and each file should have at least the 476 | "copyright" line and a pointer to where the full notice is found. 477 | 478 | 479 | Copyright (C) 480 | 481 | This library is free software; you can redistribute it and/or 482 | modify it under the terms of the GNU Lesser General Public 483 | License as published by the Free Software Foundation; either 484 | version 2.1 of the License, or (at your option) any later version. 485 | 486 | This library is distributed in the hope that it will be useful, 487 | but WITHOUT ANY WARRANTY; without even the implied warranty of 488 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 489 | Lesser General Public License for more details. 490 | 491 | You should have received a copy of the GNU Lesser General Public 492 | License along with this library; if not, write to the Free Software 493 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 494 | 495 | Also add information on how to contact you by electronic and paper mail. 496 | 497 | You should also get your employer (if you work as a programmer) or your 498 | school, if any, to sign a "copyright disclaimer" for the library, if 499 | necessary. Here is a sample; alter the names: 500 | 501 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 502 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 503 | 504 | , 1 April 1990 505 | Ty Coon, President of Vice 506 | 507 | That's all there is to it! 508 | 509 | 510 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = geoip 2 | 3 | http://github.com/cjheath/geoip 4 | 5 | == DESCRIPTION: 6 | 7 | GeoIP searches a GeoIP database for a given host or IP address, and 8 | returns information about the country where the IP address is allocated, 9 | and the city, ISP and other information, if you have that database version. 10 | 11 | == FEATURES/PROBLEMS: 12 | 13 | Does not support the new Maxmind v2 database formats. 14 | 15 | Includes support for ASN data files, thanks to Roland Matiz. 16 | This release adds support for timezone names, thanks to Tonni Aagesen. 17 | 18 | If you have required 'io/extra' and have IO#pread, cross-process file-descriptor sharing is enabled. 19 | Each GeoIP instance keeps the file descriptor open, with a Mutex for thread-safety. 20 | You should consider this if your process will fork without exec, as 21 | {modrails does}[http://www.modrails.com/documentation/Users%20guide%20Nginx.html#_smart_spawning_gotcha_1_unintential_file_descriptor_sharing] 22 | 23 | == SYNOPSIS: 24 | 25 | require 'geoip' 26 | 27 | # Use the country database: 28 | c = GeoIP.new('GeoIP.dat').country('www.nokia.com') 29 | => ["www.nokia.com", "147.243.3.83", 69, "FI", "FIN", "Finland", "EU"] 30 | c.country_code3 31 | => "FIN" 32 | c.to_hash 33 | => {:country_code3=>"FIN", :country_name=>"Finland", :continent_code=>"EU", :request=>"www.nokia.com", :country_code=>69, :country_code2=>"FI", :ip=>"147.243.3.83"} 34 | 35 | Returned values are the requested hostname, the IP address as a dotted quad, 36 | Maxmind's country code, the ISO3166-1 alpha-2 country code, the ISO3166-2 alpha-3 37 | country code, the ISO3166 country name, and the continent code. 38 | 39 | # Use the city database: 40 | c = GeoIP.new('GeoLiteCity.dat').city('github.com') 41 | => ["github.com", "207.97.227.239", "US", "USA", "United States", "NA", "CA", "San Francisco", "94110", 37.7484, -122.4156, 807, 415, "America/Los_Angeles"] 42 | >> c.longitude 43 | => -122.4156 44 | >> c.timezone 45 | => "America/Los_Angeles" 46 | 47 | GeoIP.new('GeoCity.dat').city('github.com') 48 | => ["github.com", "207.97.227.239", "US", "USA", "United States", "NA", "CA", "San Francisco", "94110", 37.7484, -122.4156, 807, 415, "America/Los_Angeles"] 49 | 50 | # Use the city ipv6 database: 51 | GeoIP.new('GeoLiteCityv6.dat').city('::151.38.39.114') 52 | => ["::151.38.39.114", "::151.38.39.114", "IT", "ITA", "Italy", "EU", "05", "Piacenza", "", 45.016699999999986, 9.666699999999992, nil, nil, "Europe/Rome"] 53 | 54 | Returned values are the requested hostname, the IP address as a dotted quad, 55 | the ISO3166-1 alpha-2 country code, the ISO3166-2 alpha-3 country code, the 56 | ISO3166 country name, the continent code, the region (state or territory) name, 57 | city name, postal_code/zipcode, latitude, longitude, USA DMA code, USA area code, 58 | timezone name. 59 | 60 | Result arrays from both city and country have mixed-in accessor methods as appropriate: 61 | request, ip, country_code, country_code2, country_code3, country_name, continent_code, 62 | region_name, city_name, postal_code, latitude, longitude, dma_code, area_code, timezone 63 | 64 | GeoIP.new('GeoIPASNum.dat').asn("www.fsb.ru") 65 | => ["AS8342", "RTComm.RU Autonomous System"] 66 | 67 | For testing or other purposes, you might wish to treat localhost IP addresses as 68 | some other address - an external one. You can do this by assigning the desired 69 | external IP address to the attribute local_ip_alias: 70 | 71 | g = GeoIP.new('GeoIP.dat') 72 | g.city("127.0.0.1") 73 | => nil 74 | g.local_ip_alias = "173.194.112.35" 75 | g.city("127.0.0.1") 76 | # 77 | 78 | == REQUIREMENTS: 79 | 80 | You need one of the free GeoLite country, city or ASN binary databases, or a subscription database version. 81 | Maxmind have unfortunately stopped updating databases in the original GeoLite format - only v2 is currently available, and the CSV form. 82 | There is no available compiler from CSV to the legacy database format, so this gem has started to go stale and needs a path forwards. Any volunteers? 83 | The download location for the GeoLite2 databases is . 84 | 85 | This API requires a gzipped binary file to be decompressed for searching. 86 | Other versions of this database are available for purchase which contain more 87 | detailed information, but not all this information is returned by this implementation. 88 | See www.maxmind.com for more information. 89 | 90 | == INSTALL: 91 | 92 | sudo gem install geoip 93 | 94 | == LICENSE: 95 | 96 | This version Copyright (C) 2005 Clifford Heath 97 | Derived from the C version, Copyright (C) 2003 MaxMind LLC 98 | 99 | This library is free software; you can redistribute it and/or 100 | modify it under the terms of the GNU Lesser General Public License, 101 | as published by the Free Software Foundation; either version 2.1 of 102 | the License, or (at your option) any later version. This follows the 103 | license applied by Maxmind to their C library, for example in the 104 | version here: 105 | . 106 | 107 | This library is distributed in the hope that it will be useful, 108 | but WITHOUT ANY WARRANTY; without even the implied warranty of 109 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 110 | Lesser General Public License for more details. 111 | 112 | You should have received a copy of the GNU Lesser General Public 113 | License along with this library; if not, write to the Free Software 114 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 115 | 116 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | 4 | require 'jeweler' 5 | require './lib/geoip' 6 | 7 | Jeweler::Tasks.new do |gem| 8 | # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options 9 | gem.name = "geoip" 10 | gem.version = GeoIP::VERSION 11 | gem.homepage = "https://github.com/cjheath/geoip" 12 | gem.license = "LGPL" 13 | gem.summary = %Q{GeoIP searches a GeoIP database for a given host or IP address, and returns information about the country where the IP address is allocated, and the city, ISP and other information, if you have that database version.} 14 | gem.description = %Q{GeoIP searches a GeoIP database for a given host or IP address, and 15 | returns information about the country where the IP address is allocated, 16 | and the city, ISP and other information, if you have that database version.} 17 | gem.email = %w[clifford.heath@gmail.com rmoriz@gmail.com] 18 | gem.authors = ["Clifford Heath", "Roland Moriz"] 19 | # Include your dependencies below. Runtime dependencies are required when using your gem, 20 | # and development dependencies are only needed for development (ie running rake tasks, tests, etc) 21 | # gem.add_runtime_dependency 'jabber4r', '> 0.1' 22 | # gem.add_development_dependency 'rspec', '> 1.2.3' 23 | gem.files.exclude "Rakefile", "website/**/*.*", "script/*", "config/*" 24 | end 25 | Jeweler::RubygemsDotOrgTasks.new 26 | 27 | require 'rake/testtask' 28 | Rake::TestTask.new(:test) do |test| 29 | test.libs << 'lib' << 'test' 30 | test.pattern = 'test/**/test_*.rb' 31 | test.verbose = true 32 | end 33 | task :default => :test 34 | 35 | require 'rdoc/task' 36 | Rake::RDocTask.new do |rdoc| 37 | rdoc.rdoc_dir = 'rdoc' 38 | rdoc.title = "geoip #{GeoIP::VERSION}" 39 | rdoc.rdoc_files.include('README.rdoc') 40 | rdoc.rdoc_files.include('History.rdoc') 41 | rdoc.rdoc_files.include('lib/**/*.rb') 42 | end 43 | 44 | desc 'Generate website files' 45 | task :website_generate do 46 | sh %q{ruby script/txt2html website/index.txt > website/index.html} 47 | end 48 | 49 | desc 'Upload website files via rsync' 50 | task :website_upload do 51 | local_dir = 'website' 52 | website_config = YAML.load(File.read("config/website.yml")) 53 | host = website_config["host"] 54 | host = host ? "#{host}:" : "" 55 | remote_dir = website_config["remote_dir"] 56 | sh %{rsync -aCv #{local_dir}/ #{host}#{remote_dir}} 57 | end 58 | 59 | -------------------------------------------------------------------------------- /bin/geoip: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | lib_dir = File.expand_path(File.join(File.dirname(__FILE__),'..','lib')) 4 | $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir) 5 | 6 | require 'geoip' 7 | 8 | data = '/usr/share/GeoIP/GeoIP.dat' 9 | data = ARGV.shift if ARGV[0] =~ /\.dat\Z/ 10 | 11 | geoip = GeoIP.new data 12 | req = case geoip.databaseType 13 | when GeoIP::Edition::CITY_REV1, 14 | GeoIP::Edition::CITY_REV0, 15 | GeoIP::Edition::CITY_REV1_V6 16 | :city 17 | when GeoIP::Edition::REGION_REV0, 18 | GeoIP::Edition::REGION_REV1 19 | :region 20 | when GeoIP::Edition::NETSPEED, GeoIP::Edition::NETSPEED_REV1 21 | :netspeed 22 | when GeoIP::Edition::ISP, # All these looked up the same way 23 | GeoIP::Edition::ORG, 24 | GeoIP::Edition::ISP, 25 | GeoIP::Edition::DOMAIN, 26 | GeoIP::Edition::ACCURACYRADIUS, 27 | GeoIP::Edition::NETSPEED, 28 | GeoIP::Edition::USERTYPE, 29 | GeoIP::Edition::USERTYPE_V6, 30 | GeoIP::Edition::REGISTRAR, 31 | GeoIP::Edition::LOCATIONA, 32 | GeoIP::Edition::CITYCONF, 33 | GeoIP::Edition::COUNTRYCONF, 34 | GeoIP::Edition::REGIONCONF, 35 | GeoIP::Edition::POSTALCONF 36 | :isp 37 | when GeoIP::Edition::ASNUM 38 | :asn 39 | when GeoIP::Edition::COUNTRY, GeoIP::Edition::PROXY, GeoIP::Edition::COUNTRY_V6 40 | :country 41 | else 42 | :asn 43 | end 44 | 45 | if ARGV.size > 0 46 | ARGV.each { |a| p geoip.send(req, a) } 47 | else 48 | while (STDIN.isatty && print('geoip> '); ip = gets) 49 | ip.chomp! 50 | result = geoip.send(req, ip) 51 | p result 52 | end 53 | STDIN.isatty && puts 54 | end 55 | -------------------------------------------------------------------------------- /config/website.yml: -------------------------------------------------------------------------------- 1 | host: cjheath@rubyforge.org 2 | remote_dir: /var/www/gforge-projects/geoip 3 | -------------------------------------------------------------------------------- /data/geoip/country_code.yml: -------------------------------------------------------------------------------- 1 | # Ordered list of the ISO3166 2-character country codes, ordered by GeoIP ID 2 | --- 3 | - -- 4 | - AP 5 | - EU 6 | - AD 7 | - AE 8 | - AF 9 | - AG 10 | - AI 11 | - AL 12 | - AM 13 | - CW 14 | - AO 15 | - AQ 16 | - AR 17 | - AS 18 | - AT 19 | - AU 20 | - AW 21 | - AZ 22 | - BA 23 | - BB 24 | - BD 25 | - BE 26 | - BF 27 | - BG 28 | - BH 29 | - BI 30 | - BJ 31 | - BM 32 | - BN 33 | - BO 34 | - BR 35 | - BS 36 | - BT 37 | - BV 38 | - BW 39 | - BY 40 | - BZ 41 | - CA 42 | - CC 43 | - CD 44 | - CF 45 | - CG 46 | - CH 47 | - CI 48 | - CK 49 | - CL 50 | - CM 51 | - CN 52 | - CO 53 | - CR 54 | - CU 55 | - CV 56 | - CX 57 | - CY 58 | - CZ 59 | - DE 60 | - DJ 61 | - DK 62 | - DM 63 | - DO 64 | - DZ 65 | - EC 66 | - EE 67 | - EG 68 | - EH 69 | - ER 70 | - ES 71 | - ET 72 | - FI 73 | - FJ 74 | - FK 75 | - FM 76 | - FO 77 | - FR 78 | - SX 79 | - GA 80 | - GB 81 | - GD 82 | - GE 83 | - GF 84 | - GH 85 | - GI 86 | - GL 87 | - GM 88 | - GN 89 | - GP 90 | - GQ 91 | - GR 92 | - GS 93 | - GT 94 | - GU 95 | - GW 96 | - GY 97 | - HK 98 | - HM 99 | - HN 100 | - HR 101 | - HT 102 | - HU 103 | - ID 104 | - IE 105 | - IL 106 | - IN 107 | - IO 108 | - IQ 109 | - IR 110 | - IS 111 | - IT 112 | - JM 113 | - JO 114 | - JP 115 | - KE 116 | - KG 117 | - KH 118 | - KI 119 | - KM 120 | - KN 121 | - KP 122 | - KR 123 | - KW 124 | - KY 125 | - KZ 126 | - LA 127 | - LB 128 | - LC 129 | - LI 130 | - LK 131 | - LR 132 | - LS 133 | - LT 134 | - LU 135 | - LV 136 | - LY 137 | - MA 138 | - MC 139 | - MD 140 | - MG 141 | - MH 142 | - MK 143 | - ML 144 | - MM 145 | - MN 146 | - MO 147 | - MP 148 | - MQ 149 | - MR 150 | - MS 151 | - MT 152 | - MU 153 | - MV 154 | - MW 155 | - MX 156 | - MY 157 | - MZ 158 | - NA 159 | - NC 160 | - NE 161 | - NF 162 | - NG 163 | - NI 164 | - NL 165 | - "NO" 166 | - NP 167 | - NR 168 | - NU 169 | - NZ 170 | - OM 171 | - PA 172 | - PE 173 | - PF 174 | - PG 175 | - PH 176 | - PK 177 | - PL 178 | - PM 179 | - PN 180 | - PR 181 | - PS 182 | - PT 183 | - PW 184 | - PY 185 | - QA 186 | - RE 187 | - RO 188 | - RU 189 | - RW 190 | - SA 191 | - SB 192 | - SC 193 | - SD 194 | - SE 195 | - SG 196 | - SH 197 | - SI 198 | - SJ 199 | - SK 200 | - SL 201 | - SM 202 | - SN 203 | - SO 204 | - SR 205 | - ST 206 | - SV 207 | - SY 208 | - SZ 209 | - TC 210 | - TD 211 | - TF 212 | - TG 213 | - TH 214 | - TJ 215 | - TK 216 | - TM 217 | - TN 218 | - TO 219 | - TL 220 | - TR 221 | - TT 222 | - TV 223 | - TW 224 | - TZ 225 | - UA 226 | - UG 227 | - UM 228 | - US 229 | - UY 230 | - UZ 231 | - VA 232 | - VC 233 | - VE 234 | - VG 235 | - VI 236 | - VN 237 | - VU 238 | - WF 239 | - WS 240 | - YE 241 | - YT 242 | - RS 243 | - ZA 244 | - ZM 245 | - ME 246 | - ZW 247 | - A1 248 | - A2 249 | - O1 250 | - AX 251 | - GG 252 | - IM 253 | - JE 254 | - BL 255 | - MF 256 | - BQ 257 | - SS 258 | -------------------------------------------------------------------------------- /data/geoip/country_code3.yml: -------------------------------------------------------------------------------- 1 | # Ordered list of the ISO3166 3-character country codes, ordered by GeoIP ID 2 | --- 3 | - -- 4 | - AP 5 | - EU 6 | - AND 7 | - ARE 8 | - AFG 9 | - ATG 10 | - AIA 11 | - ALB 12 | - ARM 13 | - CUW 14 | - AGO 15 | - ATA 16 | - ARG 17 | - ASM 18 | - AUT 19 | - AUS 20 | - ABW 21 | - AZE 22 | - BIH 23 | - BRB 24 | - BGD 25 | - BEL 26 | - BFA 27 | - BGR 28 | - BHR 29 | - BDI 30 | - BEN 31 | - BMU 32 | - BRN 33 | - BOL 34 | - BRA 35 | - BHS 36 | - BTN 37 | - BVT 38 | - BWA 39 | - BLR 40 | - BLZ 41 | - CAN 42 | - CCK 43 | - COD 44 | - CAF 45 | - COG 46 | - CHE 47 | - CIV 48 | - COK 49 | - CHL 50 | - CMR 51 | - CHN 52 | - COL 53 | - CRI 54 | - CUB 55 | - CPV 56 | - CXR 57 | - CYP 58 | - CZE 59 | - DEU 60 | - DJI 61 | - DNK 62 | - DMA 63 | - DOM 64 | - DZA 65 | - ECU 66 | - EST 67 | - EGY 68 | - ESH 69 | - ERI 70 | - ESP 71 | - ETH 72 | - FIN 73 | - FJI 74 | - FLK 75 | - FSM 76 | - FRO 77 | - FRA 78 | - SXM 79 | - GAB 80 | - GBR 81 | - GRD 82 | - GEO 83 | - GUF 84 | - GHA 85 | - GIB 86 | - GRL 87 | - GMB 88 | - GIN 89 | - GLP 90 | - GNQ 91 | - GRC 92 | - SGS 93 | - GTM 94 | - GUM 95 | - GNB 96 | - GUY 97 | - HKG 98 | - HMD 99 | - HND 100 | - HRV 101 | - HTI 102 | - HUN 103 | - IDN 104 | - IRL 105 | - ISR 106 | - IND 107 | - IOT 108 | - IRQ 109 | - IRN 110 | - ISL 111 | - ITA 112 | - JAM 113 | - JOR 114 | - JPN 115 | - KEN 116 | - KGZ 117 | - KHM 118 | - KIR 119 | - COM 120 | - KNA 121 | - PRK 122 | - KOR 123 | - KWT 124 | - CYM 125 | - KAZ 126 | - LAO 127 | - LBN 128 | - LCA 129 | - LIE 130 | - LKA 131 | - LBR 132 | - LSO 133 | - LTU 134 | - LUX 135 | - LVA 136 | - LBY 137 | - MAR 138 | - MCO 139 | - MDA 140 | - MDG 141 | - MHL 142 | - MKD 143 | - MLI 144 | - MMR 145 | - MNG 146 | - MAC 147 | - MNP 148 | - MTQ 149 | - MRT 150 | - MSR 151 | - MLT 152 | - MUS 153 | - MDV 154 | - MWI 155 | - MEX 156 | - MYS 157 | - MOZ 158 | - NAM 159 | - NCL 160 | - NER 161 | - NFK 162 | - NGA 163 | - NIC 164 | - NLD 165 | - NOR 166 | - NPL 167 | - NRU 168 | - NIU 169 | - NZL 170 | - OMN 171 | - PAN 172 | - PER 173 | - PYF 174 | - PNG 175 | - PHL 176 | - PAK 177 | - POL 178 | - SPM 179 | - PCN 180 | - PRI 181 | - PSE 182 | - PRT 183 | - PLW 184 | - PRY 185 | - QAT 186 | - REU 187 | - ROU 188 | - RUS 189 | - RWA 190 | - SAU 191 | - SLB 192 | - SYC 193 | - SDN 194 | - SWE 195 | - SGP 196 | - SHN 197 | - SVN 198 | - SJM 199 | - SVK 200 | - SLE 201 | - SMR 202 | - SEN 203 | - SOM 204 | - SUR 205 | - STP 206 | - SLV 207 | - SYR 208 | - SWZ 209 | - TCA 210 | - TCD 211 | - ATF 212 | - TGO 213 | - THA 214 | - TJK 215 | - TKL 216 | - TKM 217 | - TUN 218 | - TON 219 | - TLS 220 | - TUR 221 | - TTO 222 | - TUV 223 | - TWN 224 | - TZA 225 | - UKR 226 | - UGA 227 | - UMI 228 | - USA 229 | - URY 230 | - UZB 231 | - VAT 232 | - VCT 233 | - VEN 234 | - VGB 235 | - VIR 236 | - VNM 237 | - VUT 238 | - WLF 239 | - WSM 240 | - YEM 241 | - MYT 242 | - SRB 243 | - ZAF 244 | - ZMB 245 | - MNE 246 | - ZWE 247 | - A1 248 | - A2 249 | - O1 250 | - ALA 251 | - GGY 252 | - IMN 253 | - JEY 254 | - BLM 255 | - MAF 256 | - BES 257 | - SSD 258 | -------------------------------------------------------------------------------- /data/geoip/country_continent.yml: -------------------------------------------------------------------------------- 1 | # Ordered list of the ISO3166 2-character continent code of the countries, ordered by GeoIP ID 2 | --- 3 | - -- 4 | - AS 5 | - EU 6 | - EU 7 | - AS 8 | - AS 9 | - NA 10 | - NA 11 | - EU 12 | - AS 13 | - NA 14 | - AF 15 | - AN 16 | - SA 17 | - OC 18 | - EU 19 | - OC 20 | - NA 21 | - AS 22 | - EU 23 | - NA 24 | - AS 25 | - EU 26 | - AF 27 | - EU 28 | - AS 29 | - AF 30 | - AF 31 | - NA 32 | - AS 33 | - SA 34 | - SA 35 | - NA 36 | - AS 37 | - AN 38 | - AF 39 | - EU 40 | - NA 41 | - NA 42 | - AS 43 | - AF 44 | - AF 45 | - AF 46 | - EU 47 | - AF 48 | - OC 49 | - SA 50 | - AF 51 | - AS 52 | - SA 53 | - NA 54 | - NA 55 | - AF 56 | - AS 57 | - AS 58 | - EU 59 | - EU 60 | - AF 61 | - EU 62 | - NA 63 | - NA 64 | - AF 65 | - SA 66 | - EU 67 | - AF 68 | - AF 69 | - AF 70 | - EU 71 | - AF 72 | - EU 73 | - OC 74 | - SA 75 | - OC 76 | - EU 77 | - EU 78 | - NA 79 | - AF 80 | - EU 81 | - NA 82 | - AS 83 | - SA 84 | - AF 85 | - EU 86 | - NA 87 | - AF 88 | - AF 89 | - NA 90 | - AF 91 | - EU 92 | - AN 93 | - NA 94 | - OC 95 | - AF 96 | - SA 97 | - AS 98 | - AN 99 | - NA 100 | - EU 101 | - NA 102 | - EU 103 | - AS 104 | - EU 105 | - AS 106 | - AS 107 | - AS 108 | - AS 109 | - AS 110 | - EU 111 | - EU 112 | - NA 113 | - AS 114 | - AS 115 | - AF 116 | - AS 117 | - AS 118 | - OC 119 | - AF 120 | - NA 121 | - AS 122 | - AS 123 | - AS 124 | - NA 125 | - AS 126 | - AS 127 | - AS 128 | - NA 129 | - EU 130 | - AS 131 | - AF 132 | - AF 133 | - EU 134 | - EU 135 | - EU 136 | - AF 137 | - AF 138 | - EU 139 | - EU 140 | - AF 141 | - OC 142 | - EU 143 | - AF 144 | - AS 145 | - AS 146 | - AS 147 | - OC 148 | - NA 149 | - AF 150 | - NA 151 | - EU 152 | - AF 153 | - AS 154 | - AF 155 | - NA 156 | - AS 157 | - AF 158 | - AF 159 | - OC 160 | - AF 161 | - OC 162 | - AF 163 | - NA 164 | - EU 165 | - EU 166 | - AS 167 | - OC 168 | - OC 169 | - OC 170 | - AS 171 | - NA 172 | - SA 173 | - OC 174 | - OC 175 | - AS 176 | - AS 177 | - EU 178 | - NA 179 | - OC 180 | - NA 181 | - AS 182 | - EU 183 | - OC 184 | - SA 185 | - AS 186 | - AF 187 | - EU 188 | - EU 189 | - AF 190 | - AS 191 | - OC 192 | - AF 193 | - AF 194 | - EU 195 | - AS 196 | - AF 197 | - EU 198 | - EU 199 | - EU 200 | - AF 201 | - EU 202 | - AF 203 | - AF 204 | - SA 205 | - AF 206 | - NA 207 | - AS 208 | - AF 209 | - NA 210 | - AF 211 | - AN 212 | - AF 213 | - AS 214 | - AS 215 | - OC 216 | - AS 217 | - AF 218 | - OC 219 | - AS 220 | - EU 221 | - NA 222 | - OC 223 | - AS 224 | - AF 225 | - EU 226 | - AF 227 | - OC 228 | - NA 229 | - SA 230 | - AS 231 | - EU 232 | - NA 233 | - SA 234 | - NA 235 | - NA 236 | - AS 237 | - OC 238 | - OC 239 | - OC 240 | - AS 241 | - AF 242 | - EU 243 | - AF 244 | - AF 245 | - EU 246 | - AF 247 | - -- 248 | - -- 249 | - -- 250 | - EU 251 | - EU 252 | - EU 253 | - EU 254 | - NA 255 | - NA 256 | - NA 257 | - AF 258 | -------------------------------------------------------------------------------- /data/geoip/country_name.yml: -------------------------------------------------------------------------------- 1 | # Ordered list of the English names of the countries, ordered by GeoIP ID 2 | --- 3 | - N/A 4 | - Asia/Pacific Region 5 | - Europe 6 | - Andorra 7 | - United Arab Emirates 8 | - Afghanistan 9 | - Antigua and Barbuda 10 | - Anguilla 11 | - Albania 12 | - Armenia 13 | - Curaçao 14 | - Angola 15 | - Antarctica 16 | - Argentina 17 | - American Samoa 18 | - Austria 19 | - Australia 20 | - Aruba 21 | - Azerbaijan 22 | - Bosnia and Herzegovina 23 | - Barbados 24 | - Bangladesh 25 | - Belgium 26 | - Burkina Faso 27 | - Bulgaria 28 | - Bahrain 29 | - Burundi 30 | - Benin 31 | - Bermuda 32 | - Brunei Darussalam 33 | - Bolivia 34 | - Brazil 35 | - Bahamas 36 | - Bhutan 37 | - Bouvet Island 38 | - Botswana 39 | - Belarus 40 | - Belize 41 | - Canada 42 | - Cocos (Keeling) Islands 43 | - Congo, The Democratic Republic of the 44 | - Central African Republic 45 | - Congo 46 | - Switzerland 47 | - Cote D'Ivoire 48 | - Cook Islands 49 | - Chile 50 | - Cameroon 51 | - China 52 | - Colombia 53 | - Costa Rica 54 | - Cuba 55 | - Cape Verde 56 | - Christmas Island 57 | - Cyprus 58 | - Czech Republic 59 | - Germany 60 | - Djibouti 61 | - Denmark 62 | - Dominica 63 | - Dominican Republic 64 | - Algeria 65 | - Ecuador 66 | - Estonia 67 | - Egypt 68 | - Western Sahara 69 | - Eritrea 70 | - Spain 71 | - Ethiopia 72 | - Finland 73 | - Fiji 74 | - Falkland Islands (Malvinas) 75 | - Micronesia, Federated States of 76 | - Faroe Islands 77 | - France 78 | - Sint Maarten (Dutch part) 79 | - Gabon 80 | - United Kingdom 81 | - Grenada 82 | - Georgia 83 | - French Guiana 84 | - Ghana 85 | - Gibraltar 86 | - Greenland 87 | - Gambia 88 | - Guinea 89 | - Guadeloupe 90 | - Equatorial Guinea 91 | - Greece 92 | - South Georgia and the South Sandwich Islands 93 | - Guatemala 94 | - Guam 95 | - Guinea-Bissau 96 | - Guyana 97 | - Hong Kong 98 | - Heard Island and McDonald Islands 99 | - Honduras 100 | - Croatia 101 | - Haiti 102 | - Hungary 103 | - Indonesia 104 | - Ireland 105 | - Israel 106 | - India 107 | - British Indian Ocean Territory 108 | - Iraq 109 | - Iran, Islamic Republic of 110 | - Iceland 111 | - Italy 112 | - Jamaica 113 | - Jordan 114 | - Japan 115 | - Kenya 116 | - Kyrgyzstan 117 | - Cambodia 118 | - Kiribati 119 | - Comoros 120 | - Saint Kitts and Nevis 121 | - Korea, Democratic People's Republic of 122 | - Korea, Republic of 123 | - Kuwait 124 | - Cayman Islands 125 | - Kazakhstan 126 | - Lao People's Democratic Republic 127 | - Lebanon 128 | - Saint Lucia 129 | - Liechtenstein 130 | - Sri Lanka 131 | - Liberia 132 | - Lesotho 133 | - Lithuania 134 | - Luxembourg 135 | - Latvia 136 | - Libya 137 | - Morocco 138 | - Monaco 139 | - Moldova, Republic of 140 | - Madagascar 141 | - Marshall Islands 142 | - Macedonia 143 | - Mali 144 | - Myanmar 145 | - Mongolia 146 | - Macau 147 | - Northern Mariana Islands 148 | - Martinique 149 | - Mauritania 150 | - Montserrat 151 | - Malta 152 | - Mauritius 153 | - Maldives 154 | - Malawi 155 | - Mexico 156 | - Malaysia 157 | - Mozambique 158 | - Namibia 159 | - New Caledonia 160 | - Niger 161 | - Norfolk Island 162 | - Nigeria 163 | - Nicaragua 164 | - Netherlands 165 | - Norway 166 | - Nepal 167 | - Nauru 168 | - Niue 169 | - New Zealand 170 | - Oman 171 | - Panama 172 | - Peru 173 | - French Polynesia 174 | - Papua New Guinea 175 | - Philippines 176 | - Pakistan 177 | - Poland 178 | - Saint Pierre and Miquelon 179 | - Pitcairn Islands 180 | - Puerto Rico 181 | - Palestinian Territory 182 | - Portugal 183 | - Palau 184 | - Paraguay 185 | - Qatar 186 | - Reunion 187 | - Romania 188 | - Russian Federation 189 | - Rwanda 190 | - Saudi Arabia 191 | - Solomon Islands 192 | - Seychelles 193 | - Sudan 194 | - Sweden 195 | - Singapore 196 | - Saint Helena 197 | - Slovenia 198 | - Svalbard and Jan Mayen 199 | - Slovakia 200 | - Sierra Leone 201 | - San Marino 202 | - Senegal 203 | - Somalia 204 | - Suriname 205 | - Sao Tome and Principe 206 | - El Salvador 207 | - Syrian Arab Republic 208 | - Swaziland 209 | - Turks and Caicos Islands 210 | - Chad 211 | - French Southern Territories 212 | - Togo 213 | - Thailand 214 | - Tajikistan 215 | - Tokelau 216 | - Turkmenistan 217 | - Tunisia 218 | - Tonga 219 | - Timor-Leste 220 | - Turkey 221 | - Trinidad and Tobago 222 | - Tuvalu 223 | - Taiwan 224 | - Tanzania, United Republic of 225 | - Ukraine 226 | - Uganda 227 | - United States Minor Outlying Islands 228 | - United States 229 | - Uruguay 230 | - Uzbekistan 231 | - Holy See (Vatican City State) 232 | - Saint Vincent and the Grenadines 233 | - Venezuela 234 | - Virgin Islands, British 235 | - Virgin Islands, U.S. 236 | - Vietnam 237 | - Vanuatu 238 | - Wallis and Futuna 239 | - Samoa 240 | - Yemen 241 | - Mayotte 242 | - Serbia 243 | - South Africa 244 | - Zambia 245 | - Montenegro 246 | - Zimbabwe 247 | - Anonymous Proxy 248 | - Satellite Provider 249 | - Other 250 | - Aland Islands 251 | - Guernsey 252 | - Isle of Man 253 | - Jersey 254 | - Saint Barthelemy 255 | - Saint Martin 256 | - Bonaire, Saint Eustatius and Saba 257 | - South Sudan 258 | -------------------------------------------------------------------------------- /data/geoip/time_zone.yml: -------------------------------------------------------------------------------- 1 | # Hash of the timezone codes mapped to timezone name, per zoneinfo 2 | --- 3 | UA18: Europe/Zaporozhye 4 | NZG1: Pacific/Auckland 5 | KZ12: Asia/Qyzylorda 6 | PS: Asia/Gaza 7 | UA19: Europe/Kiev 8 | NZG2: Pacific/Auckland 9 | KZ13: Asia/Aqtobe 10 | KW: Asia/Kuwait 11 | NZG3: Pacific/Auckland 12 | KZ14: Asia/Qyzylorda 13 | KZ15: Asia/Almaty 14 | KY: America/Cayman 15 | KZ16: Asia/Aqtobe 16 | PW: Pacific/Palau 17 | KZ17: Asia/Almaty 18 | USMA: America/New_York 19 | DE: Europe/Berlin 20 | NA: Africa/Windhoek 21 | PY: America/Asuncion 22 | USTX: America/Chicago 23 | IE: Europe/Dublin 24 | MX10: America/Mazatlan 25 | USMD: America/New_York 26 | NC: Pacific/Noumea 27 | EC01: Pacific/Galapagos 28 | MX11: America/Mexico_City 29 | SA: Asia/Riyadh 30 | USME: America/New_York 31 | UY: America/Montevideo 32 | ZW: Africa/Harare 33 | MX12: America/Mexico_City 34 | SB: Pacific/Guadalcanal 35 | DJ: Africa/Djibouti 36 | NE: Africa/Niamey 37 | USHI: Pacific/Honolulu 38 | EC02: America/Guayaquil 39 | MX13: America/Mexico_City 40 | SC: Indian/Mahe 41 | DK: Europe/Copenhagen 42 | USWA: America/Los_Angeles 43 | NF: Pacific/Norfolk 44 | EC03: America/Guayaquil 45 | CAPE: America/Halifax 46 | RU20: Asia/Irkutsk 47 | MX14: America/Mazatlan 48 | SD: Africa/Khartoum 49 | NG: Africa/Lagos 50 | EC04: America/Guayaquil 51 | RU21: Europe/Moscow 52 | MX15: America/Chihuahua 53 | USMI: America/New_York 54 | DM: America/Dominica 55 | EC05: America/Guayaquil 56 | BR20: America/Fortaleza 57 | SE: Europe/Stockholm 58 | UZ01: Asia/Tashkent 59 | RU70: Europe/Volgograd 60 | RU22: Europe/Volgograd 61 | MX16: America/Mexico_City 62 | CD02: Africa/Kinshasa 63 | NI: America/Managua 64 | EC06: America/Guayaquil 65 | BR21: America/Sao_Paulo 66 | USCO: America/Denver 67 | UZ02: Asia/Samarkand 68 | RU71: Asia/Yekaterinburg 69 | RU23: Europe/Kaliningrad 70 | MX17: America/Mexico_City 71 | DO: America/Santo_Domingo 72 | EC07: America/Guayaquil 73 | BR22: America/Recife 74 | IL: Asia/Jerusalem 75 | SG: Asia/Singapore 76 | UZ03: Asia/Tashkent 77 | RU72: Europe/Moscow 78 | RU24: Europe/Volgograd 79 | MX18: America/Mazatlan 80 | EC08: America/Guayaquil 81 | BR23: America/Sao_Paulo 82 | CN01: Asia/Shanghai 83 | USRI: America/New_York 84 | IM: Europe/Isle_of_Man 85 | SH: Atlantic/St_Helena 86 | RU73: Europe/Samara 87 | RU25: Europe/Moscow 88 | MX19: America/Monterrey 89 | CD05: Africa/Lubumbashi 90 | NL: Europe/Amsterdam 91 | EC09: America/Guayaquil 92 | BR24: America/Porto_Velho 93 | CN02: Asia/Shanghai 94 | IN: Asia/Calcutta 95 | SI: Europe/Ljubljana 96 | RU26: Asia/Kamchatka 97 | USMN: America/Chicago 98 | CD06: Africa/Kinshasa 99 | BR25: America/Boa_Vista 100 | CN03: Asia/Shanghai 101 | IO: Indian/Chagos 102 | SJ: Arctic/Longyearbyen 103 | UZ06: Asia/Tashkent 104 | RU74: Asia/Krasnoyarsk 105 | RU27: Europe/Volgograd 106 | USWI: America/Chicago 107 | BR26: America/Sao_Paulo 108 | CN04: Asia/Shanghai 109 | USCT: America/New_York 110 | SK: Europe/Bratislava 111 | UZ07: Asia/Samarkand 112 | RU75: Asia/Novosibirsk 113 | RU28: Europe/Moscow 114 | USMO: America/Chicago 115 | CD08: Africa/Kinshasa 116 | BR27: America/Sao_Paulo 117 | CN05: Asia/Harbin 118 | IQ: Asia/Baghdad 119 | SL: Africa/Freetown 120 | UZ08: Asia/Samarkand 121 | RU76: Europe/Moscow 122 | RU29: Asia/Novokuznetsk 123 | ES27: Europe/Madrid 124 | "NO": Europe/Oslo 125 | BR28: America/Maceio 126 | CN06: Asia/Chongqing 127 | IR: Asia/Tehran 128 | SM: Europe/San_Marino 129 | UZ09: Asia/Samarkand 130 | RU77: Europe/Moscow 131 | PT02: Europe/Lisbon 132 | NP: Asia/Katmandu 133 | BR29: America/Sao_Paulo 134 | CN07: Asia/Shanghai 135 | IS: Atlantic/Reykjavik 136 | SN: Africa/Dakar 137 | RU78: Asia/Yekaterinburg 138 | PT03: Europe/Lisbon 139 | ES29: Europe/Madrid 140 | CN08: Asia/Harbin 141 | IT: Europe/Rome 142 | SO: Africa/Mogadishu 143 | RU79: Asia/Irkutsk 144 | PT04: Europe/Lisbon 145 | NR: Pacific/Nauru 146 | CN09: Asia/Shanghai 147 | USMS: America/Chicago 148 | PT05: Europe/Lisbon 149 | BA: Europe/Sarajevo 150 | USMT: America/Denver 151 | PT06: Europe/Lisbon 152 | SR: America/Paramaribo 153 | DZ: Africa/Algiers 154 | PT07: Europe/Lisbon 155 | NU: Pacific/Niue 156 | BB: America/Barbados 157 | PT08: Europe/Lisbon 158 | GA: Africa/Libreville 159 | PT09: Europe/Lisbon 160 | GB: Europe/London 161 | BD: Asia/Dhaka 162 | ST: Africa/Sao_Tome 163 | BE: Europe/Brussels 164 | NZE7: Pacific/Auckland 165 | GD: America/Grenada 166 | BF: Africa/Ouagadougou 167 | SV: America/El_Salvador 168 | LA: Asia/Vientiane 169 | NZE8: Pacific/Auckland 170 | USWV: America/New_York 171 | GE: Asia/Tbilisi 172 | BG: Europe/Sofia 173 | LB: Asia/Beirut 174 | USPA: America/New_York 175 | GF: America/Cayenne 176 | CANB: America/Halifax 177 | BH: Asia/Bahrain 178 | LC: America/St_Lucia 179 | NZE9: Pacific/Auckland 180 | QA: Asia/Qatar 181 | BI: Africa/Bujumbura 182 | SY: Asia/Damascus 183 | GG: Europe/Guernsey 184 | USAK: America/Anchorage 185 | BJ: Africa/Porto-Novo 186 | SZ: Africa/Mbabane 187 | USWY: America/Denver 188 | GH: Africa/Accra 189 | USAL: America/Chicago 190 | GI: Europe/Gibraltar 191 | BL: America/St_Barthelemy 192 | VA: Europe/Vatican 193 | RU01: Europe/Volgograd 194 | BM: Atlantic/Bermuda 195 | RU50: Asia/Yekaterinburg 196 | RU02: Asia/Irkutsk 197 | BN: Asia/Brunei 198 | LI: Europe/Vaduz 199 | USFL: America/New_York 200 | VC: America/St_Vincent 201 | BR01: America/Rio_Branco 202 | RU51: Europe/Moscow 203 | RU03: Asia/Novokuznetsk 204 | AR20: America/Argentina/Rio_Gallegos 205 | BO: America/La_Paz 206 | BR02: America/Maceio 207 | RU04: Asia/Novosibirsk 208 | ID20: Asia/Makassar 209 | GM: Africa/Banjul 210 | AR21: America/Argentina/Buenos_Aires 211 | LK: Asia/Colombo 212 | VE: America/Caracas 213 | BR03: America/Sao_Paulo 214 | RU52: Europe/Moscow 215 | RU05: Asia/Vladivostok 216 | ID21: Asia/Makassar 217 | GN: Africa/Conakry 218 | AR22: America/Argentina/Catamarca 219 | USAR: America/Chicago 220 | BR04: America/Manaus 221 | RU53: Asia/Novosibirsk 222 | RU06: Europe/Moscow 223 | ID22: Asia/Makassar 224 | ES51: Africa/Ceuta 225 | AR23: America/Argentina/Ushuaia 226 | CANL: America/St_Johns 227 | CN30: Asia/Chongqing 228 | VG: America/Tortola 229 | BR05: America/Bahia 230 | RU54: Asia/Omsk 231 | RU07: Europe/Volgograd 232 | ID23: Asia/Makassar 233 | ES52: Europe/Madrid 234 | GP: America/Guadeloupe 235 | AR24: America/Argentina/Tucuman 236 | BS: America/Nassau 237 | CN31: Asia/Chongqing 238 | BR06: America/Fortaleza 239 | UA20: Europe/Simferopol 240 | RU55: Europe/Samara 241 | RU08: Europe/Samara 242 | ID24: Asia/Jakarta 243 | ES53: Atlantic/Canary 244 | GQ: Africa/Malabo 245 | BT: Asia/Thimphu 246 | CN32: Asia/Chongqing 247 | CASK: America/Regina 248 | VI: America/St_Thomas 249 | BR07: America/Sao_Paulo 250 | UA21: Europe/Kiev 251 | RU56: Europe/Moscow 252 | RU09: Europe/Moscow 253 | ID25: Asia/Pontianak 254 | ES54: Europe/Madrid 255 | ES07: Europe/Madrid 256 | GR: Europe/Athens 257 | CN33: Asia/Chongqing 258 | BR08: America/Sao_Paulo 259 | UA22: Europe/Uzhgorod 260 | RU57: Europe/Samara 261 | ID26: Asia/Pontianak 262 | ES55: Europe/Madrid 263 | GS: Atlantic/South_Georgia 264 | UA23: Europe/Kiev 265 | RU58: Asia/Yekaterinburg 266 | MY01: Asia/Kuala_Lumpur 267 | ES56: Europe/Madrid 268 | BW: Africa/Gaborone 269 | GT: America/Guatemala 270 | UA24: Europe/Uzhgorod 271 | RU59: Asia/Vladivostok 272 | MY02: Asia/Kuala_Lumpur 273 | ES57: Europe/Madrid 274 | USKS: America/Chicago 275 | LR: Africa/Monrovia 276 | GU: Pacific/Guam 277 | UA25: Europe/Uzhgorod 278 | MY03: Asia/Kuala_Lumpur 279 | ES58: Europe/Madrid 280 | USAZ: America/Phoenix 281 | BY: Europe/Minsk 282 | LS: Africa/Maseru 283 | VN: Asia/Phnom_Penh 284 | UA26: Europe/Zaporozhye 285 | MY04: Asia/Kuala_Lumpur 286 | ES59: Europe/Madrid 287 | CANS: America/Halifax 288 | BZ: America/Belize 289 | LT: Europe/Vilnius 290 | GW: Africa/Bissau 291 | UA27: Europe/Kiev 292 | MY05: Asia/Kuala_Lumpur 293 | CANT: America/Yellowknife 294 | LU: Europe/Luxembourg 295 | MY06: Asia/Kuala_Lumpur 296 | CANU: America/Rankin_Inlet 297 | LV: Europe/Riga 298 | CABC: America/Vancouver 299 | GY: America/Guyana 300 | MY07: Asia/Kuala_Lumpur 301 | USDC: America/New_York 302 | MY08: Asia/Kuala_Lumpur 303 | USKY: America/New_York 304 | USUT: America/Denver 305 | USIA: America/Chicago 306 | MY09: Asia/Kuala_Lumpur 307 | USDE: America/New_York 308 | LY: Africa/Tripoli 309 | VU: Pacific/Efate 310 | EE: Europe/Tallinn 311 | USID: America/Denver 312 | JE: Europe/Jersey 313 | USNC: America/New_York 314 | EG: Africa/Cairo 315 | EC10: America/Guayaquil 316 | MX20: America/Mexico_City 317 | USND: America/Chicago 318 | EH: Africa/El_Aaiun 319 | EC11: America/Guayaquil 320 | MX21: America/Mexico_City 321 | CAQC: America/Montreal 322 | USNE: America/Chicago 323 | EC12: America/Guayaquil 324 | MX22: America/Mexico_City 325 | USSC: America/New_York 326 | MX23: America/Cancun 327 | USSD: America/Chicago 328 | TC: America/Grand_Turk 329 | EC13: America/Guayaquil 330 | RU30: Asia/Vladivostok 331 | MX24: America/Mexico_City 332 | TD: Africa/Ndjamena 333 | USNH: America/New_York 334 | CD10: Africa/Lubumbashi 335 | EC14: America/Guayaquil 336 | UZ10: Asia/Samarkand 337 | RU31: Asia/Krasnoyarsk 338 | MX25: America/Mazatlan 339 | CD11: Africa/Lubumbashi 340 | AR01: America/Argentina/Buenos_Aires 341 | EC15: America/Guayaquil 342 | BR30: America/Recife 343 | RU80: Asia/Yekaterinburg 344 | RU32: Asia/Omsk 345 | MX26: America/Hermosillo 346 | ID01: Asia/Pontianak 347 | TF: Indian/Kerguelen 348 | USNJ: America/New_York 349 | CD12: Africa/Lubumbashi 350 | AR02: America/Argentina/Catamarca 351 | USIL: America/Chicago 352 | BR31: America/Araguaina 353 | UZ12: Asia/Samarkand 354 | RU81: Europe/Samara 355 | RU33: Asia/Yekaterinburg 356 | MX27: America/Merida 357 | ID02: Asia/Makassar 358 | ES31: Europe/Madrid 359 | TG: Africa/Lome 360 | AR03: America/Argentina/Tucuman 361 | EC17: America/Guayaquil 362 | CN10: Asia/Shanghai 363 | UZ13: Asia/Tashkent 364 | RU82: Asia/Irkutsk 365 | RU34: Asia/Yekaterinburg 366 | MX28: America/Monterrey 367 | ID03: Asia/Jakarta 368 | ES32: Europe/Madrid 369 | JM: America/Jamaica 370 | TH: Asia/Bangkok 371 | AR04: America/Argentina/Rio_Gallegos 372 | USIN: America/Indianapolis 373 | EC18: America/Guayaquil 374 | YE: Asia/Aden 375 | CN11: Asia/Chongqing 376 | UZ14: Asia/Tashkent 377 | RU83: Europe/Moscow 378 | RU35: Asia/Yekaterinburg 379 | MX29: America/Mexico_City 380 | ID04: Asia/Jakarta 381 | USNM: America/Denver 382 | AR05: America/Argentina/Cordoba 383 | EC19: America/Guayaquil 384 | CN12: Asia/Shanghai 385 | UA01: Europe/Kiev 386 | RU84: Europe/Volgograd 387 | RU36: Asia/Anadyr 388 | ES34: Europe/Madrid 389 | JO: Asia/Amman 390 | TJ: Asia/Dushanbe 391 | ER: Africa/Asmera 392 | AR06: America/Argentina/Tucuman 393 | OM: Asia/Muscat 394 | CN13: Asia/Urumqi 395 | UA02: Europe/Kiev 396 | RU37: Europe/Moscow 397 | ID05: Asia/Jakarta 398 | TK: Pacific/Fakaofo 399 | AR07: America/Argentina/Buenos_Aires 400 | CN14: Asia/Chongqing 401 | JP: Asia/Tokyo 402 | TL: Asia/Dili 403 | UA03: Europe/Uzhgorod 404 | RU85: Europe/Moscow 405 | RU38: Europe/Volgograd 406 | PT10: Atlantic/Madeira 407 | ID06: Asia/Jakarta 408 | ET: Africa/Addis_Ababa 409 | AR08: America/Argentina/Buenos_Aires 410 | CN15: Asia/Chongqing 411 | UA04: Europe/Zaporozhye 412 | RU86: Europe/Moscow 413 | RU39: Asia/Krasnoyarsk 414 | PT11: Europe/Lisbon 415 | ID07: Asia/Jakarta 416 | TM: Asia/Ashgabat 417 | AR09: America/Argentina/Tucuman 418 | CN16: Asia/Chongqing 419 | UA05: Europe/Zaporozhye 420 | RU87: Asia/Novosibirsk 421 | ID08: Asia/Jakarta 422 | TN: Africa/Tunis 423 | UA06: Europe/Uzhgorod 424 | RU88: Europe/Moscow 425 | PT13: Europe/Lisbon 426 | ID09: Asia/Jayapura 427 | ES39: Europe/Madrid 428 | TO: Pacific/Tongatapu 429 | CN18: Asia/Chongqing 430 | UA07: Europe/Zaporozhye 431 | RU89: Asia/Vladivostok 432 | PT14: Europe/Lisbon 433 | KZ01: Asia/Almaty 434 | CN19: Asia/Harbin 435 | UA08: Europe/Simferopol 436 | NZF1: Pacific/Auckland 437 | KZ02: Asia/Almaty 438 | UA09: Europe/Kiev 439 | PT16: Europe/Lisbon 440 | NZF2: Pacific/Auckland 441 | KZ03: Asia/Qyzylorda 442 | TR: Asia/Istanbul 443 | PT17: Europe/Lisbon 444 | NZF3: Pacific/Auckland 445 | KZ04: Asia/Aqtobe 446 | USNV: America/Los_Angeles 447 | USGA: America/New_York 448 | AU01: Australia/Canberra 449 | PT18: Europe/Lisbon 450 | NZF4: Pacific/Auckland 451 | KZ05: Asia/Qyzylorda 452 | AU02: Australia/NSW 453 | CC: Indian/Cocos 454 | PT19: Europe/Lisbon 455 | NZF5: Pacific/Auckland 456 | KZ06: Asia/Aqtau 457 | TT: America/Port_of_Spain 458 | AU03: Australia/North 459 | KZ07: Asia/Oral 460 | USNY: America/New_York 461 | AU04: Australia/Queensland 462 | USLA: America/Chicago 463 | NZF7: Pacific/Chatham 464 | KZ08: Asia/Qyzylorda 465 | TV: Pacific/Funafuti 466 | MA: Africa/Casablanca 467 | AU05: Australia/South 468 | YT: Indian/Mayotte 469 | CF: Africa/Bangui 470 | NZF8: Pacific/Auckland 471 | KZ09: Asia/Aqtau 472 | TW: Asia/Taipei 473 | AU06: Australia/Tasmania 474 | YU: Europe/Belgrade 475 | CG: Africa/Brazzaville 476 | NZF9: Pacific/Auckland 477 | MC: Europe/Monaco 478 | AU07: Australia/Victoria 479 | CH: Europe/Zurich 480 | MX01: America/Mexico_City 481 | MD: Europe/Chisinau 482 | AU08: Australia/West 483 | CI: Africa/Abidjan 484 | MX02: America/Tijuana 485 | ME: Europe/Podgorica 486 | TZ: Africa/Dar_es_Salaam 487 | MX03: America/Hermosillo 488 | USVA: America/New_York 489 | MF: America/Marigot 490 | CK: Pacific/Rarotonga 491 | RU10: Europe/Moscow 492 | MX04: America/Merida 493 | MG: Indian/Antananarivo 494 | CL: Chile/Continental 495 | RU11: Asia/Irkutsk 496 | MX05: America/Mexico_City 497 | RE: Indian/Reunion 498 | CM: Africa/Lagos 499 | RU60: Europe/Kaliningrad 500 | MX06: America/Chihuahua 501 | BR11: America/Campo_Grande 502 | HK: Asia/Hong_Kong 503 | RU61: Europe/Volgograd 504 | RU13: Asia/Yekaterinburg 505 | MX07: America/Monterrey 506 | CO: America/Bogota 507 | RU62: Europe/Moscow 508 | RU14: Asia/Irkutsk 509 | MX08: America/Mexico_City 510 | ID30: Asia/Jakarta 511 | MK: Europe/Skopje 512 | BR13: America/Belem 513 | RU15: Asia/Anadyr 514 | MX09: America/Mexico_City 515 | ID31: Asia/Makassar 516 | ES60: Europe/Madrid 517 | ML: Africa/Bamako 518 | WF: Pacific/Wallis 519 | BR14: America/Cuiaba 520 | HN: America/Tegucigalpa 521 | RU63: Asia/Yakutsk 522 | RU16: Europe/Samara 523 | BR15: America/Sao_Paulo 524 | CR: America/Costa_Rica 525 | RU64: Asia/Sakhalin 526 | RU17: Europe/Volgograd 527 | ID33: Asia/Jakarta 528 | BR16: America/Belem 529 | MM: Asia/Rangoon 530 | RU65: Europe/Samara 531 | RU18: Asia/Krasnoyarsk 532 | BR17: America/Recife 533 | CAON: America/Toronto 534 | MN: Asia/Choibalsan 535 | RU66: Europe/Moscow 536 | BR18: America/Sao_Paulo 537 | HR: Europe/Zagreb 538 | CU: America/Havana 539 | MO: Asia/Macao 540 | RU67: Europe/Samara 541 | CV: Atlantic/Cape_Verde 542 | MP: Pacific/Saipan 543 | RU68: Europe/Volgograd 544 | MY11: Asia/Kuching 545 | HT: America/Port-au-Prince 546 | RO: Europe/Bucharest 547 | MQ: America/Martinique 548 | RU69: Europe/Moscow 549 | MY12: Asia/Kuala_Lumpur 550 | HU: Europe/Budapest 551 | CX: Indian/Christmas 552 | MR: Africa/Nouakchott 553 | MY13: Asia/Kuala_Lumpur 554 | CY: Asia/Nicosia 555 | MS: America/Montserrat 556 | MY14: Asia/Kuala_Lumpur 557 | CZ: Europe/Prague 558 | MT: Europe/Malta 559 | MY15: Asia/Kuching 560 | MU: Indian/Mauritius 561 | MY16: Asia/Kuching 562 | RS: Europe/Belgrade 563 | MV: Indian/Maldives 564 | GL01: America/Thule 565 | AD: Europe/Andorra 566 | MW: Africa/Blantyre 567 | GL02: America/Godthab 568 | WS: Pacific/Samoa 569 | AE: Asia/Dubai 570 | USVT: America/New_York 571 | GL03: America/Godthab 572 | AF: Asia/Kabul 573 | AG: America/Antigua 574 | RW: Africa/Kigali 575 | CAYT: America/Whitehorse 576 | MZ: Africa/Maputo 577 | CAMB: America/Winnipeg 578 | PA: America/Panama 579 | AI: America/Anguilla 580 | EC20: America/Guayaquil 581 | KE: Africa/Nairobi 582 | MX30: America/Mexico_City 583 | MX31: America/Merida 584 | EC22: America/Guayaquil 585 | AL: Europe/Tirane 586 | KG: Asia/Bishkek 587 | FI: Europe/Helsinki 588 | MX32: America/Monterrey 589 | PE: America/Lima 590 | AM: Asia/Yerevan 591 | KH: Asia/Phnom_Penh 592 | FJ: Pacific/Fiji 593 | PF: Pacific/Marquesas 594 | RU40: Asia/Yekaterinburg 595 | AN: America/Curacao 596 | KI: Pacific/Tarawa 597 | FK: Atlantic/Stanley 598 | PG: Pacific/Port_Moresby 599 | AO: Africa/Luanda 600 | ZA: Africa/Johannesburg 601 | USOH: America/New_York 602 | AR10: America/Argentina/Jujuy 603 | RU41: Europe/Moscow 604 | ID10: Asia/Jakarta 605 | PH: Asia/Manila 606 | AR11: America/Argentina/San_Luis 607 | RU42: Europe/Moscow 608 | ID11: Asia/Pontianak 609 | AR12: America/Argentina/La_Rioja 610 | RU43: Europe/Moscow 611 | ID12: Asia/Makassar 612 | CN20: Asia/Harbin 613 | UG: Africa/Kampala 614 | USOK: America/Chicago 615 | FO: Atlantic/Faeroe 616 | AR13: America/Argentina/Mendoza 617 | RU44: Asia/Magadan 618 | ID13: Asia/Makassar 619 | AS: US/Samoa 620 | CN21: Asia/Chongqing 621 | KM: Indian/Comoro 622 | AR14: America/Argentina/Buenos_Aires 623 | PK: Asia/Karachi 624 | UA10: Europe/Zaporozhye 625 | RU45: Europe/Samara 626 | ID14: Asia/Makassar 627 | AT: Europe/Vienna 628 | CN22: Asia/Harbin 629 | KN: America/St_Kitts 630 | AR15: America/Argentina/San_Luis 631 | PL: Europe/Warsaw 632 | UA11: Europe/Simferopol 633 | RU46: Europe/Samara 634 | ID15: Asia/Jakarta 635 | CN23: Asia/Shanghai 636 | FR: Europe/Paris 637 | AR16: America/Argentina/Buenos_Aires 638 | PM: America/Miquelon 639 | RU47: Europe/Moscow 640 | CN24: Asia/Chongqing 641 | KP: Asia/Pyongyang 642 | AR17: America/Argentina/Salta 643 | PN: Pacific/Pitcairn 644 | UA13: Europe/Kiev 645 | RU48: Europe/Moscow 646 | PT20: Europe/Lisbon 647 | ID16: Asia/Makassar 648 | AW: America/Aruba 649 | CN25: Asia/Shanghai 650 | AR18: America/Argentina/San_Juan 651 | UA14: Europe/Zaporozhye 652 | RU49: Europe/Moscow 653 | PT21: Europe/Lisbon 654 | NZ85: Pacific/Auckland 655 | ID17: Asia/Makassar 656 | AX: Europe/Mariehamn 657 | CN26: Asia/Chongqing 658 | USTN: America/Chicago 659 | KR: Asia/Seoul 660 | AR19: America/Argentina/San_Luis 661 | UA15: Europe/Uzhgorod 662 | PT22: Europe/Lisbon 663 | ID18: Asia/Makassar 664 | UA16: Europe/Zaporozhye 665 | KZ10: Asia/Qyzylorda 666 | ID19: Asia/Pontianak 667 | AZ: Asia/Baku 668 | CN28: Asia/Shanghai 669 | USOR: America/Los_Angeles 670 | UA17: Europe/Simferopol 671 | KZ11: Asia/Almaty 672 | ZM: Africa/Lusaka 673 | CN29: Asia/Chongqing 674 | USCA: America/Los_Angeles 675 | CAAB: America/Edmonton 676 | FX: Europe/Paris 677 | PR: America/Puerto_Rico 678 | -------------------------------------------------------------------------------- /geoip.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | # stub: geoip 1.6.4 ruby lib 6 | 7 | Gem::Specification.new do |s| 8 | s.name = "geoip" 9 | s.version = "1.6.4" 10 | 11 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 12 | s.require_paths = ["lib"] 13 | s.authors = ["Clifford Heath", "Roland Moriz"] 14 | s.date = "2016-08-03" 15 | s.description = "GeoIP searches a GeoIP database for a given host or IP address, and\nreturns information about the country where the IP address is allocated,\nand the city, ISP and other information, if you have that database version." 16 | s.email = ["clifford.heath@gmail.com", "rmoriz@gmail.com"] 17 | s.executables = ["geoip"] 18 | s.extra_rdoc_files = [ 19 | "LICENSE", 20 | "README.rdoc" 21 | ] 22 | s.files = [ 23 | "History.rdoc", 24 | "LICENSE", 25 | "README.rdoc", 26 | "bin/geoip", 27 | "data/geoip/country_code.yml", 28 | "data/geoip/country_code3.yml", 29 | "data/geoip/country_continent.yml", 30 | "data/geoip/country_name.yml", 31 | "data/geoip/region.yml", 32 | "data/geoip/time_zone.yml", 33 | "geoip.gemspec", 34 | "lib/geoip.rb", 35 | "test/csvORG2dat.py", 36 | "test/organizations.csv", 37 | "test/organizations.dat", 38 | "test/test_file.rb", 39 | "test/test_geoip.rb", 40 | "test/test_helper.rb" 41 | ] 42 | s.homepage = "http://github.com/cjheath/geoip" 43 | s.licenses = ["LGPL-2.1"] 44 | s.rubygems_version = "2.6.13" 45 | s.summary = "GeoIP searches a GeoIP database for a given host or IP address, and returns information about the country where the IP address is allocated, and the city, ISP and other information, if you have that database version." 46 | end 47 | 48 | -------------------------------------------------------------------------------- /lib/geoip.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Native Ruby reader for the GeoIP database 3 | # Lookup the country where IP address is allocated 4 | # 5 | # = COPYRIGHT 6 | # 7 | # This version Copyright (C) 2005 Clifford Heath 8 | # Derived from the C version, Copyright (C) 2003 MaxMind LLC 9 | # 10 | # This library is free software; you can redistribute it and/or 11 | # modify it under the terms of the GNU Lesser General Public 12 | # License as published by the Free Software Foundation; either 13 | # version 2.1 of the License, or (at your option) any later version. 14 | # 15 | # This library is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | # Lesser General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU Lesser General Public 21 | # License along with this library; if not, write to the Free Software 22 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 | # = SYNOPSIS 24 | # 25 | # require 'geoip' 26 | # p GeoIP.new('/usr/share/GeoIP/GeoIP.dat').country("www.netscape.sk") 27 | # 28 | # = DESCRIPTION 29 | # 30 | # GeoIP searches a GeoIP database for a given host or IP address, and 31 | # returns information about the country where the IP address is allocated. 32 | # 33 | # = PREREQUISITES 34 | # 35 | # You need at least the free GeoIP.dat, for which the last known download 36 | # location is 37 | # This API requires the file to be decompressed for searching. Other versions 38 | # of this database are available for purchase which contain more detailed 39 | # information, but this information is not returned by this implementation. 40 | # See www.maxmind.com for more information. 41 | # 42 | 43 | require 'thread' # Needed for Mutex 44 | require 'socket' 45 | begin 46 | require 'io/extra' # for IO.pread 47 | rescue LoadError 48 | # oh well, hope they're not forking after initializing 49 | end 50 | begin 51 | require 'ipaddr' # Needed for IPv6 support 52 | rescue LoadError 53 | # Won't work for an IPv6 database 54 | end 55 | 56 | require 'yaml' 57 | 58 | class GeoIP 59 | 60 | # The GeoIP GEM version number 61 | VERSION = "1.6.4" 62 | 63 | # The +data/+ directory for geoip 64 | DATA_DIR = File.expand_path(File.join(File.dirname(__FILE__),'..','data','geoip')) 65 | 66 | # Ordered list of the ISO3166 2-character country codes, ordered by 67 | # GeoIP ID 68 | CountryCode = YAML.load_file(File.join(DATA_DIR,'country_code.yml')) 69 | 70 | # Ordered list of the ISO3166 3-character country codes, ordered by 71 | # GeoIP ID 72 | CountryCode3 = YAML.load_file(File.join(DATA_DIR,'country_code3.yml')) 73 | 74 | # Ordered list of the English names of the countries, ordered by GeoIP ID 75 | CountryName = YAML.load_file(File.join(DATA_DIR,'country_name.yml')) 76 | 77 | # Ordered list of the ISO3166 2-character continent code of the countries, 78 | # ordered by GeoIP ID 79 | CountryContinent = YAML.load_file(File.join(DATA_DIR,'country_continent.yml')) 80 | 81 | # Load a hash of region names by region code 82 | RegionName = YAML.load_file(File.join(DATA_DIR,'region.yml')) 83 | 84 | # Hash of the timezone codes mapped to timezone name, per zoneinfo 85 | TimeZone = YAML.load_file(File.join(DATA_DIR,'time_zone.yml')) 86 | 87 | GEOIP_COUNTRY_EDITION = 1 88 | GEOIP_CITY_EDITION_REV1 = 2 89 | GEOIP_REGION_EDITION_REV1 = 3 90 | GEOIP_ISP_EDITION = 4 91 | GEOIP_ORG_EDITION = 5 92 | GEOIP_CITY_EDITION_REV0 = 6 93 | GEOIP_REGION_EDITION_REV0 = 7 94 | GEOIP_PROXY_EDITION = 8 95 | GEOIP_ASNUM_EDITION = 9 96 | GEOIP_NETSPEED_EDITION = 10 97 | GEOIP_COUNTRY_EDITION_V6 = 12 98 | GEOIP_CITY_EDITION_REV1_V6 = 30 99 | GEOIP_NETSPEED_EDITION_REV1 = 32 100 | 101 | # Editions list updated from the C API, August 2014: 102 | module Edition 103 | COUNTRY = 1 104 | REGION_REV0 = 7 105 | CITY_REV0 = 6 106 | ORG = 5 107 | ISP = 4 108 | CITY_REV1 = 2 109 | REGION_REV1 = 3 110 | PROXY = 8 111 | ASNUM = 9 112 | NETSPEED = 10 113 | DOMAIN = 11 114 | COUNTRY_V6 = 12 115 | LOCATIONA = 13 116 | ACCURACYRADIUS = 14 117 | CITYCONFIDENCE = 15 # unsupported 118 | CITYCONFIDENCEDIST = 16 # unsupported 119 | LARGE_COUNTRY = 17 120 | LARGE_COUNTRY_V6 = 18 121 | CITYCONFIDENCEDIST_ISP_ORG = 19 # unused, but gaps are not allowed 122 | CCM_COUNTRY = 20 # unused, but gaps are not allowed 123 | ASNUM_V6 = 21 124 | ISP_V6 = 22 125 | ORG_V6 = 23 126 | DOMAIN_V6 = 24 127 | LOCATIONA_V6 = 25 128 | REGISTRAR = 26 129 | REGISTRAR_V6 = 27 130 | USERTYPE = 28 131 | USERTYPE_V6 = 29 132 | CITY_REV1_V6 = 30 133 | CITY_REV0_V6 = 31 134 | NETSPEED_REV1 = 32 135 | NETSPEED_REV1_V6 = 33 136 | COUNTRYCONF = 34 137 | CITYCONF = 35 138 | REGIONCONF = 36 139 | POSTALCONF = 37 140 | ACCURACYRADIUS_V6 = 38 141 | end 142 | 143 | # Numeric codes for NETSPEED (NETSPEED_REV1* is string-based): 144 | GEOIP_UNKNOWN_SPEED = 0 145 | GEOIP_DIALUP_SPEED = 1 146 | GEOIP_CABLEDSL_SPEED = 2 147 | GEOIP_CORPORATE_SPEED = 3 148 | 149 | COUNTRY_BEGIN = 16776960 #:nodoc: 150 | STATE_BEGIN_REV0 = 16700000 #:nodoc: 151 | STATE_BEGIN_REV1 = 16000000 #:nodoc: 152 | STRUCTURE_INFO_MAX_SIZE = 20 #:nodoc: 153 | DATABASE_INFO_MAX_SIZE = 100 #:nodoc: 154 | MAX_ORG_RECORD_LENGTH = 300 #:nodoc: 155 | MAX_ASN_RECORD_LENGTH = 300 #:nodoc: unverified 156 | US_OFFSET = 1 #:nodoc: 157 | CANADA_OFFSET = 677 #:nodoc: 158 | WORLD_OFFSET = 1353 #:nodoc: 159 | FIPS_RANGE = 360 #:nodoc: 160 | FULL_RECORD_LENGTH = 50 #:nodoc: 161 | 162 | STANDARD_RECORD_LENGTH = 3 #:nodoc: 163 | SEGMENT_RECORD_LENGTH = 3 #:nodoc: 164 | 165 | class Country < Struct.new(:request, :ip, :country_code, :country_code2, :country_code3, :country_name, :continent_code) 166 | 167 | def to_hash 168 | Hash[each_pair.to_a] 169 | end 170 | 171 | end 172 | 173 | class Region < Struct.new(:request, :ip, :country_code2, :country_code3, :country_name, :continent_code, 174 | :region_code, :region_name, :timezone) 175 | 176 | def to_hash 177 | Hash[each_pair.to_a] 178 | end 179 | 180 | end 181 | 182 | # Warning: for historical reasons the region code is mis-named region_name here 183 | class City < Struct.new(:request, :ip, :country_code2, :country_code3, :country_name, :continent_code, 184 | :region_name, :city_name, :postal_code, :latitude, :longitude, :dma_code, :area_code, :timezone, :real_region_name) 185 | 186 | def to_hash 187 | Hash[each_pair.to_a] 188 | end 189 | 190 | def region_code 191 | self.region_name 192 | end 193 | 194 | end 195 | 196 | class ASN < Struct.new(:number, :asn) 197 | 198 | alias as_num number 199 | 200 | def to_hash 201 | Hash[each_pair.to_a] 202 | end 203 | 204 | end 205 | 206 | class ISP < Struct.new(:isp) 207 | def to_hash 208 | Hash[each_pair.to_a] 209 | end 210 | end 211 | 212 | # The Edition number that identifies which kind of database you've opened 213 | attr_reader :database_type 214 | 215 | # An IP that is used instead of local IPs 216 | attr_accessor :local_ip_alias 217 | 218 | alias databaseType database_type 219 | 220 | # Open the GeoIP database and determine the file format version. 221 | # 222 | # +filename+ is a String holding the path to the GeoIP.dat file 223 | # +options+ is a Hash allowing you to specify the caching options 224 | # 225 | def initialize(filename, options = {}) 226 | if options[:preload] || !IO.respond_to?(:pread) 227 | @mutex = Mutex.new 228 | end 229 | 230 | @use_pread = IO.respond_to?(:pread) && !options[:preload] 231 | @contents = nil 232 | @iter_pos = nil 233 | @options = options 234 | @database_type = Edition::COUNTRY 235 | @record_length = STANDARD_RECORD_LENGTH 236 | @file = File.open(filename, 'rb') 237 | 238 | detect_database_type! 239 | 240 | preload_data if options[:preload] 241 | end 242 | 243 | # Search the GeoIP database for the specified host, returning country 244 | # info. 245 | # 246 | # +hostname+ is a String holding the host's DNS name or numeric IP 247 | # address. 248 | # 249 | # If the database is a City database (normal), return the result that 250 | # +city+ would return. 251 | # 252 | # Otherwise, return a Country object with the seven elements: 253 | # * The host or IP address string as requested 254 | # * The IP address string after looking up the host 255 | # * The GeoIP country-ID as an integer (N.B. this is excluded from the 256 | # city results!) 257 | # * The two-character country code (ISO 3166-1 alpha-2) 258 | # * The three-character country code (ISO 3166-2 alpha-3) 259 | # * The ISO 3166 English-language name of the country 260 | # * The two-character continent code 261 | # 262 | def country(hostname) 263 | case @database_type 264 | when Edition::CITY_REV0, Edition::CITY_REV1, Edition::CITY_REV1_V6 265 | city(hostname) 266 | 267 | when Edition::REGION_REV0, Edition::REGION_REV1 268 | region(hostname) 269 | 270 | when Edition::NETSPEED, Edition::NETSPEED_REV1 271 | netspeed(hostname) 272 | 273 | when Edition::COUNTRY, Edition::PROXY, Edition::COUNTRY_V6 274 | ip = lookup_ip(hostname) 275 | if @ip_bits > 32 276 | ipaddr = IPAddr.new ip 277 | code = (seek_record(ipaddr.to_i) - COUNTRY_BEGIN) 278 | else 279 | # Convert numeric IP address to an integer 280 | ipnum = iptonum(ip) 281 | code = (seek_record(ipnum) - @database_segments[0]) 282 | end 283 | read_country(code, hostname, ip) 284 | else 285 | throw "Invalid GeoIP database type #{@database_type}, can't look up Country by IP" 286 | end 287 | end 288 | 289 | # Search a GeoIP Connection Type (Netspeed) database for the specified host, 290 | # returning the speed code. 291 | # 292 | # +hostname+ is a String holding the host's DNS name or numeric IP address. 293 | def netspeed(hostname) 294 | unless (@database_type == Edition::NETSPEED || 295 | @database_type == Edition::NETSPEED_REV1) 296 | throw "Invalid GeoIP database type #{@database_type}, can't look up Netspeed by IP" 297 | end 298 | # Convert numeric IP address to an integer 299 | ip = lookup_ip(hostname) 300 | ipnum = iptonum(ip) 301 | pos = seek_record(ipnum) 302 | read_netspeed(pos-@database_segments[0]) 303 | end 304 | 305 | # Search the GeoIP database for the specified host, returning region info. 306 | # 307 | # +hostname+ is a String holding the hosts's DNS name or numeric IP 308 | # address. 309 | # 310 | # Returns a Region object with the nine elements: 311 | # * The host or IP address string as requested 312 | # * The IP address string after looking up the host 313 | # * The two-character country code (ISO 3166-1 alpha-2) 314 | # * The three-character country code (ISO 3166-2 alpha-3) 315 | # * The ISO 3166 English-language name of the country 316 | # * The two-character continent code 317 | # * The region name (state or territory) 318 | # * The timezone name, if known 319 | # 320 | def region(hostname) 321 | if (@database_type == Edition::CITY_REV0 || 322 | @database_type == Edition::CITY_REV1 || 323 | @database_type == Edition::CITY_REV1_V6) 324 | return city(hostname) 325 | end 326 | 327 | if (@database_type == Edition::REGION_REV0 || 328 | @database_type == Edition::REGION_REV1) 329 | ip = lookup_ip(hostname) 330 | ipnum = iptonum(ip) 331 | pos = seek_record(ipnum) 332 | else 333 | throw "Invalid GeoIP database type, can't look up Region by IP" 334 | end 335 | 336 | if pos == @database_segments[0] 337 | nil 338 | else 339 | read_region(pos, hostname, ip) 340 | end 341 | end 342 | 343 | # Search the GeoIP database for the specified host, returning city info. 344 | # 345 | # +hostname+ is a String holding the host's DNS name or numeric IP 346 | # address. 347 | # 348 | # Returns a City object with the fourteen elements: 349 | # * The host or IP address string as requested 350 | # * The IP address string after looking up the host 351 | # * The two-character country code (ISO 3166-1 alpha-2) 352 | # * The three-character country code (ISO 3166-2 alpha-3) 353 | # * The ISO 3166 English-language name of the country 354 | # * The two-character continent code 355 | # * The region name (state or territory) 356 | # * The city name 357 | # * The postal code (zipcode) 358 | # * The latitude 359 | # * The longitude 360 | # * The USA dma_code if known (only REV1 City database) 361 | # * The USA area_code if known (only REV1 City database) 362 | # * The timezone name, if known 363 | # 364 | def city(hostname) 365 | ip = lookup_ip(hostname) 366 | 367 | if (@database_type == Edition::CITY_REV0 || 368 | @database_type == Edition::CITY_REV1) 369 | # Convert numeric IP address to an integer 370 | ipnum = iptonum(ip) 371 | pos = seek_record(ipnum) 372 | elsif (@database_type == Edition::CITY_REV1_V6) 373 | ipaddr = IPAddr.new ip 374 | pos = seek_record(ipaddr.to_i) 375 | else 376 | throw "Invalid GeoIP database type, can't look up City by IP" 377 | end 378 | 379 | read_city(pos-@database_segments[0], hostname, ip) 380 | end 381 | 382 | # Search a ISP GeoIP database for the specified host, returning the ISP 383 | # Not all GeoIP databases contain ISP information. 384 | # Check http://maxmind.com 385 | # 386 | # +hostname+ is a String holding the host's DNS name or numeric IP 387 | # address. 388 | # 389 | # Returns the ISP name. 390 | # 391 | def isp(hostname) 392 | ip = lookup_ip(hostname) 393 | 394 | # Convert numeric IP address to an integer 395 | ipnum = iptonum(ip) 396 | 397 | case @database_type 398 | when Edition::ORG, 399 | Edition::ISP, 400 | Edition::DOMAIN, 401 | Edition::ASNUM, 402 | Edition::ACCURACYRADIUS, 403 | Edition::NETSPEED, 404 | Edition::USERTYPE, 405 | Edition::REGISTRAR, 406 | Edition::LOCATIONA, 407 | Edition::CITYCONF, 408 | Edition::COUNTRYCONF, 409 | Edition::REGIONCONF, 410 | Edition::POSTALCONF 411 | pos = seek_record(ipnum) 412 | read_isp(pos-@database_segments[0]) 413 | else 414 | throw "Invalid GeoIP database type, can't look up Organization/ISP by IP" 415 | end 416 | end 417 | 418 | # Search a ASN GeoIP database for the specified host, returning the AS 419 | # number and description. 420 | # 421 | # Many other types of GeoIP database (e.g. userType) mis-identify as ASN type, 422 | # and this can read those too. 423 | # 424 | # +hostname+ is a String holding the host's DNS name or numeric IP address. 425 | # 426 | # Returns the AS number and description. 427 | # 428 | # Source: 429 | # http://geolite.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz 430 | # 431 | def asn(hostname) 432 | ip = lookup_ip(hostname) 433 | 434 | if (@database_type == Edition::ASNUM) 435 | # Convert numeric IP address to an integer 436 | ipnum = iptonum(ip) 437 | pos = seek_record(ipnum) 438 | elsif (@database_type == Edition::ASNUM_V6) 439 | ipaddr = IPAddr.new ip 440 | pos = seek_record(ipaddr.to_i) 441 | else 442 | throw "Invalid GeoIP database type #{@database_type}, can't look up ASN by IP" 443 | end 444 | 445 | read_asn(pos-@database_segments[0]) 446 | end 447 | 448 | # Search a ISP GeoIP database for the specified host, returning the 449 | # organization. 450 | # 451 | # +hostname+ is a String holding the host's DNS name or numeric 452 | # IP address. 453 | # 454 | # Returns the organization associated with it. 455 | # 456 | alias_method(:organization, :isp) # Untested, according to Maxmind docs this should work 457 | 458 | # Iterate through a GeoIP city database by 459 | def each 460 | return enum_for unless block_given? 461 | 462 | if (@database_type != Edition::CITY_REV0 && 463 | @database_type != Edition::CITY_REV1) 464 | throw "Invalid GeoIP database type, can't iterate thru non-City database" 465 | end 466 | 467 | @iter_pos = @database_segments[0] + 1 468 | num = 0 469 | 470 | until ((rec = read_city(@iter_pos)).nil?) 471 | yield rec 472 | print "#{num}: #{@iter_pos}\n" if((num += 1) % 1000 == 0) 473 | end 474 | 475 | @iter_pos = nil 476 | return self 477 | end 478 | 479 | # Call like this, for example: 480 | # GeoIP.new('GeoIPNetSpeedCell.dat').each{|*a| puts("0x%08X\t%d" % a)} 481 | # or: 482 | # GeoIP.new('GeoIPv6.dat').each{|*a| puts("0x%032X\t%d" % a)} 483 | def each_by_ip offset = 0, ipnum = 0, mask = nil, &callback 484 | mask ||= 1 << (@ip_bits-1) 485 | 486 | # Read the two pointers and split them: 487 | record2 = atomic_read(@record_length*2, @record_length*2*offset) 488 | record1 = record2.slice!(0, @record_length) 489 | 490 | # Traverse the left tree 491 | off1 = le_to_ui(record1.unpack('C*')) 492 | val = off1 - @database_segments[0] 493 | if val >= 0 494 | yield(ipnum, val > 0 ? read_record(ipnum.to_s, ipnum, val) : nil) 495 | elsif mask != 0 496 | each_by_ip(off1, ipnum, mask >> 1, &callback) 497 | end 498 | 499 | # Traverse the right tree 500 | off2 = le_to_ui(record2.unpack('C*')) 501 | val = off2 - @database_segments[0] 502 | if val >= 0 503 | yield(ipnum|mask, val > 0 ? read_record(ipnum.to_s, ipnum, val) : nil) 504 | elsif mask != 0 505 | each_by_ip(off2, ipnum|mask, mask >> 1, &callback) 506 | end 507 | end 508 | 509 | private 510 | 511 | def read_record hostname, ip, offset 512 | case @database_type 513 | when Edition::CITY_REV0, Edition::CITY_REV1, Edition::CITY_REV1_V6 514 | read_city(offset, hostname, ip) 515 | 516 | when Edition::REGION_REV0, Edition::REGION_REV1 517 | read_region(offset+@database_segments[0], hostname, ip) 518 | 519 | when Edition::NETSPEED, Edition::NETSPEED_REV1 520 | read_netspeed(offset) 521 | 522 | when Edition::COUNTRY, Edition::PROXY, Edition::COUNTRY_V6 523 | read_country(offset, hostname, ip) 524 | 525 | when Edition::ASNUM, Edition::ASNUM_V6 526 | read_asn(offset) 527 | 528 | # Add new types here 529 | when Edition::ISP, Edition::ORG 530 | read_isp offset 531 | 532 | else 533 | #raise "Unsupported GeoIP database type #{@database_type}" 534 | offset 535 | end 536 | end 537 | 538 | # Loads data into an in-memory frozen string which is Copy-on-write friendly 539 | def preload_data 540 | @file.seek(0) 541 | @contents = @file.read.freeze 542 | @file.close 543 | end 544 | 545 | # Detects the type of the database. 546 | def detect_database_type! # :nodoc: 547 | @file.seek(-3, IO::SEEK_END) 548 | @ip_bits = 32 549 | 550 | 0.upto(STRUCTURE_INFO_MAX_SIZE - 1) do |i| 551 | if @file.read(3).bytes.all? { |byte| byte == 255 } 552 | @database_type = 553 | if @file.respond_to?(:getbyte) 554 | @file.getbyte 555 | else 556 | @file.getc 557 | end 558 | 559 | @database_type -= 105 if @database_type >= 106 560 | 561 | if (@database_type == Edition::REGION_REV0) 562 | # Region Edition, pre June 2003 563 | @database_segments = [STATE_BEGIN_REV0] 564 | elsif (@database_type == Edition::REGION_REV1) 565 | # Region Edition, post June 2003 566 | @database_segments = [STATE_BEGIN_REV1] 567 | elsif @database_type == Edition::CITY_REV0 || 568 | @database_type == Edition::CITY_REV1 || 569 | @database_type == Edition::ORG || 570 | @database_type == Edition::ORG_V6 || 571 | @database_type == Edition::ISP || 572 | @database_type == Edition::ISP_V6 || 573 | @database_type == Edition::REGISTRAR || 574 | @database_type == Edition::REGISTRAR_V6 || 575 | @database_type == Edition::USERTYPE || # Many of these files mis-identify as ASNUM files 576 | @database_type == Edition::USERTYPE_V6 || 577 | @database_type == Edition::DOMAIN || 578 | @database_type == Edition::DOMAIN_V6 || 579 | @database_type == Edition::ASNUM || 580 | @database_type == Edition::ASNUM_V6 || 581 | @database_type == Edition::NETSPEED_REV1 || 582 | @database_type == Edition::NETSPEED_REV1_V6 || 583 | @database_type == Edition::LOCATIONA || 584 | # @database_type == Edition::LOCATIONA_V6 || 585 | @database_type == Edition::ACCURACYRADIUS || 586 | @database_type == Edition::ACCURACYRADIUS_V6 || 587 | @database_type == Edition::CITYCONF || 588 | @database_type == Edition::COUNTRYCONF || 589 | @database_type == Edition::REGIONCONF || 590 | @database_type == Edition::POSTALCONF || 591 | @database_type == Edition::CITY_REV0_V6 || 592 | @database_type == Edition::CITY_REV1_V6 593 | 594 | # City/Org Editions have two segments, read offset of second segment 595 | @database_segments = [0] 596 | sr = @file.read(3).unpack("C*") 597 | @database_segments[0] += le_to_ui(sr) 598 | 599 | end 600 | 601 | case @database_type 602 | when Edition::COUNTRY 603 | when Edition::NETSPEED_REV1 604 | when Edition::ASNUM 605 | when Edition::CITY_REV0 606 | when Edition::CITY_REV1 607 | when Edition::REGION_REV0 608 | when Edition::REGION_REV1 609 | @ip_bits = 32 610 | @record_length = 3 611 | 612 | when Edition::ORG, 613 | Edition::DOMAIN, 614 | Edition::ISP 615 | @ip_bits = 32 616 | @record_length = 4 617 | 618 | when Edition::ASNUM_V6, 619 | Edition::CITY_REV0_V6, 620 | Edition::CITY_REV1_V6, 621 | Edition::NETSPEED_REV1_V6, 622 | Edition::COUNTRY_V6, 623 | Edition::PROXY 624 | @ip_bits = 128 625 | @record_length = 3 626 | 627 | when Edition::ACCURACYRADIUS_V6, 628 | Edition::DOMAIN_V6, 629 | Edition::ISP_V6, 630 | Edition::LARGE_COUNTRY_V6, 631 | Edition::LOCATIONA_V6, 632 | Edition::ORG_V6, 633 | Edition::REGISTRAR_V6, 634 | Edition::USERTYPE_V6 635 | @ip_bits = 128 636 | @record_length = 4 637 | 638 | else 639 | raise "unimplemented database type" 640 | end 641 | 642 | break 643 | else 644 | @file.seek(-4, IO::SEEK_CUR) 645 | end 646 | end 647 | 648 | if (@database_type == Edition::COUNTRY || 649 | @database_type == Edition::PROXY || 650 | @database_type == Edition::COUNTRY_V6 || 651 | @database_type == Edition::NETSPEED) 652 | @database_segments = [COUNTRY_BEGIN] 653 | end 654 | 655 | # puts "Detected IPv#{@ip_bits == 32 ? '4' : '6'} database_type #{@database_type} with #{@database_segments[0]} records of length #{@record_length} (data starts at #{@database_segments[0]*@record_length*2})" 656 | end 657 | 658 | def read_country code, hostname, ip 659 | Country.new( 660 | hostname, # Requested hostname 661 | ip, # Ip address as dotted quad 662 | code, # GeoIP's country code 663 | CountryCode[code], # ISO3166-1 alpha-2 code 664 | CountryCode3[code], # ISO3166-2 alpha-3 code 665 | CountryName[code], # Country name, per ISO 3166 666 | CountryContinent[code] # Continent code. 667 | ) 668 | end 669 | 670 | def read_region(pos, hostname = '', ip = '') #:nodoc: 671 | if (@database_type == Edition::REGION_REV0) 672 | pos -= STATE_BEGIN_REV0 673 | if (pos >= 1000) 674 | code = 225 675 | region_code = ((pos - 1000) / 26 + 65).chr + ((pos - 1000) % 26 + 65).chr 676 | else 677 | code = pos 678 | region_code = '' 679 | end 680 | elsif (@database_type == Edition::REGION_REV1) 681 | pos -= STATE_BEGIN_REV1 682 | if (pos < US_OFFSET) 683 | code = 0 684 | region_code = '' 685 | elsif (pos < CANADA_OFFSET) 686 | code = 225 687 | region_code = ((pos - US_OFFSET) / 26 + 65).chr + ((pos - US_OFFSET) % 26 + 65).chr 688 | elsif (pos < WORLD_OFFSET) 689 | code = 38 690 | region_code = ((pos - CANADA_OFFSET) / 26 + 65).chr + ((pos - CANADA_OFFSET) % 26 + 65).chr 691 | else 692 | code = (pos - WORLD_OFFSET) / FIPS_RANGE 693 | region_code = '' 694 | end 695 | end 696 | 697 | Region.new( 698 | hostname, 699 | ip, 700 | CountryCode[code], # ISO3166-1 alpha-2 code 701 | CountryCode3[code], # ISO3166-2 alpha-3 code 702 | CountryName[code], # Country name, per ISO 3166 703 | CountryContinent[code], # Continent code. 704 | region_code, # Unfortunately this is called region_name in the City structure 705 | lookup_region_name(CountryCode[code], region_code), 706 | (TimeZone["#{CountryCode[code]}#{region_code}"] || TimeZone["#{CountryCode[code]}"]) 707 | ) 708 | end 709 | 710 | def read_asn offset 711 | return nil if offset == 0 712 | record = atomic_read(MAX_ASN_RECORD_LENGTH, index_size+offset) 713 | record.slice!(record.index("\0")..-1) 714 | 715 | # AS####, Description 716 | if record =~ /^(AS\d+)(?:\s(.*))?$/ 717 | # set the correct encoding in ruby 1.9 compatible environments: 718 | asn = $2.respond_to?(:force_encoding) ? $2.force_encoding('iso-8859-1').encode('utf-8') : $2 719 | ASN.new($1, asn) 720 | else 721 | record 722 | end 723 | end 724 | 725 | def read_netspeed(offset) 726 | return offset if @database_type == Edition::NETSPEED # Numeric value 727 | return nil if offset == 0 728 | 729 | record = atomic_read(20, index_size+offset) 730 | record.slice!(record.index("\0")..-1) 731 | record 732 | end 733 | 734 | def read_isp offset 735 | record = atomic_read(MAX_ORG_RECORD_LENGTH, index_size+offset) 736 | record = record.sub(/\000.*/n, '') 737 | record = record.force_encoding('iso-8859-1').encode('utf-8') if record.respond_to?(:force_encoding) 738 | record.start_with?('*') ? nil : ISP.new(record) 739 | end 740 | 741 | # Size of the database index (a binary tree of depth <= @ip_bits) 742 | def index_size 743 | 2 * @record_length * @database_segments[0] 744 | end 745 | 746 | def lookup_region_name(country_iso2, region_code) 747 | country_regions = RegionName[country_iso2] 748 | country_regions && country_regions[region_code] 749 | end 750 | 751 | # Search the GeoIP database for the specified host, returning city info. 752 | # 753 | # +hostname+ is a String holding the host's DNS name or numeric 754 | # IP address. 755 | # 756 | # Returns an array of fourteen elements: 757 | # * All elements from the country query (except GeoIP's country code, 758 | # bah!) 759 | # * The region (state or territory) name 760 | # * The city name 761 | # * The postal code (zipcode) 762 | # * The latitude 763 | # * The longitude 764 | # * The dma_code and area_code, if available (REV1 City database) 765 | # * The timezone name, if known 766 | # 767 | def read_city(offset, hostname = '', ip = '') #:nodoc: 768 | return nil if offset == 0 769 | record = atomic_read(FULL_RECORD_LENGTH, offset+index_size) 770 | return unless (record && record.size == FULL_RECORD_LENGTH) 771 | 772 | # The country code is the first byte: 773 | code = record[0] 774 | code = code.ord if code.respond_to?(:ord) 775 | record = record[1..-1] 776 | @iter_pos += 1 unless @iter_pos.nil? 777 | 778 | spl = record.split("\x00", 4) 779 | # Get the region code: 780 | region_code = spl[0] 781 | @iter_pos += (region_code.size + 1) unless @iter_pos.nil? 782 | 783 | # Get the city: 784 | city = spl[1] 785 | @iter_pos += (city.size + 1) unless @iter_pos.nil? 786 | # set the correct encoding in ruby 1.9 compatible environments: 787 | city = city.force_encoding('iso-8859-1').encode('utf-8') if city.respond_to?(:force_encoding) 788 | 789 | # Get the postal code: 790 | postal_code = spl[2] 791 | @iter_pos += (postal_code.size + 1) unless @iter_pos.nil? || postal_code.nil? 792 | 793 | record = spl[3] 794 | 795 | # Get the latitude/longitude: 796 | if (record && record[0,3]) 797 | latitude = (le_to_ui(record[0,3].unpack('C*')) / 10000.0) - 180 798 | record = record[3..-1] 799 | 800 | @iter_pos += 3 unless @iter_pos.nil? 801 | else 802 | latitude = '' 803 | end 804 | 805 | if (record && record[0,3]) 806 | longitude = le_to_ui(record[0,3].unpack('C*')) / 10000.0 - 180 807 | record = record[3..-1] 808 | 809 | @iter_pos += 3 unless @iter_pos.nil? 810 | else 811 | longitude = '' 812 | end 813 | 814 | # UNTESTED 815 | if (record && 816 | record[0,3] && 817 | @database_type == Edition::CITY_REV1 && 818 | CountryCode[code] == "US") 819 | 820 | dmaarea_combo = le_to_ui(record[0,3].unpack('C*')) 821 | dma_code = (dmaarea_combo / 1000) 822 | area_code = (dmaarea_combo % 1000) 823 | 824 | @iter_pos += 3 unless @iter_pos.nil? 825 | else 826 | dma_code, area_code = nil, nil 827 | end 828 | 829 | City.new( 830 | hostname, # Requested hostname 831 | ip, # Ip address as dotted quad 832 | CountryCode[code], # ISO3166-1 code 833 | CountryCode3[code], # ISO3166-2 code 834 | CountryName[code], # Country name, per IS03166 835 | CountryContinent[code], # Continent code. 836 | region_code, # Region code (called region_name, unfortunately) 837 | city, # City name 838 | postal_code, # Postal code 839 | latitude, 840 | longitude, 841 | dma_code, 842 | area_code, 843 | (TimeZone["#{CountryCode[code]}#{region_code}"] || TimeZone["#{CountryCode[code]}"]), 844 | lookup_region_name(CountryCode[code], region_code) # Real region name 845 | ) 846 | end 847 | 848 | def lookup_ip(ip_or_hostname) # :nodoc: 849 | if is_local?(ip_or_hostname) && @local_ip_alias 850 | ip_or_hostname = @local_ip_alias 851 | end 852 | 853 | if !ip_or_hostname.kind_of?(String) or ip_or_hostname =~ /^[0-9.]+$/ 854 | return ip_or_hostname 855 | end 856 | 857 | # Lookup IP address, we were given a name or IPv6 address 858 | ip = IPSocket.getaddress(ip_or_hostname) 859 | ip = '0.0.0.0' if ip == '::1' 860 | ip 861 | end 862 | 863 | def is_local?(ip_or_hostname) #:nodoc: 864 | ["127.0.0.1", "localhost", "::1", "0000::1", "0:0:0:0:0:0:0:1"].include? ip_or_hostname 865 | end 866 | 867 | # Convert numeric IP address to Integer. 868 | def iptonum(ip) #:nodoc: 869 | if (ip.kind_of?(String) && 870 | ip =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/) 871 | ip = be_to_ui(Regexp.last_match().to_a.slice(1..4)) 872 | else 873 | ip = ip.to_i 874 | end 875 | 876 | return ip 877 | end 878 | 879 | def seek_record(ipnum) #:nodoc: 880 | # Binary search in the file. 881 | # Records are pairs of little-endian integers, each of @record_length. 882 | offset = 0 883 | mask = 1 << (@ip_bits-1) 884 | 885 | @ip_bits.downto(1) do |depth| 886 | go_right = (ipnum & mask) != 0 887 | off = @record_length * (2 * offset + (go_right ? 1 : 0)) 888 | offset = le_to_ui(atomic_read(@record_length, off).unpack('C*')) 889 | 890 | return offset if offset >= @database_segments[0] 891 | mask >>= 1 892 | end 893 | end 894 | 895 | # Convert a big-endian array of numeric bytes to unsigned int. 896 | # 897 | # Returns the unsigned Integer. 898 | # 899 | def be_to_ui(s) #:nodoc: 900 | i = 0 901 | 902 | s.each { |b| i = ((i << 8) | (b.to_i & 0x0ff)) } 903 | return i 904 | end 905 | 906 | # Same for little-endian 907 | def le_to_ui(s) #:nodoc: 908 | be_to_ui(s.reverse) 909 | end 910 | 911 | # reads +length+ bytes from +pos+ as atomically as possible 912 | # if IO.pread is available, it'll use that (making it both multithread 913 | # and multiprocess-safe). Otherwise we'll use a mutex to synchronize 914 | # access (only providing protection against multiple threads, but not 915 | # file descriptors shared across multiple processes). 916 | # If the contents of the database have been preloaded it'll work with 917 | # the frozen string object directly. 918 | def atomic_read(length, pos) #:nodoc: 919 | if @contents 920 | @contents.byteslice(pos, length) || '' 921 | elsif @use_pread 922 | IO.pread(@file.fileno, length, pos) 923 | elsif @mutex 924 | @mutex.synchronize { read_unguarded(length, pos) } 925 | else 926 | read_unguarded(length, pos) 927 | end 928 | end 929 | 930 | def read_unguarded(length, pos) 931 | @file.seek(pos) 932 | @file.read(length) 933 | end 934 | end 935 | -------------------------------------------------------------------------------- /script/destroy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) 3 | 4 | begin 5 | require 'rubigen' 6 | rescue LoadError 7 | require 'rubygems' 8 | require 'rubigen' 9 | end 10 | require 'rubigen/scripts/destroy' 11 | 12 | ARGV.shift if ['--help', '-h'].include?(ARGV[0]) 13 | RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit] 14 | RubiGen::Scripts::Destroy.new.run(ARGV) 15 | -------------------------------------------------------------------------------- /script/generate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) 3 | 4 | begin 5 | require 'rubigen' 6 | rescue LoadError 7 | require 'rubygems' 8 | require 'rubigen' 9 | end 10 | require 'rubigen/scripts/generate' 11 | 12 | ARGV.shift if ['--help', '-h'].include?(ARGV[0]) 13 | RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit] 14 | RubiGen::Scripts::Generate.new.run(ARGV) 15 | -------------------------------------------------------------------------------- /script/txt2html: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' 4 | require 'redcloth' 5 | require 'erb' 6 | require './' + File.dirname(__FILE__) + '/../lib/geoip.rb' 7 | 8 | version = GeoIP::VERSION 9 | download = 'http://rubyforge.org/projects/geoip' 10 | 11 | class Fixnum 12 | def ordinal 13 | # teens 14 | return 'th' if (10..19).include?(self % 100) 15 | # others 16 | case self % 10 17 | when 1; return 'st' 18 | when 2; return 'nd' 19 | when 3; return 'rd' 20 | else return 'th' 21 | end 22 | end 23 | end 24 | 25 | class Time 26 | def pretty 27 | return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}" 28 | end 29 | end 30 | 31 | def convert_syntax(syntax, source) 32 | return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^
|
$!,'') 33 | end 34 | 35 | if ARGV.length >= 1 36 | src, template = ARGV 37 | template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml') 38 | 39 | else 40 | puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html") 41 | exit! 42 | end 43 | 44 | template = ERB.new(File.open(template).read) 45 | 46 | title = nil 47 | body = nil 48 | File.open(src) do |fsrc| 49 | title_text = fsrc.readline 50 | body_text = fsrc.read 51 | syntax_items = [] 52 | body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)!m){ 53 | ident = syntax_items.length 54 | element, syntax, source = $1, $2, $3 55 | syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}" 56 | "syntax-temp-#{ident}" 57 | } 58 | title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip 59 | body = RedCloth.new(body_text).to_html 60 | body.gsub!(%r!(?:
)?syntax-temp-(\d+)(?:
)?!){ syntax_items[$1.to_i] } 61 | end 62 | stat = File.stat(src) 63 | created = stat.ctime 64 | modified = stat.mtime 65 | 66 | $stdout << template.result(binding) 67 | -------------------------------------------------------------------------------- /test/csvORG2dat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Source : https://github.com/mteodoro/mmutils 3 | 4 | import sys 5 | import logging 6 | import logging.handlers 7 | import optparse 8 | 9 | import csv 10 | import fileinput 11 | import itertools 12 | import struct 13 | import time 14 | 15 | from functools import partial 16 | 17 | import ipaddr 18 | 19 | def init_logger(opts): 20 | level = logging.INFO 21 | handler = logging.StreamHandler() 22 | #handler = logging.handlers.SysLogHandler(address='/dev/log') 23 | if opts.debug: 24 | level = logging.DEBUG 25 | handler = logging.StreamHandler() 26 | root = logging.getLogger() 27 | root.setLevel(level) 28 | root.addHandler(handler) 29 | 30 | def parse_args(argv): 31 | if argv is None: 32 | argv = sys.argv[1:] 33 | p = optparse.OptionParser() 34 | 35 | cmdlist = [] 36 | for cmd, (f, usage) in sorted(cmds.iteritems()): 37 | cmdlist.append('%-8s\t%%prog %s' % (cmd, usage)) 38 | cmdlist = '\n '.join(cmdlist) 39 | 40 | p.usage = '%%prog [options] +\n\nExamples:\n %s' % cmdlist 41 | p.add_option('-d', '--debug', action='store_true', 42 | default=False, help="debug mode") 43 | p.add_option('-g', '--geoip', action='store_true', 44 | default=False, help='test with C GeoIP module') 45 | p.add_option('-w', '--write-dat', help='write filename.dat') 46 | opts, args = p.parse_args(argv) 47 | 48 | #sanity check 49 | if not args or args[0] not in cmds: 50 | p.error('missing command. choose from: %s' % ' '.join(sorted(cmds))) 51 | 52 | return opts, args 53 | 54 | def gen_csv(f): 55 | """peek at rows from a csv and start yielding when we get past the comments 56 | to a row that starts with an int (split at : to check IPv6)""" 57 | def startswith_int(row): 58 | try: 59 | int(row[0].split(':', 1)[0]) 60 | return True 61 | except ValueError: 62 | return False 63 | 64 | cr = csv.reader(f) 65 | #return itertools.dropwhile(lambda x: not startswith_int(x), cr) 66 | return cr 67 | 68 | class RadixTreeNode(object): 69 | __slots__ = ['segment', 'lhs', 'rhs'] 70 | def __init__(self, segment): 71 | self.segment = segment 72 | self.lhs = None 73 | self.rhs = None 74 | 75 | 76 | class RadixTree(object): 77 | def __init__(self, debug=False): 78 | self.debug = False 79 | 80 | self.netcount = 0 81 | self.segments = [RadixTreeNode(0)] 82 | self.data_offsets = {} 83 | self.data_segments = [] 84 | self.cur_offset = 1 85 | 86 | def __setitem__(self, net, data): 87 | self.netcount += 1 88 | inet = int(net) 89 | node = self.segments[0] 90 | for depth in range(self.seek_depth, self.seek_depth - (net.prefixlen-1), -1): 91 | if inet & (1 << depth): 92 | if not node.rhs: 93 | node.rhs = RadixTreeNode(len(self.segments)) 94 | self.segments.append(node.rhs) 95 | node = node.rhs 96 | else: 97 | if not node.lhs: 98 | node.lhs = RadixTreeNode(len(self.segments)) 99 | self.segments.append(node.lhs) 100 | node = node.lhs 101 | 102 | if not data in self.data_offsets: 103 | self.data_offsets[data] = self.cur_offset 104 | enc_data = self.encode(*data) 105 | self.data_segments.append(enc_data) 106 | self.cur_offset += (len(enc_data)) 107 | 108 | if self.debug: 109 | #store net after data for easier debugging 110 | data = data, net 111 | 112 | if inet & (1 << self.seek_depth - (net.prefixlen-1)): 113 | node.rhs = data 114 | else: 115 | node.lhs = data 116 | 117 | def gen_nets(self, opts, args): 118 | raise NotImplementedError 119 | 120 | def load(self, opts, args): 121 | for nets, data in self.gen_nets(opts, args): 122 | for net in nets: 123 | self[net] = data 124 | 125 | def dump_node(self, node): 126 | if not node: 127 | #empty leaf 128 | return '--' 129 | elif isinstance(node, RadixTreeNode): 130 | #internal node 131 | return node.segment 132 | else: 133 | #data leaf 134 | data = node[0] if self.debug else node 135 | return '%d %s' % (len(self.segments) + self.data_offsets[data], node) 136 | 137 | def dump(self): 138 | for node in self.segments: 139 | print node.segment, [self.dump_node(node.lhs), self.dump_node(node.rhs)] 140 | 141 | def encode(self, *args): 142 | raise NotImplementedError 143 | 144 | def encode_rec(self, rec, reclen): 145 | """encode rec as 4-byte little-endian int, then truncate it to reclen""" 146 | assert(reclen <= 4) 147 | return struct.pack('= 2 ** (8 * self.segreclen): 164 | logging.warning('too many segments for final segment record size!') 165 | 166 | for node in self.segments: 167 | f.write(self.serialize_node(node.lhs)) 168 | f.write(self.serialize_node(node.rhs)) 169 | 170 | f.write(chr(42)) #So long, and thanks for all the fish! 171 | f.write(''.join(self.data_segments)) 172 | 173 | f.write('bat.bast') #.dat file comment - can be anything 174 | f.write(chr(0xFF) * 3) 175 | f.write(chr(self.edition)) 176 | f.write(self.encode_rec(len(self.segments), self.segreclen)) 177 | 178 | class ORGIPRadixTree(RadixTree): 179 | usage = '-w mmorg.dat mmorg_ip GeoIPORG.csv' 180 | cmd = 'mmorg_ip' 181 | seek_depth = 31 182 | edition = 5 183 | reclen = 4 184 | segreclen = 4 185 | 186 | def gen_nets(self, opts, args): 187 | for lo, hi, org in gen_csv(fileinput.input(args)): 188 | lo, hi = ipaddr.IPAddress(lo), ipaddr.IPAddress(hi) 189 | nets = ipaddr.summarize_address_range(lo, hi) 190 | #print 'lo %s - li %s - nets %s - org %s' % (lo, hi, nets, org) 191 | yield nets, (org,) 192 | 193 | def encode(self, data): 194 | return data + '\0' 195 | 196 | class ORGNetworkRadixTree(RadixTree): 197 | usage = '-w mmorg.dat mmorg_net GeoIPORG.csv' 198 | cmd = 'mmorg_net' 199 | seek_depth = 31 200 | edition = 5 201 | reclen = 4 202 | segreclen = 4 203 | 204 | def gen_nets(self, opts, args): 205 | for net, org in gen_csv(fileinput.input(args)): 206 | net = [ipaddr.IPNetwork(net)] 207 | yield net, (org,) 208 | 209 | def encode(self, data): 210 | return data + '\0' 211 | 212 | def build_dat(RTree, opts, args): 213 | tstart = time.time() 214 | r = RTree(debug=opts.debug) 215 | 216 | r.load(opts, args) 217 | 218 | if opts.debug: 219 | r.dump() 220 | 221 | with open(opts.write_dat, 'wb') as f: 222 | r.serialize(f) 223 | 224 | tstop = time.time() 225 | print 'wrote %d-node trie with %d networks (%d distinct labels) in %d seconds' % ( 226 | len(r.segments), r.netcount, len(r.data_offsets), tstop - tstart) 227 | 228 | 229 | rtrees = [ORGIPRadixTree, ORGNetworkRadixTree] 230 | cmds = dict((rtree.cmd, (partial(build_dat, rtree), rtree.usage)) for rtree in rtrees) 231 | 232 | def main(argv=None): 233 | global opts 234 | opts, args = parse_args(argv) 235 | init_logger(opts) 236 | logging.debug(opts) 237 | logging.debug(args) 238 | 239 | cmd = args.pop(0) 240 | cmd, usage = cmds[cmd] 241 | return cmd(opts, args) 242 | 243 | if __name__ == '__main__': 244 | rval = main() 245 | logging.shutdown() 246 | sys.exit(rval) 247 | -------------------------------------------------------------------------------- /test/organizations.csv: -------------------------------------------------------------------------------- 1 | 10.1.1.0/24,ORG1 2 | 10.10.0.0/16,ORG1 3 | 10.20.0.0/16,ORG2 4 | 10.1.3.0/24,ORG3 5 | 13.0.0.0/16,ORG3 6 | -------------------------------------------------------------------------------- /test/organizations.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cjheath/geoip/c3f42995898f1ac92e948d5102d60e2888da25ca/test/organizations.dat -------------------------------------------------------------------------------- /test/test_file.rb: -------------------------------------------------------------------------------- 1 | # 2 | # This program walks the specified file and dumps it in IP order 3 | # 4 | require 'geoip' 5 | 6 | ARGV.each do |file| 7 | g = GeoIP.new(file) 8 | g.each_by_ip do |ip, val| 9 | ip_str = 10 | if ip >= (1<<32) 11 | (('%032X'%ip).scan(/..../)*':').sub(/\A(0000:)+/, '::') # An IPv6 address 12 | else 13 | '%d.%d.%d.%d' % [ip].pack('N').unpack('C4') 14 | end 15 | puts "#{ip_str}\t#{val ? val.to_hash.to_a.sort.map{|n,v| "#{n}=#{v.inspect}"}*', ' : 'Unassigned'}" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/test_geoip.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/test_helper.rb' 2 | 3 | class TestGeoip < Minitest::Test 4 | 5 | def setup 6 | data = '/usr/share/GeoIP/GeoIP.dat' 7 | begin 8 | @g = GeoIP.new(data) 9 | rescue Errno::ENOENT => e 10 | skip e.message 11 | end 12 | end 13 | 14 | def test_constructor 15 | assert_instance_of GeoIP, @g 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | require 'minitest/autorun' 3 | require File.dirname(__FILE__) + '/../lib/geoip' 4 | 5 | if Minitest.const_defined?('Test') 6 | # We're on Minitest 5+. Nothing to do here. 7 | else 8 | # Minitest 4 doesn't have Minitest::Test yet. 9 | Minitest::Test = MiniTest::Unit::TestCase 10 | end 11 | -------------------------------------------------------------------------------- /website/index.txt: -------------------------------------------------------------------------------- 1 | h1. geoip 2 | 3 | h2. 'Geographic info for an IP address' 4 | 5 | h2. What 6 | 7 | GeoIP searches a GeoIP database from http://maxmind.com for a given host or IP address, 8 | and returns information about the country, city and/or ISP for that IP address, 9 | depending on the database version. 10 | 11 | h2. Installing 12 | 13 |
sudo gem install geoip
14 | 15 | h2. Locations 16 | 17 | "http://github.com/cjheath/geoip":http://github.com/cjheath/geoip 18 |
19 | "http://geoip.rubyforge.org/":http://geoip.rubyforge.org/ 20 | 21 | h2. Prerequisites 22 | 23 | You need one of the free GeoLite country, city or ASN databases, or a subscription database version. 24 | The last known download locations for the GeoLite database versions are 25 | "http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz":http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz 26 | "http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz":http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz 27 | "http://geolite.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz":http://geolite.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz 28 | 29 | This API requires the database to be decompressed for searching. 30 | 31 | h2. Example 32 | 33 |
34 |     require 'geoip'
35 |     c = GeoIP.new('GeoIP.dat').country('www.nokia.com')
36 |     => ["www.nokia.com", "147.243.3.83", 69, "FI", "FIN", "Finland", "EU"]
37 |     c.country_code3
38 |     => "FIN"
39 |     c.to_hash
40 |     => {:country_code3=>"FIN", :country_name=>"Finland", :continent_code=>"EU",
41 |     :request=>"www.nokia.com", :country_code=>69, :country_code2=>"FI", :ip=>"147.243.3.83"}
42 |     
43 | c = GeoIP.new('GeoLiteCity.dat').city('github.com') 44 | => ["github.com", "207.97.227.239", "US", "USA", "United States", "NA", "CA", 45 | "San Francisco", "94110", 37.7484, -122.4156, 807, 415, "America/Los_Angeles"] 46 | >> c.longitude 47 | => -122.4156 48 | >> c.timezone 49 | => "America/Los_Angeles" 50 |
51 | c = GeoIP.new('GeoIPASNum.dat').asn("www.fsb.ru") 52 | => ["AS8342", "RTComm.RU Autonomous System"] 53 |
54 | 55 | h2. Source Repository 56 | 57 | The trunk repository is http://github.com/cjheath/geoip 58 | 59 | h2. License 60 | 61 | I don't normally use the GPL license, but this one is derived 62 | from Maxmind's code, so I use the license they use. 63 | 64 | This version Copyright (C) 2005 Clifford Heath 65 | Derived from the C version, Copyright (C) 2003 MaxMind LLC 66 | 67 | This library is free software; you can redistribute it and/or 68 | modify it under the terms of the GNU Lesser General Public 69 | License as published by the Free Software Foundation; either 70 | version 2.1 of the License, or (at your option) any later version. 71 | 72 | This library is distributed in the hope that it will be useful, 73 | but WITHOUT ANY WARRANTY; without even the implied warranty of 74 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 75 | Lesser General Public License for more details. 76 | 77 | You should have received a copy of the GNU Lesser General Public 78 | License along with this library; if not, write to the Free Software 79 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 80 | -------------------------------------------------------------------------------- /website/javascripts/rounded_corners_lite.inc.js: -------------------------------------------------------------------------------- 1 | 2 | /**************************************************************** 3 | * * 4 | * curvyCorners * 5 | * ------------ * 6 | * * 7 | * This script generates rounded corners for your divs. * 8 | * * 9 | * Version 1.2.9 * 10 | * Copyright (c) 2006 Cameron Cooke * 11 | * By: Cameron Cooke and Tim Hutchison. * 12 | * * 13 | * * 14 | * Website: http://www.curvycorners.net * 15 | * Email: info@totalinfinity.com * 16 | * Forum: http://www.curvycorners.net/forum/ * 17 | * * 18 | * * 19 | * This library is free software; you can redistribute * 20 | * it and/or modify it under the terms of the GNU * 21 | * Lesser General Public License as published by the * 22 | * Free Software Foundation; either version 2.1 of the * 23 | * License, or (at your option) any later version. * 24 | * * 25 | * This library is distributed in the hope that it will * 26 | * be useful, but WITHOUT ANY WARRANTY; without even the * 27 | * implied warranty of MERCHANTABILITY or FITNESS FOR A * 28 | * PARTICULAR PURPOSE. See the GNU Lesser General Public * 29 | * License for more details. * 30 | * * 31 | * You should have received a copy of the GNU Lesser * 32 | * General Public License along with this library; * 33 | * Inc., 59 Temple Place, Suite 330, Boston, * 34 | * MA 02111-1307 USA * 35 | * * 36 | ****************************************************************/ 37 | 38 | var isIE = navigator.userAgent.toLowerCase().indexOf("msie") > -1; var isMoz = document.implementation && document.implementation.createDocument; var isSafari = ((navigator.userAgent.toLowerCase().indexOf('safari')!=-1)&&(navigator.userAgent.toLowerCase().indexOf('mac')!=-1))?true:false; function curvyCorners() 39 | { if(typeof(arguments[0]) != "object") throw newCurvyError("First parameter of curvyCorners() must be an object."); if(typeof(arguments[1]) != "object" && typeof(arguments[1]) != "string") throw newCurvyError("Second parameter of curvyCorners() must be an object or a class name."); if(typeof(arguments[1]) == "string") 40 | { var startIndex = 0; var boxCol = getElementsByClass(arguments[1]);} 41 | else 42 | { var startIndex = 1; var boxCol = arguments;} 43 | var curvyCornersCol = new Array(); if(arguments[0].validTags) 44 | var validElements = arguments[0].validTags; else 45 | var validElements = ["div"]; for(var i = startIndex, j = boxCol.length; i < j; i++) 46 | { var currentTag = boxCol[i].tagName.toLowerCase(); if(inArray(validElements, currentTag) !== false) 47 | { curvyCornersCol[curvyCornersCol.length] = new curvyObject(arguments[0], boxCol[i]);} 48 | } 49 | this.objects = curvyCornersCol; this.applyCornersToAll = function() 50 | { for(var x = 0, k = this.objects.length; x < k; x++) 51 | { this.objects[x].applyCorners();} 52 | } 53 | } 54 | function curvyObject() 55 | { this.box = arguments[1]; this.settings = arguments[0]; this.topContainer = null; this.bottomContainer = null; this.masterCorners = new Array(); this.contentDIV = null; var boxHeight = get_style(this.box, "height", "height"); var boxWidth = get_style(this.box, "width", "width"); var borderWidth = get_style(this.box, "borderTopWidth", "border-top-width"); var borderColour = get_style(this.box, "borderTopColor", "border-top-color"); var boxColour = get_style(this.box, "backgroundColor", "background-color"); var backgroundImage = get_style(this.box, "backgroundImage", "background-image"); var boxPosition = get_style(this.box, "position", "position"); var boxPadding = get_style(this.box, "paddingTop", "padding-top"); this.boxHeight = parseInt(((boxHeight != "" && boxHeight != "auto" && boxHeight.indexOf("%") == -1)? boxHeight.substring(0, boxHeight.indexOf("px")) : this.box.scrollHeight)); this.boxWidth = parseInt(((boxWidth != "" && boxWidth != "auto" && boxWidth.indexOf("%") == -1)? boxWidth.substring(0, boxWidth.indexOf("px")) : this.box.scrollWidth)); this.borderWidth = parseInt(((borderWidth != "" && borderWidth.indexOf("px") !== -1)? borderWidth.slice(0, borderWidth.indexOf("px")) : 0)); this.boxColour = format_colour(boxColour); this.boxPadding = parseInt(((boxPadding != "" && boxPadding.indexOf("px") !== -1)? boxPadding.slice(0, boxPadding.indexOf("px")) : 0)); this.borderColour = format_colour(borderColour); this.borderString = this.borderWidth + "px" + " solid " + this.borderColour; this.backgroundImage = ((backgroundImage != "none")? backgroundImage : ""); this.boxContent = this.box.innerHTML; if(boxPosition != "absolute") this.box.style.position = "relative"; this.box.style.padding = "0px"; if(isIE && boxWidth == "auto" && boxHeight == "auto") this.box.style.width = "100%"; if(this.settings.autoPad == true && this.boxPadding > 0) 56 | this.box.innerHTML = ""; this.applyCorners = function() 57 | { for(var t = 0; t < 2; t++) 58 | { switch(t) 59 | { case 0: 60 | if(this.settings.tl || this.settings.tr) 61 | { var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var topMaxRadius = Math.max(this.settings.tl ? this.settings.tl.radius : 0, this.settings.tr ? this.settings.tr.radius : 0); newMainContainer.style.height = topMaxRadius + "px"; newMainContainer.style.top = 0 - topMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.topContainer = this.box.appendChild(newMainContainer);} 62 | break; case 1: 63 | if(this.settings.bl || this.settings.br) 64 | { var newMainContainer = document.createElement("DIV"); newMainContainer.style.width = "100%"; newMainContainer.style.fontSize = "1px"; newMainContainer.style.overflow = "hidden"; newMainContainer.style.position = "absolute"; newMainContainer.style.paddingLeft = this.borderWidth + "px"; newMainContainer.style.paddingRight = this.borderWidth + "px"; var botMaxRadius = Math.max(this.settings.bl ? this.settings.bl.radius : 0, this.settings.br ? this.settings.br.radius : 0); newMainContainer.style.height = botMaxRadius + "px"; newMainContainer.style.bottom = 0 - botMaxRadius + "px"; newMainContainer.style.left = 0 - this.borderWidth + "px"; this.bottomContainer = this.box.appendChild(newMainContainer);} 65 | break;} 66 | } 67 | if(this.topContainer) this.box.style.borderTopWidth = "0px"; if(this.bottomContainer) this.box.style.borderBottomWidth = "0px"; var corners = ["tr", "tl", "br", "bl"]; for(var i in corners) 68 | { if(i > -1 < 4) 69 | { var cc = corners[i]; if(!this.settings[cc]) 70 | { if(((cc == "tr" || cc == "tl") && this.topContainer != null) || ((cc == "br" || cc == "bl") && this.bottomContainer != null)) 71 | { var newCorner = document.createElement("DIV"); newCorner.style.position = "relative"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; if(this.backgroundImage == "") 72 | newCorner.style.backgroundColor = this.boxColour; else 73 | newCorner.style.backgroundImage = this.backgroundImage; switch(cc) 74 | { case "tl": 75 | newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.tr.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.left = -this.borderWidth + "px"; break; case "tr": 76 | newCorner.style.height = topMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.tl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderTop = this.borderString; newCorner.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; newCorner.style.left = this.borderWidth + "px"; break; case "bl": 77 | newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginRight = this.settings.br.radius - (this.borderWidth*2) + "px"; newCorner.style.borderLeft = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = -this.borderWidth + "px"; newCorner.style.backgroundPosition = "-" + (this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break; case "br": 78 | newCorner.style.height = botMaxRadius - this.borderWidth + "px"; newCorner.style.marginLeft = this.settings.bl.radius - (this.borderWidth*2) + "px"; newCorner.style.borderRight = this.borderString; newCorner.style.borderBottom = this.borderString; newCorner.style.left = this.borderWidth + "px" 79 | newCorner.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidth)) + "px"; break;} 80 | } 81 | } 82 | else 83 | { if(this.masterCorners[this.settings[cc].radius]) 84 | { var newCorner = this.masterCorners[this.settings[cc].radius].cloneNode(true);} 85 | else 86 | { var newCorner = document.createElement("DIV"); newCorner.style.height = this.settings[cc].radius + "px"; newCorner.style.width = this.settings[cc].radius + "px"; newCorner.style.position = "absolute"; newCorner.style.fontSize = "1px"; newCorner.style.overflow = "hidden"; var borderRadius = parseInt(this.settings[cc].radius - this.borderWidth); for(var intx = 0, j = this.settings[cc].radius; intx < j; intx++) 87 | { if((intx +1) >= borderRadius) 88 | var y1 = -1; else 89 | var y1 = (Math.floor(Math.sqrt(Math.pow(borderRadius, 2) - Math.pow((intx+1), 2))) - 1); if(borderRadius != j) 90 | { if((intx) >= borderRadius) 91 | var y2 = -1; else 92 | var y2 = Math.ceil(Math.sqrt(Math.pow(borderRadius,2) - Math.pow(intx, 2))); if((intx+1) >= j) 93 | var y3 = -1; else 94 | var y3 = (Math.floor(Math.sqrt(Math.pow(j ,2) - Math.pow((intx+1), 2))) - 1);} 95 | if((intx) >= j) 96 | var y4 = -1; else 97 | var y4 = Math.ceil(Math.sqrt(Math.pow(j ,2) - Math.pow(intx, 2))); if(y1 > -1) this.drawPixel(intx, 0, this.boxColour, 100, (y1+1), newCorner, -1, this.settings[cc].radius); if(borderRadius != j) 98 | { for(var inty = (y1 + 1); inty < y2; inty++) 99 | { if(this.settings.antiAlias) 100 | { if(this.backgroundImage != "") 101 | { var borderFract = (pixelFraction(intx, inty, borderRadius) * 100); if(borderFract < 30) 102 | { this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, 0, this.settings[cc].radius);} 103 | else 104 | { this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, -1, this.settings[cc].radius);} 105 | } 106 | else 107 | { var pixelcolour = BlendColour(this.boxColour, this.borderColour, pixelFraction(intx, inty, borderRadius)); this.drawPixel(intx, inty, pixelcolour, 100, 1, newCorner, 0, this.settings[cc].radius, cc);} 108 | } 109 | } 110 | if(this.settings.antiAlias) 111 | { if(y3 >= y2) 112 | { if (y2 == -1) y2 = 0; this.drawPixel(intx, y2, this.borderColour, 100, (y3 - y2 + 1), newCorner, 0, 0);} 113 | } 114 | else 115 | { if(y3 >= y1) 116 | { this.drawPixel(intx, (y1 + 1), this.borderColour, 100, (y3 - y1), newCorner, 0, 0);} 117 | } 118 | var outsideColour = this.borderColour;} 119 | else 120 | { var outsideColour = this.boxColour; var y3 = y1;} 121 | if(this.settings.antiAlias) 122 | { for(var inty = (y3 + 1); inty < y4; inty++) 123 | { this.drawPixel(intx, inty, outsideColour, (pixelFraction(intx, inty , j) * 100), 1, newCorner, ((this.borderWidth > 0)? 0 : -1), this.settings[cc].radius);} 124 | } 125 | } 126 | this.masterCorners[this.settings[cc].radius] = newCorner.cloneNode(true);} 127 | if(cc != "br") 128 | { for(var t = 0, k = newCorner.childNodes.length; t < k; t++) 129 | { var pixelBar = newCorner.childNodes[t]; var pixelBarTop = parseInt(pixelBar.style.top.substring(0, pixelBar.style.top.indexOf("px"))); var pixelBarLeft = parseInt(pixelBar.style.left.substring(0, pixelBar.style.left.indexOf("px"))); var pixelBarHeight = parseInt(pixelBar.style.height.substring(0, pixelBar.style.height.indexOf("px"))); if(cc == "tl" || cc == "bl"){ pixelBar.style.left = this.settings[cc].radius -pixelBarLeft -1 + "px";} 130 | if(cc == "tr" || cc == "tl"){ pixelBar.style.top = this.settings[cc].radius -pixelBarHeight -pixelBarTop + "px";} 131 | switch(cc) 132 | { case "tr": 133 | pixelBar.style.backgroundPosition = "-" + Math.abs((this.boxWidth - this.settings[cc].radius + this.borderWidth) + pixelBarLeft) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "tl": 134 | pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px"; break; case "bl": 135 | pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs((this.boxHeight + this.settings[cc].radius + pixelBarTop) -this.borderWidth) + "px"; break;} 136 | } 137 | } 138 | } 139 | if(newCorner) 140 | { switch(cc) 141 | { case "tl": 142 | if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "tr": 143 | if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.topContainer) this.topContainer.appendChild(newCorner); break; case "bl": 144 | if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break; case "br": 145 | if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); break;} 146 | } 147 | } 148 | } 149 | var radiusDiff = new Array(); radiusDiff["t"] = Math.abs(this.settings.tl.radius - this.settings.tr.radius) 150 | radiusDiff["b"] = Math.abs(this.settings.bl.radius - this.settings.br.radius); for(z in radiusDiff) 151 | { if(z == "t" || z == "b") 152 | { if(radiusDiff[z]) 153 | { var smallerCornerType = ((this.settings[z + "l"].radius < this.settings[z + "r"].radius)? z +"l" : z +"r"); var newFiller = document.createElement("DIV"); newFiller.style.height = radiusDiff[z] + "px"; newFiller.style.width = this.settings[smallerCornerType].radius+ "px" 154 | newFiller.style.position = "absolute"; newFiller.style.fontSize = "1px"; newFiller.style.overflow = "hidden"; newFiller.style.backgroundColor = this.boxColour; switch(smallerCornerType) 155 | { case "tl": 156 | newFiller.style.bottom = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.topContainer.appendChild(newFiller); break; case "tr": 157 | newFiller.style.bottom = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.topContainer.appendChild(newFiller); break; case "bl": 158 | newFiller.style.top = "0px"; newFiller.style.left = "0px"; newFiller.style.borderLeft = this.borderString; this.bottomContainer.appendChild(newFiller); break; case "br": 159 | newFiller.style.top = "0px"; newFiller.style.right = "0px"; newFiller.style.borderRight = this.borderString; this.bottomContainer.appendChild(newFiller); break;} 160 | } 161 | var newFillerBar = document.createElement("DIV"); newFillerBar.style.position = "relative"; newFillerBar.style.fontSize = "1px"; newFillerBar.style.overflow = "hidden"; newFillerBar.style.backgroundColor = this.boxColour; newFillerBar.style.backgroundImage = this.backgroundImage; switch(z) 162 | { case "t": 163 | if(this.topContainer) 164 | { if(this.settings.tl.radius && this.settings.tr.radius) 165 | { newFillerBar.style.height = topMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.tl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.tr.radius - this.borderWidth + "px"; newFillerBar.style.borderTop = this.borderString; if(this.backgroundImage != "") 166 | newFillerBar.style.backgroundPosition = "-" + (topMaxRadius + this.borderWidth) + "px 0px"; this.topContainer.appendChild(newFillerBar);} 167 | this.box.style.backgroundPosition = "0px -" + (topMaxRadius - this.borderWidth) + "px";} 168 | break; case "b": 169 | if(this.bottomContainer) 170 | { if(this.settings.bl.radius && this.settings.br.radius) 171 | { newFillerBar.style.height = botMaxRadius - this.borderWidth + "px"; newFillerBar.style.marginLeft = this.settings.bl.radius - this.borderWidth + "px"; newFillerBar.style.marginRight = this.settings.br.radius - this.borderWidth + "px"; newFillerBar.style.borderBottom = this.borderString; if(this.backgroundImage != "") 172 | newFillerBar.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidth) + "px -" + (this.boxHeight + (topMaxRadius + this.borderWidth)) + "px"; this.bottomContainer.appendChild(newFillerBar);} 173 | } 174 | break;} 175 | } 176 | } 177 | if(this.settings.autoPad == true && this.boxPadding > 0) 178 | { var contentContainer = document.createElement("DIV"); contentContainer.style.position = "relative"; contentContainer.innerHTML = this.boxContent; contentContainer.className = "autoPadDiv"; var topPadding = Math.abs(topMaxRadius - this.boxPadding); var botPadding = Math.abs(botMaxRadius - this.boxPadding); if(topMaxRadius < this.boxPadding) 179 | contentContainer.style.paddingTop = topPadding + "px"; if(botMaxRadius < this.boxPadding) 180 | contentContainer.style.paddingBottom = botMaxRadius + "px"; contentContainer.style.paddingLeft = this.boxPadding + "px"; contentContainer.style.paddingRight = this.boxPadding + "px"; this.contentDIV = this.box.appendChild(contentContainer);} 181 | } 182 | this.drawPixel = function(intx, inty, colour, transAmount, height, newCorner, image, cornerRadius) 183 | { var pixel = document.createElement("DIV"); pixel.style.height = height + "px"; pixel.style.width = "1px"; pixel.style.position = "absolute"; pixel.style.fontSize = "1px"; pixel.style.overflow = "hidden"; var topMaxRadius = Math.max(this.settings["tr"].radius, this.settings["tl"].radius); if(image == -1 && this.backgroundImage != "") 184 | { pixel.style.backgroundImage = this.backgroundImage; pixel.style.backgroundPosition = "-" + (this.boxWidth - (cornerRadius - intx) + this.borderWidth) + "px -" + ((this.boxHeight + topMaxRadius + inty) -this.borderWidth) + "px";} 185 | else 186 | { pixel.style.backgroundColor = colour;} 187 | if (transAmount != 100) 188 | setOpacity(pixel, transAmount); pixel.style.top = inty + "px"; pixel.style.left = intx + "px"; newCorner.appendChild(pixel);} 189 | } 190 | function insertAfter(parent, node, referenceNode) 191 | { parent.insertBefore(node, referenceNode.nextSibling);} 192 | function BlendColour(Col1, Col2, Col1Fraction) 193 | { var red1 = parseInt(Col1.substr(1,2),16); var green1 = parseInt(Col1.substr(3,2),16); var blue1 = parseInt(Col1.substr(5,2),16); var red2 = parseInt(Col2.substr(1,2),16); var green2 = parseInt(Col2.substr(3,2),16); var blue2 = parseInt(Col2.substr(5,2),16); if(Col1Fraction > 1 || Col1Fraction < 0) Col1Fraction = 1; var endRed = Math.round((red1 * Col1Fraction) + (red2 * (1 - Col1Fraction))); if(endRed > 255) endRed = 255; if(endRed < 0) endRed = 0; var endGreen = Math.round((green1 * Col1Fraction) + (green2 * (1 - Col1Fraction))); if(endGreen > 255) endGreen = 255; if(endGreen < 0) endGreen = 0; var endBlue = Math.round((blue1 * Col1Fraction) + (blue2 * (1 - Col1Fraction))); if(endBlue > 255) endBlue = 255; if(endBlue < 0) endBlue = 0; return "#" + IntToHex(endRed)+ IntToHex(endGreen)+ IntToHex(endBlue);} 194 | function IntToHex(strNum) 195 | { base = strNum / 16; rem = strNum % 16; base = base - (rem / 16); baseS = MakeHex(base); remS = MakeHex(rem); return baseS + '' + remS;} 196 | function MakeHex(x) 197 | { if((x >= 0) && (x <= 9)) 198 | { return x;} 199 | else 200 | { switch(x) 201 | { case 10: return "A"; case 11: return "B"; case 12: return "C"; case 13: return "D"; case 14: return "E"; case 15: return "F";} 202 | } 203 | } 204 | function pixelFraction(x, y, r) 205 | { var pixelfraction = 0; var xvalues = new Array(1); var yvalues = new Array(1); var point = 0; var whatsides = ""; var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x,2))); if ((intersect >= y) && (intersect < (y+1))) 206 | { whatsides = "Left"; xvalues[point] = 0; yvalues[point] = intersect - y; point = point + 1;} 207 | var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y+1,2))); if ((intersect >= x) && (intersect < (x+1))) 208 | { whatsides = whatsides + "Top"; xvalues[point] = intersect - x; yvalues[point] = 1; point = point + 1;} 209 | var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x+1,2))); if ((intersect >= y) && (intersect < (y+1))) 210 | { whatsides = whatsides + "Right"; xvalues[point] = 1; yvalues[point] = intersect - y; point = point + 1;} 211 | var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y,2))); if ((intersect >= x) && (intersect < (x+1))) 212 | { whatsides = whatsides + "Bottom"; xvalues[point] = intersect - x; yvalues[point] = 0;} 213 | switch (whatsides) 214 | { case "LeftRight": 215 | pixelfraction = Math.min(yvalues[0],yvalues[1]) + ((Math.max(yvalues[0],yvalues[1]) - Math.min(yvalues[0],yvalues[1]))/2); break; case "TopRight": 216 | pixelfraction = 1-(((1-xvalues[0])*(1-yvalues[1]))/2); break; case "TopBottom": 217 | pixelfraction = Math.min(xvalues[0],xvalues[1]) + ((Math.max(xvalues[0],xvalues[1]) - Math.min(xvalues[0],xvalues[1]))/2); break; case "LeftBottom": 218 | pixelfraction = (yvalues[0]*xvalues[1])/2; break; default: 219 | pixelfraction = 1;} 220 | return pixelfraction;} 221 | function rgb2Hex(rgbColour) 222 | { try{ var rgbArray = rgb2Array(rgbColour); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); var hexColour = "#" + IntToHex(red) + IntToHex(green) + IntToHex(blue);} 223 | catch(e){ alert("There was an error converting the RGB value to Hexadecimal in function rgb2Hex");} 224 | return hexColour;} 225 | function rgb2Array(rgbColour) 226 | { var rgbValues = rgbColour.substring(4, rgbColour.indexOf(")")); var rgbArray = rgbValues.split(", "); return rgbArray;} 227 | function setOpacity(obj, opacity) 228 | { opacity = (opacity == 100)?99.999:opacity; if(isSafari && obj.tagName != "IFRAME") 229 | { var rgbArray = rgb2Array(obj.style.backgroundColor); var red = parseInt(rgbArray[0]); var green = parseInt(rgbArray[1]); var blue = parseInt(rgbArray[2]); obj.style.backgroundColor = "rgba(" + red + ", " + green + ", " + blue + ", " + opacity/100 + ")";} 230 | else if(typeof(obj.style.opacity) != "undefined") 231 | { obj.style.opacity = opacity/100;} 232 | else if(typeof(obj.style.MozOpacity) != "undefined") 233 | { obj.style.MozOpacity = opacity/100;} 234 | else if(typeof(obj.style.filter) != "undefined") 235 | { obj.style.filter = "alpha(opacity:" + opacity + ")";} 236 | else if(typeof(obj.style.KHTMLOpacity) != "undefined") 237 | { obj.style.KHTMLOpacity = opacity/100;} 238 | } 239 | function inArray(array, value) 240 | { for(var i = 0; i < array.length; i++){ if (array[i] === value) return i;} 241 | return false;} 242 | function inArrayKey(array, value) 243 | { for(key in array){ if(key === value) return true;} 244 | return false;} 245 | function addEvent(elm, evType, fn, useCapture) { if (elm.addEventListener) { elm.addEventListener(evType, fn, useCapture); return true;} 246 | else if (elm.attachEvent) { var r = elm.attachEvent('on' + evType, fn); return r;} 247 | else { elm['on' + evType] = fn;} 248 | } 249 | function removeEvent(obj, evType, fn, useCapture){ if (obj.removeEventListener){ obj.removeEventListener(evType, fn, useCapture); return true;} else if (obj.detachEvent){ var r = obj.detachEvent("on"+evType, fn); return r;} else { alert("Handler could not be removed");} 250 | } 251 | function format_colour(colour) 252 | { var returnColour = "#ffffff"; if(colour != "" && colour != "transparent") 253 | { if(colour.substr(0, 3) == "rgb") 254 | { returnColour = rgb2Hex(colour);} 255 | else if(colour.length == 4) 256 | { returnColour = "#" + colour.substring(1, 2) + colour.substring(1, 2) + colour.substring(2, 3) + colour.substring(2, 3) + colour.substring(3, 4) + colour.substring(3, 4);} 257 | else 258 | { returnColour = colour;} 259 | } 260 | return returnColour;} 261 | function get_style(obj, property, propertyNS) 262 | { try 263 | { if(obj.currentStyle) 264 | { var returnVal = eval("obj.currentStyle." + property);} 265 | else 266 | { if(isSafari && obj.style.display == "none") 267 | { obj.style.display = ""; var wasHidden = true;} 268 | var returnVal = document.defaultView.getComputedStyle(obj, '').getPropertyValue(propertyNS); if(isSafari && wasHidden) 269 | { obj.style.display = "none";} 270 | } 271 | } 272 | catch(e) 273 | { } 274 | return returnVal;} 275 | function getElementsByClass(searchClass, node, tag) 276 | { var classElements = new Array(); if(node == null) 277 | node = document; if(tag == null) 278 | tag = '*'; var els = node.getElementsByTagName(tag); var elsLen = els.length; var pattern = new RegExp("(^|\s)"+searchClass+"(\s|$)"); for (i = 0, j = 0; i < elsLen; i++) 279 | { if(pattern.test(els[i].className)) 280 | { classElements[j] = els[i]; j++;} 281 | } 282 | return classElements;} 283 | function newCurvyError(errorMessage) 284 | { return new Error("curvyCorners Error:\n" + errorMessage) 285 | } 286 | -------------------------------------------------------------------------------- /website/stylesheets/screen.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #E1D1F1; 3 | font-family: "Georgia", sans-serif; 4 | font-size: 16px; 5 | line-height: 1.6em; 6 | padding: 1.6em 0 0 0; 7 | color: #333; 8 | } 9 | h1, h2, h3, h4, h5, h6 { 10 | color: #444; 11 | } 12 | h1 { 13 | font-family: sans-serif; 14 | font-weight: normal; 15 | font-size: 4em; 16 | line-height: 0.8em; 17 | letter-spacing: -0.1ex; 18 | margin: 5px; 19 | } 20 | li { 21 | padding: 0; 22 | margin: 0; 23 | list-style-type: square; 24 | } 25 | a { 26 | color: #5E5AFF; 27 | background-color: #DAC; 28 | font-weight: normal; 29 | text-decoration: underline; 30 | } 31 | blockquote { 32 | font-size: 90%; 33 | font-style: italic; 34 | border-left: 1px solid #111; 35 | padding-left: 1em; 36 | } 37 | .caps { 38 | font-size: 80%; 39 | } 40 | 41 | #main { 42 | width: 45em; 43 | padding: 0; 44 | margin: 0 auto; 45 | } 46 | .coda { 47 | text-align: right; 48 | color: #77f; 49 | font-size: smaller; 50 | } 51 | 52 | table { 53 | font-size: 90%; 54 | line-height: 1.4em; 55 | color: #ff8; 56 | background-color: #111; 57 | padding: 2px 10px 2px 10px; 58 | border-style: dashed; 59 | } 60 | 61 | th { 62 | color: #fff; 63 | } 64 | 65 | td { 66 | padding: 2px 10px 2px 10px; 67 | } 68 | 69 | .success { 70 | color: #0CC52B; 71 | } 72 | 73 | .failed { 74 | color: #E90A1B; 75 | } 76 | 77 | .unknown { 78 | color: #995000; 79 | } 80 | pre, code { 81 | font-family: monospace; 82 | font-size: 90%; 83 | line-height: 1.4em; 84 | color: #ff8; 85 | background-color: #111; 86 | padding: 2px 10px 2px 10px; 87 | } 88 | .comment { color: #aaa; font-style: italic; } 89 | .keyword { color: #eff; font-weight: bold; } 90 | .punct { color: #eee; font-weight: bold; } 91 | .symbol { color: #0bb; } 92 | .string { color: #6b4; } 93 | .ident { color: #ff8; } 94 | .constant { color: #66f; } 95 | .regex { color: #ec6; } 96 | .number { color: #F99; } 97 | .expr { color: #227; } 98 | 99 | #version { 100 | float: right; 101 | text-align: right; 102 | font-family: sans-serif; 103 | font-weight: normal; 104 | background-color: #B3ABFF; 105 | color: #141331; 106 | padding: 15px 20px 10px 20px; 107 | margin: 0 auto; 108 | margin-top: 15px; 109 | border: 3px solid #141331; 110 | } 111 | 112 | #version .numbers { 113 | display: block; 114 | font-size: 4em; 115 | line-height: 0.8em; 116 | letter-spacing: -0.1ex; 117 | margin-bottom: 15px; 118 | } 119 | 120 | #version p { 121 | text-decoration: none; 122 | color: #141331; 123 | background-color: #B3ABFF; 124 | margin: 0; 125 | padding: 0; 126 | } 127 | 128 | #version a { 129 | text-decoration: none; 130 | color: #141331; 131 | background-color: #B3ABFF; 132 | } 133 | 134 | .clickable { 135 | cursor: pointer; 136 | cursor: hand; 137 | } 138 | 139 | -------------------------------------------------------------------------------- /website/template.rhtml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | <%= title %> 9 | 10 | 11 | 14 | 29 | 30 | 31 |
32 | 33 |

<%= title %>

34 |
35 |

Get Version

36 | <%= version %> 37 |
38 | <%= body %> 39 |

40 | Clifford Heath, <%= modified.pretty %>
41 | Theme extended from Paul Battley 42 |

43 |
44 | 45 | 46 | 47 | 48 | 49 | --------------------------------------------------------------------------------