# 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 / IIS ###
Please see the Apache/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=<path to the pcre library>
- PCRE_INCLUDE_DIR=<path to the pcre headers folder>
- ZLIB_LIBRARY=<path to the zlib library>
- ZLIB_INCLUDE_DIR=<path to the zlib headers folder>
(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=<full path to the C compiler executable>
- CMAKE_CXX_COMPILER=<full path to the C++ compiler>
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 ###
#### Ubuntu/Debian ####
```shell
% sudo apt-get install g++ (or clang) libpcre3-dev cmake zlib1g-dev
```
#### RedHat/CentOS ####
```shell
% sudo dnf install g++ (or clang) pcre-devel cmake zlib-devel
```
#### MacOS ####
```shell
% brew install g++ (or clang ; not necessary if clang from XCode is already installed) pcre cmake zlib
```
#### FreeBSD ###
```shell
% sudo pkg install pcre cmake
(optional)
% sudo pkg install gcc (or llvm) if another compiler different from the base system is preferred
```
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
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
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. _