# DeviceAtlas Device API Usage C # This document describes the DeviceAtlas API and how it is used. The DeviceAtlas Device Identification API consists of two libraries. A core library containing all of the main identification logic and a secondary extension library to allow for easier integration into a web application context. ## Library files ## ** DeviceAtlas Enterprise API (libda.so (or libda.dylib for macOs)) ** This is the main library and is responsible for loading the data file and provides the logic for device identification. Device identification is achieved by either passing individual identifiers (e.g. User-Agent) or by passing in a `da_evidence_t` data structure along with optional Client-side properties. This library does not depend on any third party libraries. ## Data File ## The DeviceAtlas API relies on a device data file to function. To download data file please visit [DeviceAtlas Enterprise Data File Download](https://deviceatlas.com/getJSON). For a full list of the available properties please see the [properties](https://deviceatlas.com/properties) page. > Note: The 3.x API is only compatible with the version 3 data file which is downloadable from the DeviceAtlas website. The version 2 data file is only compatible with the production 2x APIs. ## Client Side Library - Apple Device Identification ## In addition to the server-side API, an optional client-side Javascript library is available. This library is important for two reasons: 1. It facilitates identification of Apple devices. Apple devices are _not_ identifiable using only HTTP User-Agent data. 2. It allows for the collection of dynamic or changing properties. When the client-side library is embedded in a web page it collects additional properties from the client's web browser. This data is then sent back to the server and must be passed to the DeviceAtlas API along with the HTTP headers. The combination of the client data and the server side data allow for accurate identification of Apple devices. The client-side library may be included on a webpage by adding the following snippet: ```Javascript <!-- Adding the DeviceAtlas client side component to get client side properties --> <script type="text/javascript" src="https://cs.deviceatlas-cdn.com/dacs.js" async></script> ``` By default, the client-side library returns the data via a cookie. If this is present in the `HttpServletRequest` object it will be automatically used. The cookie name is configurable via the `Config` class. Alternatively, the client data may be returned via AJAX and passed to the server side API manually. For additional information, please see the [Client Side Library](https://docs.deviceatlas.com/apis/clientside/latest/README.ClientSide.html) documentation. ## Setup & Usage ## The usage of the DeviceAtlas server side library is straightforward. In all cases, the data file must be loaded before any device identification calls are made. > **Note:** It is highly recommended to ensure the data file is only loaded once. This is particularly important for batch processing and web applications. ### Requirements ### - A C compiler (C++ compiler for the wrapper) - CMake 2.8 (or above). The following compilers have been tested and are able to compile all components: - GCC (4.2 or above (5.x, 7.x and onwards are recommended, the 6.x versions could possibly impact negatively the performances due to optimisations bugs)). - Clang 3.x or above. - TinyCC 0.9 or above. ### Build ### The API is compatible with ASLR with PIE enabled. #### Ubuntu/Debian #### ```shell % sudo apt-get install gcc (or clang) cmake ``` #### RedHat/CentOS #### ```shell % sudo dnf install gcc (or clang) cmake ``` #### MacOS #### ```shell % brew install gcc (or clang ; not necessary if clang from XCode is already installed) cmake ``` Note on ARM64 architecture, libatomic_ops is required in addition. ```shell % brew install libatomic_ops ``` #### FreeBSD ### ```shell sudo pkg install gcc<any version> (or llvm<any version>) if another compiler different from the base system is preferred ``` ### Install the API (Optional) ### ```shell % cd <path to the extracted api archive> % sudo make install % sudo ldconfig ``` ### Usage Examples ### The following sections shows how to call the DeviceApi. Please note that exception handling has been omitted for brevity. #### EXAMPLE 1: Basic Usage - Single User-Agent #### ```c #include <dac.h> // Optional // By default the API sets a sane memory threshold for the JSON parsing // and mapping to memory however it is possible to override the value // if it needs to fit specific constraints. The value is to be // a multiple of DA_MEMORY_MUL and to avoid unnecessary numerous // I/O operations, being rounded up slighty. size_t da_user_hint = 3 * DA_MEMORY_MUL; static size_t filereader(void *ctx, size_t count, char *buf) { return fread(buf, 1, count, ctx); } static da_status_t fileseeker(void *ctx, off_t pos) { return fseek(ctx, pos, SEEK_SET) != -1 ? DA_OK : DA_SYS; } ... da_atlas_t atlas; da_status_t status; void *ptr; size_t size; FILE *json = fopen("/path/to/datafile.json", "r"); if (!json) { fprintf(stderr, "fread failed: %s\n", jsonpath); exit(EXIT_FAILURE); } da_init(); if ((status = da_atlas_compile(json, filereader, fileseeker, &ptr, &size)) != DA_OK) { fprintf(stderr, "da_atlas_compile failed\n"); goto dafini; } /** * Or when the binary file dumped previously by da_atlas_dump_mapped * or from in RAM data */ ... const char *binarypath = "data.bin"; void *m; if ((status = da_atlas_read_mapped(binarypath, m, &ptr, &size)) != DA_OK) { fprintf(stderr, "da_atlas_read_mapped failed\n"); goto daclose; } ... da_property_decl_t extraprops[] = {{ 0, 0 }}; if ((status = da_atlas_open(&atlas, extraprops, ptr, size)) != DA_OK) { fprintf(stderr, "da_atlas_open failed\n"); goto daclose; } /** * Optional: dumping the API memory state into a binary file */ ... const char *binaryPath = "./data.bin"; void *bin; size_t binlen; if ((status = da_atlas_dump_mapped(binarypath, &bin, &binlen, ptr, size)) != DA_OK) { fprintf(stderr, "da_atlas_dump_mapped failed\n"); goto daclose; } /** * Optional: setting cache's number of entries */ atlas.config.cache_size = 10000; ... da_evidence_t ev = {.key = da_atlas_evidence_id(&atlas, "user-agent"), .value = "Mozilla/5.0 (Linux; Android 11; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36"}; if ((status = da_searchv(&atlas, &device, &ev, 1)) == DA_OK) { for (status = da_getfirstprop(&device, &curprop); status == DA_OK; status = da_getnextprop(&device, &curprop)) { da_getpropname(&device, *curprop, &curpropname); ... da_getproptype(&device, *curprop, &curtype); ... switch (curtype) { case DA_TYPE_NUMBER: case DA_TYPE_INTEGER: { long curvalue; da_getpropinteger(&device, *curprop, &curvalue); sprintf(propbuf, "%ld", curvalue); break; } case DA_TYPE_BOOLEAN: { bool curvalue; da_getpropboolean(&device, *curprop, &curvalue); sprintf(propbuf, "%d", curvalue); break; } case DA_TYPE_STRING: { const char *curvalue; size_t curvaluelen; da_getpropstring(&device, *curprop, &curvalue); curvaluelen = strlen(curvalue); if (curvaluelen > sizeof(propbuf) - 1) curvaluelen = sizeof(propbuf) - 1; strncpy(propbuf, curvalue, curvaluelen); propbuf[curvaluelen] = 0; break; } case DA_TYPE_ARRAY: case DA_TYPE_NONE: default: break; } ... or ... da_type_t curtype; uintptr_t val; if (da_getpropval(&device, *curprop, &val, &curtype) == DA_OK) { case DA_TYPE_NUMBER: case DA_TYPE_INTEGER: { sprintf(propbuf, "%ld", (int)val); break; } case DA_TYPE_BOOLEAN: { sprintf(propbuf, "%d", (bool)val); break; } case DA_TYPE_STRING: { size_t curvaluelen; const char *curval = (const char *)val; curvaluelen = strlen(curvalue); if (curvaluelen > sizeof(propbuf) - 1) curvaluelen = sizeof(propbuf) - 1; strncpy(propbuf, curvalue, curvaluelen); propbuf[curvaluelen] = 0; break; } ... printf("%s => %s\n", curpropname, propbuf); ... } } da_close(&device); da_atlas_close(&atlas); ``` #### EXAMPLE 2: Passing Client Side Data #### The data collected by the DeviceAtlas Client-side Library is automatically passed when a `HttpServletRequest` is used. If a `HttpServletRequest` object is not available the client-side data can be passed manually as a second parameter to the `getProperties()` method. ```c ... da_evidence_t ev[2]; ev[0].key = da_atlas_header_evidence_id(&atlas, "user-Agent"); ev[0].value = "Mozilla/5.0 (Linux; Android 12; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/ Mobile Safari/537.36"; ev[1].key = da_atlas_clientprop_evidence_id(&atlas); ev[1].value = "scsVersion:2.1|bcookieSupport:1|bcss.animations:1|bcss.columns:1|bcss.transforms:1|bcss.transitions:1|sdeviceAspectRatio:1795/1010" ... ``` #### Additional Examples #### Please find more complete examples in the /Examples directory. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _ Copyright (c) DeviceAtlas Limited 2023. All Rights Reserved. _ _ https://deviceatlas.com _ <!-- 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="disabled"><a href="README.DeviceApi.html">Device API Usage C</a></li><li><a href="README.ClientHints.html">Client Hints Support</a></li><li><a href="README.CarrierApi.html">Carrier Identification API</a></li><li><a href="README.Upgrade.html">Device API Upgrade</a></li><li><a href="README.Cpp.html">Device API C++</a></li><li><a href="README.Nginx.html">NGINX Module</a></li><li><a href="README.Apache2.html">Apache2 Module</a></li><li><a href="README.JsonConverter.html">Device Identification API C JSONConverter</a></li><li><a href="README.Go-DeviceApi.html">Device API Usage Go</a></li><li><a href="README.Go-Upgrade.html">Device API Upgrade Go</a></li><li><a href="https://docs.deviceatlas.com/apis/clientside/latest/README.ClientSide.html" target="_blank">Client-side component</a></li><li class="divider"></li><li><a href="./ApiDocs/index.html">C ApiDocs</a></li><li><a href="./ApiCppDocs/index.html">C ++ interface ApiDocs</a></li><li><a href="./Go-ApiDocs/carrier.html">Go ApiDocs Carrier</a></li><li><a href="./Go-ApiDocs/device.html">Go ApiDocs Device</a></li></ul></div>