# DeviceAtlas Device Detection API # The DeviceAtlas Device Detection API provides a way to detect devices based on the User-Agent HTTP headers. Using the headers, the API returns device information such as screen width, screen height, is mobile, vendor, model etc. To see a full list of properties in DeviceAtlas please visit: https://deviceatlas.com/resources/available-properties . ### Data File ### The DeviceAtlas API relies on a device data file to function. DeviceAtlas provides daily data file updates so it is recommended to download the data file on a regular basis. This can be done manually from your account page or automated via the https://deviceatlas.com/getJSON page. For more information please see: https://deviceatlas.com/resources/getting-the-data ### Dependencies ### This library does not depend on any third party libraries. ### Apache / NGINX / IIS ### Please see the Apache/NGINX/IIS section for building and using the API as a webserver module. ### Client-side Component ### In addition to the properties from the data file, properties can be gathered from the client's browser and used both on the client side and on the server side. Please see the Client-side Component readme file for more information. ### Pre-Requirements ### - C/C++ compiler - CMake 2.6 (or above) Following compilers have been tested and are able to compile all components: - GCC/G++ (3.4 or above; 4.x recommended) - Clang/LLVM (3.0 or above) Following compiler has been tested with the main library only: - Sun Pro family (12.x or above) ** Note ** For Windows users, please see the Windows section for building the API. ### CMake ### CMake should be able to find out all necessary components if you installed them through your package manager. ** PCRE and ZLIB library and headers ** If your PCRE and/or ZLIB libraries and headers are located in custom locations it is possible to set these cmake environment variables: - PCRELIB=&lt;path to the pcre library&gt; - PCRE_INCLUDE_DIR=&lt;path to the pcre headers folder&gt; - ZLIB_LIBRARY=&lt;path to the zlib library&gt; - ZLIB_INCLUDE_DIR=&lt;path to the zlib headers folder&gt; (e.g. Red Hat based operating systems, the needed packages are pcre-devel and zlib-devel; for Debian, the names are libpcre3-dev and zlib1g-dev; otherwise, please check pcre and zlib development packages with your package manager). For example: ```shell cmake -DPCRELIB=/home/test/pcre/lib/libpcre.so -DPCRE_INCLUDE_DIR=/home/test/pcre/include \ -ZLIB_LIBRARY=/home/test/zlib/lib/libz.so -DZLIB_INCLUDE_DIR=/home/test/zlib/include ``` Compatibility: For older PCRE versions it is possible to disable PCRE Study feature with cmake -DPCRE_STUDY=0. It is highly recommended to do NOT disable PCRE Study for better performance. ** Custom compiler location ** - CMAKE_C_COMPILER=&lt;full path to the C compiler executable&gt; - CMAKE_CXX_COMPILER=&lt;full path to the C++ compiler&gt; For example: ```shell cmake -DCMAKE_C_COMPILER=/home/test/gcc-4.8.3/bin/gcc -DCMAKE_CXX_COMPILER=/home/test/gcc-4.8.3/bin/g++ ``` NB: For a static API version used with another shared library it is possible to use CFLAGS/CXXFLAGS with -fPIC/fpic compilation flag. ### Building the API ### 1. Unpack the API 2. Change to the API directory 3. Run cmake command (Optional) You can set an installation script with cmake -DINSTALL_SCRIPT=(common/redhat/debian/freebsd) * common/debian: will install libraries in /usr/lib and headers in /usr/include (also works for Ubuntu). * redhat: will install libraries in /usr/lib64 in 64bits version (also works for CentOS). * freebsd: will install libraries in /usr/local/lib and headers in /usr/local/include. 4. Run make ``` % tar -zxvf deviceatlas-enterprise-cpp-{version}.tgz % cd deviceatlas-enterprise-cpp-{version} % cmake . % make ``` Note: replace {version} with your API version (e.g. 2.1). You can set DTrace support option cmake -DDTRACE_SUPPORT=1. The following probes can be enabled ``` #pragma D option quiet mobidevatlas$target:::readfile-begin { /* N.B. If you use the new loadDataFromFile method */ /* It is possible to get the JSON file implied */ /* printf("JSON file : %s\n", copyinstr(arg0)); */ } mobidevatlas$target:::lookup-end ... mobidevatlas$target:::lookup-begin { printf("User-Agent : %s\n", copyinstr(arg0)); } mobidevatlas$target:::lookup-end ... mobidevatlas$target:::lookupuarule-begin { printf("User-Agent : %s\n", copyinstr(arg0)); } mobidevatlas$target:::lookupuarule-end ... mobidevatlas$target:::refine-begin { printf("Cookie : %s\n", copyinstr(arg0)); } mobidevatlas$target:::refine-end ... ``` ### Basic Usage ### #### Example 1 #### The following example code shows the API loading the data file and using a property name to find a certain value for a given User-Agent. ```cpp using namespace Mobi::Mtld; const char *dbFile = argv[1]; // DeviceAtlas data file const char *uaString = argv[2]; // User-Agent header. const char *property = argv[3]; // Name of property to retrieve. // Get std::istream for file contents // Create DeviceAtlas from this database. // Previously std::ifstream ifs; ifs.open(dbFile); if (ifs.good()) { // Deprecated constructor Da::DeviceAtlas atlas(ifs); Da::DeliveryContext dc(atlas, uaString); const Da::Value *value; try { // Deprecated method dc.getProperty(property, value); std::cout << (std::string)*value << std::endl; } catch(std::exception &e) { std::cerr << e.what() << std::endl; } } // Now const char *ucBrowserUa = argv[3]; const char *property = argv[4]; Da::DeviceAtlas atlas; try { atlas.loadDataFromFile(dbFile); Da::HeaderMap map; map["user-agent"] = uaString; map["x-ucbrowser-ua"] = ucBrowserUa; ... Da::HttpHeaders headers(map); Da::DeliveryContext dc(atlas, headers); // [] accessor to get a Value const Da::Value *value = dc[property]; if (value) std::cout << (std::string)*value << std::endl; // or getting an iterator Da::DeliveryContext::const_iterator it = dc.find(property); if (it != dc.end()) std::cout << (std::string)*it->second << std::endl; } catch(std::exception &e) { std::cerr << e.what() << std::endl; } ``` #### Example 2 #### This next example is very similar to the previous but instead of getting a single property all properties found for the User-Agent are printed out: ```cpp using namespace Mobi::Mtld const char *dbFile = argv[1]; // JSON data ... // After the first argument, should be http headers one formed like this // "User-Agent:iPhone" "Accept-Language:en, en-US" ... // Open JSON file as istream, and create DeviceAtlas from data. // Previously std::ifstream ifs; ifs.open(dbFile); if (ifs.good()) { // Deprecated constructor Da::DeviceAtlas atlas(ifs); ... // Now Da::DeviceAtlas atlas; // // the User Agent Rules and Langage Headers support can be disabled Da::DaConfig dcfg; dcfg.include_ua_props = true; dcfg.include_lang_props = false; Da::DeviceAtlas atlas(dcfg); try { atlas.loadDataFromFile(dbFile); } catch(Da::MissingJsonException & e) { std::cerr << e.what() << std::endl; } ... // Create "evidence"/HttpHeaders: in this case, add all Http headers needed. Da::HeaderMap map; for(size_t i = 1; i < argc; ++i) { std::stringstream ss; ss << argv[i]; std::string key, value; getline(ss, key, ':'); getline(ss, value, ':'); if (key != "" && value != "") map[key] = value; } Da::HttpHeaders headers(map); // Create the delivery context for this user agent. (This performs the atlas lookup) Da::DeliveryContext device(atlas, headers); // Contains method checks if the property isMobile exists and if the value is true // The value expected is type compatible with the property's one bool containsMobilePropertyAndItsTrue = dc.contains("isMobile", true); // Output each property by iterating over the delivery context cout << "[" << uaString << "]\n"; for (Da::DeliveryContext::const_iterator it = device.begin(); it != device.end(); ++it) { const Da::Property *prop = it->first; const Da::Value *val = it->second; std::cout << prop->name << "=" << *val << "\n"; } std::cout << "\n"; ``` ### Sample applications ### There are multiple examples in the /examples folder to test the API. In addition, in the /devatlas folder, there is a multicore application to process User-Agent list, HTTP headers or dump the tree. ./devatlas -h will display all the possible options. ### Details by class #### Da::DeviceAtlas: DeviceAtlas() loadDataFromFile(const char *) Constructed from a file's path containing the JSON data. The C++ std namespace provides streams for files and strings amongst other things. Streams for sockets, etc, are easy to implement. getProperty(const HttpHeaders &deviceid, (const Property &prop|const char *prop), (const Value *&|int & | const char *&, bool &) Retrieves a single property for the identified device ID. The primitive types int, bool, and const char *, will throw an exception if the property specified has a type that mismatches the assumed type in the call. The "const Value *&" retrieves a discriminated union that you can further interrogate to find its type. You may specify the property by name or by "Property" descriptor. Property descriptors can be retrieved by presenting a name to the "getPropertyDescr" method const Property &getPropertyDescr(const char *); Given a property name, retrieve the descriptor for that property from this atlas. Reusing the descriptor across multiple calls to "getProperty()" saves substantial overhead of looking up the property name strings in the atlas. Da::Property The API will only provide you with const instances. Fields are: name: the name of the property type: an enumeration of String, Numeric, or Boolean A reference to a property object is valid only as long as the atlas that created it exists. Da::Value JSON::Type type Field representing the type of the object: Either JSON::String or JSON::Number. Note that because the supplied data contains mismatches between the property types and the values they represent (eg, boolean properties can refer to either string or integer values), both properties and values need separate type fields. std::string toString() Return string representation of the property. long longval(): Return the long integer value of this property. Throws an exception of the value is not an numeric type. const char *strval(): Return the long integer value of this property. Throws an exception of the value is not an numeric type. A reference to a property object is valid only as long as the atlas that created it exists. Da::HttpHeaders Represents the evidence presented to the API to find the properties for a device. Two constructors are available, one taking a const char *, the other taking a map HTTP headername -> HTTP header value. Only the User-Agent field is currently supported. The content of the HttpHeaders object should be considered opaque. It is purely to be presented as a constructor to Da::DeliveryContext Da::DeliveryContext Da::DeliveryContext(const DeviceAtlas &, const HttpHeaders &) Create a delivery context containing properties for the identified device. This object can be queried faster for each property of the specified device than the atlas itself can. begin() and end() provide const iterators over std::pair<const Da::Property *, const Da::Value *> getProperty((const Property & | const char *), (const Value *&|int &|const char *&|bool &)) Get a property from the context, specified by string or Property descriptor. Other than the "Value" returning variant, an exception will be thrown if the assumed type does not match the type of th value refine(const char *) Handles a sum of parameters which must be in this format for example: idevicePixelRatio:1|ideviceAspectRatio:16/70 It will add additional properties and will provide an accurate model name according to client side properties rules. [propertyname] [] property accessor, raises an exception if the key does not exist find(const char *propname) Returns a const_iterator for this property or the end boundary if not found template<typename T> contains(const char *, T) Checks the presence of a property and checks the equality of T value against this property value if found. The T type have to be compatible. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _ Copyright (c) DeviceAtlas Limited 2021. All Rights Reserved. _ <!-- HTML+JS for document formatting when opened in browser --> <div class="btn-group" id="main-menu" style="float:right"><a class="btn dropdown-toggle" data-toggle="dropdown" href="#">Menu<span class="caret"></span></a><ul class="dropdown-menu"><li><a href="README.html">Main</a></li><li class="divider"></li><li class="disabled"><a href="README.DeviceApi.html">Device Detection API</a></li><li><a href="README.DeviceApi-Config.html">Device Detection API Config</a></li><li><a href="README.CarrierApi.html">Carrier Identification API</a></li><li><a href="README.Windows.html">C++ API and Windows</a></li><li class="divider"></li><li><a href="README.ClientSide.html">Client-side Component</a></li><li class="divider"></li><li><a href="README.Nginx.html">Nginx Module Installation</a></li><li><a href="README.ConnectivityAnalyser-Nginx.html">Nginx Connectivity Analyser</a></li><li class="divider"></li><li><a href="README.Apache2.html">Apache2 Module Installation</a></li><li><a href="README.ConnectivityAnalyser-Apache2.html">Apache 2 Connectivity Analyser</a></li><li class="divider"></li><li><a href="ApiDocs/index.html">DeviceAtlas ApiDocs</a></li></ul></div>