blob: bf166ae547c5eca7116ffc74d8ee797019975995 [file] [log] [blame]
David Carlier0470d702019-04-26 12:02:28 +00001#ifndef MOBI_DA_DAC_H
2#define MOBI_DA_DAC_H
3
4/**
5 * @file dac.h
6 * @author Afilias Technologies
7 *
8 * @brief API main header file
9 */
10
11#include <sys/types.h>
12#include <limits.h>
13#include <inttypes.h>
14#include <stdlib.h>
15#include <stdarg.h>
16
17#ifndef __cplusplus
18#ifndef true
19#ifdef HAVE_NO_BUILTIN__BOOL
20typedef int _Bool;
21#endif
22#define bool _Bool
23
24#define true 1
25#define false 0
26#endif
27#endif
28
29#define MOBI_DA_MAJOR 2
30#define MOBI_DA_MINOR 1
31#define MOBI_DA_DUMMY_LIBRARY 1
32
33
34/**
35 * @brief All values returned by the API have one of these types.
36 * da_getprop*() return data in the appropriate C type for the given da_type.
37 */
38enum da_type {
39 DA_TYPE_NONE,
40 DA_TYPE_BOOLEAN,
41 DA_TYPE_INTEGER,
42 DA_TYPE_NUMBER,
43 DA_TYPE_STRING,
44 DA_TYPE_ARRAY,
45 DA_TYPE_OBJECT,
46 DA_TYPE_NULL
47};
48
49/**
50 * Any method that returns a da_status may potentially fail for one of these reasons.
51 * XXX: Error reporting needs to be improved.
52 */
53enum da_status {
54 DA_OK, /* Success. */
55 DA_INVALID_JSON, /* The JSON format is invalid, or the content is unexpected in a given context. */
56 DA_OVERFLOW, /* Overflow occurred. Note this is used to indicate an unfinished string parse in JSON */
57 DA_FORMAT_ERROR, /* The data supplied is formatted incorrectly. */
58 DA_NOMEM, /* There was not enough space to complete the operation */
59 DA_SYS, /* A system error occurred - consult the OS for more details (eg, check errno) */
60 DA_NOTIMPL, /* This method is not implemented */
61 DA_NOTFOUND, /* The requested item was not found. */
62 DA_REGEXBAD, /* An invalid regex was provided. */
63 DA_NOMORE, /* Used to indicate the end of an iterator. */
64 DA_INVALID_COOKIE, /* Cookie value supplied was invalid */
65 DA_INVALID_TYPE, /* A value of an unexpected type was found. */
66 DA_INTERNAL_ERROR,
67 DA_STATUS_LAST /* Placeholder to indicate highest possible error value. (value will change as API matures) */
68};
69
70enum da_severity {
71 DA_SEV_FATAL, /* The operation will not continue, and the operation will return an error. */
72 DA_SEV_ERROR, /* An error occurred, but the API call will return at least some valid information */
Ilya Shipitsince7b00f2020-03-23 22:28:40 +050073 DA_SEV_WARN, /* An unexpected event occurred, but the system dealt with it */
David Carlier0470d702019-04-26 12:02:28 +000074 DA_SEV_INFO /* An informational message. */
75};
76/* Forward references to tagged types */
77struct atlas_image;
78struct da_atlas;
79struct da_deviceinfo;
80struct da_jsonparser;
81struct da_node;
82struct da_propset;
83union da_value;
84struct da_evidence;
85struct da_bitset;
86struct da_allocator;
87struct da_config;
88
89/**
90 * @brief Primary types of the interface.
91 * Primary types used by API client.
92 * Non-typedef structures and unions are considered private to the API.
93 *
94 */
95typedef enum da_severity da_severity_t; /* A severity for the error callback. */
96typedef enum da_status da_status_t; /* An error code - returned from most API calls. */
97typedef da_status_t (*da_setpos_fn)(void *ctx, off_t off); /* callback provided to API to rewind input stream */
98typedef enum da_type da_type_t; /* A value type (integer, string, etc) */
99
100/**
101 * @brief An operation on an atlas involves converting a set of evidence strings into a set of property/value pairs.
102 * The ID for a particular type of evidence is extract from the atlas (eg, for a specific HTTP header, use:
103 *
104 * da_evidence_id_t evidence = da_atlas_header_evidence_id(atlas, "User-Agent");
105 *
106 */
107typedef int da_evidence_id_t;
108
109/**
110 * @brief The search result encompasses a key/value set. Keys are handles retrieved via
111 * _either_ da_atlas_getpropid() or da_getpropid().
112 * Some search results may have keys not available when the atlas is opened (eg,
113 * when the name of the property itself is contained within the evidence)
114 * Such properties by necessity are given a "local" da_propid_t
115 *
116 * You can ensure any properties you are interested in get a global propid by
117 * passing a list of interesting named properties to da_atlas_open()
118 */
119typedef int da_propid_t;
120typedef size_t (*da_read_fn)(void *ctx, size_t maxlen, char *ptr);
121typedef struct da_atlas da_atlas_t;
122typedef struct da_deviceinfo da_deviceinfo_t;
123typedef struct da_evidence da_evidence_t;
124typedef struct da_jsonparser da_jsonparser_t;
125typedef struct da_node da_node_t;
126typedef struct da_property_decl da_property_decl_t;
127typedef struct da_propset da_propset_t;
128typedef struct da_config da_config_t;
129typedef void *(*da_alloc_fn)(void *ctx, size_t);
130typedef void (*da_free_fn)(void *ctx, void *);
131typedef void *(*da_realloc_fn)(void *ctx, void *, size_t);
132typedef void (*da_errorfunc_t)(da_severity_t severity, da_status_t status, const char *msg, va_list args);
133
134
135/* Manifest constants. */
136enum {
137 /*
138 * used as the initial guess for the compiled size of an atlas.
139 * If atlas sizes grow more beyond this, it can be expanded to avoid multiple scans of the data.
140 */
141 DA_INITIAL_MEMORY_ESTIMATE = 1024 * 1024 * 14
142};
143
144struct da_config {
145 unsigned int ua_props;
146 unsigned int lang_props;
147 unsigned int __reserved[14]; /* enough reserved keywords for future use */
148};
149
150/**
151 * Functional interface.
152 */
153
154/**
155 * @brief Initialize process to use the DA API.
156 */
157void da_init(void);
158
159
160/**
161 * @brief Release all resources used by the API
162 */
163void da_fini(void);
164
165/**
166 * @brief User-supplied callback to be invoked with information about an error.
167 * Note this may use thread-local storage etc to store the info on return from the current call
168 * It is guaranteed that an error-reporting function returning an error-code will have called
169 * this function at least once.
170 * @param callback function
171 */
172void da_seterrorfunc(da_errorfunc_t callback);
173
174/**
175 * @brief Given a specific HTTP header, return the associated ID for that header.
176 * When passing evidence to the API, its type is identified using its da_evidince_id_t.
177 * @param atlas atlas instance
178 * @param header_name Header's name
179 * @return evidence id
180 */
181da_evidence_id_t da_atlas_header_evidence_id(const da_atlas_t *atlas, const char *header_name);
182/**
183 * @brief Return the associated ID of the client side properties evidence
184 * @param atlas Atlas instance
185 * @return evidence id
186 */
187da_evidence_id_t da_atlas_clientprop_evidence_id(const da_atlas_t *atlas);
188/**
189 * @brief Return the associated ID of the accept language header evidence
190 * @param atlas Atlas instance
191 * @return evidence id
192 */
193da_evidence_id_t da_atlas_accept_language_evidence_id(const da_atlas_t *atlas);
194
195/**
196 * @brief readfn should present JSON content from ctx.
197 * atlasp points to an uninitialized da_atlas structure.
198 * Result is a compiled atlas at atlasp.
199 * Result is allocated via normal memory-allocation methods, malloc/calloc/realloc, so should be
200 * Free'd with free()
201 * XXX TODO: Change this to take a da_allocator
202 * @param ctx pointer given to read the json file
203 * @param readfn function pointer, set accordingly to the attended given pointer
204 * @param setposfn function pointer
205 * @param ptr Pointer dynamically allocated if the json parsing happened normally
206 * @param len size of the atlas image
207 * @return status of atlas compilation
208 */
209da_status_t da_atlas_compile(void *ctx, da_read_fn readfn, da_setpos_fn setposfn, void **ptr, size_t *len);
210
211/**
212 * @brief opens a previously compiled atlas for operations. extra_props will be available in calls to
213 * da_getpropid on the atlas, and if generated by the search, the ID will be consistent across
214 * different calls to search.
215 * Properties added by a search that are neither in the compiled atlas, nor in the extra_props list
216 * Are assigned an ID within the context that is not transferrable through different search results
217 * within the same atlas.
218 * @param atlas Atlas instance
219 * @param extra_props properties
220 * @param ptr given pointer from previously compiled atlas
221 * @param pos atlas image size
222 * @return status of atlas data opening
223 */
224da_status_t da_atlas_open(da_atlas_t *atlas, da_property_decl_t *extra_props, const void *ptr, size_t pos);
225
226/**
David Carlierb81483c2022-01-27 18:13:54 +0000227 * @brief read from a mapped data which then replace da_atlas_compile call
228 *
229 * @param dumppath, anonymous if NULL
Ilya Shipitsin3b64a282022-07-29 22:26:53 +0500230 * @param map for anonymous, it is the responsibility of the caller to unmap it, ignored otherwise
David Carlierb81483c2022-01-27 18:13:54 +0000231 * @param maplen for anonymous, it is the size of the mapped data, ignored otherwise
232 * @param ptr Pointer dynamically allocated if the mapping happened normally
233 * @param len size of the atlas image
234 * @return status of mapping
235 */
236da_status_t da_atlas_read_mapped(const char *path, void *m, void **p, size_t *l);
237/**
David Carlier0470d702019-04-26 12:02:28 +0000238 * @brief Release any resources associated with the atlas structure atlas, which was previously generated from
239 * da_read_atlas or da_compile_atlas.
240 * @param atlas instance
241 */
242void da_atlas_close(da_atlas_t *atlas);
243
244/**
245 * @brief Find device properties given a set of evidence.
246 * Search results are returned in da_deviceinfo_t, and must be cleaned using da_close
247 * "Evidence" is an array of length count, of string data tagged with an evidence ID.
248 * @param atlas Atlas instance
249 * @param info Device info
250 * @param ev Array of evidences
251 * @param count Number of evidence given
252 * @return status of the search
253 */
254da_status_t da_searchv(const da_atlas_t *atlas, da_deviceinfo_t *info, da_evidence_t *ev, size_t count);
255
256/**
257 * @brief As da_search, but unrolls the evidence array into variable arguments for simpler calling
258 * convention with known evidence types.
259 * varargs are pairs of (da_evidence_id, string), terminated with da_evidence_id DA_END
260 * @code da_search(&myAtlas, &deviceInfo, da_get_header_evidence_id("User-Agent"),
261 * "Mozilla/5.0 (Linux...", DA_END);
262 * @endcode
263 * @param atlas Atlas instance
264 * @param info given device info which holds on device properties
265 * @param pairs of evidence id / evidence value
266 * @return status of the search
267 */
268da_status_t da_search(const da_atlas_t *atlas, da_deviceinfo_t *info, ...);
269
270/**
271 * @brief After finishing with a search result, release resources associated with it.
272 * @param info Device info previously allocated by search functions
273 */
274void da_close(da_deviceinfo_t *info);
275
276/**
277 * @brief Given a property name (Eg, "displayWidth"), return the property ID associated with it for the
278 * specified atlas.
279 * @param atlas Atlas instance
280 * @param propname Property name
281 * @param propid Property id
282 * @return status of the property id search
283 */
284da_status_t da_atlas_getpropid(const da_atlas_t *atlas, const char *propname, da_propid_t *propid);
285
286/**
287 * @brief Given a property ID, return the type of that property.
288 * @code
289 * da_getproptype(&myAtlas, da_getpropid(&myAtlas, "displayWidth"), &propertyType);
290 * assert(propertyType == DA_TYPE_INT);
291 * @endcode
292 * @param atlas Atlas instance
293 * @param propid Property id
294 * @param type Type id of the property
295 * @return status of the type id search
296 */
297da_status_t da_atlas_getproptype(const da_atlas_t *atlas, da_propid_t propid, da_type_t *type);
298
299/**
300 * @brief Given a property ID, return the name of that property.
301 * @code
302 * da_atlas_getpropname(&myAtlas, da_getpropid(&myAtlas, "displayWidth"), &propertyName);
303 * assert(strcmp("displayWidth", propertyName) == 0);
304 * @endcode
305 * @param atlas Atlas instance
306 * @param propid property id
307 * @param propname property name returned
308 * @return status of the property name search
309 */
310da_status_t da_atlas_getpropname(const da_atlas_t *atlas, da_propid_t propid, const char **propname);
311
312
313/**
314 * @brief Given an atlas instance, return its counters + the builtins
315 * @code
316 * da_atlas_getpropcount(&myAtlas);
317 * @endcode
318 * @param atlas Atlas instance
319 * @return counters
320 */
321size_t da_atlas_getpropcount(const da_atlas_t *atlas);
322
323/**
324 * @brief Given an atlas instance, set the detection config
325 * @param atlas Atlas instance
326 * @param config instance
327 */
328void da_atlas_setconfig(da_atlas_t *atlas, da_config_t *config);
329
330/**
331 * @brief Given a search result, find the value of a specific property.
332 * @code
333 * long displayWidth; // width of display in pixels.
334 * da_getpropinteger(&deviceInfo, da_getpropid(&myAtlas, "displayWidth"), &displayWidth);
335 * @endcode
336 * String contents are owned by the search result, and are valid until the search is closed.
337 */
338/**
339 * @brief returns a property value as a string from a given string typed property id
340 * @param info Device info
341 * @param propid Property id
342 * @param value Value of the property
343 * @return status of property value search
344 */
345da_status_t da_getpropstring(const da_deviceinfo_t *info, da_propid_t propid, const char **value);
346/**
347 * @brief returns a property value as a long from a given long typed property id
348 * @param info Device info
349 * @param propid Property id
350 * @param value Value of the property
351 * @return status of property value search
352 */
353da_status_t da_getpropinteger(const da_deviceinfo_t *info, da_propid_t propid, long *value);
354/**
355 * @brief returns a property value as a boolean from a given boolean typed property id
356 * @param info Device info
357 * @param propid Property id
358 * @param value Value of the property
359 * @return status of property value search
360 */
361da_status_t da_getpropboolean(const da_deviceinfo_t *info, da_propid_t propid, bool *value);
362/**
363 * @brief returns a property value as a float from a given float typed property id
364 * @param info Device info
365 * @param propid Property id
366 * @param value Value of the property
367 * @return status of property value search
368 */
369da_status_t da_getpropfloat(const da_deviceinfo_t *info, da_propid_t propid, double *value);
370
371/**
372 * @brief Some properties may not be not known to the atlas before the search commences.
373 * Such properties cannot have a da_propid_t assigned to them on the atlas, but will
374 * have a local property assigned during search. The name and type of such properties
375 * can be discovered here.
376 *
377 * Properties that are used in the atlas source and properties specifically registered
378 * with da_atlas_open() will always be assigned to a property discovered during search.
379 * Therefore, if there are specific properties that you want to use, and are unsure
380 * if they are in your device atlas source, registering them with da_atlas_open will
381 * make access to them easier and more efficient
382 */
383/**
384 * @brief returns the type of a given device property from the search functions
385 * @param info Device info
386 * @param propid Property id
387 * @param type Type id
388 * @return status of property type search
389 */
390da_status_t da_getproptype(const da_deviceinfo_t *info, da_propid_t propid, da_type_t *type);
391/**
392 * @brief returns the name of a given device property from the search functions
393 * @param info Device info
394 * @param propid Property id
395 * @param propname Property name
396 * @return status of property type search
397 */
398da_status_t da_getpropname(const da_deviceinfo_t *info, da_propid_t propid, const char **propname);
399
400/**
401 * @brief da_getfirstprop/da_getnextprop provide iteration over all properties
402 * in a search result.
403 * Both will return DA_OK if there is a result available, and DA_NOMORE
404 * if the search is complete.
405 * @code
406 *
407 * da_propid_t *propidp;
408 * for (da_status_t status = da_getfirstprop(&result, &propidp);
409 * status == DA_OK;
410 * status = da_getnextprop(&result, &propidp)) {
411 * const char *propname;
412 * if (da_getpropname(&result, *propidp, &propname) == DA_OK)
413 * fprintf("found property %s\n", propname);
414 * }
415 * @endcode
416 */
417
418/**
419 * @brief returns the first property from device info
420 * @param info Device info
421 * @param propid Property
422 * @return status
423 */
424da_status_t da_getfirstprop(const da_deviceinfo_t *info, da_propid_t **propid);
425/**
426 * @brief device info properties iterator
427 * @param info Device info
428 * @param propid Property
429 * @return status
430 */
431da_status_t da_getnextprop(const da_deviceinfo_t *info, da_propid_t **propid);
432
433/**
434 * @brief Report an error, as per a report from the API to the user-callback.
435 * @param severity Severity level of the error
436 * @param fmt format error message
437 * @param va_list
438 * @return status
439 */
440da_status_t da_reporterror(da_status_t severity, const char *fmt, ...);
441
442/**
443 * @brief returns a textual description of the type "type".
444 * @param type Type id
445 * @return type name
446 */
447const char *da_typename(da_type_t type);
448
449/**
450 * @brief returns the version from the JSON in memory
451 * @param atlas
452 * @return version
453 */
454char *da_getdataversion(da_atlas_t *atlas);
455
456/**
457 * @brief returns the date creation's timestamp from the JSON in memory
458 * @param atlas
459 * @return version
460 */
461time_t da_getdatacreation(da_atlas_t *atlas);
462
463/**
464 * @brief returns the revision's number from the JSON in memory
465 * @param atlas
466 * @return version
467 */
468int da_getdatarevision(da_atlas_t *atlas);
469
470/**
471 * @brief returns the name of a global property
472 * @param atlas Atlas instance
473 * @param propid Property id
474 * @return property name
475 */
476const char *da_get_property_name(const da_atlas_t *atlas, da_propid_t propid);
477
478/**
479 * @brief returns the number of properties in a result.
480 * @param info Device info
481 * @return properties count
482 */
483size_t da_getpropcount(const da_deviceinfo_t *info);
484
485/*
486 * Details below should not be required for usage of the API
487 */
488
489/**
490 * @brief Represents a usable device atlas interface.
491 *
492 * No user servicable parts inside: access should
493 * be via the functional API.
494 */
495struct da_atlas {
496 const struct atlas_image *image;
497 struct header_evidence_entry *header_priorities;
498 size_t header_evidence_count;
499
500 struct pcre_regex_info *uar_regexes;
501 size_t uar_regex_count;
502
503 struct pcre_regex_info *replacement_regexes;
504 size_t replacement_regex_count;
505
506 da_evidence_id_t user_agent_evidence;
507 da_evidence_id_t clientprops_evidence;
508 da_evidence_id_t accept_language_evidence;
509 da_evidence_id_t next_evidence;
510
511 da_propset_t *properties;
512 da_propid_t id_propid;
513 da_propid_t id_proplang;
514 da_propid_t id_proplang_locale;
515
516 da_config_t config;
517
518 da_deviceinfo_t **cpr_props;
519 size_t cpr_count;
520};
521
522/* fixed constants. */
523enum {
524 DA_BUFSIZE = 16000
525};
526
527/**
528 * Represents a chunk of memory. See comments on da_deviceinfo.
529 * This is presented here to allow aggregation in da_deviceinfo:
530 * Not for public consumption.
531 */
532struct da_buf {
533 struct da_buf *next;
534 char *cur;
535 char *limit;
536 char buf[DA_BUFSIZE];
537};
538
539/**
540 * A callback interface for allocating memory from some source
541 * Not for public consumption.
542 */
543struct da_allocator {
544 da_alloc_fn alloc;
545 da_free_fn free;
546 da_realloc_fn realloc;
547 void *context;
548};
549
550
551/**
552 * Represents a search result
553 * Can be used to retrieve values of known properties discovered from the evidence,
554 * iterate over the properties with known values, and query property types that are
555 * local to this result.
556 *
557 * The atlas the search is carried out on must survive any da_deviceinfo results
558 * it provides.
559 */
560struct da_deviceinfo {
561 struct da_allocator allocator;
562 const da_atlas_t *atlas; /* reference to the atlas the search was carried out on. */
563 struct da_bitset *present; /* property received from tree */
564 struct da_bitset *localprop; /* property was received from UAR rule or CPR */
565 struct da_bitset *cprprop; /* property was received from CPR */
566 union da_value *properties; /* properties - indexed by property id. */
567 da_propid_t *proplist; /* list of properties present in this result. */
568 size_t propcount; /* size of proplist */
569 da_propset_t *local_types; /* property descriptors local to this search result. */
570
571 /**
572 * The per-deviceinfo heap is stored here. Allocations for data in the result
573 * come from the raw data in these buffers. The size of the fixed-size buffer
574 * built in to da_buf is sized such that all known search results will not
575 * require memory allocation via malloc()
576 */
577 struct da_buf *heap;
578 struct da_buf initial_heap;
579};
580
581/**
582 * Used to pass evidence to da_searchv()
583 */
584struct da_evidence {
585 da_evidence_id_t key;
586 char *value;
587};
588
589/**
590 * Used to pass properties the API intends to query to the da_atlas_open function
591 * This can be used to improve performance of lookup on properties well-known
592 * to the API user, but not present in the JSON database.
593 */
594struct da_property_decl {
595 const char *name;
596 da_type_t type;
597};
598
599
600#endif /* DEVATLAS_DAC_H */