database: Add get_key function
[apps/agl-service-data-persistence.git] / ll-database-binding / src / ll-database-binding.c
1 /*
2  * Copyright 2017 IoT.bzh
3  *
4  * author: Loïc Collignon <loic.collignon@iot.bzh>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #define _GNU_SOURCE
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <pwd.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <linux/limits.h>
26 #include <json-c/json.h>
27 #include <db.h>
28
29 #define AFB_BINDING_VERSION 2
30 #include <afb/afb-binding.h>
31
32 #include "utils.h"
33
34 #ifndef MAX_PATH
35 #define MAX_PATH 1024
36 #endif
37
38 #define DBFILE          "/ll-database-binding.db"
39 #define USERNAME        "agl"
40 #define APPNAME         "firefox"
41
42 // ----- Globals -----
43 static DB*              database;
44 static char     database_file[MAX_PATH];
45
46 // ----- Binding's declarations -----
47 static int ll_database_binding_init();
48 static void verb_insert(struct afb_req req);
49 static void verb_update(struct afb_req req);
50 static void verb_delete(struct afb_req req);
51 static void verb_read(struct afb_req req);
52
53 // ----- Binding's implementations -----
54
55 /**
56  * @brief Initialize the binding.
57  * @return Exit code, zero if success.
58  */
59 static int ll_database_binding_init()
60 {
61         struct passwd pwd;
62         struct passwd* result;
63         char buf[MAX_PATH];
64         size_t bufsize;
65         int ret;
66
67         bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
68         if (bufsize == -1 || bufsize > MAX_PATH) bufsize = MAX_PATH;
69
70         ret = getpwuid_r(getuid(), &pwd, buf, bufsize, &result);
71         if (result == NULL)
72         {
73                 if (ret == 0) AFB_ERROR("User not found");
74                 else AFB_ERROR("getpwuid_r failed with %d code", ret);
75                 return -1;
76         }
77
78         memset(database_file, 0, MAX_PATH);
79         strcat(database_file, result->pw_dir);
80         strcat(database_file, DBFILE);
81
82         AFB_INFO("The database file is '%s'", database_file);
83
84         if ((ret = db_create(&database, NULL, 0)) != 0)
85         {
86                 AFB_ERROR("Failed to create database: %s.", db_strerror(ret));
87                 return -1;
88         }
89
90         if ((ret = database->open(database, NULL, database_file, NULL, DB_BTREE, DB_CREATE, 0664)) != 0)
91         {
92                 AFB_ERROR("Failed to open the '%s' database: %s.", database_file, db_strerror(ret));
93                 database->close(database, 0);
94                 return -1;
95         }
96
97         return 0;
98 }
99
100 /**
101  * Returns the database key for the 'req'
102  */
103 static int get_key(struct afb_req req, DBT *key)
104 {
105         char *appid, *data;
106         const char *jkey;
107
108         size_t ljkey, lappid, size;
109
110         struct json_object* args;
111         struct json_object* item;
112
113         /* get the key */
114         args = afb_req_json(req);
115         if (!json_object_object_get_ex(args, "key", &item))
116         {
117                 afb_req_fail(req, "no-key", NULL);
118                 return -1;
119         }
120         if (!item
121          || !(jkey = json_object_get_string(item))
122          || !(ljkey = strlen(jkey)))
123         {
124                 afb_req_fail(req, "bad-key", NULL);
125                 return -1;
126         }
127
128         /* get the appid */
129         appid = afb_req_get_application_id(req);
130         if (!appid)
131         {
132                 afb_req_fail(req, "bad-context", NULL);
133                 return -1;
134         }
135
136         /* make the db-key */
137         lappid = strlen(appid);
138         size = lappid + ljkey + 2;
139         data = realloc(appid, size);
140         if (!data)
141         {
142                 free(appid);
143                 afb_req_fail(req, "out-of-memory", NULL);
144                 return -1;
145         }
146         data[lappid] = ':';
147         memcpy(&data[lappid + 1], jkey, ljkey + 1);
148
149         /* return the key */
150         key->data = data;
151         key->size = (uint32_t)size;
152         return 0;
153 }
154
155 /**
156  * @brief Handle the @c read verb.
157  * @param[in] req The query.
158  */
159 static void verb_insert(struct afb_req req)
160 {
161         int ret;
162         DBT key;
163         DBT data;
164
165         const char* value;
166
167         struct json_object* args;
168         struct json_object* item;
169
170         args = afb_req_json(req);
171
172         if (!json_object_object_get_ex(args, "value", &item) || !item) value = NULL;
173         else value = json_object_get_string(item);
174
175         if (!value || !strlen(value))
176         {
177                 afb_req_fail(req, "No value provided.", NULL);
178                 return;
179         }
180
181         if (get_key(req, &key))
182                 return;
183
184         AFB_INFO("insert: key=%s, value=%s", (char*)key.data, value);
185
186         data.data = (void*)value;
187         data.size = (uint32_t)strlen(value);
188
189         if ((ret = database->put(database, NULL, &key, &data, DB_NOOVERWRITE)) == 0)
190                 afb_req_success_f(req, NULL, "db success: insertion %s=%s.", (char*)key.data, (char*)data.data);
191         else
192                 afb_req_fail_f(req, "Failed to insert datas.", "db fail: insertion : %s=%s - %s", (char*)key.data, (char*)data.data, db_strerror(ret));
193         free(key.data);
194 }
195
196 static void verb_update(struct afb_req req)
197 {
198         DBT key;
199         DBT data;
200         int ret;
201
202         char* rkey;
203         const char* tag;
204         const char* value;
205
206         struct json_object* args;
207         struct json_object* item;
208
209         args = afb_req_json(req);
210         // username should be get from identity binding
211         // application should be get from smack
212         // tag should be get using get_json_string(args, "tag");
213
214         if (!args)
215         {
216                 afb_req_fail(req, "No argument provided.", NULL);
217                 return;
218         }
219
220         if (!json_object_object_get_ex(args, "tag", &item) || !item) tag = NULL;
221         else tag = json_object_get_string(item);
222
223         if (!tag || !strlen(tag))
224         {
225                 afb_req_fail(req, "No tag provided.", NULL);
226                 return;
227         }
228
229         if (!json_object_object_get_ex(args, "value", &item) || !item) value = NULL;
230         else value = json_object_get_string(item);
231
232         if (!value || !strlen(value))
233         {
234                 afb_req_fail(req, "No value provided.", NULL);
235                 return;
236         }
237
238         rkey = malloc(strlen(USERNAME) + strlen(APPNAME) + strlen(tag) + 3);
239         strcpy(rkey, USERNAME);
240         strcat(rkey, ":");
241         strcat(rkey, APPNAME);
242         strcat(rkey, ":");
243         strcat(rkey, tag);
244
245         AFB_INFO("update: key=%s, value=%s", rkey, value);
246
247         memset(&key, 0, sizeof(key));
248         memset(&data, 0, sizeof(data));
249
250         key.data = rkey;
251         key.size = (uint32_t)strlen(rkey);
252
253         data.data = (void*)value;
254         data.size = (uint32_t)strlen(value);
255
256         if ((ret = database->put(database, NULL, &key, &data, 0)) == 0)
257                 afb_req_success_f(req, NULL, "db success: update %s=%s.", (char*)key.data, (char*)data.data);
258         else
259                 afb_req_fail_f(req, "Failed to update datas.", "db fail: update %s=%s - %s", (char*)key.data, (char*)data.data, db_strerror(ret));
260         free(rkey);
261 }
262
263 static void verb_delete(struct afb_req req)
264 {
265         DBT key;
266         int ret;
267
268         char* rkey;
269         const char* tag;
270
271         struct json_object* args;
272         struct json_object* item;
273
274         args = afb_req_json(req);
275
276         if (!args)
277         {
278                 afb_req_fail(req, "No argument provided.", NULL);
279                 return;
280         }
281
282         if (!json_object_object_get_ex(args, "tag", &item) || !item) tag = NULL;
283         else tag = json_object_get_string(item);
284
285         if (!tag || !strlen(tag))
286         {
287                 afb_req_fail(req, "No tag provided.", NULL);
288                 return;
289         }
290
291         rkey = malloc(strlen(USERNAME) + strlen(APPNAME) + strlen(tag) + 3);
292         strcpy(rkey, USERNAME);
293         strcat(rkey, ":");
294         strcat(rkey, APPNAME);
295         strcat(rkey, ":");
296         strcat(rkey, tag);
297
298         AFB_INFO("delete: key=%s", rkey);
299
300         memset(&key, 0, sizeof(key));
301
302         key.data = rkey;
303         key.size = (uint32_t)strlen(rkey);
304
305         if ((ret = database->del(database, NULL, &key, 0)) == 0)
306                 afb_req_success_f(req, NULL, "db success: delete %s.", (char *)key.data);
307         else
308                 afb_req_fail_f(req, "Failed to delete datas.", "db fail: delete %s - %s", (char*)key.data, db_strerror(ret));
309         free(rkey);
310 }
311
312 static void verb_read(struct afb_req req)
313 {
314         DBT key;
315         DBT data;
316         int ret;
317
318         char* rkey;
319         const char* tag;
320         char value[4096];
321
322         struct json_object* args;
323         struct json_object* item;
324         struct json_object* result;
325         struct json_object* val;
326
327         args = afb_req_json(req);
328
329         if (!args)
330         {
331                 afb_req_fail(req, "No argument provided.", NULL);
332                 return;
333         }
334
335         if (!json_object_object_get_ex(args, "tag", &item) || !item) tag = NULL;
336         else tag = json_object_get_string(item);
337
338         if (!tag || !strlen(tag))
339         {
340                 afb_req_fail(req, "No tag provided.", NULL);
341                 return;
342         }
343
344         rkey = malloc(strlen(USERNAME) + strlen(APPNAME) + strlen(tag) + 3);
345         strcpy(rkey, USERNAME);
346         strcat(rkey, ":");
347         strcat(rkey, APPNAME);
348         strcat(rkey, ":");
349         strcat(rkey, tag);
350
351         AFB_INFO("update: key=%s, value=%s", rkey, value);
352
353         memset(&key, 0, sizeof(key));
354         memset(&data, 0, sizeof(data));
355         memset(&value, 0, 4096);
356
357         key.data = rkey;
358         key.size = (uint32_t)strlen(rkey);
359
360         data.data = value;
361         data.ulen = 4096;
362         data.flags = DB_DBT_USERMEM;
363
364         if ((ret = database->get(database, NULL, &key, &data, 0)) == 0)
365         {
366                 result = json_object_new_object();
367                 val = json_tokener_parse((char*)data.data);
368                 json_object_object_add(result, "value", val ? val : json_object_new_string((char*)data.data));
369
370                 afb_req_success_f(req, result, "db success: read %s=%s.", (char*)key.data, (char*)data.data);
371         }
372         else
373                 afb_req_fail_f(req, "Failed to read datas.", "db fail: read %s - %s", (char*)key.data, db_strerror(ret));
374         free(rkey);
375 }
376
377 // ----- Binding's configuration -----
378 /*
379 static const struct afb_auth ll_database_binding_auths[] = {
380 };
381 */
382
383 static const afb_verb_v2 ll_database_binding_verbs[]= {
384                 REGISTER_VERB(insert,   NULL, NULL, AFB_SESSION_NONE_V2),
385                 REGISTER_VERB(update,   NULL, NULL, AFB_SESSION_NONE_V2),
386                 REGISTER_VERB(delete,   NULL, NULL, AFB_SESSION_NONE_V2),
387                 REGISTER_VERB(read,             NULL, NULL, AFB_SESSION_NONE_V2),
388         { .verb = NULL}
389 };
390
391 const struct afb_binding_v2 afbBindingV2 = {
392                 .api = "ll-database",
393                 .specification = NULL,
394                 .verbs = ll_database_binding_verbs,
395                 .preinit = NULL,
396                 .init = ll_database_binding_init,
397                 .onevent = NULL,
398                 .noconcurrency = 0
399 };