├── .eslintrc.js ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── defaultResult.json ├── examples ├── connectInfo.js ├── connectSecurityAntiFakeCrawler.js ├── googleBot.js └── simpleUserAgentAndIp.js ├── index.js ├── package-lock.json ├── package.json ├── test ├── cache.js ├── clientBrowserChrome.js ├── clientBrowserChromeJson.js ├── clientBrowserChromeJsonFull.js ├── clientBrowserIEMobile.js ├── clientBrowserIEMobileJson.js ├── clientBrowserIEMobileJsonFull.js ├── clientCrawlerGoogleBot.js ├── clientCrawlerGoogleBotIp.js ├── clientCrawlerGoogleBotIpJson.js ├── clientCrawlerGoogleBotIpJsonFull.js ├── clientCrawlerGoogleBotJson.js ├── clientCrawlerGoogleBotJsonFull.js ├── clientCrawlerPingoMeter.js ├── clientCrawlerPingoMeterJson.js ├── clientCrawlerPingoMeterJsonFull.js ├── clientUnrecognized.js ├── clientUnrecognizedJson.js ├── clientUnrecognizedJsonFull.js ├── datacenterGoogleIpV4.js ├── datacenterGoogleIpV4Json.js ├── datacenterGoogleIpV4JsonFull.js ├── datacenterOvhCgiProxyIpV6.js ├── datacenterOvhCgiProxyIpV6Json.js ├── datacenterOvhCgiProxyIpV6JsonFull.js ├── datacenterOvhIpV6.js ├── datacenterOvhIpV6Json.js ├── datacenterOvhIpV6JsonFull.js ├── db │ └── udgerdb_v3_test.dat ├── dbConnect.js ├── helperGetDatabaseInfo.js ├── helperGetIPsClassification.js ├── helperGetUAClientsClassification.js ├── helperGetUACrawlersClassification.js ├── helperGetUACrawlersFamilies.js.bug ├── helperRandomIPv4.js ├── helperRandomUAClients.js ├── helperRandomUAClientsRegex.js ├── helperRandomUACrawlers.js ├── ip2long.js ├── lib │ └── config.js ├── setNoData.js ├── setString.js └── setUnknowAttribute.js └── utils.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "eslint:recommended", 3 | "env": { 4 | "node": true, 5 | "browser": true, 6 | "es6": true 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 2018, 10 | "sourceType": "module" 11 | }, 12 | "rules": { 13 | "linebreak-style":2, 14 | "comma-dangle": ["error", "only-multiline"], 15 | "comma-spacing": ["error", { "before": false, "after": true }], 16 | "eol-last": ["error", "always"], 17 | "indent": ["error", 4, { "MemberExpression": 1 }], 18 | "no-multiple-empty-lines": ["error"], 19 | "no-new-symbol": "error", 20 | "no-trailing-spaces": ["error"], 21 | "no-undef": ["error"], 22 | "no-unused-vars": ["error"], 23 | "object-curly-spacing": ["error", "always"], 24 | "object-shorthand": "error", 25 | "prefer-const": 2, 26 | "quotes": ["error", "single"], 27 | "semi": ["error", "always"], 28 | "space-in-parens": ["error", "never"], 29 | "strict": [2, "never"], 30 | "no-console":"off", 31 | "no-useless-escape":"off" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.nyc_output/ 2 | /node_modules/ 3 | /test/db/* 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | - "9" 5 | - "10" 6 | - "12" 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.5.0](https://github.com/udger/udger-nodejs/compare/v1.5.0...v1.4.0) - 2023-01-14 8 | ### Changed 9 | * packages update 10 | * fix #45 11 | 12 | ## [1.4.0](https://github.com/udger/udger-nodejs/compare/v1.4.0...v1.3.7) - 2022-03-02 13 | ### Changed 14 | * packages update and code changed 15 | * fix #44 16 | * fix #45 17 | 18 | ## [1.3.8](https://github.com/udger/udger-nodejs/compare/v1.3.8...v1.3.7) - 2020-09-31 19 | ### Changed 20 | * packages update 21 | 22 | ## [1.3.7](https://github.com/udger/udger-nodejs/compare/v1.3.7...v1.3.6) - 2019-06-11 23 | ### Changed 24 | * packages update 25 | * fix ip2long [#41](https://github.com/udger/udger-nodejs/issues/41) 26 | 27 | ## [1.3.6](https://github.com/udger/udger-nodejs/compare/v1.3.6...v1.3.5) - 2019-04-13 28 | ### Changed 29 | * packages update [#40](https://github.com/udger/udger-nodejs/issues/40) 30 | 31 | ## [1.3.5](https://github.com/udger/udger-nodejs/compare/v1.3.5...v1.3.4) - 2018-05-19 32 | ### Changed 33 | * packages update [#37](https://github.com/udger/udger-nodejs/issues/37) 34 | 35 | ## [1.3.4](https://github.com/udger/udger-nodejs/compare/v1.3.4...v1.3.3) - 2018-05-19 36 | ### Added 37 | * helper getDatabaseInfo [#33](https://github.com/udger/udger-nodejs/issues/33) 38 | 39 | ## [1.3.3](https://github.com/udger/udger-nodejs/compare/v1.3.3...v1.3.2) - 2018-05-08 40 | ### Changed 41 | * family are not unique [#31](https://github.com/udger/udger-nodejs/issues/31) 42 | * typo README [#30](https://github.com/udger/udger-nodejs/issues/30) 43 | 44 | ## [1.3.2](https://github.com/udger/udger-nodejs/compare/v1.3.2...v1.3.1) - 2018-05-08 45 | ### Changed 46 | * refactor helpers [#27](https://github.com/udger/udger-nodejs/issues/27) 47 | ### Added 48 | * add helpers [#28](https://github.com/udger/udger-nodejs/issues/28) 49 | 50 | ## [1.3.2](https://github.com/udger/udger-nodejs/compare/v1.3.1...v1.3.0) - 2018-05-08 51 | ### Changed 52 | * fix README.md [#26](https://github.com/udger/udger-nodejs/issues/26) 53 | 54 | ## [1.3.0](https://github.com/udger/udger-nodejs/compare/v1.3.0...v1.2.1) - 2018-05-08 55 | ### Added 56 | * implement helpers [#25](https://github.com/udger/udger-nodejs/issues/25) 57 | 58 | ## [1.2.1](https://github.com/udger/udger-nodejs/compare/v1.2.1...v1.2.0) - 2018-05-07 59 | ### Changed 60 | * update npm packages [#21](https://github.com/udger/udger-nodejs/issues/21) 61 | 62 | ## [1.2.0](https://github.com/udger/udger-nodejs/compare/v1.2.0...v1.0.1) - 2018-03-01 63 | ### Added 64 | * Missing features for udger database update [#19](https://github.com/udger/udger-nodejs/issues/19) 65 | 66 | ## [1.0.1](https://github.com/udger/udger-nodejs/compare/v1.0.1...v1.0.0) - 2017-12-01 67 | ### Fixed 68 | * defaultResult.json path [#18](https://github.com/udger/udger-nodejs/issues/18) 69 | 70 | ## 1.0.0 - 2017-12-01 71 | ### Added 72 | * Compact output JSON Format [#12](https://github.com/udger/udger-nodejs/issues/18) 73 | * Full output JSON Format, respecting Udger data [#12](https://github.com/udger/udger-nodejs/issues/18) 74 | 75 | ### Changed 76 | * Start following SemVer properly. 77 | 78 | ## 0.9.2 - 2017-11-29 - Beta 79 | ### Added 80 | * Tests 81 | 82 | ## 0.9.0 - 2017-11-28 - Alpha 83 | ### Added 84 | * Udger output format 85 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # udger-nodejs 2 | 3 | # Udger client for NodeJS (data ver. 3) 4 | Local parser is very fast and accurate useragent string detection solution. 5 | Enables developers to locally install and integrate a highly-scalable product. 6 | We provide the detection of the devices (personal computer, tablet, Smart TV, Game console etc.), 7 | operating system, client SW type (browser, e-mail client etc.) and devices market name 8 | (example: Sony Xperia Tablet S, Nokia Lumia 820 etc.). It also provides information about 9 | IP addresses (Public proxies, VPN services, Tor exit nodes, Fake crawlers, Web scrapers, 10 | Datacenter name .. etc.) 11 | 12 | - Tested with more the 50.000 unique user agents. 13 | - Up to date data provided by https://udger.com/ 14 | 15 | ## Requirements 16 | - nodejs >= 8.9.0 17 | - datafile v3 (udgerdb_v3.dat) from https://data.udger.com/ 18 | 19 | ## Features 20 | - Fast 21 | - LRU cache 22 | - Released under the MIT 23 | 24 | ## Install 25 | npm install udger-nodejs 26 | 27 | ## Usage 28 | You should review the included examples in `examples/` and `test/` directory. 29 | 30 | Here's a quick example: 31 | 32 | ```js 33 | const udgerParser = require('udger-nodejs')('./udgerdb_v3.dat'); 34 | 35 | udgerParser.set({ 36 | ua:'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 37 | ip:'2A02:598:7000:116:0:0:0:101' 38 | }); 39 | 40 | let ret = udgerParser.parse(); 41 | 42 | // beautify json output with 4 spaces indent 43 | console.log(JSON.stringify(ret, null, 4)); 44 | ``` 45 | 46 | Result 47 | 48 | ``` 49 | { 50 | "user_agent": { 51 | "ua_string": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36", 52 | "ua_class": "Browser", 53 | "ua_class_code": "browser", 54 | "ua": "Chrome 62.0.3202.94", 55 | "ua_version": "62.0.3202.94", 56 | "ua_version_major": "62", 57 | "ua_uptodate_current_version": "62", 58 | "ua_family": "Chrome", 59 | "ua_family_code": "chrome", 60 | "ua_family_homepage": "http://www.google.com/chrome/", 61 | "ua_family_vendor": "Google Inc.", 62 | "ua_family_vendor_code": "google_inc", 63 | "ua_family_vendor_homepage": "https://www.google.com/about/company/", 64 | "ua_family_icon": "chrome.png", 65 | "ua_family_icon_big": "chrome_big.png", 66 | "ua_family_info_url": "https://udger.com/resources/ua-list/browser-detail?browser=Chrome", 67 | "ua_engine": "WebKit/Blink", 68 | "os": "Windows 10", 69 | "os_code": "windows_10", 70 | "os_homepage": "https://en.wikipedia.org/wiki/Windows_10", 71 | "os_icon": "windows10.png", 72 | "os_icon_big": "windows10_big.png", 73 | "os_info_url": "https://udger.com/resources/ua-list/os-detail?os=Windows 10", 74 | "os_family": "Windows", 75 | "os_family_code": "windows", 76 | "os_family_vendor": "Microsoft Corporation.", 77 | "os_family_vendor_code": "microsoft_corporation", 78 | "os_family_vendor_homepage": "https://www.microsoft.com/about/", 79 | "device_class": "Desktop", 80 | "device_class_code": "desktop", 81 | "device_class_icon": "desktop.png", 82 | "device_class_icon_big": "desktop_big.png", 83 | "device_class_info_url": "https://udger.com/resources/ua-list/device-detail?device=Desktop", 84 | "device_marketname": "", 85 | "device_brand": "", 86 | "device_brand_code": "", 87 | "device_brand_homepage": "", 88 | "device_brand_icon": "", 89 | "device_brand_icon_big": "", 90 | "device_brand_info_url": "", 91 | "crawler_last_seen": "", 92 | "crawler_category": "", 93 | "crawler_category_code": "", 94 | "crawler_respect_robotstxt": "" 95 | }, 96 | "ip_address": { 97 | "ip": "2a02:598:7000:116:0:0:0:101", 98 | "ip_ver": 6, 99 | "ip_classification": "Crawler", 100 | "ip_classification_code": "crawler", 101 | "ip_hostname": "", 102 | "ip_last_seen": "2016-02-12 04:28:56", 103 | "ip_country": "Czech Republic", 104 | "ip_country_code": "CZ", 105 | "ip_city": "Prague", 106 | "crawler_name": "SeznamBot/3.2-test1", 107 | "crawler_ver": "3.2-test1", 108 | "crawler_ver_major": "3", 109 | "crawler_family": "SeznamBot", 110 | "crawler_family_code": "seznambot", 111 | "crawler_family_homepage": "http://napoveda.seznam.cz/en/seznambot-intro/", 112 | "crawler_family_vendor": "Seznam.cz, a.s.", 113 | "crawler_family_vendor_code": "seznam-cz_as", 114 | "crawler_family_vendor_homepage": "http://onas.seznam.cz/", 115 | "crawler_family_icon": "seznam.png", 116 | "crawler_family_info_url": "https://udger.com/resources/ua-list/bot-detail?bot=SeznamBot#id12590", 117 | "crawler_last_seen": "2016-08-31 15:19:38", 118 | "crawler_category": "Search engine bot", 119 | "crawler_category_code": "search_engine_bot", 120 | "crawler_respect_robotstxt": "yes", 121 | "datacenter_name": "Seznam.cz", 122 | "datacenter_name_code": "seznam_cz", 123 | "datacenter_homepage": "http://onas.seznam.cz/" 124 | }, 125 | "from_cache": false 126 | } 127 | ``` 128 | 129 | 130 | ## Result formats 131 | By default, JSON Udger format is used. The JSON Udger format is coming from the PHP Parser (compatibility) 132 | Others JSON format are available, by passing options in `udger.parse()` function. 133 | 134 | ### Native Udger JSON Format (default) 135 | 136 | #### Usage 137 | 138 | ```js 139 | udgerParser.parse(); 140 | ``` 141 | 142 | #### Result 143 | 144 | ``` 145 | { 146 | "ip_address": { 147 | "ip": "66.249.64.73", 148 | "ip_ver": 4, 149 | "ip_classification": "Crawler", 150 | "ip_classification_code": "crawler", 151 | "ip_hostname": "crawl-66-249-64-73.googlebot.com", 152 | "ip_last_seen": "2016-10-02 09:16:57", 153 | "ip_country": "United States", 154 | "ip_country_code": "US", 155 | "ip_city": "Mountain View", 156 | "crawler_name": "Googlebot/2.1", 157 | "crawler_ver": "2.1", 158 | "crawler_ver_major": "2", 159 | "crawler_family": "Googlebot", 160 | "crawler_family_code": "googlebot", 161 | "crawler_family_homepage": "http://www.google.com/bot.html", 162 | "crawler_family_vendor": "Google Inc.", 163 | "crawler_family_vendor_code": "google_inc", 164 | "crawler_family_vendor_homepage": "https://www.google.com/about/company/", 165 | "crawler_family_icon": "bot_googlebot.png", 166 | "crawler_family_info_url": "https://udger.com/resources/ua-list/bot-detail?bot=Googlebot#id31", 167 | "crawler_last_seen": "2017-01-06 17:52:46", 168 | "crawler_category": "Search engine bot", 169 | "crawler_category_code": "search_engine_bot", 170 | "crawler_respect_robotstxt": "yes", 171 | "datacenter_name": "Google sites", 172 | "datacenter_name_code": "googgle_sites", 173 | "datacenter_homepage": "http://sites.google.com/" 174 | } 175 | } 176 | ``` 177 | 178 | ### Compact JSON Format 179 | 180 | #### Usage 181 | 182 | ```js 183 | udgerParser.parse({json:true}); 184 | ``` 185 | 186 | #### Result 187 | 188 | ```js 189 | { 190 | "ipAddress": { 191 | "ip": "66.249.64.73", 192 | "classification": "crawler", 193 | "lastSeen": "2016-10-02 09:16:57", 194 | "hostname": "crawl-66-249-64-73.googlebot.com", 195 | "geo": { 196 | "country": { 197 | "name": "United States", 198 | "code": "US" 199 | }, 200 | "city": "Mountain View" 201 | }, 202 | "crawler": { 203 | "name": "Googlebot/2.1", 204 | "family": "googlebot", 205 | "category": "search_engine_bot", 206 | "lastSeen": "2017-01-06 17:52:46" 207 | }, 208 | "datacenter": "googgle_sites" 209 | } 210 | } 211 | ``` 212 | 213 | ### Full JSON Format 214 | 215 | #### Usage 216 | 217 | ```js 218 | udgerParser.parse({json:true, full:true}); 219 | ``` 220 | 221 | #### Result 222 | 223 | ```js 224 | { 225 | "ipAddress": { 226 | "ip": "66.249.64.73", 227 | "version": 4, 228 | "classification": { 229 | "name": "Crawler", 230 | "code": "crawler" 231 | }, 232 | "lastSeen": "2016-10-02 09:16:57", 233 | "hostname": "crawl-66-249-64-73.googlebot.com", 234 | "geo": { 235 | "country": { 236 | "name": "United States", 237 | "code": "US" 238 | }, 239 | "city": "Mountain View" 240 | }, 241 | "crawler": { 242 | "name": "Googlebot/2.1", 243 | "version": { 244 | "current": "2.1", 245 | "major": "2" 246 | }, 247 | "family": { 248 | "name": "Googlebot", 249 | "code": "googlebot", 250 | "homepage": "http://www.google.com/bot.html", 251 | "vendor": { 252 | "name": "Google Inc.", 253 | "code": "google_inc", 254 | "homepage": "https://www.google.com/about/company/" 255 | }, 256 | "icon": "bot_googlebot.png", 257 | "infoUrl": "https://udger.com/resources/ua-list/bot-detail?bot=Googlebot#id31" 258 | }, 259 | "lastSeen": "2017-01-06 17:52:46", 260 | "category": { 261 | "name": "Search engine bot", 262 | "code": "search_engine_bot" 263 | }, 264 | "respectRobotsTxt": "search_engine_bot" 265 | }, 266 | "datacenter": { 267 | "name": "Google sites", 268 | "code": "googgle_sites", 269 | "homepage": "http://sites.google.com/" 270 | } 271 | }, 272 | "fromCache": false 273 | } 274 | ``` 275 | 276 | ## LRU Cache 277 | By default, cache is disable. To enable cache, just add this line BEFORE using udgerParser.set(): 278 | 279 | // by default, cache size is 4000 keys (a key can be an UA, or UA+IP) 280 | // you can modify this limit 281 | udgerParser.setCacheSize(1000); 282 | udgerParser.setCacheEnable(true) 283 | 284 | When a record is coming from the cache, the "from_cache" attribute in the response is "true" 285 | 286 | ## Helpers 287 | 288 | ### randomUACrawlers 289 | 290 | // return callback with random ua_string from udger_crawler_list table 291 | // [ 292 | // { ua_string: 'Googlebot/2.1 (+http://www.google.com/bot.html)' }, 293 | // { ua_string: 'Mozilla/5.0 (compatible; SeznamBot/3.2-test1; +http://napoveda.seznam.cz/en/seznambot-intro/)' }, 294 | // ...... 295 | // ] 296 | 297 | udgerParser.randomUACrawlers(10, (err, results) => {}); 298 | 299 | ### randomUAClientsRegex 300 | 301 | // return callback with random regstring from udger_client_regex table 302 | // [ 303 | // { regstring: '/^Microsoft Office\\/16.*Microsoft Outlook Mail 16/si' }, 304 | // { regstring: '/Mozilla.*Linux.*Android.*WebKit.*Version\\/([0-9\\.]+)/si' }, 305 | // ...... 306 | // ] 307 | 308 | udgerParser.randomUAClientsRegex(10, (err, results) => {}); 309 | 310 | ### randomUAClients 311 | 312 | // return callback with random generated UA strings based in regstring (udger_client_regex) 313 | // [ 314 | // { 315 | // regstring: '/^Mozilla.*MSIE.*Windows Phone.*IEMobile\\/([0-9\\.]+)/si', 316 | // randomUA: 'MozillaMSIEQSs-0Windows PhoneTD7Y9IEMobile/24304' 317 | // }, 318 | // { 319 | // regstring: '/mozilla.*rv:[0-9\\.]+.*servo.*firefox\\/([0-9a-z\\+\\-\\.]+).*/si', 320 | // randomUA: 'mozillatWrv:90221.GeservoTKqzfirefox/y-l+h' 321 | // } 322 | // ...... 323 | // ] 324 | 325 | udgerParser.randomUAClients(10, (err, results) => {}); 326 | 327 | ### randomIPv4 328 | 329 | // return callback with random ipv4 matching XXX.XXX.XXX.XXX from udger_ip_list (identified as "bad" IPs) 330 | // [ 331 | // { ip: '108.61.199.93' }, 332 | // { ip: '66.249.64.73' } 333 | // ..... 334 | // ] 335 | 336 | udgerParser.randomIPv4(10, (err, results) => {}); 337 | 338 | ### getUAClientsClassification 339 | 340 | // return callback with data from udger_client_class table 341 | // [ 342 | // { client_classification: 'Browser', client_classification_code: 'browser' }, 343 | // { client_classification: 'Offline browser', client_classification_code: 'offline_browser' }, 344 | // ..... 345 | // ] 346 | 347 | udgerParser.getUAClientsClassification((err, results) => {}); 348 | 349 | ### getUACrawlersClassification 350 | 351 | // return callback with data from udger_crawler_class table 352 | // [ 353 | // { crawler_classification: 'Uncategorised', crawler_classification_code: 'uncategorised' }, 354 | // { crawler_classification: 'Search engine bot', crawler_classification_code: 'search_engine_bot' }, 355 | // ..... 356 | // ] 357 | 358 | udgerParser.getUACrawlersClassification((err, results) => {}); 359 | 360 | ### getUACrawlersFamilies 361 | 362 | // return callback with data from mixed udger_crawler_class, udger_crawler_list tables 363 | // [ 364 | // { 365 | // family_code: 'googlebot', 366 | // crawler_classification_code: 'search_engine_bot' 367 | // }, 368 | // .... 369 | // ] 370 | 371 | udgerParser.getUACrawlersFamilies((err, results) => {}); 372 | 373 | ### getIpsClassification 374 | 375 | // return callback with data from udger_ip_class table 376 | // [ 377 | // { ip_classification: 'Tor exit node', ip_classification_code: 'tor_exit_node' }, 378 | // { ip_classification: 'Fake crawler', ip_classification_code: 'fake_crawler' }, 379 | // ..... 380 | // ] 381 | 382 | udgerParser.getIpsClassification((err, results) => {}); 383 | 384 | ### getDatabaseInfo 385 | 386 | // return callback with single record from udger_db_info table 387 | // { 388 | // version: '20170106-01', 389 | // information: 'Data v3 for Local parser - test data, no full database', 390 | // lastupdate: 1483690193 391 | // } 392 | 393 | udgerParser.getDatabaseInfo((err, result) => {}); 394 | 395 | ## Running tests 396 | npm test 397 | 398 | 399 | ## Automatic updates download 400 | - for autoupdate data use Udger data updater (https://udger.com/support/documentation/?doc=62) 401 | 402 | 403 | ## Documentation for programmers 404 | - https://udger.com/pub/documentation/parser/NodeJS/html/ 405 | 406 | 407 | ## Author 408 | - The Udger.com Team (info@udger.com) 409 | 410 | 411 | ## old v2 format 412 | This module does not support v2 format 413 | -------------------------------------------------------------------------------- /defaultResult.json: -------------------------------------------------------------------------------- 1 | { 2 | "user_agent": { 3 | "ua_string": "", 4 | "ua_class": "", 5 | "ua_class_code": "", 6 | "ua": "", 7 | "ua_version": "", 8 | "ua_version_major": "", 9 | "ua_uptodate_current_version": "", 10 | "ua_family": "", 11 | "ua_family_code": "", 12 | "ua_family_homepage": "", 13 | "ua_family_vendor": "", 14 | "ua_family_vendor_code": "", 15 | "ua_family_vendor_homepage": "", 16 | "ua_family_icon": "", 17 | "ua_family_icon_big": "", 18 | "ua_family_info_url": "", 19 | "ua_engine": "", 20 | "os": "", 21 | "os_code": "", 22 | "os_homepage": "", 23 | "os_icon": "", 24 | "os_icon_big": "", 25 | "os_info_url": "", 26 | "os_family": "", 27 | "os_family_code": "", 28 | "os_family_vendor": "", 29 | "os_family_vendor_code": "", 30 | "os_family_vendor_homepage": "", 31 | "device_class": "", 32 | "device_class_code": "", 33 | "device_class_icon": "", 34 | "device_class_icon_big": "", 35 | "device_class_info_url": "", 36 | "device_marketname": "", 37 | "device_brand": "", 38 | "device_brand_code": "", 39 | "device_brand_homepage": "", 40 | "device_brand_icon": "", 41 | "device_brand_icon_big": "", 42 | "device_brand_info_url": "", 43 | "crawler_last_seen": "", 44 | "crawler_category": "", 45 | "crawler_category_code": "", 46 | "crawler_respect_robotstxt": "" 47 | }, 48 | "ip_address": { 49 | "ip": "", 50 | "ip_ver": "", 51 | "ip_classification": "", 52 | "ip_classification_code": "", 53 | "ip_hostname": "", 54 | "ip_last_seen": "", 55 | "ip_country": "", 56 | "ip_country_code": "", 57 | "ip_city": "", 58 | "crawler_name": "", 59 | "crawler_ver": "", 60 | "crawler_ver_major": "", 61 | "crawler_family": "", 62 | "crawler_family_code": "", 63 | "crawler_family_homepage": "", 64 | "crawler_family_vendor": "", 65 | "crawler_family_vendor_code": "", 66 | "crawler_family_vendor_homepage": "", 67 | "crawler_family_icon": "", 68 | "crawler_family_info_url": "", 69 | "crawler_last_seen": "", 70 | "crawler_category": "", 71 | "crawler_category_code": "", 72 | "crawler_respect_robotstxt": "", 73 | "datacenter_name": "", 74 | "datacenter_name_code": "", 75 | "datacenter_homepage": "" 76 | }, 77 | "from_cache":false 78 | } 79 | -------------------------------------------------------------------------------- /examples/connectInfo.js: -------------------------------------------------------------------------------- 1 | const udgerParser = require('../')('test/db/udgerdb_v3_test.dat'); 2 | const http = require('http'); 3 | 4 | var app = require('connect')(); 5 | 6 | http.createServer(app).listen(8082, '127.0.0.1'); 7 | 8 | app.use(function (req, res, next) { 9 | 10 | udgerParser.set({ 11 | ua:req.headers['user-agent'], 12 | ip:req.headers['x-forwarded-for'] || req.connection.remoteAddress 13 | }); 14 | 15 | const result = udgerParser.parse(); 16 | 17 | res.setHeader('Content-Type', 'application/json'); 18 | res.end(JSON.stringify(result, null, 4)); 19 | 20 | next(); 21 | }); 22 | -------------------------------------------------------------------------------- /examples/connectSecurityAntiFakeCrawler.js: -------------------------------------------------------------------------------- 1 | const udgerParser = require('../')('test/db/udgerdb_v3_test.dat'); 2 | const http = require('http'); 3 | 4 | var app = require('connect')(); 5 | 6 | http.createServer(app).listen(8082, '127.0.0.1'); 7 | 8 | app.use(function (req, res) { 9 | 10 | udgerParser.set({ 11 | ua:req.headers['user-agent'], 12 | ip:req.headers['x-forwarded-for'] || req.connection.remoteAddress 13 | }); 14 | 15 | const result = udgerParser.parse(); 16 | 17 | if (result['ip_address']['ip_classification_code'] === 'fake_crawler') { 18 | res.status(403); 19 | res.end('Sorry, you are not allowed'); 20 | return; 21 | } else { 22 | res.end('Welcome !'); 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /examples/googleBot.js: -------------------------------------------------------------------------------- 1 | const udgerParser = require('../')('test/db/udgerdb_v3_test.dat'); 2 | 3 | udgerParser.set({ 4 | ua:'Googlebot/2.1 (+http://www.google.com/bot.html)', 5 | ip:'66.249.64.73' 6 | }); 7 | 8 | const ret = udgerParser.parse({ json:true }); 9 | 10 | // beautify json output with 4 spaces indent 11 | console.log(JSON.stringify(ret, null, 4)); 12 | -------------------------------------------------------------------------------- /examples/simpleUserAgentAndIp.js: -------------------------------------------------------------------------------- 1 | const udgerParser = require('../')('test/db/udgerdb_v3_test.dat'); 2 | 3 | udgerParser.set({ 4 | ua:'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 5 | ip:'2A02:598:7000:116:0:0:0:101' 6 | }); 7 | 8 | const ret = udgerParser.parse(); 9 | 10 | // beautify json output with 4 spaces indent 11 | console.log(JSON.stringify(ret, null, 4)); 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Database = require('better-sqlite3'); 2 | const debug = require('debug')('udger-nodejs'); 3 | const Address6 = require('ip-address').Address6; 4 | const utils = require('./utils'); 5 | const fs = require('fs-extra'); 6 | const dotProp = require('dot-prop'); 7 | const path = require('path'); 8 | const RandExp = require('randexp'); 9 | 10 | /** Class exposing udger parser methods */ 11 | class UdgerParser { 12 | 13 | /** 14 | * Load udger SQLite3 database. 15 | * @param {string} file - full path to udgerdb_v3.dat 16 | */ 17 | constructor(file) { 18 | this.db = new Database(file, { readonly: true, fileMustExist: true }); 19 | this.file = file; 20 | this.ip = null; 21 | this.ua = null; 22 | 23 | this.cacheEnable = false; 24 | this.cacheMaxRecords = 4000; 25 | this.cache = {}; 26 | this.keyCache = ''; 27 | 28 | this.defaultRet = fs.readJsonSync(path.resolve(__dirname+'/defaultResult.json')); 29 | this.retUa = {}; 30 | this.retIp = {}; 31 | } 32 | 33 | /** 34 | * Connect (reconnect) sqlite database 35 | * @return {Boolean} true if db has been opened, false if already connected 36 | */ 37 | connect() { 38 | if (!this.db) { 39 | this.db = new Database(this.file, { readonly: true, fileMustExist: true }); 40 | 41 | // see https://www.sqlite.org/wal.html 42 | this.db.pragma('journal_mode = WAL'); 43 | 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | /** 50 | * Disconnect sqlite database, avoid read/write conflict 51 | * see https://github.com/udger/udger-updater-nodejs/issues/5 52 | * @return {Boolean} true if db has been closed, false if no db opened 53 | */ 54 | disconnect() { 55 | if (this.db) { 56 | this.db.close(); 57 | this.db = null; 58 | return true; 59 | } 60 | return false; 61 | } 62 | 63 | /** 64 | * Initialize User-Agent or IP(v4/v6), or both 65 | * @param {Object} data - An object 66 | * @param {String} data.ua - User-Agent 67 | * @param {String} data.ip - IP Address 68 | */ 69 | set(data) { 70 | 71 | const help = 'set() is waiting for an object having only ip and/or ua attribute'; 72 | 73 | if (!data) { 74 | throw new Error(help); 75 | } 76 | 77 | if (typeof data === 'string') { 78 | throw new Error(help); 79 | } 80 | 81 | for (const key in data) { 82 | if (key === 'ua') { 83 | this.ua = data.ua; 84 | } else if (key === 'ip') { 85 | this.ip = data.ip.toLowerCase(); 86 | } else { 87 | throw new Error(help); 88 | } 89 | } 90 | 91 | this.keyCache = ''; 92 | 93 | if (this.cacheEnable) { 94 | if (this.ip) this.keyCache = this.ip; 95 | if (this.ua) this.keyCache += this.ua; 96 | } 97 | 98 | this.retUa = JSON.parse(JSON.stringify(this.defaultRet['user_agent'])); 99 | this.retIp = JSON.parse(JSON.stringify(this.defaultRet['ip_address'])); 100 | } 101 | 102 | /** 103 | * Activate cache 104 | * @param {Boolean} cache - true or false 105 | */ 106 | setCacheEnable(cache) { 107 | this.cacheEnable = cache; 108 | } 109 | 110 | /** 111 | * Return if the cache is enable or not 112 | * @return {Boolean} true if the cache is enable, false if not 113 | */ 114 | isCacheEnable() { 115 | return this.cacheEnable; 116 | } 117 | 118 | /** 119 | * Set Cache Size 120 | * @param {Number} records - the maximum number of items we want to keep in the cache 121 | */ 122 | setCacheSize(records) { 123 | this.cacheMaxRecords = records; 124 | } 125 | 126 | /** 127 | * Check if a key exist in the cache 128 | * @param {Number} key - key can be UA or UA+IP 129 | * @return {Boolean} return true if the key exist in the cache, false if not 130 | */ 131 | cacheKeyExist(key) { 132 | if (this.cache[key]) { 133 | return true; 134 | } 135 | return false; 136 | } 137 | 138 | /** 139 | * Return an item from the cache 140 | * @param {String} key - key can be UA or UA+IP 141 | * @return {Object} stored parser result 142 | */ 143 | cacheRead(key, opts) { 144 | const ret = this.cache[key]; 145 | if (opts && opts.json) { 146 | if (opts.full) { 147 | ret['fromCache'] = true; 148 | } 149 | } else { 150 | ret['from_cache'] = true; 151 | } 152 | return ret; 153 | } 154 | 155 | /** 156 | * Write an item into the cache 157 | * @param {String} key - key can be UA or UA+IP 158 | */ 159 | cacheWrite(key, data) { 160 | if (this.cache[key]) { 161 | // already in the cache 162 | return; 163 | } 164 | 165 | this.cache[key] = data; 166 | 167 | debug('cache: store result of %s (length=%s)', key); 168 | debug('cache: entries count: %s/%s', (Object.keys(this.cache).length || 0), this.cacheMaxRecords); 169 | 170 | // warning, js object is used for performance reason 171 | // as opposite of php object, we can not use splice/pop stuff here 172 | // so, when an entry must be remove because the cache is full, we 173 | // can not determine which one will be removed 174 | while (Object.keys(this.cache).length > this.cacheMaxRecords) { 175 | debug('cache: removing entry', Object.keys(this.cache)[0]); 176 | delete this.cache[Object.keys(this.cache)[0]]; 177 | } 178 | } 179 | 180 | /** 181 | * Clean the cache 182 | */ 183 | cacheClean() { 184 | this.cache = {}; 185 | } 186 | 187 | /** 188 | * Parse the User-Agent string 189 | * @param {String} ua - An User-Agent string 190 | */ 191 | parseUa(ua, opts) { 192 | 193 | const rua = JSON.parse(JSON.stringify(this.retUa)); 194 | const ruaJson = {}; 195 | 196 | if (!ua) return { 197 | udger: rua, 198 | json: ruaJson 199 | }; 200 | 201 | let q; 202 | let r; 203 | let e; 204 | 205 | let client_id = 0; 206 | let client_class_id = -1; 207 | let os_id = 0; 208 | let deviceclass_id = 0; 209 | 210 | debug('parse useragent string: START (useragent: ' + ua + ')'); 211 | 212 | rua['ua_string'] = ua; 213 | rua['ua_class'] = 'Unrecognized'; 214 | rua['ua_class_code'] = 'unrecognized'; 215 | 216 | dotProp.set(ruaJson, 'ua.string', ua); 217 | if (opts.full) { 218 | dotProp.set(ruaJson, 'ua.class.name', 'Unrecognized'); 219 | dotProp.set(ruaJson, 'ua.class.code', 'unrecognized'); 220 | } else { 221 | dotProp.set(ruaJson, 'ua.class', 'unrecognized'); 222 | } 223 | 224 | //////////////////////////////////////////////// 225 | // search for crawlers 226 | //////////////////////////////////////////////// 227 | 228 | q = this.db.prepare( 229 | 'SELECT ' + 230 | 'udger_crawler_list.id as botid,' + 231 | 'name, ver, ver_major, last_seen, respect_robotstxt,' + 232 | 'family, family_code, family_homepage, family_icon,' + 233 | 'vendor, vendor_code, vendor_homepage,' + 234 | 'crawler_classification, crawler_classification_code ' + 235 | 'FROM udger_crawler_list ' + 236 | 'LEFT JOIN udger_crawler_class ON udger_crawler_class.id=udger_crawler_list.class_id ' + 237 | 'WHERE ua_string=?' 238 | ); 239 | 240 | r = q.get(ua); 241 | 242 | if (r) { 243 | 244 | debug('parse useragent string: crawler found'); 245 | 246 | client_class_id = 99; 247 | 248 | // UDGER FORMAT 249 | rua['ua_class'] = 'Crawler'; 250 | rua['ua_class_code'] = 'crawler'; 251 | rua['ua'] = r['name'] || ''; 252 | rua['ua_version'] = r['ver'] || ''; 253 | rua['ua_version_major'] = r['ver_major'] || ''; 254 | rua['ua_family'] = r['family'] || ''; 255 | rua['ua_family_code'] = r['family_code'] || ''; 256 | rua['ua_family_homepage'] = r['family_homepage'] || ''; 257 | rua['ua_family_vendor'] = r['vendor'] || ''; 258 | rua['ua_family_vendor_code'] = r['vendor_code'] || ''; 259 | rua['ua_family_vendor_homepage'] = r['vendor_homepage'] || ''; 260 | rua['ua_family_icon'] = r['family_icon'] || ''; 261 | rua['ua_family_info_url'] = 'https://udger.com/resources/ua-list/bot-detail?bot=' + (r['family'] || '') + '#id' + (r['botid'] || ''); 262 | 263 | rua['crawler_last_seen'] = r['last_seen'] || ''; 264 | rua['crawler_category'] = r['crawler_classification'] || ''; 265 | rua['crawler_category_code'] = r['crawler_classification_code'] || ''; 266 | rua['crawler_respect_robotstxt'] = r['respect_robotstxt'] || ''; 267 | 268 | // JSON FORMAT 269 | rua['ua'] && dotProp.set(ruaJson, 'ua.name', rua['ua']); 270 | 271 | if (opts.full) { 272 | dotProp.set(ruaJson, 'ua.class.name', 'Crawler'); 273 | dotProp.set(ruaJson, 'ua.class.code', 'crawler'); 274 | rua['ua_version'] && dotProp.set(ruaJson, 'ua.version.current', rua['ua_version']); 275 | rua['ua_version_major'] && dotProp.set(ruaJson, 'ua.version.major', rua['ua_version_major']); 276 | 277 | rua['ua_family'] && dotProp.set(ruaJson, 'ua.family.name', rua['ua_family']); 278 | rua['ua_family_code'] && dotProp.set(ruaJson, 'ua.family.code', rua['ua_family_code']); 279 | rua['ua_family_homepage'] && dotProp.set(ruaJson, 'ua.family.homepage', rua['ua_family_homepage']); 280 | rua['ua_family_vendor'] && dotProp.set(ruaJson, 'ua.family.vendor.name', rua['ua_family_vendor']); 281 | rua['ua_family_vendor_code'] && dotProp.set(ruaJson, 'ua.family.vendor.code', rua['ua_family_vendor_code']); 282 | rua['ua_family_homepage'] && dotProp.set(ruaJson, 'ua.family.vendor.homepage', rua['ua_family_homepage']); 283 | rua['ua_family_icon'] && dotProp.set(ruaJson, 'ua.family.icon', rua['ua_family_icon']); 284 | rua['ua_family'] && r['botid'] && dotProp.set(ruaJson, 'ua.family.infoUrl', rua['ua_family_info_url']); 285 | 286 | } else { 287 | dotProp.set(ruaJson, 'ua.class', 'crawler'); 288 | rua['ua_family_code'] && dotProp.set(ruaJson, 'ua.family.code', rua['ua_family_code']); 289 | rua['ua_family_homepage'] && dotProp.set(ruaJson, 'ua.family.homepage', rua['ua_family_homepage']); 290 | rua['ua_family_vendor_code'] && dotProp.set(ruaJson, 'ua.family.vendor', rua['ua_family_vendor_code']); 291 | } 292 | 293 | rua['crawler_last_seen'] && dotProp.set(ruaJson, 'crawler.lastSeen', rua['crawler_last_seen']); 294 | 295 | if (opts.full) { 296 | rua['crawler_category'] && dotProp.set(ruaJson, 'crawler.category.name', rua['crawler_category']); 297 | rua['crawler_category_code'] && dotProp.set(ruaJson, 'crawler.category.code', rua['crawler_category_code']); 298 | rua['crawler_respect_robotstxt'] && dotProp.set(ruaJson, 'crawler.respectRobotsTxt', rua['crawler_respect_robotstxt']); 299 | } else { 300 | rua['crawler_category_code'] && dotProp.set(ruaJson, 'crawler.category', rua['crawler_category_code']); 301 | } 302 | } else { 303 | 304 | q = this.db.prepare( 305 | 'SELECT class_id,client_id,regstring,name,name_code,homepage,icon,icon_big,engine,vendor,vendor_code,vendor_homepage,uptodate_current_version,client_classification,client_classification_code ' + 306 | 'FROM udger_client_regex ' + 307 | 'JOIN udger_client_list ON udger_client_list.id=udger_client_regex.client_id ' + 308 | 'JOIN udger_client_class ON udger_client_class.id=udger_client_list.class_id ' + 309 | 'ORDER BY sequence ASC' 310 | ); 311 | 312 | for (r of q.iterate()) { 313 | e = ua.match(utils.phpRegexpToJs(r['regstring'])); 314 | if (e) { 315 | 316 | debug('parse useragent string: client found'); 317 | 318 | client_id = r['client_id']; 319 | client_class_id = r['class_id']; 320 | 321 | rua['ua_class'] = r['client_classification']; 322 | rua['ua_class_code'] = r['client_classification_code']; 323 | 324 | if (opts.full) { 325 | dotProp.set(ruaJson, 'ua.class.name', rua['ua_class']); 326 | dotProp.set(ruaJson, 'ua.class.code', rua['ua_class_code']); 327 | } else { 328 | dotProp.set(ruaJson, 'ua.class', rua['ua_class_code']); 329 | } 330 | if (e[1]) { 331 | rua['ua'] = r['name'] + ' ' + e[1]; 332 | rua['ua_version'] = e[1]; 333 | rua['ua_version_major'] = e[1].split('.')[0]; 334 | } else { 335 | rua['ua'] = r['name']; 336 | rua['ua_version'] = ''; 337 | rua['ua_version_major'] = ''; 338 | } 339 | 340 | if (rua['ua']) { 341 | dotProp.set(ruaJson, 'ua.name', rua['ua']); 342 | } else { 343 | dotProp.delete(ruaJson, 'ua.name'); 344 | } 345 | 346 | if (opts.full) { 347 | if (rua['ua_version']) { 348 | dotProp.set(ruaJson, 'ua.version.current', rua['ua_version']); 349 | } else { 350 | dotProp.delete(ruaJson, 'ua.version.current'); 351 | } 352 | 353 | if (rua['ua_version_major']) { 354 | dotProp.set(ruaJson, 'ua.version.current', rua['ua_version_major']); 355 | } else { 356 | dotProp.delete(ruaJson, 'ua.version.current'); 357 | } 358 | } 359 | 360 | rua['ua_uptodate_current_version'] = r['uptodate_current_version'] || ''; 361 | rua['ua_family'] = r['name'] || ''; 362 | rua['ua_family_code'] = r['name_code'] || ''; 363 | rua['ua_family_homepage'] = r['homepage'] || ''; 364 | rua['ua_family_vendor'] = r['vendor'] || ''; 365 | rua['ua_family_vendor_code'] = r['vendor_code'] || ''; 366 | rua['ua_family_vendor_homepage'] = r['vendor_homepage'] || ''; 367 | rua['ua_family_icon'] = r['icon'] || ''; 368 | rua['ua_family_icon_big'] = r['icon_big'] || ''; 369 | rua['ua_family_info_url'] = 'https://udger.com/resources/ua-list/browser-detail?browser=' + (r['name'] || ''); 370 | rua['ua_engine'] = r['engine'] || ''; 371 | 372 | if (opts.full) { 373 | rua['ua_uptodate_current_version'] && dotProp.set(ruaJson, 'ua.uptodateCurrentVersion', rua['ua_uptodate_current_version']); 374 | rua['ua_family'] && dotProp.set(ruaJson, 'ua.family.name', rua['ua_family']); 375 | rua['ua_family_code'] && dotProp.set(ruaJson, 'ua.family.code', rua['ua_family_code']); 376 | rua['ua_family_homepage'] && dotProp.set(ruaJson, 'ua.family.homepage', rua['ua_family_homepage']); 377 | rua['ua_family_vendor'] && dotProp.set(ruaJson, 'ua.family.vendor.name', rua['ua_family_vendor']); 378 | rua['ua_family_vendor_code'] && dotProp.set(ruaJson, 'ua.family.vendor.code', rua['ua_family_vendor_code']); 379 | rua['ua_family_vendor_homepage'] && dotProp.set(ruaJson, 'ua.family.vendor.homepage', rua['ua_family_vendor_homepage']); 380 | rua['ua_family_icon'] && dotProp.set(ruaJson, 'ua.family.icon', rua['ua_family_icon']); 381 | rua['ua_family_icon_big'] && dotProp.set(ruaJson, 'ua.family.iconBig', rua['ua_family_icon_big']); 382 | if (r['name']) { 383 | dotProp.set(ruaJson, 'ua.family.infoUrl', rua['ua_family_info_url']); 384 | } 385 | } else { 386 | rua['ua_family_code'] && dotProp.set(ruaJson, 'ua.family', rua['ua_family_code']); 387 | } 388 | rua['ua_engine'] && dotProp.set(ruaJson, 'ua.engine', rua['ua_engine']); 389 | 390 | break; 391 | } 392 | } 393 | } 394 | 395 | //////////////////////////////////////////////// 396 | // os 397 | //////////////////////////////////////////////// 398 | q = this.db.prepare( 399 | 'SELECT os_id,regstring,family,family_code,name,name_code,homepage,icon,icon_big,vendor,vendor_code,vendor_homepage ' + 400 | 'FROM udger_os_regex ' + 401 | 'JOIN udger_os_list ON udger_os_list.id=udger_os_regex.os_id ' + 402 | 'ORDER BY sequence ASC' 403 | ); 404 | 405 | for (r of q.iterate()) { 406 | e = ua.match(utils.phpRegexpToJs(r['regstring'])); 407 | if (e) { 408 | 409 | debug('parse useragent string: os found'); 410 | 411 | os_id = r['os_id']; 412 | rua['os'] = r['name'] || ''; 413 | rua['os_code'] = r['name_code'] || ''; 414 | rua['os_homepage'] = r['homepage'] || ''; 415 | rua['os_icon'] = r['icon'] || ''; 416 | rua['os_icon_big'] = r['icon_big'] || ''; 417 | rua['os_info_url'] = 'https://udger.com/resources/ua-list/os-detail?os=' + (r['name'] || ''); 418 | rua['os_family'] = r['family'] || ''; 419 | rua['os_family_code'] = r['family_code'] || ''; 420 | rua['os_family_vendor'] = r['vendor'] || ''; 421 | rua['os_family_vendor_code'] = r['vendor_code'] || ''; 422 | rua['os_family_vendor_homepage'] = r['vendor_homepage'] || ''; 423 | 424 | if (opts.full) { 425 | rua['os'] && dotProp.set(ruaJson, 'os.name', rua['os']); 426 | rua['os_code'] && dotProp.set(ruaJson, 'os.code', rua['os_code']); 427 | rua['os_homepage'] && dotProp.set(ruaJson, 'os.homepage', rua['os_homepage']); 428 | rua['os_icon'] && dotProp.set(ruaJson, 'os.icon', rua['os_icon']); 429 | rua['os_icon_big'] && dotProp.set(ruaJson, 'os.iconBig', rua['os_icon_big']); 430 | rua['os_info_url'] && dotProp.set(ruaJson, 'os.infoUrl', rua['os_info_url']); 431 | rua['os_family'] && dotProp.set(ruaJson, 'os.family.name', rua['os_family']); 432 | rua['os_family_code'] && dotProp.set(ruaJson, 'os.family.code', rua['os_family_code']); 433 | rua['os_family_vendor'] && dotProp.set(ruaJson, 'os.family.vendor.name', rua['os_family_vendor']); 434 | rua['os_family_vendor_code'] && dotProp.set(ruaJson, 'os.family.vendor.code', rua['os_family_vendor_code']); 435 | rua['os_family_vendor_homepage'] && dotProp.set(ruaJson, 'os.family.vendor.homepage', rua['os_family_vendor_homepage']); 436 | } else { 437 | rua['os_code'] && dotProp.set(ruaJson, 'os.code', rua['os_code']); 438 | rua['os_family_code'] && dotProp.set(ruaJson, 'os.family', rua['os_family_code']); 439 | } 440 | break; 441 | } 442 | } 443 | 444 | //////////////////////////////////////////////// 445 | // client/os relation 446 | //////////////////////////////////////////////// 447 | 448 | if (os_id == 0 && client_id != 0) { 449 | 450 | q = this.db.prepare( 451 | 'SELECT os_id,family,family_code,name,name_code,homepage,icon,icon_big,vendor,vendor_code,vendor_homepage ' + 452 | 'FROM udger_client_os_relation ' + 453 | 'JOIN udger_os_list ON udger_os_list.id=udger_client_os_relation.os_id ' + 454 | 'WHERE client_id=?' 455 | ); 456 | 457 | r = q.get(client_id); 458 | 459 | if (r) { 460 | 461 | debug('parse useragent string: client os relation found'); 462 | 463 | os_id = r['os_id']; 464 | rua['os'] = r['name'] || ''; 465 | rua['os_code'] = r['name_code'] || ''; 466 | rua['os_homepage'] = r['homepage'] || ''; 467 | rua['os_icon'] = r['icon'] || ''; 468 | rua['os_icon_big'] = r['icon_big'] || ''; 469 | rua['os_info_url'] = 'https://udger.com/resources/ua-list/os-detail?os=' + (r['name'] || ''); 470 | rua['os_family'] = r['family'] || ''; 471 | rua['os_family_code'] = r['family_code'] || ''; 472 | rua['os_family_vendor'] = r['vendor'] || ''; 473 | rua['os_family_vendor_code'] = r['vendor_code'] || ''; 474 | rua['os_family_vendor_homepage'] = r['vendor_homepage'] || ''; 475 | 476 | rua['os'] && dotProp.set(ruaJson, 'os.name', rua['os']); 477 | rua['os_code'] && dotProp.set(ruaJson, 'os.code', rua['os_code']); 478 | rua['os_homepage'] && dotProp.set(ruaJson, 'os.homepage', rua['os_homepage']); 479 | rua['os_icon'] && dotProp.set(ruaJson, 'os.icon', rua['os_icon']); 480 | rua['os_icon_big'] && dotProp.set(ruaJson, 'os.iconBig', rua['os_icon_big']); 481 | rua['os_info_url'] && dotProp.set(ruaJson, 'os.infoUrl', rua['os_info_url']); 482 | rua['os_family'] && dotProp.set(ruaJson, 'os.family.name', rua['os_family']); 483 | rua['os_family_code'] && dotProp.set(ruaJson, 'os.family.code', rua['os_family_code']); 484 | rua['os_family_vendor'] && dotProp.set(ruaJson, 'os.family.vendor.name', rua['os_family_vendor']); 485 | rua['os_family_vendor_code'] && dotProp.set(ruaJson, 'os.family.vendor.code', rua['os_family_vendor_code']); 486 | rua['os_family_vendor_homepage'] && dotProp.set(ruaJson, 'os.family.vendor.homepage', rua['os_family_vendor_homepage']); 487 | 488 | } 489 | } 490 | 491 | //////////////////////////////////////////////// 492 | // device 493 | //////////////////////////////////////////////// 494 | 495 | q = this.db.prepare( 496 | 'SELECT deviceclass_id,regstring,name,name_code,icon,icon_big ' + 497 | 'FROM udger_deviceclass_regex ' + 498 | 'JOIN udger_deviceclass_list ON udger_deviceclass_list.id=udger_deviceclass_regex.deviceclass_id ' + 499 | 'ORDER BY sequence ASC' 500 | ); 501 | 502 | for (r of q.iterate()) { 503 | e = ua.match(utils.phpRegexpToJs(r['regstring'])); 504 | if (e) { 505 | 506 | debug('parse useragent string: device found by regex'); 507 | 508 | deviceclass_id = r['deviceclass_id']; 509 | rua['device_class'] = r['name'] || ''; 510 | rua['device_class_code'] = r['name_code'] || ''; 511 | rua['device_class_icon'] = r['icon'] || ''; 512 | rua['device_class_icon_big'] = r['icon_big'] || ''; 513 | rua['device_class_info_url'] = 'https://udger.com/resources/ua-list/device-detail?device=' + r['name']; 514 | 515 | if (opts.full) { 516 | rua['device_class'] && dotProp.set(ruaJson, 'device.class.name', rua['device_class']); 517 | rua['device_class_code'] && dotProp.set(ruaJson, 'device.class.code', rua['device_class_code']); 518 | rua['device_class_icon'] && dotProp.set(ruaJson, 'device.class.icon', rua['device_class_icon']); 519 | rua['device_class_icon_big'] && dotProp.set(ruaJson, 'device.class.iconBig', rua['device_class_icon_big']); 520 | rua['device_class_info_url'] && dotProp.set(ruaJson, 'device.class.infoUrl', rua['device_class_info_url']); 521 | } else { 522 | rua['device_class_code'] && dotProp.set(ruaJson, 'device.class', rua['device_class_code']); 523 | } 524 | 525 | break; 526 | } 527 | } 528 | 529 | if (deviceclass_id == 0 && client_class_id != -1) { 530 | q = this.db.prepare( 531 | 'SELECT deviceclass_id,name,name_code,icon,icon_big ' + 532 | 'FROM udger_deviceclass_list ' + 533 | 'JOIN udger_client_class ON udger_client_class.deviceclass_id=udger_deviceclass_list.id ' + 534 | 'WHERE udger_client_class.id=?' 535 | ); 536 | 537 | r = q.get(client_class_id); 538 | 539 | if (r) { 540 | 541 | debug('parse useragent string: device found by deviceclass'); 542 | 543 | deviceclass_id = r['deviceclass_id']; 544 | rua['device_class'] = r['name'] || ''; 545 | rua['device_class_code'] = r['name_code'] || ''; 546 | rua['device_class_icon'] = r['icon'] || ''; 547 | rua['device_class_icon_big'] = r['icon_big'] || ''; 548 | rua['device_class_info_url'] = 'https://udger.com/resources/ua-list/device-detail?device=' + (r['name'] || ''); 549 | 550 | if (opts.full) { 551 | rua['device_class'] && dotProp.set(ruaJson, 'device.class.name', rua['device_class']); 552 | rua['device_class_code'] && dotProp.set(ruaJson, 'device.class.code', rua['device_class_code']); 553 | rua['device_class_icon'] && dotProp.set(ruaJson, 'device.class.icon', rua['device_class_icon']); 554 | rua['device_class_icon_big'] && dotProp.set(ruaJson, 'device.class.iconBig', rua['device_class_icon_big']); 555 | rua['device_class_info_url'] && dotProp.set(ruaJson, 'device.class.infoUrl', rua['device_class_info_url']); 556 | } else { 557 | rua['device_class_code'] && dotProp.set(ruaJson, 'device.class', rua['device_class_code']); 558 | } 559 | } 560 | } 561 | 562 | //////////////////////////////////////////////// 563 | // device marketname 564 | //////////////////////////////////////////////// 565 | 566 | if (rua['os_family_code']) { 567 | q = this.db.prepare( 568 | 'SELECT id,regstring FROM udger_devicename_regex ' + 569 | 'WHERE (' + 570 | '(os_family_code=? AND os_code=\'-all-\') OR ' + 571 | '(os_family_code=? AND os_code=?)' + 572 | ') ' + 573 | 'ORDER BY sequence' 574 | ); 575 | 576 | const bindParams = [ 577 | rua['os_family_code'], 578 | rua['os_family_code'], 579 | rua['os_code'] 580 | ]; 581 | 582 | let match; 583 | let rId; 584 | for (const r of q.iterate(bindParams)) { 585 | e = ua.match(utils.phpRegexpToJs(r['regstring'])); 586 | if (e && e[1]) { 587 | match = e[1].trim(); 588 | rId = r['id']; 589 | break; 590 | } 591 | } 592 | 593 | const qC = this.db.prepare( 594 | 'SELECT marketname,brand_code,brand,brand_url,icon,icon_big ' + 595 | 'FROM udger_devicename_list ' + 596 | 'JOIN udger_devicename_brand ON udger_devicename_brand.id=udger_devicename_list.brand_id ' + 597 | 'WHERE regex_id=? AND code=?' 598 | ); 599 | 600 | const rC = qC.get(rId, match); 601 | 602 | if (rC) { 603 | 604 | debug('parse useragent string: device marketname found'); 605 | 606 | rua['device_marketname'] = rC['marketname'] || ''; 607 | rua['device_brand'] = rC['brand'] || ''; 608 | rua['device_brand_code'] = rC['brand_code'] || ''; 609 | rua['device_brand_homepage'] = rC['brand_url'] || ''; 610 | rua['device_brand_icon'] = rC['icon'] || ''; 611 | rua['device_brand_icon_big'] = rC['icon_big'] || ''; 612 | rua['device_brand_info_url'] = 'https://udger.com/resources/ua-list/devices-brand-detail?brand=' + (rC['brand_code'] || ''); 613 | 614 | rua['device_marketname'] && dotProp.set(ruaJson, 'device.marketName', rua['device_marketname']); 615 | rua['device_brand'] && dotProp.set(ruaJson, 'device.brand.name', rua['device_brand']); 616 | rua['device_brand_code'] && dotProp.set(ruaJson, 'device.brand.code', rua['device_brand_code']); 617 | rua['device_brand_homepage'] && dotProp.set(ruaJson, 'device.brand.homepage', rua['device_brand_homepage']); 618 | rua['device_brand_icon'] && dotProp.set(ruaJson, 'device.brand.icon', rua['device_brand_icon']); 619 | rua['device_brand_icon_big'] && dotProp.set(ruaJson, 'device.brand.iconBig', rua['device_brand_icon_big']); 620 | rua['device_brand_info_url'] && dotProp.set(ruaJson, 'device.brand.infoUrl', rua['device_brand_info_url']); 621 | 622 | } 623 | } 624 | 625 | debug('parse useragent string: END, unset useragent string'); 626 | 627 | return { 628 | udger:rua, 629 | json:ruaJson 630 | }; 631 | } 632 | 633 | /** 634 | * Parse the IP Address 635 | * @param {String} ip - An IPv4 or IPv6 Address 636 | */ 637 | parseIp(ip, opts) { 638 | 639 | const rip = JSON.parse(JSON.stringify(this.retIp)); 640 | const ripJson = {}; 641 | 642 | if (!ip) return { 643 | udger:rip, 644 | json:ripJson 645 | }; 646 | 647 | 648 | let q; 649 | let r; 650 | let ipInt; 651 | let ipa; 652 | 653 | debug('parse IP address: START (IP: ' + ip + ')'); 654 | 655 | rip['ip'] = ip; 656 | dotProp.set(ripJson, 'ip', ip); 657 | 658 | const ipver = utils.getIpVersion(ip); 659 | 660 | if (ipver === 4 || ipver === 6) { 661 | if (ipver === 6) { 662 | ip = utils.inetNtop(utils.inetPton(ip)); 663 | debug('compress IP address is:' + ip); 664 | } 665 | } 666 | 667 | rip['ip_ver'] = ipver; 668 | if (opts.full) { 669 | dotProp.set(ripJson, 'version', ipver); 670 | } 671 | 672 | q = this.db.prepare( 673 | 'SELECT udger_crawler_list.id as botid, ip_last_seen, ip_hostname, ip_country, ip_city, ' + 674 | 'ip_country_code, ip_classification, ip_classification_code, name, ver, ver_major, last_seen, '+ 675 | 'respect_robotstxt, family, family_code, family_homepage, family_icon, vendor, vendor_code, '+ 676 | 'vendor_homepage, crawler_classification, crawler_classification_code '+ 677 | 'FROM udger_ip_list '+ 678 | 'JOIN udger_ip_class ON udger_ip_class.id=udger_ip_list.class_id '+ 679 | 'LEFT JOIN udger_crawler_list ON udger_crawler_list.id=udger_ip_list.crawler_id '+ 680 | 'LEFT JOIN udger_crawler_class ON udger_crawler_class.id=udger_crawler_list.class_id '+ 681 | 'WHERE ip=? ORDER BY sequence' 682 | ); 683 | 684 | r = q.get(ip); 685 | 686 | if (r) { 687 | 688 | // UDGER FORMAT 689 | rip['ip_classification'] = r['ip_classification'] || ''; 690 | rip['ip_classification_code'] = r['ip_classification_code'] || ''; 691 | rip['ip_last_seen'] = r['ip_last_seen'] || ''; 692 | rip['ip_hostname'] = r['ip_hostname'] || ''; 693 | rip['ip_country'] = r['ip_country'] || ''; 694 | rip['ip_country_code'] = r['ip_country_code'] || ''; 695 | rip['ip_city'] = r['ip_city'] || ''; 696 | 697 | rip['crawler_name'] = r['name'] || ''; 698 | rip['crawler_ver'] = r['ver'] || ''; 699 | rip['crawler_ver_major'] = r['ver_major'] || ''; 700 | rip['crawler_family'] = r['family'] || ''; 701 | rip['crawler_family_code'] = r['family_code'] || ''; 702 | rip['crawler_family_homepage'] = r['family_homepage'] || ''; 703 | rip['crawler_family_vendor'] = r['vendor'] || ''; 704 | rip['crawler_family_vendor_code'] = r['vendor_code'] || ''; 705 | rip['crawler_family_vendor_homepage'] = r['vendor_homepage'] || ''; 706 | rip['crawler_family_icon'] = r['family_icon'] || ''; 707 | if (r['ip_classification_code'] === 'crawler') { 708 | rip['crawler_family_info_url'] = 'https://udger.com/resources/ua-list/bot-detail?bot=' + (r['family'] || '') + '#id' + (r['botid']|| ''); 709 | } 710 | rip['crawler_last_seen'] = r['last_seen'] || ''; 711 | rip['crawler_category'] = r['crawler_classification'] || ''; 712 | rip['crawler_category_code'] = r['crawler_classification_code'] || ''; 713 | rip['crawler_respect_robotstxt'] = r['respect_robotstxt'] || ''; 714 | 715 | // JSON FORMAT 716 | if (opts.full) { 717 | rip['ip_classification'] && dotProp.set(ripJson, 'classification.name', rip['ip_classification']); 718 | rip['ip_classification_code'] && dotProp.set(ripJson, 'classification.code', rip['ip_classification_code']); 719 | } else { 720 | rip['ip_classification_code'] && dotProp.set(ripJson, 'classification', rip['ip_classification_code']); 721 | } 722 | 723 | rip['ip_last_seen'] && dotProp.set(ripJson, 'lastSeen', rip['ip_last_seen']); 724 | rip['ip_hostname'] && dotProp.set(ripJson, 'hostname', rip['ip_hostname']); 725 | rip['ip_country'] && dotProp.set(ripJson, 'geo.country.name', rip['ip_country']); 726 | rip['ip_country_code'] && dotProp.set(ripJson, 'geo.country.code', rip['ip_country_code']); 727 | rip['ip_city'] && dotProp.set(ripJson, 'geo.city', rip['ip_city']); 728 | 729 | rip['crawler_name'] && dotProp.set(ripJson, 'crawler.name', rip['crawler_name']); 730 | if (opts.full) { 731 | rip['crawler_ver'] && dotProp.set(ripJson, 'crawler.version.current', rip['crawler_ver']); 732 | rip['crawler_ver_major'] && dotProp.set(ripJson, 'crawler.version.major', rip['crawler_ver_major']); 733 | rip['crawler_family'] && dotProp.set(ripJson, 'crawler.family.name', rip['crawler_family']); 734 | rip['crawler_family_code'] && dotProp.set(ripJson, 'crawler.family.code', rip['crawler_family_code']); 735 | rip['crawler_family_homepage'] && dotProp.set(ripJson, 'crawler.family.homepage', rip['crawler_family_homepage']); 736 | rip['crawler_family_vendor'] && dotProp.set(ripJson, 'crawler.family.vendor.name', rip['crawler_family_vendor']); 737 | rip['crawler_family_vendor_code'] && dotProp.set(ripJson, 'crawler.family.vendor.code', rip['crawler_family_vendor_code']); 738 | rip['crawler_family_vendor_homepage'] && dotProp.set(ripJson, 'crawler.family.vendor.homepage', rip['crawler_family_vendor_homepage']); 739 | rip['crawler_family_icon'] && dotProp.set(ripJson, 'crawler.family.icon', rip['crawler_family_icon']); 740 | if (r['ip_classification_code'] === 'crawler') { 741 | rip['crawler_family_info_url'] && dotProp.set(ripJson, 'crawler.family.infoUrl', rip['crawler_family_info_url']); 742 | } 743 | rip['crawler_last_seen'] && dotProp.set(ripJson, 'crawler.lastSeen', rip['crawler_last_seen']); 744 | rip['crawler_category'] && dotProp.set(ripJson, 'crawler.category.name', rip['crawler_category']); 745 | rip['crawler_category_code'] && dotProp.set(ripJson, 'crawler.category.code', rip['crawler_category_code']); 746 | rip['crawler_respect_robotstxt'] && dotProp.set(ripJson, 'crawler.respectRobotsTxt', rip['crawler_category_code']); 747 | } else { 748 | rip['crawler_family_code'] && dotProp.set(ripJson, 'crawler.family', rip['crawler_family_code']); 749 | rip['crawler_category_code'] && dotProp.set(ripJson, 'crawler.category', rip['crawler_category_code']); 750 | rip['crawler_last_seen'] && dotProp.set(ripJson, 'crawler.lastSeen', rip['crawler_last_seen']); 751 | } 752 | 753 | } else { 754 | 755 | rip['ip_classification'] = 'Unrecognized'; 756 | rip['ip_classification_code'] = 'unrecognized'; 757 | 758 | if (opts.full) { 759 | dotProp.set(ripJson, 'classification.name', rip['ip_classification']); 760 | dotProp.set(ripJson, 'classification.code', rip['ip_classification_code']); 761 | } else { 762 | dotProp.set(ripJson, 'classification', rip['ip_classification_code']); 763 | } 764 | } 765 | 766 | if (ipver === 4) { 767 | 768 | ipInt = utils.ip2long(ip); 769 | 770 | q = this.db.prepare( 771 | 'SELECT name, name_code, homepage '+ 772 | 'FROM udger_datacenter_range '+ 773 | 'JOIN udger_datacenter_list ON udger_datacenter_range.datacenter_id=udger_datacenter_list.id '+ 774 | 'WHERE iplong_from <=? AND iplong_to >=?' 775 | ); 776 | 777 | r = q.get(ipInt, ipInt); 778 | 779 | if (r) { 780 | 781 | rip['datacenter_name'] = r['name'] || ''; 782 | rip['datacenter_name_code'] = r['name_code'] || ''; 783 | rip['datacenter_homepage'] = r['homepage'] || ''; 784 | 785 | if (opts.full) { 786 | rip['datacenter_name'] && dotProp.set(ripJson, 'datacenter.name', rip['datacenter_name']); 787 | rip['datacenter_name_code'] && dotProp.set(ripJson, 'datacenter.code', rip['datacenter_name_code']); 788 | rip['datacenter_homepage'] && dotProp.set(ripJson, 'datacenter.homepage', rip['datacenter_homepage']); 789 | } else { 790 | rip['datacenter_name_code'] && dotProp.set(ripJson, 'datacenter', rip['datacenter_name_code']); 791 | } 792 | 793 | } 794 | 795 | } else if (ipver === 6) { 796 | 797 | ipa = new Address6(ip); 798 | const t = ipa.canonicalForm().split(':'); 799 | const ipInts = {}; 800 | t.forEach((h, i) => { 801 | ipInts['ipInt'+i] = parseInt(h, 16); 802 | }); 803 | 804 | q = this.db.prepare( 805 | 'SELECT name, name_code, homepage '+ 806 | 'FROM udger_datacenter_range6 '+ 807 | 'JOIN udger_datacenter_list ON udger_datacenter_range6.datacenter_id=udger_datacenter_list.id '+ 808 | 'WHERE '+ 809 | 'iplong_from0 <= @ipInt0 AND iplong_to0 >= @ipInt0 AND '+ 810 | 'iplong_from1 <= @ipInt1 AND iplong_to1 >= @ipInt1 AND '+ 811 | 'iplong_from2 <= @ipInt2 AND iplong_to2 >= @ipInt2 AND '+ 812 | 'iplong_from3 <= @ipInt3 AND iplong_to3 >= @ipInt3 AND '+ 813 | 'iplong_from4 <= @ipInt4 AND iplong_to4 >= @ipInt4 AND '+ 814 | 'iplong_from5 <= @ipInt5 AND iplong_to5 >= @ipInt5 AND '+ 815 | 'iplong_from6 <= @ipInt6 AND iplong_to6 >= @ipInt6 AND '+ 816 | 'iplong_from7 <= @ipInt7 AND iplong_to7 >= @ipInt7' 817 | ); 818 | 819 | r = q.get(ipInts); 820 | 821 | if (r) { 822 | 823 | rip['datacenter_name'] = r['name'] || ''; 824 | rip['datacenter_name_code'] = r['name_code'] || ''; 825 | rip['datacenter_homepage'] = r['homepage'] || ''; 826 | 827 | if (opts.full) { 828 | rip['datacenter_name'] && dotProp.set(ripJson, 'datacenter.name', rip['datacenter_name']); 829 | rip['datacenter_name_code'] && dotProp.set(ripJson, 'datacenter.code', rip['datacenter_name_code']); 830 | rip['datacenter_homepage'] && dotProp.set(ripJson, 'datacenter.homepage', rip['datacenter_homepage']); 831 | } else { 832 | rip['datacenter_name_code'] && dotProp.set(ripJson, 'datacenter', rip['datacenter_name_code']); 833 | } 834 | } 835 | 836 | } 837 | 838 | debug('parse IP address: END'); 839 | 840 | return { 841 | udger:rip, 842 | json:ripJson 843 | }; 844 | } 845 | 846 | /** 847 | * Main parser 848 | * @return {Object} Parsing result 849 | */ 850 | parse(opts) { 851 | 852 | if (!this.db) return {}; 853 | 854 | if ( 855 | this.isCacheEnable() && 856 | this.cacheKeyExist(this.keyCache) 857 | ) { 858 | return this.cacheRead(this.keyCache, opts); 859 | } 860 | 861 | const ret = {}; 862 | if (!opts) opts = {}; 863 | 864 | if (opts.json) { 865 | if (this.ua) ret.userAgent =this.parseUa(this.ua, opts).json; 866 | if (this.ip) ret.ipAddress = this.parseIp(this.ip, opts).json; 867 | if (opts.full) ret.fromCache = false; 868 | } else { 869 | ret['user_agent'] = this.parseUa(this.ua, opts).udger; 870 | ret['ip_address'] = this.parseIp(this.ip, opts).udger; 871 | ret['from_cache'] = false; 872 | } 873 | 874 | if (this.isCacheEnable()) { 875 | this.cacheWrite(this.keyCache, ret); 876 | } 877 | 878 | return ret; 879 | } 880 | 881 | randomSanityChecks(max, callback) { 882 | if (!this.db) { 883 | callback(new Error('Database not ready')); 884 | return false; 885 | } 886 | 887 | if (!max) { 888 | callback(new Error('Please specify maximum number of records')); 889 | return false; 890 | } 891 | 892 | if (typeof max!= 'number') { 893 | callback(new Error('Maximum number of records is not a number')); 894 | return false; 895 | } 896 | 897 | return true; 898 | } 899 | 900 | randomUACrawlers(max, callback) { 901 | 902 | if (!this.randomSanityChecks(max, callback)) return; 903 | 904 | const q = this.db.prepare( 905 | 'SELECT ua_string FROM udger_crawler_list ORDER BY RANDOM() LIMIT ?' 906 | ); 907 | 908 | callback(null, q.all(max)); 909 | return; 910 | } 911 | 912 | randomUAClientsRegex(max, callback) { 913 | if (!this.randomSanityChecks(max, callback)) return; 914 | 915 | const q = this.db.prepare( 916 | 'SELECT regstring FROM udger_client_regex ORDER BY RANDOM() LIMIT ?' 917 | ); 918 | 919 | callback(null, q.all(max)); 920 | return; 921 | } 922 | 923 | randomUAClients(max, callback) { 924 | 925 | if (!this.randomSanityChecks(max, callback)) return; 926 | this.randomUAClientsRegex(max, (err, results) => { 927 | let regexClean; 928 | let randomUA; 929 | let re; 930 | let reClean; 931 | for (let i = 0, len=results.length; i { 41 | 42 | config.udgerParser.setCacheEnable(true); 43 | config.udgerParser.setCacheSize(1); 44 | 45 | let ret; 46 | 47 | config.udgerParser.set({ ua:'FakeUAJustToAddAnEntryInTheCache' }); 48 | config.udgerParser.parse(); 49 | 50 | expected['from_cache'] = false; 51 | config.udgerParser.set({ ua:myUa }); 52 | ret = config.udgerParser.parse(); 53 | t.same(ret, expected, 'should not coming from cache'); 54 | 55 | expected['from_cache'] = true; 56 | config.udgerParser.set({ ua:myUa }); 57 | ret = config.udgerParser.parse(); 58 | t.same(ret, expected, 'should coming from cache'); 59 | 60 | config.udgerParser.set({ ua:'FakeUAJustToAddAnEntryInTheCache' }); 61 | config.udgerParser.parse(); 62 | 63 | expected['from_cache'] = false; 64 | config.udgerParser.set({ ua:myUa }); 65 | ret = config.udgerParser.parse(); 66 | t.same(ret, expected, ' should not coming from cache'); 67 | 68 | t.end(); 69 | } 70 | ); 71 | -------------------------------------------------------------------------------- /test/clientBrowserChrome.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const defaultResult = config.defaultResult; 5 | 6 | const myUa = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'; 7 | 8 | let expected = { 9 | 'user_agent': { 10 | 'ua_string': myUa, 11 | 'ua_class': 'Browser', 12 | 'ua_class_code': 'browser', 13 | 'ua': 'Chrome 62.0.3202.94', 14 | 'ua_version': '62.0.3202.94', 15 | 'ua_version_major': '62', 16 | 'ua_uptodate_current_version': '55', 17 | 'ua_family': 'Chrome', 18 | 'ua_family_code': 'chrome', 19 | 'ua_family_homepage': 'http://www.google.com/chrome/', 20 | 'ua_family_vendor': 'Google Inc.', 21 | 'ua_family_vendor_code': 'google_inc', 22 | 'ua_family_vendor_homepage': 'https://www.google.com/about/company/', 23 | 'ua_family_icon': 'chrome.png', 24 | 'ua_family_icon_big': 'chrome_big.png', 25 | 'ua_family_info_url': 'https://udger.com/resources/ua-list/browser-detail?browser=Chrome', 26 | 'ua_engine': 'WebKit/Blink', 27 | 'os': 'Windows 10', 28 | 'os_code': 'windows_10', 29 | 'os_homepage': 'https://en.wikipedia.org/wiki/Windows_10', 30 | 'os_icon': 'windows10.png', 31 | 'os_icon_big': 'windows10_big.png', 32 | 'os_info_url': 'https://udger.com/resources/ua-list/os-detail?os=Windows 10', 33 | 'os_family': 'Windows', 34 | 'os_family_code': 'windows', 35 | 'os_family_vendor': 'Microsoft Corporation.', 36 | 'os_family_vendor_code': 'microsoft_corporation', 37 | 'os_family_vendor_homepage': 'https://www.microsoft.com/about/', 38 | 'device_class': 'Desktop', 39 | 'device_class_code': 'desktop', 40 | 'device_class_icon': 'desktop.png', 41 | 'device_class_icon_big': 'desktop_big.png', 42 | 'device_class_info_url': 'https://udger.com/resources/ua-list/device-detail?device=Desktop' 43 | } 44 | }; 45 | 46 | expected = config.merge(defaultResult, expected); 47 | 48 | tap.test( 49 | 'User Agent: Chrome Browser should be recognized', 50 | (t) => { 51 | config.udgerParser.set({ ua:myUa }); 52 | const ret = config.udgerParser.parse(); 53 | t.same(ret, expected); 54 | t.end(); 55 | } 56 | ); 57 | -------------------------------------------------------------------------------- /test/clientBrowserChromeJson.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myUa = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'; 5 | 6 | const expected = { 7 | 'userAgent': { 8 | 'ua': { 9 | 'string': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 10 | 'class': 'browser', 11 | 'name': 'Chrome 62.0.3202.94', 12 | 'family': 'chrome', 13 | 'engine': 'WebKit/Blink' 14 | }, 15 | 'os': { 16 | 'code': 'windows_10', 17 | 'family': 'windows' 18 | }, 19 | 'device': { 20 | 'class': 'desktop' 21 | } 22 | } 23 | }; 24 | 25 | tap.test( 26 | 'User Agent: Chrome Browser should be recognized', 27 | (t) => { 28 | config.udgerParser.set({ ua:myUa }); 29 | const ret = config.udgerParser.parse({ json:true }); 30 | t.same(ret, expected); 31 | t.end(); 32 | } 33 | ); 34 | -------------------------------------------------------------------------------- /test/clientBrowserChromeJsonFull.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myUa = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36'; 5 | 6 | const expected = { 7 | 'userAgent': { 8 | 'ua': { 9 | 'string': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 10 | 'class': { 11 | 'name': 'Browser', 12 | 'code': 'browser' 13 | }, 14 | 'name': 'Chrome 62.0.3202.94', 15 | 'version': { 16 | 'current': '62' 17 | }, 18 | 'uptodateCurrentVersion': '55', 19 | 'family': { 20 | 'name': 'Chrome', 21 | 'code': 'chrome', 22 | 'homepage': 'http://www.google.com/chrome/', 23 | 'vendor': { 24 | 'name': 'Google Inc.', 25 | 'code': 'google_inc', 26 | 'homepage': 'https://www.google.com/about/company/' 27 | }, 28 | 'icon': 'chrome.png', 29 | 'iconBig': 'chrome_big.png', 30 | 'infoUrl': 'https://udger.com/resources/ua-list/browser-detail?browser=Chrome' 31 | }, 32 | 'engine': 'WebKit/Blink' 33 | }, 34 | 'os': { 35 | 'name': 'Windows 10', 36 | 'code': 'windows_10', 37 | 'homepage': 'https://en.wikipedia.org/wiki/Windows_10', 38 | 'icon': 'windows10.png', 39 | 'iconBig': 'windows10_big.png', 40 | 'infoUrl': 'https://udger.com/resources/ua-list/os-detail?os=Windows 10', 41 | 'family': { 42 | 'name': 'Windows', 43 | 'code': 'windows', 44 | 'vendor': { 45 | 'name': 'Microsoft Corporation.', 46 | 'code': 'microsoft_corporation', 47 | 'homepage': 'https://www.microsoft.com/about/' 48 | } 49 | } 50 | }, 51 | 'device': { 52 | 'class': { 53 | 'name': 'Desktop', 54 | 'code': 'desktop', 55 | 'icon': 'desktop.png', 56 | 'iconBig': 'desktop_big.png', 57 | 'infoUrl': 'https://udger.com/resources/ua-list/device-detail?device=Desktop' 58 | } 59 | } 60 | }, 61 | 'fromCache': false 62 | }; 63 | 64 | 65 | tap.test( 66 | 'User Agent: Chrome Browser should be recognized', 67 | (t) => { 68 | config.udgerParser.set({ ua:myUa }); 69 | const ret = config.udgerParser.parse({ json:true, full:true }); 70 | t.same(ret, expected); 71 | t.end(); 72 | } 73 | ); 74 | -------------------------------------------------------------------------------- /test/clientBrowserIEMobile.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const defaultResult = config.defaultResult; 5 | 6 | const myUa = 'IEMobile 1.1'; 7 | 8 | let expected = { 9 | 'user_agent': { 10 | 'ua_string': myUa, 11 | 'ua_class': 'Mobile browser', 12 | 'ua_class_code': 'mobile_browser', 13 | 'ua': 'IE Mobile 1.1', 14 | 'ua_version': '1.1', 15 | 'ua_version_major': '1', 16 | 'ua_family': 'IE Mobile', 17 | 'ua_family_code': 'ie_mobile', 18 | 'ua_family_homepage': 'https://en.wikipedia.org/wiki/Internet_Explorer_Mobile', 19 | 'ua_family_vendor': 'Microsoft Corporation.', 20 | 'ua_family_vendor_code': 'microsoft_corporation', 21 | 'ua_family_vendor_homepage': 'https://www.microsoft.com/about/', 22 | 'ua_family_icon': 'iemobile.png', 23 | 'ua_family_info_url': 'https://udger.com/resources/ua-list/browser-detail?browser=IE Mobile', 24 | 'ua_engine': 'Trident', 25 | 'device_class': 'Smartphone', 26 | 'device_class_code': 'smartphone', 27 | 'device_class_icon': 'phone.png', 28 | 'device_class_icon_big': 'phone_big.png', 29 | 'device_class_info_url': 'https://udger.com/resources/ua-list/device-detail?device=Smartphone' 30 | } 31 | }; 32 | 33 | expected = config.merge(defaultResult, expected); 34 | 35 | tap.test( 36 | 'User Agent: IEMobile 1.1 should be recognized', 37 | (t) => { 38 | config.udgerParser.set({ ua:myUa }); 39 | const ret = config.udgerParser.parse(); 40 | t.same(ret, expected); 41 | t.end(); 42 | } 43 | ); 44 | -------------------------------------------------------------------------------- /test/clientBrowserIEMobileJson.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myUa = 'IEMobile 1.1'; 5 | 6 | const expected = { 7 | 'userAgent': { 8 | 'ua': { 9 | 'string': 'IEMobile 1.1', 10 | 'class': 'mobile_browser', 11 | 'name': 'IE Mobile 1.1', 12 | 'family': 'ie_mobile', 13 | 'engine': 'Trident' 14 | }, 15 | 'device': { 16 | 'class': 'smartphone' 17 | } 18 | } 19 | }; 20 | 21 | tap.test( 22 | 'User Agent: IEMobile 1.1 should be recognized', 23 | (t) => { 24 | config.udgerParser.set({ ua:myUa }); 25 | const ret = config.udgerParser.parse({ json:true }); 26 | t.same(ret, expected); 27 | t.end(); 28 | } 29 | ); 30 | -------------------------------------------------------------------------------- /test/clientBrowserIEMobileJsonFull.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const defaultResult = config.defaultResult; 5 | 6 | const myUa = 'IEMobile 1.1'; 7 | 8 | let expected = { 9 | 'user_agent': { 10 | 'ua_string': myUa, 11 | 'ua_class': 'Mobile browser', 12 | 'ua_class_code': 'mobile_browser', 13 | 'ua': 'IE Mobile 1.1', 14 | 'ua_version': '1.1', 15 | 'ua_version_major': '1', 16 | 'ua_family': 'IE Mobile', 17 | 'ua_family_code': 'ie_mobile', 18 | 'ua_family_homepage': 'https://en.wikipedia.org/wiki/Internet_Explorer_Mobile', 19 | 'ua_family_vendor': 'Microsoft Corporation.', 20 | 'ua_family_vendor_code': 'microsoft_corporation', 21 | 'ua_family_vendor_homepage': 'https://www.microsoft.com/about/', 22 | 'ua_family_icon': 'iemobile.png', 23 | 'ua_family_info_url': 'https://udger.com/resources/ua-list/browser-detail?browser=IE Mobile', 24 | 'ua_engine': 'Trident', 25 | 'device_class': 'Smartphone', 26 | 'device_class_code': 'smartphone', 27 | 'device_class_icon': 'phone.png', 28 | 'device_class_icon_big': 'phone_big.png', 29 | 'device_class_info_url': 'https://udger.com/resources/ua-list/device-detail?device=Smartphone' 30 | } 31 | }; 32 | 33 | expected = config.merge(defaultResult, expected); 34 | 35 | tap.test( 36 | 'User Agent: IEMobile 1.1 should be recognized', 37 | (t) => { 38 | config.udgerParser.set({ ua:myUa }); 39 | const ret = config.udgerParser.parse(); 40 | t.same(ret, expected); 41 | t.end(); 42 | } 43 | ); 44 | -------------------------------------------------------------------------------- /test/clientCrawlerGoogleBot.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const defaultResult = config.defaultResult; 5 | 6 | const myUa = 'Googlebot/2.1 (+http://www.google.com/bot.html)'; 7 | 8 | let expected = { 9 | 'user_agent': { 10 | 'ua_string': myUa, 11 | 'ua_class': 'Crawler', 12 | 'ua_class_code': 'crawler', 13 | 'ua': 'Googlebot/2.1', 14 | 'ua_version': '2.1', 15 | 'ua_version_major': '2', 16 | 'ua_family': 'Googlebot', 17 | 'ua_family_code': 'googlebot', 18 | 'ua_family_homepage': 'http://www.google.com/bot.html', 19 | 'ua_family_vendor': 'Google Inc.', 20 | 'ua_family_vendor_code': 'google_inc', 21 | 'ua_family_vendor_homepage': 'https://www.google.com/about/company/', 22 | 'ua_family_icon': 'bot_googlebot.png', 23 | 'ua_family_info_url': 'https://udger.com/resources/ua-list/bot-detail?bot=Googlebot#id4966', 24 | 'device_class': 'Unrecognized', 25 | 'device_class_code': 'unrecognized', 26 | 'device_class_icon': 'other.png', 27 | 'device_class_icon_big': 'other_big.png', 28 | 'device_class_info_url': 'https://udger.com/resources/ua-list/device-detail?device=Unrecognized', 29 | 'crawler_last_seen': '2017-01-06 08:57:43', 30 | 'crawler_category': 'Search engine bot', 31 | 'crawler_category_code': 'search_engine_bot', 32 | 'crawler_respect_robotstxt': 'yes' 33 | } 34 | }; 35 | 36 | expected = config.merge(defaultResult, expected); 37 | 38 | tap.test( 39 | 'User Agent: GoogleBot should be recognized', 40 | (t) => { 41 | config.udgerParser.set({ ua:myUa }); 42 | const ret = config.udgerParser.parse(); 43 | t.same(ret, expected); 44 | t.end(); 45 | } 46 | ); 47 | -------------------------------------------------------------------------------- /test/clientCrawlerGoogleBotIp.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const defaultResult = config.defaultResult; 5 | 6 | const myUa = 'Googlebot/2.1 (+http://www.google.com/bot.html)'; 7 | const myIp = '66.249.64.73'; 8 | 9 | let expected = { 10 | 'user_agent': { 11 | 'ua_string': myUa, 12 | 'ua_class': 'Crawler', 13 | 'ua_class_code': 'crawler', 14 | 'ua': 'Googlebot/2.1', 15 | 'ua_version': '2.1', 16 | 'ua_version_major': '2', 17 | 'ua_family': 'Googlebot', 18 | 'ua_family_code': 'googlebot', 19 | 'ua_family_homepage': 'http://www.google.com/bot.html', 20 | 'ua_family_vendor': 'Google Inc.', 21 | 'ua_family_vendor_code': 'google_inc', 22 | 'ua_family_vendor_homepage': 'https://www.google.com/about/company/', 23 | 'ua_family_icon': 'bot_googlebot.png', 24 | 'ua_family_info_url': 'https://udger.com/resources/ua-list/bot-detail?bot=Googlebot#id4966', 25 | 'device_class': 'Unrecognized', 26 | 'device_class_code': 'unrecognized', 27 | 'device_class_icon': 'other.png', 28 | 'device_class_icon_big': 'other_big.png', 29 | 'device_class_info_url': 'https://udger.com/resources/ua-list/device-detail?device=Unrecognized', 30 | 'crawler_last_seen': '2017-01-06 08:57:43', 31 | 'crawler_category': 'Search engine bot', 32 | 'crawler_category_code': 'search_engine_bot', 33 | 'crawler_respect_robotstxt': 'yes' 34 | }, 35 | 'ip_address': { 36 | 'ip': myIp, 37 | 'ip_ver': 4, 38 | 'ip_classification': 'Crawler', 39 | 'ip_classification_code': 'crawler', 40 | 'ip_hostname': 'crawl-66-249-64-73.googlebot.com', 41 | 'ip_last_seen': '2016-10-02 09:16:57', 42 | 'ip_country': 'United States', 43 | 'ip_country_code': 'US', 44 | 'ip_city': 'Mountain View', 45 | 'crawler_name': 'Googlebot/2.1', 46 | 'crawler_ver': '2.1', 47 | 'crawler_ver_major': '2', 48 | 'crawler_family': 'Googlebot', 49 | 'crawler_family_code': 'googlebot', 50 | 'crawler_family_homepage': 'http://www.google.com/bot.html', 51 | 'crawler_family_vendor': 'Google Inc.', 52 | 'crawler_family_vendor_code': 'google_inc', 53 | 'crawler_family_vendor_homepage': 'https://www.google.com/about/company/', 54 | 'crawler_family_icon': 'bot_googlebot.png', 55 | 'crawler_family_info_url': 'https://udger.com/resources/ua-list/bot-detail?bot=Googlebot#id31', 56 | 'crawler_last_seen': '2017-01-06 17:52:46', 57 | 'crawler_category': 'Search engine bot', 58 | 'crawler_category_code': 'search_engine_bot', 59 | 'crawler_respect_robotstxt': 'yes', 60 | 'datacenter_name': 'Google sites', 61 | 'datacenter_name_code': 'googgle_sites', 62 | 'datacenter_homepage': 'http://sites.google.com/' 63 | }, 64 | 65 | }; 66 | 67 | expected = config.merge(defaultResult, expected); 68 | 69 | tap.test( 70 | 'User Agent: GoogleBot should be recognized', 71 | (t) => { 72 | config.udgerParser.set({ ua:myUa, ip:myIp }); 73 | const ret = config.udgerParser.parse(); 74 | t.same(ret, expected); 75 | t.end(); 76 | } 77 | ); 78 | -------------------------------------------------------------------------------- /test/clientCrawlerGoogleBotIpJson.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myUa = 'Googlebot/2.1 (+http://www.google.com/bot.html)'; 5 | const myIp = '66.249.64.73'; 6 | 7 | const expected = { 8 | 'userAgent': { 9 | 'ua': { 10 | 'string': 'Googlebot/2.1 (+http://www.google.com/bot.html)', 11 | 'class': 'crawler', 12 | 'name': 'Googlebot/2.1', 13 | 'family': { 14 | 'code': 'googlebot', 15 | 'homepage': 'http://www.google.com/bot.html', 16 | 'vendor': 'google_inc' 17 | } 18 | }, 19 | 'crawler': { 20 | 'lastSeen': '2017-01-06 08:57:43', 21 | 'category': 'search_engine_bot' 22 | }, 23 | 'device': { 24 | 'class': 'unrecognized' 25 | } 26 | }, 27 | 'ipAddress': { 28 | 'ip': '66.249.64.73', 29 | 'classification': 'crawler', 30 | 'lastSeen': '2016-10-02 09:16:57', 31 | 'hostname': 'crawl-66-249-64-73.googlebot.com', 32 | 'geo': { 33 | 'country': { 34 | 'name': 'United States', 35 | 'code': 'US' 36 | }, 37 | 'city': 'Mountain View' 38 | }, 39 | 'crawler': { 40 | 'name': 'Googlebot/2.1', 41 | 'family': 'googlebot', 42 | 'category': 'search_engine_bot', 43 | 'lastSeen': '2017-01-06 17:52:46' 44 | }, 45 | 'datacenter': 'googgle_sites' 46 | } 47 | }; 48 | 49 | 50 | tap.test( 51 | 'User Agent: GoogleBot should be recognized', 52 | (t) => { 53 | config.udgerParser.set({ ua:myUa, ip:myIp }); 54 | const ret = config.udgerParser.parse({ json:true }); 55 | t.same(ret, expected); 56 | t.end(); 57 | } 58 | ); 59 | -------------------------------------------------------------------------------- /test/clientCrawlerGoogleBotIpJsonFull.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myUa = 'Googlebot/2.1 (+http://www.google.com/bot.html)'; 5 | const myIp = '66.249.64.73'; 6 | 7 | const expected = { 8 | 'userAgent': { 9 | 'ua': { 10 | 'string': 'Googlebot/2.1 (+http://www.google.com/bot.html)', 11 | 'class': { 12 | 'name': 'Crawler', 13 | 'code': 'crawler' 14 | }, 15 | 'name': 'Googlebot/2.1', 16 | 'version': { 17 | 'current': '2.1', 18 | 'major': '2' 19 | }, 20 | 'family': { 21 | 'name': 'Googlebot', 22 | 'code': 'googlebot', 23 | 'homepage': 'http://www.google.com/bot.html', 24 | 'vendor': { 25 | 'name': 'Google Inc.', 26 | 'code': 'google_inc', 27 | 'homepage': 'http://www.google.com/bot.html' 28 | }, 29 | 'icon': 'bot_googlebot.png', 30 | 'infoUrl': 'https://udger.com/resources/ua-list/bot-detail?bot=Googlebot#id4966' 31 | } 32 | }, 33 | 'crawler': { 34 | 'lastSeen': '2017-01-06 08:57:43', 35 | 'category': { 36 | 'name': 'Search engine bot', 37 | 'code': 'search_engine_bot' 38 | }, 39 | 'respectRobotsTxt': 'yes' 40 | }, 41 | 'device': { 42 | 'class': { 43 | 'name': 'Unrecognized', 44 | 'code': 'unrecognized', 45 | 'icon': 'other.png', 46 | 'iconBig': 'other_big.png', 47 | 'infoUrl': 'https://udger.com/resources/ua-list/device-detail?device=Unrecognized' 48 | } 49 | } 50 | }, 51 | 'ipAddress': { 52 | 'ip': '66.249.64.73', 53 | 'version': 4, 54 | 'classification': { 55 | 'name': 'Crawler', 56 | 'code': 'crawler' 57 | }, 58 | 'lastSeen': '2016-10-02 09:16:57', 59 | 'hostname': 'crawl-66-249-64-73.googlebot.com', 60 | 'geo': { 61 | 'country': { 62 | 'name': 'United States', 63 | 'code': 'US' 64 | }, 65 | 'city': 'Mountain View' 66 | }, 67 | 'crawler': { 68 | 'name': 'Googlebot/2.1', 69 | 'version': { 70 | 'current': '2.1', 71 | 'major': '2' 72 | }, 73 | 'family': { 74 | 'name': 'Googlebot', 75 | 'code': 'googlebot', 76 | 'homepage': 'http://www.google.com/bot.html', 77 | 'vendor': { 78 | 'name': 'Google Inc.', 79 | 'code': 'google_inc', 80 | 'homepage': 'https://www.google.com/about/company/' 81 | }, 82 | 'icon': 'bot_googlebot.png', 83 | 'infoUrl': 'https://udger.com/resources/ua-list/bot-detail?bot=Googlebot#id31' 84 | }, 85 | 'lastSeen': '2017-01-06 17:52:46', 86 | 'category': { 87 | 'name': 'Search engine bot', 88 | 'code': 'search_engine_bot' 89 | }, 90 | 'respectRobotsTxt': 'search_engine_bot' 91 | }, 92 | 'datacenter': { 93 | 'name': 'Google sites', 94 | 'code': 'googgle_sites', 95 | 'homepage': 'http://sites.google.com/' 96 | } 97 | }, 98 | 'fromCache': false 99 | }; 100 | 101 | tap.test( 102 | 'User Agent: GoogleBot should be recognized', 103 | (t) => { 104 | config.udgerParser.set({ ua:myUa, ip:myIp }); 105 | const ret = config.udgerParser.parse({ json:true, full:true }); 106 | t.same(ret, expected); 107 | t.end(); 108 | } 109 | ); 110 | -------------------------------------------------------------------------------- /test/clientCrawlerGoogleBotJson.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | let myUa = 'Googlebot/2.1 (+http://www.google.com/bot.html)'; 5 | 6 | let expected = { 7 | 'userAgent': { 8 | 'ua': { 9 | 'string': 'Googlebot/2.1 (+http://www.google.com/bot.html)', 10 | 'class': 'crawler', 11 | 'name': 'Googlebot/2.1', 12 | 'family': { 13 | 'code': 'googlebot', 14 | 'homepage': 'http://www.google.com/bot.html', 15 | 'vendor': 'google_inc' 16 | } 17 | }, 18 | 'crawler': { 19 | 'lastSeen': '2017-01-06 08:57:43', 20 | 'category': 'search_engine_bot' 21 | }, 22 | 'device': { 23 | 'class': 'unrecognized' 24 | } 25 | } 26 | }; 27 | 28 | 29 | tap.test( 30 | 'User Agent: GoogleBot should be recognized', 31 | (t) => { 32 | config.udgerParser.set({ ua:myUa }); 33 | const ret = config.udgerParser.parse({ json:true }); 34 | t.same(ret, expected); 35 | t.end(); 36 | } 37 | ); 38 | -------------------------------------------------------------------------------- /test/clientCrawlerGoogleBotJsonFull.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myUa = 'Googlebot/2.1 (+http://www.google.com/bot.html)'; 5 | 6 | const expected = { 7 | 'userAgent': { 8 | 'ua': { 9 | 'string': 'Googlebot/2.1 (+http://www.google.com/bot.html)', 10 | 'class': { 11 | 'name': 'Crawler', 12 | 'code': 'crawler' 13 | }, 14 | 'name': 'Googlebot/2.1', 15 | 'version': { 16 | 'current': '2.1', 17 | 'major': '2' 18 | }, 19 | 'family': { 20 | 'name': 'Googlebot', 21 | 'code': 'googlebot', 22 | 'homepage': 'http://www.google.com/bot.html', 23 | 'vendor': { 24 | 'name': 'Google Inc.', 25 | 'code': 'google_inc', 26 | 'homepage': 'http://www.google.com/bot.html' 27 | }, 28 | 'icon': 'bot_googlebot.png', 29 | 'infoUrl': 'https://udger.com/resources/ua-list/bot-detail?bot=Googlebot#id4966' 30 | } 31 | }, 32 | 'crawler': { 33 | 'lastSeen': '2017-01-06 08:57:43', 34 | 'category': { 35 | 'name': 'Search engine bot', 36 | 'code': 'search_engine_bot' 37 | }, 38 | 'respectRobotsTxt': 'yes' 39 | }, 40 | 'device': { 41 | 'class': { 42 | 'name': 'Unrecognized', 43 | 'code': 'unrecognized', 44 | 'icon': 'other.png', 45 | 'iconBig': 'other_big.png', 46 | 'infoUrl': 'https://udger.com/resources/ua-list/device-detail?device=Unrecognized' 47 | } 48 | } 49 | }, 50 | 'fromCache': false 51 | }; 52 | 53 | tap.test( 54 | 'User Agent: GoogleBot should be recognized', 55 | (t) => { 56 | config.udgerParser.set({ ua:myUa }); 57 | const ret = config.udgerParser.parse({ json:true, full:true }); 58 | t.same(ret, expected); 59 | t.end(); 60 | } 61 | ); 62 | -------------------------------------------------------------------------------- /test/clientCrawlerPingoMeter.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const defaultResult = config.defaultResult; 5 | 6 | const myUa = 'PINGOMETER_BOT_(HTTPS://PINGOMETER.COM)'; 7 | 8 | let expected = { 9 | 'user_agent': { 10 | 'ua_string': myUa, 11 | 'ua_class': 'Crawler', 12 | 'ua_class_code': 'crawler', 13 | 'ua': 'PINGOMETER', 14 | 'ua_family': 'PINGOMETER', 15 | 'ua_family_code': 'pingometer', 16 | 'ua_family_vendor': 'Pingometer, LLC', 17 | 'ua_family_vendor_code': 'pingometer_llc', 18 | 'ua_family_vendor_homepage': 'http://pingometer.com/', 19 | 'ua_family_icon': 'bot_pingometer.png', 20 | 'ua_family_info_url': 'https://udger.com/resources/ua-list/bot-detail?bot=PINGOMETER#id20112', 21 | 'device_class': 'Unrecognized', 22 | 'device_class_code': 'unrecognized', 23 | 'device_class_icon': 'other.png', 24 | 'device_class_icon_big': 'other_big.png', 25 | 'device_class_info_url': 'https://udger.com/resources/ua-list/device-detail?device=Unrecognized', 26 | 'crawler_last_seen': '2017-01-06 18:49:59', 27 | 'crawler_category': 'Site monitor', 28 | 'crawler_category_code': 'site_monitor', 29 | 'crawler_respect_robotstxt': 'no' 30 | } 31 | }; 32 | 33 | expected = config.merge(defaultResult, expected); 34 | 35 | tap.test( 36 | 'User Agent: PingoMeter should be recognized', 37 | (t) => { 38 | config.udgerParser.set({ ua:myUa }); 39 | const ret = config.udgerParser.parse(); 40 | t.same(ret, expected); 41 | t.end(); 42 | } 43 | ); 44 | -------------------------------------------------------------------------------- /test/clientCrawlerPingoMeterJson.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myUa = 'PINGOMETER_BOT_(HTTPS://PINGOMETER.COM)'; 5 | 6 | const expected = { 7 | 'userAgent': { 8 | 'ua': { 9 | 'string': 'PINGOMETER_BOT_(HTTPS://PINGOMETER.COM)', 10 | 'class': 'crawler', 11 | 'name': 'PINGOMETER', 12 | 'family': { 13 | 'code': 'pingometer', 14 | 'vendor': 'pingometer_llc' 15 | } 16 | }, 17 | 'crawler': { 18 | 'lastSeen': '2017-01-06 18:49:59', 19 | 'category': 'site_monitor' 20 | }, 21 | 'device': { 22 | 'class': 'unrecognized' 23 | } 24 | } 25 | }; 26 | 27 | 28 | tap.test( 29 | 'User Agent: PingoMeter should be recognized', 30 | (t) => { 31 | config.udgerParser.set({ ua:myUa }); 32 | const ret = config.udgerParser.parse({ json:true }); 33 | t.same(ret, expected); 34 | t.end(); 35 | } 36 | ); 37 | -------------------------------------------------------------------------------- /test/clientCrawlerPingoMeterJsonFull.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myUa = 'PINGOMETER_BOT_(HTTPS://PINGOMETER.COM)'; 5 | 6 | const expected = { 7 | 'userAgent': { 8 | 'ua': { 9 | 'string': 'PINGOMETER_BOT_(HTTPS://PINGOMETER.COM)', 10 | 'class': { 11 | 'name': 'Crawler', 12 | 'code': 'crawler' 13 | }, 14 | 'name': 'PINGOMETER', 15 | 'family': { 16 | 'name': 'PINGOMETER', 17 | 'code': 'pingometer', 18 | 'vendor': { 19 | 'name': 'Pingometer, LLC', 20 | 'code': 'pingometer_llc' 21 | }, 22 | 'icon': 'bot_pingometer.png', 23 | 'infoUrl': 'https://udger.com/resources/ua-list/bot-detail?bot=PINGOMETER#id20112' 24 | } 25 | }, 26 | 'crawler': { 27 | 'lastSeen': '2017-01-06 18:49:59', 28 | 'category': { 29 | 'name': 'Site monitor', 30 | 'code': 'site_monitor' 31 | }, 32 | 'respectRobotsTxt': 'no' 33 | }, 34 | 'device': { 35 | 'class': { 36 | 'name': 'Unrecognized', 37 | 'code': 'unrecognized', 38 | 'icon': 'other.png', 39 | 'iconBig': 'other_big.png', 40 | 'infoUrl': 'https://udger.com/resources/ua-list/device-detail?device=Unrecognized' 41 | } 42 | } 43 | }, 44 | 'fromCache': false 45 | }; 46 | 47 | tap.test( 48 | 'User Agent: PingoMeter should be recognized', 49 | (t) => { 50 | config.udgerParser.set({ ua:myUa }); 51 | const ret = config.udgerParser.parse({ json:true, full:true }); 52 | t.same(ret, expected); 53 | t.end(); 54 | } 55 | ); 56 | -------------------------------------------------------------------------------- /test/clientUnrecognized.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const defaultResult = config.defaultResult; 5 | 6 | const myUa = 'myUnknowUA'; 7 | 8 | let expected = { 9 | 'user_agent': { 10 | 'ua_string': 'myUnknowUA', 11 | 'ua_class': 'Unrecognized', 12 | 'ua_class_code': 'unrecognized' 13 | } 14 | }; 15 | 16 | expected = config.merge(defaultResult, expected); 17 | 18 | tap.test( 19 | 'User Agent: myUnknowUA should return unrecognized', 20 | (t) => { 21 | config.udgerParser.set({ ua:myUa }); 22 | const ret = config.udgerParser.parse(); 23 | t.same(ret, expected); 24 | t.end(); 25 | } 26 | ); 27 | -------------------------------------------------------------------------------- /test/clientUnrecognizedJson.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myUa = 'myUnknowUA'; 5 | 6 | const expected = { 7 | 'userAgent': { 8 | 'ua': { 9 | 'string': 'myUnknowUA', 10 | 'class': 'unrecognized' 11 | } 12 | } 13 | }; 14 | 15 | tap.test( 16 | 'User Agent: myUnknowUA should return unrecognized', 17 | (t) => { 18 | config.udgerParser.set({ ua:myUa }); 19 | const ret = config.udgerParser.parse({ json:true }); 20 | t.same(ret, expected); 21 | t.end(); 22 | } 23 | ); 24 | -------------------------------------------------------------------------------- /test/clientUnrecognizedJsonFull.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myUa = 'myUnknowUA'; 5 | 6 | const expected = { 7 | 'userAgent': { 8 | 'ua': { 9 | 'string': 'myUnknowUA', 10 | 'class': { 11 | 'name': 'Unrecognized', 12 | 'code': 'unrecognized' 13 | } 14 | } 15 | }, 16 | 'fromCache': false 17 | }; 18 | 19 | 20 | tap.test( 21 | 'User Agent: myUnknowUA should return unrecognized', 22 | (t) => { 23 | config.udgerParser.set({ ua:myUa }); 24 | const ret = config.udgerParser.parse({ json:true, full:true }); 25 | t.same(ret, expected); 26 | t.end(); 27 | } 28 | ); 29 | -------------------------------------------------------------------------------- /test/datacenterGoogleIpV4.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const defaultResult = config.defaultResult; 5 | 6 | const myIp = '66.249.64.73'; 7 | 8 | let expected = { 9 | 'ip_address': { 10 | 'ip': '66.249.64.73', 11 | 'ip_ver': 4, 12 | 'ip_classification': 'Crawler', 13 | 'ip_classification_code': 'crawler', 14 | 'ip_hostname': 'crawl-66-249-64-73.googlebot.com', 15 | 'ip_last_seen': '2016-10-02 09:16:57', 16 | 'ip_country': 'United States', 17 | 'ip_country_code': 'US', 18 | 'ip_city': 'Mountain View', 19 | 'crawler_name': 'Googlebot/2.1', 20 | 'crawler_ver': '2.1', 21 | 'crawler_ver_major': '2', 22 | 'crawler_family': 'Googlebot', 23 | 'crawler_family_code': 'googlebot', 24 | 'crawler_family_homepage': 'http://www.google.com/bot.html', 25 | 'crawler_family_vendor': 'Google Inc.', 26 | 'crawler_family_vendor_code': 'google_inc', 27 | 'crawler_family_vendor_homepage': 'https://www.google.com/about/company/', 28 | 'crawler_family_icon': 'bot_googlebot.png', 29 | 'crawler_family_info_url': 'https://udger.com/resources/ua-list/bot-detail?bot=Googlebot#id31', 30 | 'crawler_last_seen': '2017-01-06 17:52:46', 31 | 'crawler_category': 'Search engine bot', 32 | 'crawler_category_code': 'search_engine_bot', 33 | 'crawler_respect_robotstxt': 'yes', 34 | 'datacenter_name': 'Google sites', 35 | 'datacenter_name_code': 'googgle_sites', 36 | 'datacenter_homepage': 'http://sites.google.com/' 37 | } 38 | }; 39 | 40 | expected = config.merge(defaultResult, expected); 41 | 42 | tap.test( 43 | 'IP Address: '+myIp+' google should be in datacenter ipv4 list', 44 | (t) => { 45 | config.udgerParser.set({ ip:myIp }); 46 | const ret = config.udgerParser.parse(); 47 | t.same(ret, expected); 48 | t.end(); 49 | } 50 | ); 51 | -------------------------------------------------------------------------------- /test/datacenterGoogleIpV4Json.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myIp = '66.249.64.73'; 5 | 6 | const expected = { 7 | 'ipAddress': { 8 | 'ip': '66.249.64.73', 9 | 'classification': 'crawler', 10 | 'lastSeen': '2016-10-02 09:16:57', 11 | 'hostname': 'crawl-66-249-64-73.googlebot.com', 12 | 'geo': { 13 | 'country': { 14 | 'name': 'United States', 15 | 'code': 'US' 16 | }, 17 | 'city': 'Mountain View' 18 | }, 19 | 'crawler': { 20 | 'name': 'Googlebot/2.1', 21 | 'family': 'googlebot', 22 | 'category': 'search_engine_bot', 23 | 'lastSeen': '2017-01-06 17:52:46' 24 | }, 25 | 'datacenter': 'googgle_sites' 26 | } 27 | }; 28 | 29 | tap.test( 30 | 'IP Address: '+myIp+' google should be in datacenter ipv4 list', 31 | (t) => { 32 | config.udgerParser.set({ ip:myIp }); 33 | const ret = config.udgerParser.parse({ json:true }); 34 | t.same(ret, expected); 35 | t.end(); 36 | } 37 | ); 38 | -------------------------------------------------------------------------------- /test/datacenterGoogleIpV4JsonFull.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myIp = '66.249.64.73'; 5 | 6 | const expected = { 7 | 'ipAddress': { 8 | 'ip': '66.249.64.73', 9 | 'version': 4, 10 | 'classification': { 11 | 'name': 'Crawler', 12 | 'code': 'crawler' 13 | }, 14 | 'lastSeen': '2016-10-02 09:16:57', 15 | 'hostname': 'crawl-66-249-64-73.googlebot.com', 16 | 'geo': { 17 | 'country': { 18 | 'name': 'United States', 19 | 'code': 'US' 20 | }, 21 | 'city': 'Mountain View' 22 | }, 23 | 'crawler': { 24 | 'name': 'Googlebot/2.1', 25 | 'version': { 26 | 'current': '2.1', 27 | 'major': '2' 28 | }, 29 | 'family': { 30 | 'name': 'Googlebot', 31 | 'code': 'googlebot', 32 | 'homepage': 'http://www.google.com/bot.html', 33 | 'vendor': { 34 | 'name': 'Google Inc.', 35 | 'code': 'google_inc', 36 | 'homepage': 'https://www.google.com/about/company/' 37 | }, 38 | 'icon': 'bot_googlebot.png', 39 | 'infoUrl': 'https://udger.com/resources/ua-list/bot-detail?bot=Googlebot#id31' 40 | }, 41 | 'lastSeen': '2017-01-06 17:52:46', 42 | 'category': { 43 | 'name': 'Search engine bot', 44 | 'code': 'search_engine_bot' 45 | }, 46 | 'respectRobotsTxt': 'search_engine_bot' 47 | }, 48 | 'datacenter': { 49 | 'name': 'Google sites', 50 | 'code': 'googgle_sites', 51 | 'homepage': 'http://sites.google.com/' 52 | } 53 | }, 54 | 'fromCache': false 55 | }; 56 | 57 | 58 | tap.test( 59 | 'IP Address: '+myIp+' google should be in datacenter ipv4 list', 60 | (t) => { 61 | config.udgerParser.set({ ip:myIp }); 62 | const ret = config.udgerParser.parse({ json:true, full:true }); 63 | t.same(ret, expected); 64 | t.end(); 65 | } 66 | ); 67 | -------------------------------------------------------------------------------- /test/datacenterOvhCgiProxyIpV6.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const defaultResult = config.defaultResult; 5 | 6 | const myIp = '2001:41d0:8:d54c::1'; 7 | 8 | let expected = { 9 | 'ip_address': { 10 | 'ip': myIp, // OVH datacenter 11 | 'ip_ver': 6, 12 | 'ip_classification': 'Cgi proxy', 13 | 'ip_classification_code': 'cgi_proxy', 14 | 'ip_last_seen': '2017-01-06 03:44:15', 15 | 'ip_country': 'France', 16 | 'ip_country_code': 'FR', 17 | 'ip_city': 'Cachan', 18 | 'datacenter_name': 'OVH', 19 | 'datacenter_name_code': 'ovh', 20 | 'datacenter_homepage': 'http://www.ovh.com/' 21 | } 22 | }; 23 | 24 | expected = config.merge(defaultResult, expected); 25 | 26 | tap.test( 27 | 'IP Address: '+myIp+' ovh cgi should be in datacenter ipv6 list', 28 | (t) => { 29 | config.udgerParser.set({ ip:myIp }); 30 | const ret = config.udgerParser.parse(); 31 | t.same(ret, expected); 32 | t.end(); 33 | } 34 | ); 35 | -------------------------------------------------------------------------------- /test/datacenterOvhCgiProxyIpV6Json.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myIp = '2001:41d0:8:d54c::1'; 5 | 6 | const expected = { 7 | 'ipAddress': { 8 | 'ip': '2001:41d0:8:d54c::1', 9 | 'classification': 'cgi_proxy', 10 | 'lastSeen': '2017-01-06 03:44:15', 11 | 'geo': { 12 | 'country': { 13 | 'name': 'France', 14 | 'code': 'FR' 15 | }, 16 | 'city': 'Cachan' 17 | }, 18 | 'datacenter': 'ovh' 19 | } 20 | }; 21 | 22 | tap.test( 23 | 'IP Address: '+myIp+' ovh cgi should be in datacenter ipv6 list', 24 | (t) => { 25 | config.udgerParser.set({ ip:myIp }); 26 | const ret = config.udgerParser.parse({ json:true }); 27 | t.same(ret, expected); 28 | t.end(); 29 | } 30 | ); 31 | -------------------------------------------------------------------------------- /test/datacenterOvhCgiProxyIpV6JsonFull.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myIp = '2001:41d0:8:d54c::1'; 5 | 6 | const expected = { 7 | 'ipAddress': { 8 | 'ip': '2001:41d0:8:d54c::1', 9 | 'version': 6, 10 | 'classification': { 11 | 'name': 'Cgi proxy', 12 | 'code': 'cgi_proxy' 13 | }, 14 | 'lastSeen': '2017-01-06 03:44:15', 15 | 'geo': { 16 | 'country': { 17 | 'name': 'France', 18 | 'code': 'FR' 19 | }, 20 | 'city': 'Cachan' 21 | }, 22 | 'datacenter': { 23 | 'name': 'OVH', 24 | 'code': 'ovh', 25 | 'homepage': 'http://www.ovh.com/' 26 | } 27 | }, 28 | 'fromCache': false 29 | }; 30 | 31 | 32 | tap.test( 33 | 'IP Address: '+myIp+' ovh cgi should be in datacenter ipv6 list', 34 | (t) => { 35 | config.udgerParser.set({ ip:myIp }); 36 | const ret = config.udgerParser.parse({ json:true, full:true }); 37 | t.same(ret, expected); 38 | t.end(); 39 | } 40 | ); 41 | -------------------------------------------------------------------------------- /test/datacenterOvhIpV6.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const defaultResult = config.defaultResult; 5 | 6 | const myIp = '2001:41d0::'; 7 | 8 | let expected = { 9 | 'ip_address': { 10 | 'ip': myIp, // OVH datacenter 11 | 'ip_ver': 6, 12 | 'ip_classification': 'Unrecognized', 13 | 'ip_classification_code': 'unrecognized', 14 | 'datacenter_name': 'OVH', 15 | 'datacenter_name_code': 'ovh', 16 | 'datacenter_homepage': 'http://www.ovh.com/' 17 | } 18 | }; 19 | 20 | expected = config.merge(defaultResult, expected); 21 | 22 | tap.test( 23 | 'IP Address: '+myIp+' ovh should be in datacenter ipv6 list', 24 | (t) => { 25 | config.udgerParser.set({ ip:myIp }); 26 | const ret = config.udgerParser.parse(); 27 | t.same(ret, expected); 28 | t.end(); 29 | } 30 | ); 31 | -------------------------------------------------------------------------------- /test/datacenterOvhIpV6Json.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myIp = '2001:41d0::'; 5 | 6 | const expected = { 7 | 'ipAddress': { 8 | 'ip': '2001:41d0::', 9 | 'classification': 'unrecognized', 10 | 'datacenter': 'ovh' 11 | } 12 | }; 13 | 14 | tap.test( 15 | 'IP Address: '+myIp+' ovh should be in datacenter ipv6 list', 16 | (t) => { 17 | config.udgerParser.set({ ip:myIp }); 18 | const ret = config.udgerParser.parse({ json:true }); 19 | t.same(ret, expected); 20 | t.end(); 21 | } 22 | ); 23 | -------------------------------------------------------------------------------- /test/datacenterOvhIpV6JsonFull.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const myIp = '2001:41d0::'; 5 | 6 | const expected = { 7 | 'ipAddress': { 8 | 'ip': '2001:41d0::', 9 | 'version': 6, 10 | 'classification': { 11 | 'name': 'Unrecognized', 12 | 'code': 'unrecognized' 13 | }, 14 | 'datacenter': { 15 | 'name': 'OVH', 16 | 'code': 'ovh', 17 | 'homepage': 'http://www.ovh.com/' 18 | } 19 | }, 20 | 'fromCache': false 21 | }; 22 | 23 | tap.test( 24 | 'IP Address: '+myIp+' ovh should be in datacenter ipv6 list', 25 | (t) => { 26 | config.udgerParser.set({ ip:myIp }); 27 | const ret = config.udgerParser.parse({ json:true, full:true }); 28 | t.same(ret, expected); 29 | t.end(); 30 | } 31 | ); 32 | -------------------------------------------------------------------------------- /test/db/udgerdb_v3_test.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udger/udger-nodejs/def04e2def73cdeecad7e33b6063bd7a3700518f/test/db/udgerdb_v3_test.dat -------------------------------------------------------------------------------- /test/dbConnect.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | tap.test( 5 | 'Connect: disconnect() should return true', 6 | (t) => { 7 | const disconnected = config.udgerParser.disconnect(); 8 | t.same(disconnected, true); 9 | t.end(); 10 | } 11 | ); 12 | 13 | tap.test( 14 | 'Connect: disconnect() should return false (already disconnected)', 15 | (t) => { 16 | const disconnected = config.udgerParser.disconnect(); 17 | t.same(disconnected, false); 18 | t.end(); 19 | } 20 | ); 21 | 22 | tap.test( 23 | 'Connect: parse() should return {} because disconnected', 24 | (t) => { 25 | const ret = config.udgerParser.parse(); 26 | t.same(ret, {}); 27 | t.end(); 28 | } 29 | ); 30 | 31 | tap.test( 32 | 'Connect: connect() should return true', 33 | (t) => { 34 | const reconnected = config.udgerParser.connect(); 35 | t.same(reconnected, true); 36 | t.end(); 37 | } 38 | ); 39 | 40 | tap.test( 41 | 'Connect: connect() should return false (already connected)', 42 | (t) => { 43 | const reconnected = config.udgerParser.connect(); 44 | t.same(reconnected, false); 45 | t.end(); 46 | } 47 | ); 48 | 49 | tap.test( 50 | 'Connect: parse() should return an object', 51 | (t) => { 52 | const ret = config.udgerParser.parse(); 53 | t.same(ret, { 54 | from_cache:false, 55 | ip_address:{}, 56 | user_agent:{} 57 | }); 58 | t.end(); 59 | } 60 | ); 61 | -------------------------------------------------------------------------------- /test/helperGetDatabaseInfo.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | tap.test( 5 | 'Get database info', 6 | (t) => { 7 | config.udgerParser.getDatabaseInfo((err, result) => { 8 | 9 | t.equal( 10 | result.version, 11 | '20170106-01', 12 | 'version attribute should be 20170106-01' 13 | ); 14 | 15 | t.equal( 16 | result.information, 17 | 'Data v3 for Local parser - test data, no full database', 18 | 'information attribute should be "Data v3 for Local parser - test data, no full database"' 19 | ); 20 | 21 | t.equal( 22 | result.lastupdate, 23 | 1483690193, 24 | 'lastupdate attribute should be 1483690193' 25 | ); 26 | 27 | t.end(); 28 | }); 29 | } 30 | ); 31 | -------------------------------------------------------------------------------- /test/helperGetIPsClassification.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | tap.test( 5 | 'Get ip classification', 6 | (t) => { 7 | config.udgerParser.getIPsClassification((err, results) => { 8 | t.equal(err, null, 'should NOT return an error'); 9 | t.equal(results.length>0, true, 'should return some results'); 10 | t.end(); 11 | }); 12 | } 13 | ); 14 | -------------------------------------------------------------------------------- /test/helperGetUAClientsClassification.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | tap.test( 5 | 'Get User-Agent Clients classification', 6 | (t) => { 7 | config.udgerParser.getUAClientsClassification((err, results) => { 8 | t.equal(err, null, 'should NOT return an error'); 9 | t.equal(results.length>0, true, 'should return some results'); 10 | t.end(); 11 | }); 12 | } 13 | ); 14 | -------------------------------------------------------------------------------- /test/helperGetUACrawlersClassification.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | tap.test( 5 | 'Get User-Agent Crawlers classification', 6 | (t) => { 7 | config.udgerParser.getUACrawlersClassification((err, results) => { 8 | t.equal(err, null, 'should NOT return an error'); 9 | t.equal(results.length>0, true, 'should return some results'); 10 | t.end(); 11 | }); 12 | } 13 | ); 14 | -------------------------------------------------------------------------------- /test/helperGetUACrawlersFamilies.js.bug: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | tap.test( 5 | 'Get User-Agent Crawlers families', 6 | (t) => { 7 | config.udgerParser.getUACrawlersFamilies((err, results) => { 8 | t.equal(err, null, 'should NOT return an error'); 9 | t.equal(results.length>0, true, 'should return some results'); 10 | t.end(); 11 | }); 12 | } 13 | ); 14 | -------------------------------------------------------------------------------- /test/helperRandomIPv4.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const max = 2; 5 | 6 | tap.test( 7 | 'Random Bad IP Addresses ('+max+')', 8 | (t) => { 9 | config.udgerParser.randomIPv4(max, (err, results) => { 10 | t.equal(err, null, 'should NOT return an error'); 11 | t.equal(results.length, max, 'should return '+max+' results'); 12 | t.end(); 13 | }); 14 | } 15 | ); 16 | -------------------------------------------------------------------------------- /test/helperRandomUAClients.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const max = 20; 5 | 6 | tap.test( 7 | 'Random User-Agent Clients ('+max+')', 8 | (t) => { 9 | config.udgerParser.randomUAClients(max, (err, results) => { 10 | t.equal(err, null, 'should NOT return an error'); 11 | t.equal(results.length, max, 'should return '+max+' results'); 12 | t.end(); 13 | }); 14 | } 15 | ); 16 | -------------------------------------------------------------------------------- /test/helperRandomUAClientsRegex.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const max = 3; 5 | 6 | tap.test( 7 | 'Random User-Agent Clients Regexp ('+max+')', 8 | (t) => { 9 | config.udgerParser.randomUAClientsRegex(max, (err, results) => { 10 | t.equal(err, null, 'should NOT return an error'); 11 | t.equal(results.length, max, 'should return '+max+' results'); 12 | t.end(); 13 | }); 14 | } 15 | ); 16 | -------------------------------------------------------------------------------- /test/helperRandomUACrawlers.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | const max = 3; 5 | 6 | tap.test( 7 | 'Random User-Agent Crawlers ('+max+')', 8 | (t) => { 9 | config.udgerParser.randomUACrawlers(max, (err, results) => { 10 | t.equal(err, null, 'should NOT return an error'); 11 | t.equal(results.length, max, 'should return '+max+' results'); 12 | t.end(); 13 | }); 14 | } 15 | ); 16 | -------------------------------------------------------------------------------- /test/ip2long.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const utils = require('../utils'); 3 | 4 | tap.test( 5 | 'ip2long', 6 | (t) => { 7 | const iplg = utils.ip2long('213.183.51.0'); 8 | t.equal(iplg, 3585553152, '213.183.51.0 should be equal to 3585553152'); 9 | t.end(); 10 | } 11 | ); 12 | -------------------------------------------------------------------------------- /test/lib/config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | const db = path.resolve(__dirname, '../db/udgerdb_v3_test.dat'); 4 | const merge = require('merge-deep'); 5 | 6 | const defaultResult = fs.readJsonSync('./defaultResult.json'); 7 | 8 | const udgerParser = require('../../')(db); 9 | 10 | module.exports = { 11 | defaultResult, 12 | udgerParser, 13 | merge 14 | }; 15 | -------------------------------------------------------------------------------- /test/setNoData.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | tap.test( 5 | 'set: should failed because no data passed', 6 | (t) => { 7 | t.throws( 8 | ()=> { 9 | config.udgerParser.set(); 10 | }, 11 | {} 12 | ); 13 | t.end(); 14 | } 15 | ); 16 | 17 | -------------------------------------------------------------------------------- /test/setString.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | tap.test( 5 | 'set: should failed because string passed', 6 | (t) => { 7 | t.throws( 8 | ()=> { 9 | config.udgerParser.set('myString'); 10 | }, 11 | {} 12 | ); 13 | t.end(); 14 | } 15 | ); 16 | 17 | -------------------------------------------------------------------------------- /test/setUnknowAttribute.js: -------------------------------------------------------------------------------- 1 | const tap = require('tap'); 2 | const config = require('./lib/config'); 3 | 4 | tap.test( 5 | 'set: should failed because unknow attribute passed', 6 | (t) => { 7 | t.throws( 8 | ()=> { 9 | config.udgerParser.set({ foo:'bar' }); 10 | }, 11 | {} 12 | ); 13 | t.end(); 14 | } 15 | ); 16 | -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | const Address6 = require('ip-address').Address6; 2 | const Address4 = require('ip-address').Address4; 3 | 4 | function phpRegexpToJs(str) { 5 | let re = str.replace(/^\//, '').trim(); 6 | let flags = re.match(/\/([a-z]{0,3})$/); 7 | flags = flags[1].replace(/s/, ''); 8 | re = re.replace(/\/[a-z]{0,3}$/, ''); 9 | return new RegExp(re, flags); 10 | } 11 | 12 | function getIpVersion(ip) { 13 | 14 | let addr; 15 | try { 16 | addr = new Address6(ip); 17 | } catch(e) { 18 | // 19 | } 20 | 21 | if (addr) return 6; 22 | 23 | try { 24 | addr = new Address4(ip); 25 | } catch(e) { 26 | // 27 | } 28 | 29 | if (addr) return 4; 30 | 31 | return false; 32 | } 33 | 34 | function inetPton (a) { 35 | let m; 36 | let x; 37 | let i; 38 | let j; 39 | const f = String.fromCharCode; 40 | // IPv4 41 | m = a.match(/^(?:\d{1,3}(?:\.|$)){4}/); 42 | if (m) { 43 | m = m[0].split('.'); 44 | m = f(m[0]) + f(m[1]) + f(m[2]) + f(m[3]); 45 | // Return if 4 bytes, otherwise false. 46 | return m.length === 4 ? m : false; 47 | } 48 | const r = /^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/; 49 | // IPv6 50 | m = a.match(r); 51 | if (m) { 52 | // Translate each hexadecimal value. 53 | for (j = 1; j < 4; j++) { 54 | // Indice 2 is :: and if no length, continue. 55 | if (j === 2 || m[j].length === 0) { 56 | continue; 57 | } 58 | m[j] = m[j].split(':'); 59 | for (i = 0; i < m[j].length; i++) { 60 | m[j][i] = parseInt(m[j][i], 16); 61 | // Would be NaN if it was blank, return false. 62 | if (isNaN(m[j][i])) { 63 | // Invalid IP. 64 | return false; 65 | } 66 | m[j][i] = f(m[j][i] >> 8) + f(m[j][i] & 0xFF); 67 | } 68 | m[j] = m[j].join(''); 69 | } 70 | x = m[1].length + m[3].length; 71 | if (x === 16) { 72 | return m[1] + m[3]; 73 | } else if (x < 16 && m[2].length > 0) { 74 | return m[1] + (new Array(16 - x + 1)).join('\x00') + m[3]; 75 | } 76 | } 77 | // Invalid IP 78 | return false; 79 | } 80 | 81 | function inetNtop (a) { 82 | let i = 0; 83 | let m = ''; 84 | const c = []; 85 | a += ''; 86 | if (a.length === 4) { 87 | // IPv4 88 | return [ 89 | a.charCodeAt(0), 90 | a.charCodeAt(1), 91 | a.charCodeAt(2), 92 | a.charCodeAt(3) 93 | ].join('.'); 94 | } else if (a.length === 16) { 95 | // IPv6 96 | for (i = 0; i < 16; i++) { 97 | c.push(((a.charCodeAt(i++) << 8) + a.charCodeAt(i)).toString(16)); 98 | } 99 | return c.join(':') 100 | .replace(/((^|:)0(?=:|$))+:?/g, function (t) { 101 | m = (t.length > m.length) ? t : m; 102 | return t; 103 | }) 104 | .replace(m || ' ', '::'); 105 | } else { 106 | // Invalid length 107 | return false; 108 | } 109 | } 110 | 111 | 112 | const multipliers = [0x1000000, 0x10000, 0x100, 1]; 113 | 114 | function ip2long(ip) { 115 | var longValue = 0; 116 | ip.split('.').forEach(function(part, i) {longValue += part * multipliers[i];}); 117 | return longValue; 118 | } 119 | 120 | function long2ip(longValue) { 121 | return multipliers.map(function(multiplier) { 122 | return Math.floor((longValue % (multiplier * 0x100)) / multiplier); 123 | }).join('.'); 124 | } 125 | 126 | module.exports = { 127 | phpRegexpToJs, 128 | getIpVersion, 129 | inetPton, 130 | inetNtop, 131 | ip2long, 132 | long2ip 133 | }; 134 | --------------------------------------------------------------------------------