summaryrefslogtreecommitdiff
path: root/node_modules/ua-parser/cpp
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/ua-parser/cpp')
-rw-r--r--node_modules/ua-parser/cpp/CMakeLists.txt30
-rw-r--r--node_modules/ua-parser/cpp/LICENSE8
-rw-r--r--node_modules/ua-parser/cpp/Makefile29
-rw-r--r--node_modules/ua-parser/cpp/README.md27
-rw-r--r--node_modules/ua-parser/cpp/UaParser.cpp191
-rw-r--r--node_modules/ua-parser/cpp/UaParser.h54
-rw-r--r--node_modules/ua-parser/cpp/UaParserTest.cpp116
-rw-r--r--node_modules/ua-parser/cpp/main.cpp42
-rw-r--r--node_modules/ua-parser/cpp/ua_parser.cpp230
-rw-r--r--node_modules/ua-parser/cpp/ua_parser.h147
-rw-r--r--node_modules/ua-parser/cpp/ua_parser_test.cpp161
11 files changed, 1035 insertions, 0 deletions
diff --git a/node_modules/ua-parser/cpp/CMakeLists.txt b/node_modules/ua-parser/cpp/CMakeLists.txt
new file mode 100644
index 0000000..3a5a25a
--- /dev/null
+++ b/node_modules/ua-parser/cpp/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright 2013 Andrew Punch
+#
+# Licensed under the Apache License, Version 2.0 (the 'License')
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cmake_minimum_required(VERSION 2.8)
+
+set(CMAKE_CXX_FLAGS "-g")
+
+if(COMMAND cmake_policy)
+ cmake_policy(SET CMP0003 NEW)
+endif(COMMAND cmake_policy)
+
+add_library(ua_parser ua_parser.cpp)
+target_link_libraries(ua_parser yaml-cpp boost_regex)
+
+add_executable(ua_parser_cli main.cpp)
+target_link_libraries(ua_parser_cli ua_parser)
+
+add_executable(ua_parser_test ua_parser_test.cpp)
+target_link_libraries(ua_parser_test ua_parser gtest pthread)
diff --git a/node_modules/ua-parser/cpp/LICENSE b/node_modules/ua-parser/cpp/LICENSE
new file mode 100644
index 0000000..8d9abf4
--- /dev/null
+++ b/node_modules/ua-parser/cpp/LICENSE
@@ -0,0 +1,8 @@
+The MIT License (MIT)
+Copyright (c) 2014 Alex Şuhan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/node_modules/ua-parser/cpp/Makefile b/node_modules/ua-parser/cpp/Makefile
new file mode 100644
index 0000000..99a51db
--- /dev/null
+++ b/node_modules/ua-parser/cpp/Makefile
@@ -0,0 +1,29 @@
+CC=g++
+LDFLAGS=-lboost_regex -lboost_system -lyaml-cpp -lglog
+CFLAGS=-std=c++0x -Wall -Werror -g -O3
+
+# wildcard object build target
+%.o: %.cpp
+ $(CC) -c $(CFLAGS) $*.cpp -o $*.o $(LDFLAGS)
+ @$(CC) -MM $(CFLAGS) $*.cpp $(LDFLAGS) > $*.d
+
+uaparser_cpp: libuaparser_cpp.a
+
+libuaparser_cpp.a: UaParser.o
+ ar rcs $@ $^
+
+UaParserTest: libuaparser_cpp.a UaParserTest.o
+ $(CC) $(CFLAGS) $^ -o $@ libuaparser_cpp.a $(LDFLAGS) -lgtest -lpthread
+
+test: UaParserTest
+ ./UaParserTest
+
+# clean everything generated
+clean:
+ find . -name "*.o" -exec rm -rf {} \; # clean up object files
+ find . -name "*.d" -exec rm -rf {} \; # clean up dependencies
+ rm -f UaParserTest *.a
+
+# automatically include the generated *.d dependency make targets
+# that are created from the wildcard %.o build target above
+-include $(OBJS:.o=.d)
diff --git a/node_modules/ua-parser/cpp/README.md b/node_modules/ua-parser/cpp/README.md
new file mode 100644
index 0000000..3010a2a
--- /dev/null
+++ b/node_modules/ua-parser/cpp/README.md
@@ -0,0 +1,27 @@
+ua_parser C++ Library
+=====================
+
+Usage
+-----
+
+To build the (static) library:
+
+ $ make uaparser_cpp
+
+To build and run the tests:
+
+ $ make test
+
+Dependencies
+------------
+
+* boost_regex, boost_system, yaml-cpp (0.3 API)
+* glog (for the `CHECK` macro)
+* gtest (for testing)
+
+Author:
+-------
+
+ * Alex Şuhan <alex.suhan@gmail.com>
+
+ Based on the D implementation by Shripad K and using agent data from BrowserScope.
diff --git a/node_modules/ua-parser/cpp/UaParser.cpp b/node_modules/ua-parser/cpp/UaParser.cpp
new file mode 100644
index 0000000..71813dd
--- /dev/null
+++ b/node_modules/ua-parser/cpp/UaParser.cpp
@@ -0,0 +1,191 @@
+#include "UaParser.h"
+
+#include <fstream>
+#include <string>
+#include <vector>
+#include <unordered_set>
+
+#include <boost/regex.hpp>
+#include <glog/logging.h>
+#include <yaml-cpp/yaml.h>
+
+namespace {
+
+struct DeviceStore {
+ std::string replacement;
+ boost::regex regExpr;
+};
+
+struct AgentStore : DeviceStore {
+ std::string majorVersionReplacement;
+ std::string minorVersionReplacement;
+};
+
+typedef AgentStore OsStore;
+typedef AgentStore BrowserStore;
+
+#define FILL_AGENT_STORE(node, agent_store, repl, maj_repl, min_repl) \
+ CHECK(node.Type() == YAML::NodeType::Map); \
+ for (auto it = node.begin(); it != node.end(); ++it) { \
+ const std::string key = it.first().to<std::string>(); \
+ const std::string value = it.second().to<std::string>(); \
+ if (key == "regex") { \
+ agent_store.regExpr = value; \
+ } else if (key == repl) { \
+ agent_store.replacement = value; \
+ } else if (key == maj_repl && !value.empty()) { \
+ agent_store.majorVersionReplacement = value; \
+ } else if (key == min_repl && !value.empty()) { \
+ try { \
+ agent_store.minorVersionReplacement = value; \
+ } catch (...) {} \
+ } else { \
+ CHECK(false); \
+ } \
+ }
+
+struct UAStore {
+ explicit UAStore(const std::string& regexes_file_path) {
+ std::ifstream in_stream(regexes_file_path);
+ CHECK(in_stream.good());
+
+ YAML::Parser yaml_parser(in_stream);
+ YAML::Node regexes;
+ CHECK(yaml_parser.GetNextDocument(regexes));
+
+ const auto& user_agent_parsers = regexes["user_agent_parsers"];
+ for (const auto& user_agent : user_agent_parsers) {
+ BrowserStore browser;
+ FILL_AGENT_STORE(user_agent, browser, "family_replacement",
+ "v1_replacement", "v2_replacement");
+ browserStore.push_back(browser);
+ }
+
+ const auto& os_parsers = regexes["os_parsers"];
+ for (const auto& o : os_parsers) {
+ OsStore os;
+ FILL_AGENT_STORE(o, os, "os_replacement", "os_v1_replacement",
+ "os_v2_replacement");
+ osStore.push_back(os);
+ }
+
+ const auto& device_parsers = regexes["device_parsers"];
+ for (const auto& d : device_parsers) {
+ DeviceStore device;
+ for (auto it = d.begin(); it != d.end(); ++it) {
+ const std::string key = it.first().to<std::string>();
+ const std::string value = it.second().to<std::string>();
+ if (key == "regex") {
+ device.regExpr = value;
+ } else if (key == "device_replacement") {
+ device.replacement = value;
+ } else {
+ CHECK(false);
+ }
+ }
+ deviceStore.push_back(device);
+ }
+ }
+
+ std::vector<DeviceStore> deviceStore;
+ std::vector<OsStore> osStore;
+ std::vector<BrowserStore> browserStore;
+};
+
+template<class AGENT, class AGENT_STORE>
+void fillAgent(AGENT& agent, const AGENT_STORE& store, const boost::smatch& m) {
+ CHECK(!m.empty());
+ if (m.size() > 1) {
+ agent.family = !store.replacement.empty()
+ ? boost::regex_replace(store.replacement, boost::regex("\\$1"), m[1].str())
+ : m[1];
+ } else {
+ agent.family = !store.replacement.empty()
+ ? boost::regex_replace(store.replacement, boost::regex("\\$1"), m[0].str())
+ : m[0];
+ }
+ if (!store.majorVersionReplacement.empty()) {
+ agent.major = store.majorVersionReplacement;
+ } else if (m.size() > 2) {
+ const auto s = m[2].str();
+ if (!s.empty()) {
+ agent.major = s;
+ }
+ }
+ if (!store.minorVersionReplacement.empty()) {
+ agent.minor = store.minorVersionReplacement;
+ } else if (m.size() > 3) {
+ const auto s = m[3].str();
+ if (!s.empty()) {
+ agent.minor = s;
+ }
+ }
+ if (m.size() > 4) {
+ const auto s = m[4].str();
+ if (!s.empty()) {
+ agent.patch = s;
+ }
+ }
+}
+
+UserAgent parseImpl(const std::string& ua, const UAStore* ua_store) {
+ UserAgent uagent;
+
+ for (const auto& b : ua_store->browserStore) {
+ auto& browser = uagent.browser;
+ boost::smatch m;
+ if (boost::regex_search(ua, m, b.regExpr)) {
+ fillAgent(browser, b, m);
+ break;
+ } else {
+ browser.family = "Other";
+ }
+ }
+
+ for (const auto& o : ua_store->osStore) {
+ auto& os = uagent.os;
+ boost::smatch m;
+ if (boost::regex_search(ua, m, o.regExpr)) {
+ fillAgent(os, o, m);
+ break;
+ } else {
+ os.family = "Other";
+ }
+ }
+
+ for (const auto& d : ua_store->deviceStore) {
+ auto& device = uagent.device;
+ boost::smatch m;
+ if (boost::regex_search(ua, m, d.regExpr)) {
+ if (m.size() > 1) {
+ device.family = !d.replacement.empty()
+ ? boost::regex_replace(d.replacement, boost::regex("\\$1"), m[1].str())
+ : m[1].str();
+ } else if (m.size() == 1) {
+ device.family = !d.replacement.empty()
+ ? boost::regex_replace(d.replacement, boost::regex("\\$1"), m[0].str())
+ : m[0].str();
+ }
+ break;
+ } else {
+ device.family = "Other";
+ }
+ }
+
+ return uagent;
+}
+
+} // namespace
+
+UserAgentParser::UserAgentParser(const std::string& regexes_file_path)
+ : regexes_file_path_ { regexes_file_path } {
+ ua_store_ = new UAStore(regexes_file_path);
+}
+
+UserAgentParser::~UserAgentParser() {
+ delete static_cast<const UAStore*>(ua_store_);
+}
+
+UserAgent UserAgentParser::parse(const std::string& ua) const {
+ return parseImpl(ua, static_cast<const UAStore*>(ua_store_));
+}
diff --git a/node_modules/ua-parser/cpp/UaParser.h b/node_modules/ua-parser/cpp/UaParser.h
new file mode 100644
index 0000000..ea34a8c
--- /dev/null
+++ b/node_modules/ua-parser/cpp/UaParser.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <string>
+
+struct Device {
+ std::string family;
+};
+
+struct Agent : Device {
+ std::string major;
+ std::string minor;
+ std::string patch;
+
+ std::string toString() const {
+ return family + " " + toVersionString();
+ }
+
+ std::string toVersionString() const {
+ return (major.empty() ? "0" : major) + "." +
+ (minor.empty() ? "0" : minor) + "." +
+ (patch.empty() ? "0" : patch);
+ }
+};
+
+typedef Agent Os;
+typedef Agent Browser;
+
+struct UserAgent {
+ Device device;
+
+ Os os;
+ Browser browser;
+
+ std::string toFullString() const {
+ return browser.toString() + "/" + os.toString();
+ }
+
+ bool isSpider() const {
+ return device.family == "Spider";
+ }
+};
+
+class UserAgentParser {
+ public:
+ explicit UserAgentParser(const std::string& regexes_file_path);
+
+ UserAgent parse(const std::string&) const;
+
+ ~UserAgentParser();
+
+ private:
+ const std::string regexes_file_path_;
+ const void* ua_store_;
+};
diff --git a/node_modules/ua-parser/cpp/UaParserTest.cpp b/node_modules/ua-parser/cpp/UaParserTest.cpp
new file mode 100644
index 0000000..3636fc4
--- /dev/null
+++ b/node_modules/ua-parser/cpp/UaParserTest.cpp
@@ -0,0 +1,116 @@
+#include "UaParser.h"
+#include <fstream>
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+#include <yaml-cpp/yaml.h>
+#include <string>
+
+namespace {
+
+const UserAgentParser g_ua_parser("../regexes.yaml");
+
+TEST(UserAgentParser, basic) {
+ const auto uagent = g_ua_parser.parse(
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 5_1_1 like Mac OS X) AppleWebKit/534.46 "
+ "(KHTML, like Gecko) Version/5.1 Mobile/9B206 Safari/7534.48.3");
+ ASSERT_EQ("Mobile Safari", uagent.browser.family);
+ ASSERT_EQ("5", uagent.browser.major);
+ ASSERT_EQ("1", uagent.browser.minor);
+ ASSERT_EQ("", uagent.browser.patch);
+ ASSERT_EQ("Mobile Safari 5.1.0", uagent.browser.toString());
+ ASSERT_EQ("5.1.0", uagent.browser.toVersionString());
+
+ ASSERT_EQ("iOS", uagent.os.family);
+ ASSERT_EQ("5", uagent.os.major);
+ ASSERT_EQ("1", uagent.os.minor);
+ ASSERT_EQ("1", uagent.os.patch);
+ ASSERT_EQ("iOS 5.1.1", uagent.os.toString());
+ ASSERT_EQ("5.1.1", uagent.os.toVersionString());
+
+ ASSERT_EQ("Mobile Safari 5.1.0/iOS 5.1.1", uagent.toFullString());
+
+ ASSERT_EQ("iPhone", uagent.device.family);
+
+ ASSERT_FALSE(uagent.isSpider());
+}
+
+namespace {
+
+std::string string_field(const YAML::Node& root, const std::string& fname) {
+ const auto& yaml_field = root[fname];
+ return YAML::IsNull(yaml_field) ? "" : yaml_field.to<std::string>();
+}
+
+void test_browser_or_os(const char* file_path, const bool browser) {
+ std::ifstream in_stream(file_path);
+ CHECK(in_stream.good());
+ YAML::Parser yaml_parser(in_stream);
+ YAML::Node root;
+ CHECK(yaml_parser.GetNextDocument(root));
+ const auto& test_cases = root["test_cases"];
+ for (const auto& test : test_cases) {
+ // TODO(alex): add support for JS user agent
+ if (test.FindValue("js_ua")) {
+ continue;
+ }
+ const auto major = string_field(test, "major");
+ const auto minor = string_field(test, "minor");
+ const auto patch = string_field(test, "patch");
+ const auto family = string_field(test, "family");
+ const auto unparsed = string_field(test, "user_agent_string");
+ const auto uagent = g_ua_parser.parse(unparsed);
+ const auto& agent = browser ? uagent.browser : uagent.os;
+ ASSERT_EQ(major, agent.major);
+ ASSERT_EQ(minor, agent.minor);
+ ASSERT_EQ(patch, agent.patch);
+ ASSERT_EQ(family, agent.family);
+ }
+}
+
+void test_device(const char* file_path) {
+ std::ifstream in_stream(file_path);
+ CHECK(in_stream.good());
+ YAML::Parser yaml_parser(in_stream);
+ YAML::Node root;
+ CHECK(yaml_parser.GetNextDocument(root));
+ const auto& test_cases = root["test_cases"];
+ for (const auto& test : test_cases) {
+ const auto unparsed = string_field(test, "user_agent_string");
+ const auto uagent = g_ua_parser.parse(unparsed);
+ const auto family = string_field(test, "family");
+ ASSERT_EQ(family, uagent.device.family);
+ }
+}
+
+} // namespace
+
+TEST(BrowserVersion, test_user_agent_parser) {
+ test_browser_or_os("../test_resources/test_user_agent_parser.yaml", true);
+}
+
+TEST(BrowserVersion, firefox_user_agent_strings) {
+ test_browser_or_os("../test_resources/firefox_user_agent_strings.yaml", true);
+}
+
+TEST(BrowserVersion, pgts_browser_list) {
+ test_browser_or_os("../test_resources/pgts_browser_list.yaml", true);
+}
+
+TEST(OsVersion, test_user_agent_parser_os) {
+ test_browser_or_os("../test_resources/test_user_agent_parser_os.yaml", false);
+}
+
+TEST(OsVersion, additional_os_tests) {
+ test_browser_or_os("../test_resources/additional_os_tests.yaml", false);
+}
+
+TEST(DeviceFamily, test_device) {
+ test_device("../test_resources/test_device.yaml");
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/node_modules/ua-parser/cpp/main.cpp b/node_modules/ua-parser/cpp/main.cpp
new file mode 100644
index 0000000..956a8ac
--- /dev/null
+++ b/node_modules/ua-parser/cpp/main.cpp
@@ -0,0 +1,42 @@
+/*
+# Copyright 2013 Andrew Punch
+#
+# Licensed under the Apache License, Version 2.0 (the 'License')
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.#include "ua_parser.h"
+*/
+
+#include "ua_parser.h"
+
+int main(int argc, char *argv[]) {
+ std::string user_agent_string("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/30.0.1599.114 Chrome/30.0.1599.114 Safari/537.36");
+ std::string yaml_file("../regexes.yaml");
+
+ if (argc>1 && std::string(argv[1])=="--help") {
+ std::cout << "Usage: ua_parser_cli [user agent string] [regexes.yaml]" << std::endl;
+ return 1;
+ } else if (argc>1)
+ user_agent_string = argv[1];
+
+ if (argc>2)
+ yaml_file = argv[2];
+
+ ua_parser::Parser uap(yaml_file);
+
+ ua_parser::UserAgent ua = uap.Parse(user_agent_string);
+
+ if (ua.browser.family.empty())
+ return 2;
+
+ std::cout << ua.browser.family << std::endl;
+ std::cout << ua.os.os << std::endl;
+ std::cout << ua.device << std::endl;
+}
diff --git a/node_modules/ua-parser/cpp/ua_parser.cpp b/node_modules/ua-parser/cpp/ua_parser.cpp
new file mode 100644
index 0000000..6b50681
--- /dev/null
+++ b/node_modules/ua-parser/cpp/ua_parser.cpp
@@ -0,0 +1,230 @@
+/*
+# Copyright 2013 Andrew Punch
+#
+# Licensed under the Apache License, Version 2.0 (the 'License')
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+*/
+
+
+#include <fstream>
+#include <boost/algorithm/string/replace.hpp>
+#include <yaml-cpp/yaml.h>
+#include "ua_parser.h"
+
+
+
+namespace ua_parser {
+BrowserParser::BrowserParser(const YAML::Node &yaml_attributes) {
+ yaml_attributes["regex"] >> pattern;
+ regex = boost::regex(pattern);
+
+ if (const YAML::Node *pName =yaml_attributes.FindValue("family_replacement"))
+ overrides.family = pName->to<std::string>();
+ if (const YAML::Node *pName =yaml_attributes.FindValue("v1_replacement"))
+ *pName >> overrides.major;
+ if (const YAML::Node *pName =yaml_attributes.FindValue("v2_replacement"))
+ *pName >> overrides.minor;
+}
+
+
+
+Browser BrowserParser::Parse(const std::string &user_agent_string) {
+ Browser browser;
+ using namespace std;
+
+ boost::smatch result;
+ if (boost::regex_search(user_agent_string.begin(), user_agent_string.end(), result, regex)) {
+ if (overrides.family.empty() && result.size()>1)
+ browser.family = result[1].str();
+ else {
+ browser.family = overrides.family;
+ boost::replace_all(browser.family, "$1", result[1].str());
+ }
+
+ // If the group is not found, use the override value even if it is blank
+
+ if (overrides.major.empty()&&result.size()>2)
+ browser.major = result[2].str();
+ else
+ browser.major = overrides.major;
+
+ if (overrides.minor.empty()&&result.size()>3)
+ browser.minor = result[3].str();
+ else
+ browser.minor = overrides.minor;
+
+ if (overrides.patch.empty()&&result.size()>4)
+ browser.patch = result[4].str();
+ else
+ browser.patch = overrides.patch;
+
+ return browser;
+ }
+
+ return browser;
+
+}
+
+OperatingSystemParser::OperatingSystemParser(const YAML::Node &yaml_attributes) {
+ yaml_attributes["regex"] >> pattern;
+ regex = boost::regex(pattern);
+
+ if (const YAML::Node *pName =yaml_attributes.FindValue("os_replacement"))
+ *pName >> overrides.os;
+ if (const YAML::Node *pName =yaml_attributes.FindValue("os_v1_replacement"))
+ *pName >> overrides.major;
+ if (const YAML::Node *pName =yaml_attributes.FindValue("os_v2_replacement"))
+ *pName >> overrides.minor;
+}
+
+OperatingSystem OperatingSystemParser::Parse(const std::string &user_agent_string) {
+ OperatingSystem os;
+ using namespace std;
+
+ boost::smatch result;
+ if (boost::regex_search(user_agent_string.begin(), user_agent_string.end(), result, regex)) {
+ if (overrides.os.empty() && result.size()>1)
+ os.os = result[1].str();
+ else {
+ os.os = overrides.os;
+ boost::replace_all(os.os, "$1", result[1].str());
+ }
+
+ // If the group is not found, use the override value even if it is blank
+
+ if (overrides.major.empty()&&result.size()>2)
+ os.major = result[2].str();
+ else
+ os.major = overrides.major;
+
+ if (overrides.minor.empty()&&result.size()>3)
+ os.minor = result[3].str();
+ else
+ os.minor = overrides.minor;
+
+ if (overrides.patch.empty()&&result.size()>4)
+ os.patch = result[4].str();
+ else
+ os.patch = overrides.patch;
+
+ if (overrides.patch_minor.empty()&&result.size()>5)
+ os.patch_minor = result[5].str();
+ else
+ os.patch_minor = overrides.patch_minor;
+
+ return os;
+ }
+ return os;
+}
+
+DeviceParser::DeviceParser(const YAML::Node &yaml_attributes) {
+ yaml_attributes["regex"] >> pattern;
+ regex = boost::regex(pattern);
+
+ if (const YAML::Node *pName=yaml_attributes.FindValue("device_replacement"))
+ *pName >> overrides;
+}
+
+Device DeviceParser::Parse(const std::string &user_agent_string) {
+ Device device;
+ using namespace std;
+
+ boost::smatch result;
+ if (boost::regex_search(user_agent_string.begin(), user_agent_string.end(), result, regex)) {
+ if (overrides.empty() && result.size()>1)
+ device = result[1].str();
+ else {
+ device = overrides;
+ boost::replace_all(device, "$1", result[1].str());
+ }
+
+ return device;
+ }
+
+ return device;
+}
+
+
+
+template<class ParserType>
+static std::vector<ParserType> ParseYaml(const YAML::Node &yaml_regexes) {
+ std::vector<ParserType> parsers;
+
+ for (YAML::Iterator i=yaml_regexes.begin(); i!=yaml_regexes.end(); i++) {
+ ParserType parser(*i);
+
+ parsers.push_back(parser);
+ }
+
+ return parsers;
+}
+
+
+Parser::Parser(const std::string &yaml_file) {
+ std::ifstream in(yaml_file.c_str());
+ if (!in.good()) {
+ std::cerr << "Could not open YAML file" << yaml_file << std::endl;
+ return;
+ }
+
+ YAML::Parser parser(in);
+
+ YAML::Node doc;
+ parser.GetNextDocument(doc);
+
+ _browser_parsers = ParseYaml<BrowserParser>(doc["user_agent_parsers"]);
+ _os_parsers = ParseYaml<OperatingSystemParser>(doc["os_parsers"]);
+ _device_parsers = ParseYaml<DeviceParser>(doc["device_parsers"]);
+
+}
+
+UserAgent Parser::Parse(const std::string &user_agent_string) {
+ UserAgent user_agent;
+
+ user_agent.browser.family = "Other";
+ user_agent.os.os = "Other";
+ user_agent.device = "Other";
+
+ for (std::vector<BrowserParser>::iterator i=_browser_parsers.begin(); i!=_browser_parsers.end(); i++) {
+ Browser browser = i->Parse(user_agent_string);
+ if (!browser.family.empty()) {
+ user_agent.browser = browser;
+ break;
+ }
+
+ }
+
+ for (std::vector<OperatingSystemParser>::iterator i=_os_parsers.begin(); i!=_os_parsers.end(); i++) {
+ OperatingSystem os = i->Parse(user_agent_string);
+ if (!os.os.empty()) {
+ user_agent.os = os;
+ break;
+ }
+
+ }
+
+ for (std::vector<DeviceParser>::iterator i=_device_parsers.begin(); i!=_device_parsers.end(); i++) {
+ Device device = i->Parse(user_agent_string);
+ if (!device.empty()) {
+ user_agent.device = device;
+ break;
+ }
+
+ }
+
+
+
+ return user_agent;
+}
+
+}
+
diff --git a/node_modules/ua-parser/cpp/ua_parser.h b/node_modules/ua-parser/cpp/ua_parser.h
new file mode 100644
index 0000000..20fa4ec
--- /dev/null
+++ b/node_modules/ua-parser/cpp/ua_parser.h
@@ -0,0 +1,147 @@
+/*
+# Copyright 2013 Andrew Punch
+#
+# Licensed under the Apache License, Version 2.0 (the 'License')
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+*/
+
+#ifndef __UA_PARSER_H__
+#define __UA_PARSER_H__
+
+#include <string>
+#include <vector>
+#include <boost/regex.hpp>
+#include <yaml-cpp/yaml.h>
+
+namespace ua_parser {
+class Browser {
+ public:
+ std::string family;
+ std::string major;
+ std::string minor;
+ std::string patch;
+ std::string patch_minor;
+
+ bool operator==(const Browser &rhs) const {
+ return family==rhs.family
+ && major==rhs.major
+ && minor==rhs.minor
+ && patch==rhs.patch
+ && patch_minor==rhs.patch_minor;
+ }
+
+
+};
+
+
+inline std::ostream& operator<<(std::ostream &o, const Browser &browser) {
+ return o <<"Browser: ("
+ << browser.family<<","
+ <<browser.major<<","
+ <<browser.minor<<","
+ <<browser.patch<<","
+ <<browser.patch_minor<<") ";
+}
+
+
+class OperatingSystem {
+ public:
+ std::string os;
+ std::string major;
+ std::string minor;
+ std::string patch;
+ std::string patch_minor;
+
+ bool operator==(const OperatingSystem &rhs) const {
+ return os==rhs.os
+ && major==rhs.major
+ && minor==rhs.minor
+ && patch==rhs.patch
+ && patch_minor==rhs.patch_minor;
+ }
+};
+
+inline std::ostream& operator<<(std::ostream &o, const OperatingSystem &os) {
+ return o<<"OS: ("
+ <<os.os<<","
+ <<os.major<<","
+ <<os.minor<<","
+ <<os.patch<<","
+ <<os.patch_minor<<") ";
+}
+
+
+typedef std::string Device;
+
+class BrowserParser {
+ public:
+ BrowserParser(const YAML::Node &yaml_attributes);
+ Browser Parse(const std::string &user_agent_string);
+
+ std::string pattern;
+ boost::regex regex;
+ Browser overrides;
+};
+
+
+class OperatingSystemParser {
+ public:
+ OperatingSystemParser(const YAML::Node &yaml_attributes);
+ OperatingSystem Parse(const std::string &user_agent_string);
+
+ std::string pattern;
+ boost::regex regex;
+ OperatingSystem overrides;
+};
+
+class DeviceParser {
+ public:
+ DeviceParser(const YAML::Node &yaml_attributes);
+ Device Parse(const std::string &user_agent_string);
+ std::string pattern;
+ boost::regex regex;
+ Device overrides;
+};
+
+
+class UserAgent {
+ public:
+ Browser browser;
+ OperatingSystem os;
+ Device device;
+ std::string user_agent_string;
+
+};
+
+inline std::ostream& operator<<(std::ostream &o, UserAgent &ua) {
+ return o <<ua.browser
+ <<ua.os
+ <<"Device: ("<<ua.device<<")";
+}
+
+
+class Parser {
+ public:
+ Parser(const std::string &yaml_file);
+ UserAgent Parse(const std::string &user_agent_string);
+
+ private:
+ std::vector<BrowserParser> _browser_parsers;
+ std::vector<OperatingSystemParser> _os_parsers;
+ std::vector<DeviceParser> _device_parsers;
+};
+
+
+}
+
+
+#endif
diff --git a/node_modules/ua-parser/cpp/ua_parser_test.cpp b/node_modules/ua-parser/cpp/ua_parser_test.cpp
new file mode 100644
index 0000000..d306a79
--- /dev/null
+++ b/node_modules/ua-parser/cpp/ua_parser_test.cpp
@@ -0,0 +1,161 @@
+/*
+# Copyright 2013 Andrew Punch
+#
+# Licensed under the Apache License, Version 2.0 (the 'License')
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+*/
+
+#include <string>
+#include <fstream>
+#include <gtest/gtest.h>
+#include "ua_parser.h"
+
+static const std::string TEST_RESOURCES_DIR="../test_resources/";
+
+class UaParserTest : public testing::Test {
+ protected:
+ bool yaml_isnull(const YAML::Node &node) {
+ return node.Type()==YAML::NodeType::Null;
+ }
+
+ void runUserAgentTestsFromYAML(const std::string &file_name) {
+ std::ifstream in(file_name.c_str());
+ if (!in.good()) {
+ FAIL() << "Could not open YAML file" << file_name;
+ return;
+ }
+
+ YAML::Parser parser(in);
+
+ YAML::Node doc;
+ parser.GetNextDocument(doc);
+
+ const YAML::Node &test_cases = doc["test_cases"];
+ for (YAML::Iterator i=test_cases.begin(); i!=test_cases.end(); i++) {
+ // Inputs to Parse()
+ const YAML::Node& test_case = *i;
+ std::string user_agent_string = test_case["user_agent_string"].to<std::string>();
+
+ // The expected results
+ ua_parser::Browser expected;
+ if (!yaml_isnull(test_case["family"])) expected.family = test_case["family"].to<std::string>();
+ if (!yaml_isnull(test_case["major"])) expected.major = test_case["major"].to<std::string>();
+ if (!yaml_isnull(test_case["minor"])) expected.minor = test_case["minor"].to<std::string>();
+ if (!yaml_isnull(test_case["patch"])) expected.patch = test_case["patch"].to<std::string>();
+
+ // js_ua not supported
+ if (test_case.FindValue("js_ua"))
+ continue;
+
+ ua_parser::Parser user_agent_parser("../regexes.yaml");
+ ua_parser::UserAgent result = user_agent_parser.Parse(user_agent_string);
+ EXPECT_EQ(expected, result.browser)<<user_agent_string;
+ }
+ }
+
+ void runOSTestsFromYAML(const std::string &file_name) {
+ std::ifstream in(file_name.c_str());
+ if (!in.good()) {
+ FAIL() << "Could not open YAML file" << file_name;
+ return;
+ }
+
+ YAML::Parser parser(in);
+
+ YAML::Node doc;
+ parser.GetNextDocument(doc);
+
+ const YAML::Node &test_cases = doc["test_cases"];
+
+ for (YAML::Iterator i=test_cases.begin(); i!=test_cases.end(); i++) {
+ // Inputs to Parse()
+ const YAML::Node &test_case = *i;
+ std::string user_agent_string = test_case["user_agent_string"].to<std::string>();
+
+ // The expected results
+ ua_parser::OperatingSystem expected;
+ if (!yaml_isnull(test_case["family"])) expected.os = test_case["family"].to<std::string>();
+ if (!yaml_isnull(test_case["major"])) expected.major = test_case["major"].to<std::string>();
+ if (!yaml_isnull(test_case["minor"])) expected.minor = test_case["minor"].to<std::string>();
+ if (!yaml_isnull(test_case["patch"])) expected.patch = test_case["patch"].to<std::string>();
+ if (!yaml_isnull(test_case["patch_minor"])) expected.patch_minor = test_case["patch_minor"].to<std::string>();
+
+ ua_parser::Parser user_agent_parser("../regexes.yaml");
+ ua_parser::UserAgent result = user_agent_parser.Parse(user_agent_string);
+ EXPECT_EQ(result.os, expected) << user_agent_string;
+ }
+ }
+
+ void runDeviceTestsFromYAML(const std::string &file_name) {
+ std::ifstream in(file_name.c_str());
+ if (!in.good()) {
+ FAIL() << "Could not open YAML file" << file_name;
+ return;
+ }
+
+ YAML::Parser parser(in);
+
+ YAML::Node doc;
+ parser.GetNextDocument(doc);
+
+ const YAML::Node &test_cases = doc["test_cases"];
+
+ for (YAML::Iterator i=test_cases.begin(); i!=test_cases.end(); i++) {
+ const YAML::Node &test_case = *i;
+ // Inputs to Parse()
+ std::string user_agent_string = test_case["user_agent_string"].to<std::string>();
+
+ // The expected results
+ ua_parser::Device expected = test_case["family"].to<std::string>();
+
+ ua_parser::Parser user_agent_parser("../regexes.yaml");
+ ua_parser::UserAgent result = user_agent_parser.Parse(user_agent_string);
+ EXPECT_EQ(result.device, expected)<<user_agent_string;
+ }
+ }
+
+
+
+};
+
+
+TEST_F(UaParserTest, TestBrowserscopeStrings) {
+ runUserAgentTestsFromYAML(
+ TEST_RESOURCES_DIR+"test_user_agent_parser.yaml");
+}
+
+
+TEST_F(UaParserTest, TestBrowserscopeStringsOS) {
+ runOSTestsFromYAML(
+ TEST_RESOURCES_DIR+"test_user_agent_parser_os.yaml");
+}
+
+TEST_F(UaParserTest, TestStringsOS) {
+ runOSTestsFromYAML(
+ TEST_RESOURCES_DIR+"additional_os_tests.yaml");
+}
+
+TEST_F(UaParserTest, TestStringsDevice) {
+ runDeviceTestsFromYAML(
+ TEST_RESOURCES_DIR+"test_device.yaml");
+}
+
+TEST_F(UaParserTest, TestMozillaStrings) {
+ runUserAgentTestsFromYAML(
+ TEST_RESOURCES_DIR+"firefox_user_agent_strings.yaml");
+}
+
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}