From 520aa79ae3b967cb4ef366f92266b53b78c2f1c9 Mon Sep 17 00:00:00 2001 From: Loïc Collignon Date: Thu, 8 Feb 2018 11:06:33 +0100 Subject: moved files to root folder. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I7103241843736e1a5747253781485afa457a64d0 Signed-off-by: Loïc Collignon --- CMakeLists.txt | 23 + README.md | 38 +- conf.d/cmake/FindBerkeleyDB.cmake | 51 +++ conf.d/cmake/FindGDBM.cmake | 51 +++ conf.d/cmake/config.cmake | 197 +++++++++ conf.d/wgt/config.xml.in | 19 + htdocs/persistence/AFB-websock.js | 174 ++++++++ htdocs/persistence/binding-debug.css | 61 +++ htdocs/persistence/index.html | 41 ++ htdocs/persistence/persistence-binding.js | 203 +++++++++ ll-database-binding/.gitmodules | 3 - ll-database-binding/CMakeLists.txt | 23 - ll-database-binding/README.md | 38 -- .../conf.d/cmake/FindBerkeleyDB.cmake | 51 --- ll-database-binding/conf.d/cmake/FindGDBM.cmake | 51 --- ll-database-binding/conf.d/cmake/config.cmake | 197 --------- ll-database-binding/conf.d/wgt/config.xml.in | 19 - .../htdocs/persistence/AFB-websock.js | 174 -------- .../htdocs/persistence/binding-debug.css | 61 --- ll-database-binding/htdocs/persistence/index.html | 41 -- .../htdocs/persistence/persistence-binding.js | 203 --------- ll-database-binding/src/CMakeLists.txt | 28 -- ll-database-binding/src/export.map | 1 - ll-database-binding/src/persistence-binding.c | 469 --------------------- src/CMakeLists.txt | 28 ++ src/export.map | 1 + src/persistence-binding.c | 469 +++++++++++++++++++++ 27 files changed, 1355 insertions(+), 1360 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 conf.d/cmake/FindBerkeleyDB.cmake create mode 100644 conf.d/cmake/FindGDBM.cmake create mode 100644 conf.d/cmake/config.cmake create mode 100644 conf.d/wgt/config.xml.in create mode 100644 htdocs/persistence/AFB-websock.js create mode 100644 htdocs/persistence/binding-debug.css create mode 100644 htdocs/persistence/index.html create mode 100644 htdocs/persistence/persistence-binding.js delete mode 100644 ll-database-binding/.gitmodules delete mode 100644 ll-database-binding/CMakeLists.txt delete mode 100644 ll-database-binding/README.md delete mode 100644 ll-database-binding/conf.d/cmake/FindBerkeleyDB.cmake delete mode 100644 ll-database-binding/conf.d/cmake/FindGDBM.cmake delete mode 100644 ll-database-binding/conf.d/cmake/config.cmake delete mode 100644 ll-database-binding/conf.d/wgt/config.xml.in delete mode 100644 ll-database-binding/htdocs/persistence/AFB-websock.js delete mode 100644 ll-database-binding/htdocs/persistence/binding-debug.css delete mode 100644 ll-database-binding/htdocs/persistence/index.html delete mode 100644 ll-database-binding/htdocs/persistence/persistence-binding.js delete mode 100644 ll-database-binding/src/CMakeLists.txt delete mode 100644 ll-database-binding/src/export.map delete mode 100644 ll-database-binding/src/persistence-binding.c create mode 100644 src/CMakeLists.txt create mode 100644 src/export.map create mode 100644 src/persistence-binding.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..56ab411 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,23 @@ +########################################################################### +# Copyright 2017 IoT.bzh +# +# author: Loïc Collignon +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +cmake_minimum_required(VERSION 3.6) +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/) +include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake) + +install(DIRECTORY htdocs/persistence DESTINATION htdocs) diff --git a/README.md b/README.md index 1d1a1e8..fb420dc 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,38 @@ -# agl-service-data-persistence +# Database Binding +This binding provide a database API with key/value semantics. +The backend is currently a Berkeley DB. + +## Verbs +* **insert**: + This verb insert a key/value pair in the database. + If the key already exist, the verb fails. + +* **update**: + This verb update an existing record. + If the key doesn't exist, the verb fails. + +* **delete**: + This verb remove an existing key/value pair from the database. + If no matching record is found, the verb fails. + +* **read**: + This verb get the value associated with the specified key. + If no matching record is found, the verb fails. + +## Arguments +* The **read** and **delete** verbs need only a **key** to work: +``` +{ + "key": "mykey" +} +``` + +* The **insert** and **update** verbs need a **key** and a **value** to work: +``` +{ + "key": "mykey", + "value": "my value" +} +``` +The **value** can be any valid json. diff --git a/conf.d/cmake/FindBerkeleyDB.cmake b/conf.d/cmake/FindBerkeleyDB.cmake new file mode 100644 index 0000000..1f94785 --- /dev/null +++ b/conf.d/cmake/FindBerkeleyDB.cmake @@ -0,0 +1,51 @@ +# -*- cmake -*- + +# - Find BerkeleyDB +# Find the BerkeleyDB includes and library +# This module defines +# DB_INCLUDE_DIR, where to find db.h, etc. +# DB_LIBRARIES, the libraries needed to use BerkeleyDB. +# DB_FOUND, If false, do not try to use BerkeleyDB. +# also defined, but not for general use are +# DB_LIBRARY, where to find the BerkeleyDB library. + +FIND_PATH(DB_INCLUDE_DIR db.h + /usr/local/include/db4 + /usr/local/include + /usr/include/db4 + /usr/include + ) + +SET(DB_NAMES ${DB_NAMES} db) +FIND_LIBRARY(DB_LIBRARY + NAMES ${DB_NAMES} + PATHS /usr/lib /usr/local/lib + ) + +IF (DB_LIBRARY AND DB_INCLUDE_DIR) + SET(DB_LIBRARIES ${DB_LIBRARY}) + SET(DB_FOUND "YES") +ELSE (DB_LIBRARY AND DB_INCLUDE_DIR) + SET(DB_FOUND "NO") +ENDIF (DB_LIBRARY AND DB_INCLUDE_DIR) + + +IF (DB_FOUND) + IF (NOT DB_FIND_QUIETLY) + MESSAGE(STATUS "Found BerkeleyDB: ${DB_LIBRARIES}") + ENDIF (NOT DB_FIND_QUIETLY) +ELSE (DB_FOUND) + IF (DB_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find BerkeleyDB library") + ENDIF (DB_FIND_REQUIRED) +ENDIF (DB_FOUND) + +# Deprecated declarations. +SET (NATIVE_DB_INCLUDE_PATH ${DB_INCLUDE_DIR} ) +GET_FILENAME_COMPONENT (NATIVE_DB_LIB_PATH ${DB_LIBRARY} PATH) + +MARK_AS_ADVANCED( + DB_LIBRARY + DB_INCLUDE_DIR + ) + diff --git a/conf.d/cmake/FindGDBM.cmake b/conf.d/cmake/FindGDBM.cmake new file mode 100644 index 0000000..abc9987 --- /dev/null +++ b/conf.d/cmake/FindGDBM.cmake @@ -0,0 +1,51 @@ +# -*- cmake -*- + +# - Find gdbm +# Find the gdbm includes and library +# This module defines +# DB_INCLUDE_DIR, where to find db.h, etc. +# DB_LIBRARIES, the libraries needed to use BerkeleyDB. +# DB_FOUND, If false, do not try to use BerkeleyDB. +# also defined, but not for general use are +# DB_LIBRARY, where to find the BerkeleyDB library. + +FIND_PATH(DB_INCLUDE_DIR gdbm.h + /usr/local/include/gdbm + /usr/local/include + /usr/include/gdbm + /usr/include + ) + +SET(DB_NAMES ${DB_NAMES} gdbm) +FIND_LIBRARY(DB_LIBRARY + NAMES ${DB_NAMES} + PATHS /usr/lib /usr/local/lib + ) + +IF (DB_LIBRARY AND DB_INCLUDE_DIR) + SET(DB_LIBRARIES ${DB_LIBRARY}) + SET(DB_FOUND "YES") +ELSE (DB_LIBRARY AND DB_INCLUDE_DIR) + SET(DB_FOUND "NO") +ENDIF (DB_LIBRARY AND DB_INCLUDE_DIR) + + +IF (DB_FOUND) + IF (NOT DB_FIND_QUIETLY) + MESSAGE(STATUS "Found gdbm: ${DB_LIBRARIES}") + ENDIF (NOT DB_FIND_QUIETLY) +ELSE (DB_FOUND) + IF (DB_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find gdbm library") + ENDIF (DB_FIND_REQUIRED) +ENDIF (DB_FOUND) + +# Deprecated declarations. +SET (NATIVE_DB_INCLUDE_PATH ${DB_INCLUDE_DIR} ) +GET_FILENAME_COMPONENT (NATIVE_DB_LIB_PATH ${DB_LIBRARY} PATH) + +MARK_AS_ADVANCED( + DB_LIBRARY + DB_INCLUDE_DIR + ) + diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake new file mode 100644 index 0000000..5f59f66 --- /dev/null +++ b/conf.d/cmake/config.cmake @@ -0,0 +1,197 @@ +########################################################################### +# Copyright 2017 IoT.bzh +# +# author: Loïc Collignon +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# Project Info +# ------------------ +set(PROJECT_NAME persistence-binding) +set(PROJECT_VERSION "0.1") +set(PROJECT_PRETTY_NAME "Low Level Database Binding") +set(PROJECT_DESCRIPTION "") +set(PROJECT_URL "") +set(PROJECT_ICON "icon.png") +set(PROJECT_AUTHOR "Collignon, Loïc") +set(PROJECT_AUTHOR_MAIL "loic.collignon@iot.bzh") +set(PROJECT_LICENSE "MIT") +set(PROJECT_LANGUAGES,"C") + +# Where are stored default templates files from submodule or subtree app-templates in your project tree +# relative to the root project directory +set(PROJECT_APP_TEMPLATES_DIR "conf.d/app-templates") + +# Where are stored your external libraries for your project. This is 3rd party library that you don't maintain +# but used and must be built and linked. +# set(PROJECT_LIBDIR "libs") + +# Where are stored data for your application. Pictures, static resources must be placed in that folder. +# set(PROJECT_RESOURCES "data") + +# Which directories inspect to find CMakeLists.txt target files +# set(PROJECT_SRC_DIR_PATTERN "*") + +# Kernel selection if needed. You can choose between a +# mandatory version to impose a minimal version. +# Or check Kernel minimal version and just print a Warning +# about missing features and define a preprocessor variable +# to be used as preprocessor condition in code to disable +# incompatibles features. Preprocessor define is named +# KERNEL_MINIMAL_VERSION_OK. +# +# NOTE*** FOR NOW IT CHECKS KERNEL Yocto environment and +# Yocto SDK Kernel version. +# ----------------------------------------------- +#set (kernel_mandatory_version 4.8) +#set (kernel_minimal_version 4.8) + +# Compiler selection if needed. Impose a minimal version. +# ----------------------------------------------- +set (gcc_minimal_version 4.9) + +# PKG_CONFIG required packages +# ----------------------------- +set (PKG_REQUIRED_LIST + json-c + afb-daemon +) + +# Prefix path where will be installed the files +# Default: /usr/local (need root permission to write in) +# ------------------------------------------------------ +#set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) + +# Customize link option +# ----------------------------- +#list(APPEND link_libraries -an-option) + +# Compilation options definition +# Use CMake generator expressions to specify only for a specific language +# Values are prefilled with default options that is currently used. +# Either separate options with ";", or each options must be quoted separately +# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED ! +# ---------------------------------------------------------------------------- +#set(COMPILE_OPTIONS +# -Wall +# -Wextra +# -Wconversion +# -Wno-unused-parameter +# -Wno-sign-compare +# -Wno-sign-conversion +# -Werror=maybe-uninitialized +# -Werror=implicit-function-declaration +# -ffunction-sections +# -fdata-sections +# -fPIC +# CACHE STRING "Compilation flags") +#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.") +#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.") +#set(PROFILING_COMPILE_OPTIONS +# -g +# -O0 +# -pg +# -Wp,-U_FORTIFY_SOURCE +# CACHE STRING "Compilation flags for PROFILING build type.") +#set(DEBUG_COMPILE_OPTIONS +# -g +# -ggdb +# -Wp,-U_FORTIFY_SOURCE +# CACHE STRING "Compilation flags for DEBUG build type.") +#set(CCOV_COMPILE_OPTIONS +# -g +# -O2 +# --coverage +# CACHE STRING "Compilation flags for CCOV build type.") +#set(RELEASE_COMPILE_OPTIONS +# -g +# -O2 +# CACHE STRING "Compilation flags for RELEASE build type.") + +# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable] +# --------------------------------------------------------------------- +set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig) +set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib) + +# Optional location for config.xml.in +# ----------------------------------- +#set(WIDGET_ICON conf.d/wgt/${PROJECT_ICON} CACHE PATH "Path to the widget icon") +set(WIDGET_CONFIG_TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in CACHE PATH "Path to widget config file template (config.xml.in)") + +# Mandatory widget Mimetype specification of the main unit +# -------------------------------------------------------------------------- +# Choose between : +#- text/html : HTML application, +# content.src designates the home page of the application +# +#- application/vnd.agl.native : AGL compatible native, +# content.src designates the relative path of the binary. +# +# - application/vnd.agl.service: AGL service, content.src is not used. +# +#- ***application/x-executable***: Native application, +# content.src designates the relative path of the binary. +# For such application, only security setup is made. +# +set(WIDGET_TYPE application/vnd.agl.service) + +# Mandatory Widget entry point file of the main unit +# -------------------------------------------------------------- +# This is the file that will be executed, loaded, +# at launch time by the application framework. +# +set(WIDGET_ENTRY_POINT config.xml) + +# Optional dependencies order +# --------------------------- +#set(EXTRA_DEPENDENCIES_ORDER) + +# Optional Extra global include path +# ----------------------------------- +#set(EXTRA_INCLUDE_DIRS) + +# Optional extra libraries +# ------------------------- +#set(EXTRA_LINK_LIBRARIES) + +# Optional force binding Linking flag +# ------------------------------------ +# set(BINDINGS_LINK_FLAG LinkOptions ) + +# Optional force package prefix generation, like widget +# ----------------------------------------------------- +# set(PKG_PREFIX DestinationPath) + +# Optional Application Framework security token +# and port use for remote debugging. +#------------------------------------------------------------ +set(AFB_TOKEN "" CACHE PATH "Default binder security token") +set(AFB_REMPORT "1234" CACHE PATH "Default binder listening port") + +# Print a helper message when every thing is finished +# ---------------------------------------------------- +set(CLOSING_MESSAGE "Typical binding launch: afb-daemon --port=${AFB_REMPORT} --workdir=${CMAKE_BINARY_DIR}/package --ldpaths=lib --roothttp=htdocs --token=\"${AFB_TOKEN}\" --tracereq=common --verbose") +set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt") + +# Optional schema validator about now only XML, LUA and JSON +# are supported +#------------------------------------------------------------ +#set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler") +#set(XML_CHECKER "xmllint" CACHE STRING "XML linter") +#set(JSON_CHECKER "json_verify" CACHE STRING "JSON linter") + +# This include is mandatory and MUST happens at the end +# of this file, else you expose you to unexpected behavior +# ----------------------------------------------------------- +include(${PROJECT_APP_TEMPLATES_DIR}/cmake/common.cmake) diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in new file mode 100644 index 0000000..bbd3212 --- /dev/null +++ b/conf.d/wgt/config.xml.in @@ -0,0 +1,19 @@ + + + @PROJECT_NAME@ + + + @PROJECT_DESCRIPTION@ + @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@> + @PROJECT_LICENSE@ + + + + + + + + + + + diff --git a/htdocs/persistence/AFB-websock.js b/htdocs/persistence/AFB-websock.js new file mode 100644 index 0000000..08a7ffe --- /dev/null +++ b/htdocs/persistence/AFB-websock.js @@ -0,0 +1,174 @@ +AFB = function(base, initialtoken){ + +var urlws = "ws://"+window.location.host+"/"+base; +var urlhttp = "http://"+window.location.host+"/"+base; + +/*********************************************/ +/**** ****/ +/**** AFB_context ****/ +/**** ****/ +/*********************************************/ +var AFB_context; +{ + var UUID = undefined; + var TOKEN = initialtoken; + + var context = function(token, uuid) { + this.token = token; + this.uuid = uuid; + } + + context.prototype = { + get token() {return TOKEN;}, + set token(tok) {if(tok) TOKEN=tok;}, + get uuid() {return UUID;}, + set uuid(id) {if(id) UUID=id;} + }; + + AFB_context = new context(); +} +/*********************************************/ +/**** ****/ +/**** AFB_websocket ****/ +/**** ****/ +/*********************************************/ +var AFB_websocket; +{ + var CALL = 2; + var RETOK = 3; + var RETERR = 4; + var EVENT = 5; + + var PROTO1 = "x-afb-ws-json1"; + + AFB_websocket = function(onopen, onabort) { + var u = urlws; + if (AFB_context.token) { + u = u + '?x-afb-token=' + AFB_context.token; + if (AFB_context.uuid) + u = u + '&x-afb-uuid=' + AFB_context.uuid; + } + this.ws = new WebSocket(u, [ PROTO1 ]); + this.pendings = {}; + this.awaitens = {}; + this.counter = 0; + this.ws.onopen = onopen.bind(this); + this.ws.onerror = onerror.bind(this); + this.ws.onclose = onclose.bind(this); + this.ws.onmessage = onmessage.bind(this); + this.onopen = onopen; + this.onabort = onabort; + this.onclose = onabort; + } + + function onerror(event) { + var f = this.onabort; + if (f) { + delete this.onopen; + delete this.onabort; + f && f(this); + } + this.onerror && this.onerror(this); + } + + function onopen(event) { + var f = this.onopen; + delete this.onopen; + delete this.onabort; + f && f(this); + } + + function onclose(event) { + for (var id in this.pendings) { + var ferr = this.pendings[id].onerror; + ferr && ferr(null, this); + } + this.pendings = {}; + this.onclose && this.onclose(); + } + + function fire(awaitens, name, data) { + var a = awaitens[name]; + if (a) + a.forEach(function(handler){handler(data);}); + var i = name.indexOf("/"); + if (i >= 0) { + a = awaitens[name.substring(0,i)]; + if (a) + a.forEach(function(handler){handler(data);}); + } + a = awaitens["*"]; + if (a) + a.forEach(function(handler){handler(data);}); + } + + function reply(pendings, id, ans, offset) { + if (id in pendings) { + var p = pendings[id]; + delete pendings[id]; + var f = p[offset]; + f(ans); + } + } + + function onmessage(event) { + var obj = JSON.parse(event.data); + var code = obj[0]; + var id = obj[1]; + var ans = obj[2]; + AFB_context.token = obj[3]; + switch (code) { + case RETOK: + reply(this.pendings, id, ans, 0); + break; + case RETERR: + reply(this.pendings, id, ans, 1); + break; + case EVENT: + default: + fire(this.awaitens, id, ans); + break; + } + } + + function close() { + this.ws.close(); + this.onabort(); + } + + function call(method, request) { + return new Promise((function(resolve, reject){ + var id, arr; + do { + id = String(this.counter = 4095 & (this.counter + 1)); + } while (id in this.pendings); + this.pendings[id] = [ resolve, reject ]; + arr = [CALL, id, method, request ]; + if (AFB_context.token) arr.push(AFB_context.token); + this.ws.send(JSON.stringify(arr)); + }).bind(this)); + } + + function onevent(name, handler) { + var id = name; + var list = this.awaitens[id] || (this.awaitens[id] = []); + list.push(handler); + } + + AFB_websocket.prototype = { + close: close, + call: call, + onevent: onevent + }; +} +/*********************************************/ +/**** ****/ +/**** ****/ +/**** ****/ +/*********************************************/ +return { + context: AFB_context, + ws: AFB_websocket +}; +}; + diff --git a/htdocs/persistence/binding-debug.css b/htdocs/persistence/binding-debug.css new file mode 100644 index 0000000..f41c940 --- /dev/null +++ b/htdocs/persistence/binding-debug.css @@ -0,0 +1,61 @@ +#debug-panel { + float: right; +} + +#debug-panel.collapsed { + background-color: transparent; + overflow-x: hidden; +} + +#debug-panel.expanded { + background-color: lightyellow; + max-width: 25%; + overflow-x: scroll; +} + +#debug-panel.collapsed > #debug-panel-collapse { + display: none; +} + +#debug-panel.expanded > #debug-panel-collapse { + display: block; +} + +#debug-panel.collapsed > #debug-panel-expand { + display: block; +} + +#debug-panel.expanded > #debug-panel-expand { + display: none; +} + +#debug-panel.expanded > #debug-panel-content { + display: block; +} + +#debug-panel.collapsed > #debug-panel-content { + display: none; +} + +.json-key { + color: cornflowerblue; + font-weight: bold; +} + +.json-string { + color: crimson; +} + +.json-number { + color: sandybrown; +} + +.json-boolean { + color: fuchsia; + font-weight: bold; +} + +.json-null { + color: black; + font-weight: bold; +} diff --git a/htdocs/persistence/index.html b/htdocs/persistence/index.html new file mode 100644 index 0000000..50999d9 --- /dev/null +++ b/htdocs/persistence/index.html @@ -0,0 +1,41 @@ + + + + agl-service-data-persistence + + + + + + +
+

agl-service-identity

+

Raw data access

+

+

    +
  • +
  • +

    +

    +

    +

    +
  • +
+

+

User Profile (VIN: )

+
    +
  • +
  • +

    Update user profile

    +
      +
    • +
    • +
    • +
    • +
    • + +
    +
  • +
+ + diff --git a/htdocs/persistence/persistence-binding.js b/htdocs/persistence/persistence-binding.js new file mode 100644 index 0000000..9b5550c --- /dev/null +++ b/htdocs/persistence/persistence-binding.js @@ -0,0 +1,203 @@ +var afb = new AFB("api", "HELLO"); +var ws; + +function add_debbug_panel() { + + if (document.getElementById("debug-panel")) + return; + + var itm = document.getElementById("debug-panel-container"); + if (itm) + { + var pnl = + "
\n" + + " \n" + + " \n" + + "
\n" + + "

Debug

\n" + + "

Call

\n" + + "
    \n" + + "
  • api :
  • \n" + + "
  • verb :
  • \n" + + "
  • query :
  • \n" + + "
\n" + + "
\n" +
+			"        
\n" + + "

Response

\n" +
+			"        

Event

\n" +
+			"    
\n" + + "
\n"; + itm.insertAdjacentHTML("afterbegin", pnl); + } +} + +function createClass(name,rules) { + var style = document.createElement('style'); + style.type = 'text/css'; + document.getElementsByTagName('head')[0].appendChild(style); + if(!(style.sheet||{}).insertRule) + (style.styleSheet || style.sheet).addRule(name, rules); + else + style.sheet.insertRule(name+"{"+rules+"}",0); +} + +function syntaxHighlight(json) { + if (typeof json != 'string') + json = JSON.stringify(json, undefined, 2); + + json = json.replace(/&/g, '&').replace(//g, '>'); + return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { + var cls = 'json-number'; + if (/^"/.test(match)) { + if (/:$/.test(match)) { + cls = 'json-key'; + } else { + cls = 'json-string'; + } + } else if (/true|false/.test(match)) { + cls = 'json-boolean'; + } else if (/null/.test(match)) { + cls = 'json-null'; + } + return '' + match + ''; + }); +} + +function set_item_html(id, text) +{ + var itm = document.getElementById(id); + if (itm) itm.innerHTML = text; +} + +function set_item_text(id, text) +{ + var itm = document.getElementById(id); + if (itm) itm.innerText = text; +} + +function debug_panel_collapse() { + var pnl = document.getElementById('debug-panel'); + if (pnl) + { + pnl.classList.remove('expanded'); + pnl.classList.add('collapsed'); + } +} + +function debug_panel_expand() { + var pnl = document.getElementById('debug-panel'); + if (pnl) + { + pnl.classList.remove('collapsed'); + pnl.classList.add('expanded'); + } +} + +function init() { + add_debbug_panel(); + ws = new afb.ws(onopen, onabort); +} + +function onopen() { + //callbinder("ll-auth", "getuser", ""); + ws.onevent("*", gotevent); +} + +function onabort() { +} + +function replyok(obj) { + console.log("replyok:" + JSON.stringify(obj)); + set_item_html("debug-panel-response", syntaxHighlight(JSON.stringify(obj, null, 4))); +} + +function replyerr(obj) { + console.log("replyerr:" + JSON.stringify(obj)); + set_item_html("debug-panel-response", syntaxHighlight(JSON.stringify(obj, null, 4))); +} + +function gotevent(obj) { + console.log("gotevent:" + JSON.stringify(obj)); + set_item_html("debug-panel-event", syntaxHighlight(JSON.stringify(obj, null, 4))); +} + +function callbinder(api, verb, query) { + console.log ("subscribe api="+api+" verb="+verb+" query=" +query); + + set_item_text("debug-panel-call-api", api); + set_item_text("debug-panel-call-verb", verb); + set_item_html("debug-panel-call-query", syntaxHighlight(JSON.stringify(query, null, 4))); + + ws.call(api+"/"+verb, query).then(replyok, replyerr); +} + +function value(id){ + return document.getElementById(id).value; +} + +function readData() { + callbinder("persistence", "read", { "key": value("read-key") }); +} + +function deleteData() { + callbinder("persistence", "delete", { "key": value("read-key")}); +} + +function writeData() { + callbinder("persistence", "update", { + "key": value("write-key"), + "value": value("write-data") + }); +} + +function readProfile() { + var vin = value("profile-vin") + var token = value("get-profile-key") + var url = "https://agl-graphapi.forgerocklabs.org:443/getuserprofilefromtoken?vin=" + vin + "&kind=nfc&keytoken=" + token + + callbinder("persistence", "read", { + "key": { + "url": url, + "vin": vin, + "kind": "nfc", + "key": token + } + }); +} + +function deleteProfile() { + var vin = value("profile-vin") + var token = value("get-profile-key") + var url = "https://agl-graphapi.forgerocklabs.org:443/getuserprofilefromtoken?vin=" + vin + "&kind=nfc&keytoken=" + token + + callbinder("persistence", "delete", { + "key": { + "url": url, + "vin": vin, + "kind": "nfc", + "key": token + } + }); +} + +function writeProfile() { + var vin = value("profile-vin") + var token = value("set-profile-key") + var url = "https://agl-graphapi.forgerocklabs.org:443/getuserprofilefromtoken?vin=" + vin + "&kind=nfc&keytoken=" + token + + callbinder("persistence", "update", { + "key": { + "url": url, + "vin": vin, + "kind": "nfc", + "key": token + }, + "value": { + "keytoken": token, + "name": value("set-profile-login"), + "first_name": value("set-profile-first-name"), + "last_name": value("set-profile-last-name"), + "graphPreferredLanguage": value("set-profile-language") + } + }); +} diff --git a/ll-database-binding/.gitmodules b/ll-database-binding/.gitmodules deleted file mode 100644 index e07cae1..0000000 --- a/ll-database-binding/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "conf.d/app-templates"] - path = conf.d/app-templates - url = https://gerrit.automotivelinux.org/gerrit/p/apps/app-templates.git diff --git a/ll-database-binding/CMakeLists.txt b/ll-database-binding/CMakeLists.txt deleted file mode 100644 index 56ab411..0000000 --- a/ll-database-binding/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -########################################################################### -# Copyright 2017 IoT.bzh -# -# author: Loïc Collignon -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -########################################################################### - -cmake_minimum_required(VERSION 3.6) -SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/) -include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake) - -install(DIRECTORY htdocs/persistence DESTINATION htdocs) diff --git a/ll-database-binding/README.md b/ll-database-binding/README.md deleted file mode 100644 index fb420dc..0000000 --- a/ll-database-binding/README.md +++ /dev/null @@ -1,38 +0,0 @@ - -# Database Binding -This binding provide a database API with key/value semantics. -The backend is currently a Berkeley DB. - -## Verbs -* **insert**: - This verb insert a key/value pair in the database. - If the key already exist, the verb fails. - -* **update**: - This verb update an existing record. - If the key doesn't exist, the verb fails. - -* **delete**: - This verb remove an existing key/value pair from the database. - If no matching record is found, the verb fails. - -* **read**: - This verb get the value associated with the specified key. - If no matching record is found, the verb fails. - -## Arguments -* The **read** and **delete** verbs need only a **key** to work: -``` -{ - "key": "mykey" -} -``` - -* The **insert** and **update** verbs need a **key** and a **value** to work: -``` -{ - "key": "mykey", - "value": "my value" -} -``` -The **value** can be any valid json. diff --git a/ll-database-binding/conf.d/cmake/FindBerkeleyDB.cmake b/ll-database-binding/conf.d/cmake/FindBerkeleyDB.cmake deleted file mode 100644 index 1f94785..0000000 --- a/ll-database-binding/conf.d/cmake/FindBerkeleyDB.cmake +++ /dev/null @@ -1,51 +0,0 @@ -# -*- cmake -*- - -# - Find BerkeleyDB -# Find the BerkeleyDB includes and library -# This module defines -# DB_INCLUDE_DIR, where to find db.h, etc. -# DB_LIBRARIES, the libraries needed to use BerkeleyDB. -# DB_FOUND, If false, do not try to use BerkeleyDB. -# also defined, but not for general use are -# DB_LIBRARY, where to find the BerkeleyDB library. - -FIND_PATH(DB_INCLUDE_DIR db.h - /usr/local/include/db4 - /usr/local/include - /usr/include/db4 - /usr/include - ) - -SET(DB_NAMES ${DB_NAMES} db) -FIND_LIBRARY(DB_LIBRARY - NAMES ${DB_NAMES} - PATHS /usr/lib /usr/local/lib - ) - -IF (DB_LIBRARY AND DB_INCLUDE_DIR) - SET(DB_LIBRARIES ${DB_LIBRARY}) - SET(DB_FOUND "YES") -ELSE (DB_LIBRARY AND DB_INCLUDE_DIR) - SET(DB_FOUND "NO") -ENDIF (DB_LIBRARY AND DB_INCLUDE_DIR) - - -IF (DB_FOUND) - IF (NOT DB_FIND_QUIETLY) - MESSAGE(STATUS "Found BerkeleyDB: ${DB_LIBRARIES}") - ENDIF (NOT DB_FIND_QUIETLY) -ELSE (DB_FOUND) - IF (DB_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find BerkeleyDB library") - ENDIF (DB_FIND_REQUIRED) -ENDIF (DB_FOUND) - -# Deprecated declarations. -SET (NATIVE_DB_INCLUDE_PATH ${DB_INCLUDE_DIR} ) -GET_FILENAME_COMPONENT (NATIVE_DB_LIB_PATH ${DB_LIBRARY} PATH) - -MARK_AS_ADVANCED( - DB_LIBRARY - DB_INCLUDE_DIR - ) - diff --git a/ll-database-binding/conf.d/cmake/FindGDBM.cmake b/ll-database-binding/conf.d/cmake/FindGDBM.cmake deleted file mode 100644 index abc9987..0000000 --- a/ll-database-binding/conf.d/cmake/FindGDBM.cmake +++ /dev/null @@ -1,51 +0,0 @@ -# -*- cmake -*- - -# - Find gdbm -# Find the gdbm includes and library -# This module defines -# DB_INCLUDE_DIR, where to find db.h, etc. -# DB_LIBRARIES, the libraries needed to use BerkeleyDB. -# DB_FOUND, If false, do not try to use BerkeleyDB. -# also defined, but not for general use are -# DB_LIBRARY, where to find the BerkeleyDB library. - -FIND_PATH(DB_INCLUDE_DIR gdbm.h - /usr/local/include/gdbm - /usr/local/include - /usr/include/gdbm - /usr/include - ) - -SET(DB_NAMES ${DB_NAMES} gdbm) -FIND_LIBRARY(DB_LIBRARY - NAMES ${DB_NAMES} - PATHS /usr/lib /usr/local/lib - ) - -IF (DB_LIBRARY AND DB_INCLUDE_DIR) - SET(DB_LIBRARIES ${DB_LIBRARY}) - SET(DB_FOUND "YES") -ELSE (DB_LIBRARY AND DB_INCLUDE_DIR) - SET(DB_FOUND "NO") -ENDIF (DB_LIBRARY AND DB_INCLUDE_DIR) - - -IF (DB_FOUND) - IF (NOT DB_FIND_QUIETLY) - MESSAGE(STATUS "Found gdbm: ${DB_LIBRARIES}") - ENDIF (NOT DB_FIND_QUIETLY) -ELSE (DB_FOUND) - IF (DB_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find gdbm library") - ENDIF (DB_FIND_REQUIRED) -ENDIF (DB_FOUND) - -# Deprecated declarations. -SET (NATIVE_DB_INCLUDE_PATH ${DB_INCLUDE_DIR} ) -GET_FILENAME_COMPONENT (NATIVE_DB_LIB_PATH ${DB_LIBRARY} PATH) - -MARK_AS_ADVANCED( - DB_LIBRARY - DB_INCLUDE_DIR - ) - diff --git a/ll-database-binding/conf.d/cmake/config.cmake b/ll-database-binding/conf.d/cmake/config.cmake deleted file mode 100644 index a698e70..0000000 --- a/ll-database-binding/conf.d/cmake/config.cmake +++ /dev/null @@ -1,197 +0,0 @@ -########################################################################### -# Copyright 2017 IoT.bzh -# -# author: Loïc Collignon -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -########################################################################### - -# Project Info -# ------------------ -set(PROJECT_NAME persistence-binding) -set(PROJECT_VERSION "0.1") -set(PROJECT_PRETTY_NAME "Low Level Database Binding") -set(PROJECT_DESCRIPTION "") -set(PROJECT_URL "") -set(PROJECT_ICON "icon.png") -set(PROJECT_AUTHOR "Collignon, Loïc") -set(PROJECT_AUTHOR_MAIL "loic.collignon@iot.bzh") -set(PROJECT_LICENSE "MIT") -set(PROJECT_LANGUAGES,"C") - -# Where are stored default templates files from submodule or subtree app-templates in your project tree -# relative to the root project directory -set(PROJECT_APP_TEMPLATES_DIR "../conf.d/app-templates") - -# Where are stored your external libraries for your project. This is 3rd party library that you don't maintain -# but used and must be built and linked. -# set(PROJECT_LIBDIR "libs") - -# Where are stored data for your application. Pictures, static resources must be placed in that folder. -# set(PROJECT_RESOURCES "data") - -# Which directories inspect to find CMakeLists.txt target files -# set(PROJECT_SRC_DIR_PATTERN "*") - -# Kernel selection if needed. You can choose between a -# mandatory version to impose a minimal version. -# Or check Kernel minimal version and just print a Warning -# about missing features and define a preprocessor variable -# to be used as preprocessor condition in code to disable -# incompatibles features. Preprocessor define is named -# KERNEL_MINIMAL_VERSION_OK. -# -# NOTE*** FOR NOW IT CHECKS KERNEL Yocto environment and -# Yocto SDK Kernel version. -# ----------------------------------------------- -#set (kernel_mandatory_version 4.8) -#set (kernel_minimal_version 4.8) - -# Compiler selection if needed. Impose a minimal version. -# ----------------------------------------------- -set (gcc_minimal_version 4.9) - -# PKG_CONFIG required packages -# ----------------------------- -set (PKG_REQUIRED_LIST - json-c - afb-daemon -) - -# Prefix path where will be installed the files -# Default: /usr/local (need root permission to write in) -# ------------------------------------------------------ -#set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) - -# Customize link option -# ----------------------------- -#list(APPEND link_libraries -an-option) - -# Compilation options definition -# Use CMake generator expressions to specify only for a specific language -# Values are prefilled with default options that is currently used. -# Either separate options with ";", or each options must be quoted separately -# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED ! -# ---------------------------------------------------------------------------- -#set(COMPILE_OPTIONS -# -Wall -# -Wextra -# -Wconversion -# -Wno-unused-parameter -# -Wno-sign-compare -# -Wno-sign-conversion -# -Werror=maybe-uninitialized -# -Werror=implicit-function-declaration -# -ffunction-sections -# -fdata-sections -# -fPIC -# CACHE STRING "Compilation flags") -#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.") -#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.") -#set(PROFILING_COMPILE_OPTIONS -# -g -# -O0 -# -pg -# -Wp,-U_FORTIFY_SOURCE -# CACHE STRING "Compilation flags for PROFILING build type.") -#set(DEBUG_COMPILE_OPTIONS -# -g -# -ggdb -# -Wp,-U_FORTIFY_SOURCE -# CACHE STRING "Compilation flags for DEBUG build type.") -#set(CCOV_COMPILE_OPTIONS -# -g -# -O2 -# --coverage -# CACHE STRING "Compilation flags for CCOV build type.") -#set(RELEASE_COMPILE_OPTIONS -# -g -# -O2 -# CACHE STRING "Compilation flags for RELEASE build type.") - -# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable] -# --------------------------------------------------------------------- -set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig) -set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib) - -# Optional location for config.xml.in -# ----------------------------------- -#set(WIDGET_ICON conf.d/wgt/${PROJECT_ICON} CACHE PATH "Path to the widget icon") -set(WIDGET_CONFIG_TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in CACHE PATH "Path to widget config file template (config.xml.in)") - -# Mandatory widget Mimetype specification of the main unit -# -------------------------------------------------------------------------- -# Choose between : -#- text/html : HTML application, -# content.src designates the home page of the application -# -#- application/vnd.agl.native : AGL compatible native, -# content.src designates the relative path of the binary. -# -# - application/vnd.agl.service: AGL service, content.src is not used. -# -#- ***application/x-executable***: Native application, -# content.src designates the relative path of the binary. -# For such application, only security setup is made. -# -set(WIDGET_TYPE application/vnd.agl.service) - -# Mandatory Widget entry point file of the main unit -# -------------------------------------------------------------- -# This is the file that will be executed, loaded, -# at launch time by the application framework. -# -set(WIDGET_ENTRY_POINT config.xml) - -# Optional dependencies order -# --------------------------- -#set(EXTRA_DEPENDENCIES_ORDER) - -# Optional Extra global include path -# ----------------------------------- -#set(EXTRA_INCLUDE_DIRS) - -# Optional extra libraries -# ------------------------- -#set(EXTRA_LINK_LIBRARIES) - -# Optional force binding Linking flag -# ------------------------------------ -# set(BINDINGS_LINK_FLAG LinkOptions ) - -# Optional force package prefix generation, like widget -# ----------------------------------------------------- -# set(PKG_PREFIX DestinationPath) - -# Optional Application Framework security token -# and port use for remote debugging. -#------------------------------------------------------------ -set(AFB_TOKEN "" CACHE PATH "Default binder security token") -set(AFB_REMPORT "1234" CACHE PATH "Default binder listening port") - -# Print a helper message when every thing is finished -# ---------------------------------------------------- -set(CLOSING_MESSAGE "Typical binding launch: afb-daemon --port=${AFB_REMPORT} --workdir=${CMAKE_BINARY_DIR}/package --ldpaths=lib --roothttp=htdocs --token=\"${AFB_TOKEN}\" --tracereq=common --verbose") -set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt") - -# Optional schema validator about now only XML, LUA and JSON -# are supported -#------------------------------------------------------------ -#set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler") -#set(XML_CHECKER "xmllint" CACHE STRING "XML linter") -#set(JSON_CHECKER "json_verify" CACHE STRING "JSON linter") - -# This include is mandatory and MUST happens at the end -# of this file, else you expose you to unexpected behavior -# ----------------------------------------------------------- -include(${PROJECT_APP_TEMPLATES_DIR}/cmake/common.cmake) diff --git a/ll-database-binding/conf.d/wgt/config.xml.in b/ll-database-binding/conf.d/wgt/config.xml.in deleted file mode 100644 index bbd3212..0000000 --- a/ll-database-binding/conf.d/wgt/config.xml.in +++ /dev/null @@ -1,19 +0,0 @@ - - - @PROJECT_NAME@ - - - @PROJECT_DESCRIPTION@ - @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@> - @PROJECT_LICENSE@ - - - - - - - - - - - diff --git a/ll-database-binding/htdocs/persistence/AFB-websock.js b/ll-database-binding/htdocs/persistence/AFB-websock.js deleted file mode 100644 index 08a7ffe..0000000 --- a/ll-database-binding/htdocs/persistence/AFB-websock.js +++ /dev/null @@ -1,174 +0,0 @@ -AFB = function(base, initialtoken){ - -var urlws = "ws://"+window.location.host+"/"+base; -var urlhttp = "http://"+window.location.host+"/"+base; - -/*********************************************/ -/**** ****/ -/**** AFB_context ****/ -/**** ****/ -/*********************************************/ -var AFB_context; -{ - var UUID = undefined; - var TOKEN = initialtoken; - - var context = function(token, uuid) { - this.token = token; - this.uuid = uuid; - } - - context.prototype = { - get token() {return TOKEN;}, - set token(tok) {if(tok) TOKEN=tok;}, - get uuid() {return UUID;}, - set uuid(id) {if(id) UUID=id;} - }; - - AFB_context = new context(); -} -/*********************************************/ -/**** ****/ -/**** AFB_websocket ****/ -/**** ****/ -/*********************************************/ -var AFB_websocket; -{ - var CALL = 2; - var RETOK = 3; - var RETERR = 4; - var EVENT = 5; - - var PROTO1 = "x-afb-ws-json1"; - - AFB_websocket = function(onopen, onabort) { - var u = urlws; - if (AFB_context.token) { - u = u + '?x-afb-token=' + AFB_context.token; - if (AFB_context.uuid) - u = u + '&x-afb-uuid=' + AFB_context.uuid; - } - this.ws = new WebSocket(u, [ PROTO1 ]); - this.pendings = {}; - this.awaitens = {}; - this.counter = 0; - this.ws.onopen = onopen.bind(this); - this.ws.onerror = onerror.bind(this); - this.ws.onclose = onclose.bind(this); - this.ws.onmessage = onmessage.bind(this); - this.onopen = onopen; - this.onabort = onabort; - this.onclose = onabort; - } - - function onerror(event) { - var f = this.onabort; - if (f) { - delete this.onopen; - delete this.onabort; - f && f(this); - } - this.onerror && this.onerror(this); - } - - function onopen(event) { - var f = this.onopen; - delete this.onopen; - delete this.onabort; - f && f(this); - } - - function onclose(event) { - for (var id in this.pendings) { - var ferr = this.pendings[id].onerror; - ferr && ferr(null, this); - } - this.pendings = {}; - this.onclose && this.onclose(); - } - - function fire(awaitens, name, data) { - var a = awaitens[name]; - if (a) - a.forEach(function(handler){handler(data);}); - var i = name.indexOf("/"); - if (i >= 0) { - a = awaitens[name.substring(0,i)]; - if (a) - a.forEach(function(handler){handler(data);}); - } - a = awaitens["*"]; - if (a) - a.forEach(function(handler){handler(data);}); - } - - function reply(pendings, id, ans, offset) { - if (id in pendings) { - var p = pendings[id]; - delete pendings[id]; - var f = p[offset]; - f(ans); - } - } - - function onmessage(event) { - var obj = JSON.parse(event.data); - var code = obj[0]; - var id = obj[1]; - var ans = obj[2]; - AFB_context.token = obj[3]; - switch (code) { - case RETOK: - reply(this.pendings, id, ans, 0); - break; - case RETERR: - reply(this.pendings, id, ans, 1); - break; - case EVENT: - default: - fire(this.awaitens, id, ans); - break; - } - } - - function close() { - this.ws.close(); - this.onabort(); - } - - function call(method, request) { - return new Promise((function(resolve, reject){ - var id, arr; - do { - id = String(this.counter = 4095 & (this.counter + 1)); - } while (id in this.pendings); - this.pendings[id] = [ resolve, reject ]; - arr = [CALL, id, method, request ]; - if (AFB_context.token) arr.push(AFB_context.token); - this.ws.send(JSON.stringify(arr)); - }).bind(this)); - } - - function onevent(name, handler) { - var id = name; - var list = this.awaitens[id] || (this.awaitens[id] = []); - list.push(handler); - } - - AFB_websocket.prototype = { - close: close, - call: call, - onevent: onevent - }; -} -/*********************************************/ -/**** ****/ -/**** ****/ -/**** ****/ -/*********************************************/ -return { - context: AFB_context, - ws: AFB_websocket -}; -}; - diff --git a/ll-database-binding/htdocs/persistence/binding-debug.css b/ll-database-binding/htdocs/persistence/binding-debug.css deleted file mode 100644 index f41c940..0000000 --- a/ll-database-binding/htdocs/persistence/binding-debug.css +++ /dev/null @@ -1,61 +0,0 @@ -#debug-panel { - float: right; -} - -#debug-panel.collapsed { - background-color: transparent; - overflow-x: hidden; -} - -#debug-panel.expanded { - background-color: lightyellow; - max-width: 25%; - overflow-x: scroll; -} - -#debug-panel.collapsed > #debug-panel-collapse { - display: none; -} - -#debug-panel.expanded > #debug-panel-collapse { - display: block; -} - -#debug-panel.collapsed > #debug-panel-expand { - display: block; -} - -#debug-panel.expanded > #debug-panel-expand { - display: none; -} - -#debug-panel.expanded > #debug-panel-content { - display: block; -} - -#debug-panel.collapsed > #debug-panel-content { - display: none; -} - -.json-key { - color: cornflowerblue; - font-weight: bold; -} - -.json-string { - color: crimson; -} - -.json-number { - color: sandybrown; -} - -.json-boolean { - color: fuchsia; - font-weight: bold; -} - -.json-null { - color: black; - font-weight: bold; -} diff --git a/ll-database-binding/htdocs/persistence/index.html b/ll-database-binding/htdocs/persistence/index.html deleted file mode 100644 index 50999d9..0000000 --- a/ll-database-binding/htdocs/persistence/index.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - agl-service-data-persistence - - - - - - -
-

agl-service-identity

-

Raw data access

-

-

    -
  • -
  • -

    -

    -

    -

    -
  • -
-

-

User Profile (VIN: )

-
    -
  • -
  • -

    Update user profile

    -
      -
    • -
    • -
    • -
    • -
    • - -
    -
  • -
- - diff --git a/ll-database-binding/htdocs/persistence/persistence-binding.js b/ll-database-binding/htdocs/persistence/persistence-binding.js deleted file mode 100644 index 9b5550c..0000000 --- a/ll-database-binding/htdocs/persistence/persistence-binding.js +++ /dev/null @@ -1,203 +0,0 @@ -var afb = new AFB("api", "HELLO"); -var ws; - -function add_debbug_panel() { - - if (document.getElementById("debug-panel")) - return; - - var itm = document.getElementById("debug-panel-container"); - if (itm) - { - var pnl = - "
\n" + - " \n" + - " \n" + - "
\n" + - "

Debug

\n" + - "

Call

\n" + - "
    \n" + - "
  • api :
  • \n" + - "
  • verb :
  • \n" + - "
  • query :
  • \n" + - "
\n" + - "
\n" +
-			"        
\n" + - "

Response

\n" +
-			"        

Event

\n" +
-			"    
\n" + - "
\n"; - itm.insertAdjacentHTML("afterbegin", pnl); - } -} - -function createClass(name,rules) { - var style = document.createElement('style'); - style.type = 'text/css'; - document.getElementsByTagName('head')[0].appendChild(style); - if(!(style.sheet||{}).insertRule) - (style.styleSheet || style.sheet).addRule(name, rules); - else - style.sheet.insertRule(name+"{"+rules+"}",0); -} - -function syntaxHighlight(json) { - if (typeof json != 'string') - json = JSON.stringify(json, undefined, 2); - - json = json.replace(/&/g, '&').replace(//g, '>'); - return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { - var cls = 'json-number'; - if (/^"/.test(match)) { - if (/:$/.test(match)) { - cls = 'json-key'; - } else { - cls = 'json-string'; - } - } else if (/true|false/.test(match)) { - cls = 'json-boolean'; - } else if (/null/.test(match)) { - cls = 'json-null'; - } - return '' + match + ''; - }); -} - -function set_item_html(id, text) -{ - var itm = document.getElementById(id); - if (itm) itm.innerHTML = text; -} - -function set_item_text(id, text) -{ - var itm = document.getElementById(id); - if (itm) itm.innerText = text; -} - -function debug_panel_collapse() { - var pnl = document.getElementById('debug-panel'); - if (pnl) - { - pnl.classList.remove('expanded'); - pnl.classList.add('collapsed'); - } -} - -function debug_panel_expand() { - var pnl = document.getElementById('debug-panel'); - if (pnl) - { - pnl.classList.remove('collapsed'); - pnl.classList.add('expanded'); - } -} - -function init() { - add_debbug_panel(); - ws = new afb.ws(onopen, onabort); -} - -function onopen() { - //callbinder("ll-auth", "getuser", ""); - ws.onevent("*", gotevent); -} - -function onabort() { -} - -function replyok(obj) { - console.log("replyok:" + JSON.stringify(obj)); - set_item_html("debug-panel-response", syntaxHighlight(JSON.stringify(obj, null, 4))); -} - -function replyerr(obj) { - console.log("replyerr:" + JSON.stringify(obj)); - set_item_html("debug-panel-response", syntaxHighlight(JSON.stringify(obj, null, 4))); -} - -function gotevent(obj) { - console.log("gotevent:" + JSON.stringify(obj)); - set_item_html("debug-panel-event", syntaxHighlight(JSON.stringify(obj, null, 4))); -} - -function callbinder(api, verb, query) { - console.log ("subscribe api="+api+" verb="+verb+" query=" +query); - - set_item_text("debug-panel-call-api", api); - set_item_text("debug-panel-call-verb", verb); - set_item_html("debug-panel-call-query", syntaxHighlight(JSON.stringify(query, null, 4))); - - ws.call(api+"/"+verb, query).then(replyok, replyerr); -} - -function value(id){ - return document.getElementById(id).value; -} - -function readData() { - callbinder("persistence", "read", { "key": value("read-key") }); -} - -function deleteData() { - callbinder("persistence", "delete", { "key": value("read-key")}); -} - -function writeData() { - callbinder("persistence", "update", { - "key": value("write-key"), - "value": value("write-data") - }); -} - -function readProfile() { - var vin = value("profile-vin") - var token = value("get-profile-key") - var url = "https://agl-graphapi.forgerocklabs.org:443/getuserprofilefromtoken?vin=" + vin + "&kind=nfc&keytoken=" + token - - callbinder("persistence", "read", { - "key": { - "url": url, - "vin": vin, - "kind": "nfc", - "key": token - } - }); -} - -function deleteProfile() { - var vin = value("profile-vin") - var token = value("get-profile-key") - var url = "https://agl-graphapi.forgerocklabs.org:443/getuserprofilefromtoken?vin=" + vin + "&kind=nfc&keytoken=" + token - - callbinder("persistence", "delete", { - "key": { - "url": url, - "vin": vin, - "kind": "nfc", - "key": token - } - }); -} - -function writeProfile() { - var vin = value("profile-vin") - var token = value("set-profile-key") - var url = "https://agl-graphapi.forgerocklabs.org:443/getuserprofilefromtoken?vin=" + vin + "&kind=nfc&keytoken=" + token - - callbinder("persistence", "update", { - "key": { - "url": url, - "vin": vin, - "kind": "nfc", - "key": token - }, - "value": { - "keytoken": token, - "name": value("set-profile-login"), - "first_name": value("set-profile-first-name"), - "last_name": value("set-profile-last-name"), - "graphPreferredLanguage": value("set-profile-language") - } - }); -} diff --git a/ll-database-binding/src/CMakeLists.txt b/ll-database-binding/src/CMakeLists.txt deleted file mode 100644 index fc65420..0000000 --- a/ll-database-binding/src/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -PROJECT_TARGET_ADD(persistence-binding) - -find_package(GDBM) -if(DB_FOUND) - add_definitions(-DUSE_GDBM) -else(DB_FOUND) - find_package(BerkeleyDB REQUIRED) -endif(DB_FOUND) -include_directories(${DB_INCLUDE_DIR}) - -add_library(persistence-binding MODULE persistence-binding.c) -target_link_libraries(persistence-binding ${DB_LIBRARY}) - -set_target_properties(persistence-binding PROPERTIES - PREFIX "afb-" - LABELS "BINDING" - LINK_FLAGS ${BINDINGS_LINK_FLAG} - OUTPUT_NAME ${TARGET_NAME}) - -add_custom_command(TARGET ${TARGET_NAME} - PRE_BUILD - COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/../package/htdocs - COMMAND cp -rv ${CMAKE_CURRENT_SOURCE_DIR}/../htdocs ${CMAKE_CURRENT_BINARY_DIR}/../package/) - -install(TARGETS persistence-binding - RUNTIME DESTINATION bin - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) \ No newline at end of file diff --git a/ll-database-binding/src/export.map b/ll-database-binding/src/export.map deleted file mode 100644 index ee2f413..0000000 --- a/ll-database-binding/src/export.map +++ /dev/null @@ -1 +0,0 @@ -{ global: afbBindingV*; local: *; }; diff --git a/ll-database-binding/src/persistence-binding.c b/ll-database-binding/src/persistence-binding.c deleted file mode 100644 index e16b93b..0000000 --- a/ll-database-binding/src/persistence-binding.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright 2017 IoT.bzh - * - * author: Loïc Collignon - * author: Jose Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include - -#include - -#define AFB_BINDING_VERSION 2 -#include - -#if !defined(TO_STRING_FLAGS) -# if !defined(JSON_C_TO_STRING_NOSLASHESCAPE) -# define JSON_C_TO_STRING_NOSLASHESCAPE (1<<4) -# endif -# define TO_STRING_FLAGS (JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE) -#endif - -#if defined(USE_BERKELEY_DB) -# undef USE_BERKELEY_DB -#endif - -#if defined(USE_GDBM) -# undef USE_GDBM -# define USE_GDBM 1 -# define USE_BERKELEY_DB 0 -#else -# define USE_GDBM 0 -# define USE_BERKELEY_DB 1 -#endif - -// ----- Berkeley database ----- -#if USE_BERKELEY_DB - -#include - -#define DBFILE "ll-database-binding.db" -#define DATA DBT -#define DATA_SET(k,d,s) do{memset((k),0,sizeof*(k));(k)->data=(void*)d;(k)->size=(uint32_t)s;}while(0) -#define DATA_PTR(k) ((void*)((k).data)) -#define DATA_STR(k) ((char*)((k).data)) -#define DATA_SZ(k) ((size_t)((k).size)) - -static DB *database; - -static int xdb_open(const char *path) -{ - int ret; - - ret = db_create(&database, NULL, 0); - if (ret != 0) - { - AFB_ERROR("Failed to create database: %s.", db_strerror(ret)); - return -1; - } - - ret = database->open(database, NULL, path, NULL, DB_BTREE, DB_CREATE, 0600); - if (ret != 0) - { - AFB_ERROR("Failed to open the '%s' database: %s.", path, db_strerror(ret)); - database->close(database, 0); - return -1; - } - return 0; -} - -static void xdb_put(struct afb_req req, DBT *key, DBT *data, int replace) -{ - int ret; - - ret = database->put(database, NULL, key, data, replace ? 0 : DB_NOOVERWRITE); - if (ret == 0) - afb_req_success(req, NULL, NULL); - else - { - AFB_ERROR("can't %s key %s with %s", replace ? "replace" : "insert", DATA_STR(*key), DATA_STR(*data)); - afb_req_fail_f(req, "failed", "%s", db_strerror(ret)); - } -} - -static void xdb_delete(struct afb_req req, DBT *key) -{ - int ret; - - ret = database->del(database, NULL, key, 0); - if (ret == 0) - afb_req_success_f(req, NULL, NULL); - else - { - AFB_ERROR("can't delete key %s", DATA_STR(*key)); - afb_req_fail_f(req, "failed", "%s", db_strerror(ret)); - } - - free(DATA_PTR(key)); -} - -static void verb_read(struct afb_req req) -{ - DATA key; - DATA data; - int ret; - - char value[4096]; - - struct json_object* result; - struct json_object* val; - - - if (get_key(req, &key)) - return; - - AFB_INFO("read: key=%s", DATA_STR(key)); - - memset(&data, 0, sizeof data); - data.data = value; - data.ulen = 4096; - data.flags = DB_DBT_USERMEM; - - ret = database->get(database, NULL, &key, &data, 0); - if (ret == 0) - { - result = json_object_new_object(); - val = json_tokener_parse(DATA_STR(data)); - json_object_object_add(result, "value", val ? val : json_object_new_string(DATA_STR(data))); - - afb_req_success_f(req, result, "db success: read %s=%s.", DATA_STR(key), DATA_STR(data)); - } - else - afb_req_fail_f(req, "Failed to read datas.", "db fail: read %s - %s", DATA_STR(key), db_strerror(ret)); - - free(DATA_PTR(key)); -} - -#endif - -// ----- gdbm database ----- -#if USE_GDBM - -#include -#include - -#define DBFILE "ll-database-binding.dbm" -#define DATA datum -#define DATA_SET(k,d,s) do{(k)->dptr=(char*)d;(k)->dsize=(int)s;}while(0) -#define DATA_PTR(k) ((void*)((k).dptr)) -#define DATA_STR(k) ((char*)((k).dptr)) -#define DATA_SZ(k) ((size_t)((k).dsize)) - -#if GDBM_VERSION_MAJOR > 1 || (GDBM_VERSION_MAJOR == 1 && GDBM_VERSION_MINOR >= 13) -# define IFSYS(yes,no) (gdbm_syserr[gdbm_errno] ? (yes) : (no)) -#else -# define IFSYS(yes,no) (no) -#endif - -static GDBM_FILE database; - -static void onfatal(const char *text) -{ - AFB_ERROR("fatal gdbm message: %s", text); -} - -static int xdb_open(const char *path) -{ - database = gdbm_open(path, 512, GDBM_WRCREAT|GDBM_SYNC, 0600, onfatal); - if (!database) - { - AFB_ERROR("Fail to open/create database: %s%s%s", - gdbm_errlist[gdbm_errno], - IFSYS(", ", ""), - IFSYS(strerror(errno), "")); - return -1; - - } - return 0; -} - -static void xdb_put(struct afb_req req, datum *key, datum *data, int replace) -{ - int ret; - - ret = gdbm_store(database, *key, *data, replace ? GDBM_REPLACE : GDBM_INSERT); - if (ret == 0) - afb_req_success(req, NULL, NULL); - else - { - AFB_ERROR("can't %s key %s with %s: %s%s%s", - replace ? "replace" : "insert", - DATA_STR(*key), - DATA_STR(*data), - gdbm_errlist[gdbm_errno], - IFSYS(", ", ""), - IFSYS(strerror(errno), "")); - afb_req_fail_f(req, "failed", "%s", ret > 0 ? "key already exists" : gdbm_errlist[gdbm_errno]); - } -} - -static void xdb_delete(struct afb_req req, datum *key) -{ - int ret; - - ret = gdbm_delete(database, *key); - if (ret == 0) - afb_req_success_f(req, NULL, NULL); - else - { - AFB_ERROR("can't delete key %s: %s%s%s", - DATA_STR(*key), - gdbm_errlist[gdbm_errno], - IFSYS(", ", ""), - IFSYS(strerror(errno), "")); - afb_req_fail_f(req, "failed", "%s", gdbm_errlist[gdbm_errno]); - } -} - -static void xdb_get(struct afb_req req, datum *key) -{ - struct json_object* obj; - datum result; - - result = gdbm_fetch(database, *key); - if (result.dptr) - { - obj = json_object_new_object(); - json_object_object_add(obj, "value", json_tokener_parse(result.dptr)); - afb_req_success(req, obj, NULL); - free(result.dptr); - } - else - { - AFB_ERROR("can't get key %s: %s%s%s", - DATA_STR(*key), - gdbm_errlist[gdbm_errno], - IFSYS(", ", ""), - IFSYS(strerror(errno), "")); - afb_req_fail_f(req, "failed", "%s", gdbm_errlist[gdbm_errno]); - } -} -#endif - -// ----- Binding's implementations ----- - -/** - * @brief Get the path to the database - */ -static int get_database_path(char *buffer, size_t size) -{ - static const char dbfile[] = DBFILE; - - char *home, *config; - int rc; - - config = secure_getenv("XDG_CONFIG_HOME"); - if (config) - rc = snprintf(buffer, size, "%s/%s", config, dbfile); - else - { - home = secure_getenv("HOME"); - if (home) - rc = snprintf(buffer, size, "%s/.config/%s", home, dbfile); - else - { - uid_t uid = getuid(); - struct passwd *pwd = getpwuid(uid); - if (pwd) - rc = snprintf(buffer, size, "%s/.config/%s", pwd->pw_dir, dbfile); - else - rc = snprintf(buffer, size, "/home/%d/.config/%s", (int)uid, dbfile); - } - } - return rc; -} - -/** - * @brief Initialize the binding. - * @return Exit code, zero if success. - */ -static int ll_database_binding_init() -{ - char path[PATH_MAX]; - int ret; - - ret = get_database_path(path, sizeof path); - if (ret < 0 || (int)ret >= (int)(sizeof path)) - { - AFB_ERROR("Can't compute the database filename"); - return -1; - } - - AFB_INFO("opening database %s", path); - return xdb_open(path); -} - -/** - * Returns the database key for the 'req' - */ -static int get_key(struct afb_req req, DATA *key) -{ - char *appid, *data; - const char *jkey; - - size_t ljkey, lappid, size; - - struct json_object* args; - struct json_object* item; - - /* get the key */ - args = afb_req_json(req); - if (!json_object_object_get_ex(args, "key", &item)) - { - afb_req_fail(req, "no-key", NULL); - return -1; - } - if (!item - || !(jkey = json_object_to_json_string_ext(item, JSON_C_TO_STRING_PLAIN)) - || !(ljkey = strlen(jkey))) - { - afb_req_fail(req, "bad-key", NULL); - return -1; - } - - /* get the appid */ - appid = afb_req_get_application_id(req); -#if 1 - if (!appid) - appid = strdup("#UNKNOWN-APP#"); -#endif - if (!appid) - { - afb_req_fail(req, "bad-context", NULL); - return -1; - } - - /* make the db-key */ - lappid = strlen(appid); - size = lappid + ljkey + 2; - data = realloc(appid, size); - if (!data) - { - free(appid); - afb_req_fail(req, "out-of-memory", NULL); - return -1; - } - data[lappid] = ':'; - memcpy(&data[lappid + 1], jkey, ljkey + 1); - - /* return the key */ - DATA_SET(key, data, size); - return 0; -} - -static void put(struct afb_req req, int replace) -{ - DATA key; - DATA data; - - const char* value; - - struct json_object* args; - struct json_object* item; - - /* get the value */ - args = afb_req_json(req); - if (!json_object_object_get_ex(args, "value", &item)) - { - afb_req_fail(req, "no-value", NULL); - return; - } - value = json_object_to_json_string_ext(item, TO_STRING_FLAGS); - if (!value) - { - afb_req_fail(req, "out-of-memory", NULL); - return; - } - DATA_SET(&data, value, strlen(value) + 1); /* includes the tailing null */ - - /* get the key */ - if (get_key(req, &key)) - return; - - AFB_INFO("put: key=%s, value=%s", DATA_STR(key), DATA_STR(data)); - xdb_put(req, &key, &data, replace); - free(DATA_PTR(key)); -} - -static void verb_insert(struct afb_req req) -{ - put(req, 0); -} - -static void verb_update(struct afb_req req) -{ - put(req, 1); -} - -static void verb_delete(struct afb_req req) -{ - DATA key; - - if (get_key(req, &key)) - return; - - AFB_INFO("delete: key=%s", DATA_STR(key)); - xdb_delete(req, &key); - free(DATA_PTR(key)); -} - -static void verb_read(struct afb_req req) -{ - DATA key; - - if (get_key(req, &key)) - return; - - AFB_INFO("read: key=%s", DATA_STR(key)); - xdb_get(req, &key); - free(DATA_PTR(key)); -} - -// ----- Binding's configuration ----- -/* -static const struct afb_auth ll_database_binding_auths[] = { -}; -*/ - -#define VERB(name_,auth_,info_,sess_) {\ - .verb = #name_, \ - .callback = verb_##name_, \ - .auth = auth_, \ - .info = info_, \ - .session = sess_ } - -static const afb_verb_v2 ll_database_binding_verbs[]= { - VERB(insert, NULL, NULL, AFB_SESSION_NONE_V2), - VERB(update, NULL, NULL, AFB_SESSION_NONE_V2), - VERB(delete, NULL, NULL, AFB_SESSION_NONE_V2), - VERB(read, NULL, NULL, AFB_SESSION_NONE_V2), - { .verb = NULL} -}; - -const struct afb_binding_v2 afbBindingV2 = { - .api = "persistence", - .specification = NULL, - .verbs = ll_database_binding_verbs, - .preinit = NULL, - .init = ll_database_binding_init, - .onevent = NULL, - .noconcurrency = 0 -}; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..fc65420 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,28 @@ +PROJECT_TARGET_ADD(persistence-binding) + +find_package(GDBM) +if(DB_FOUND) + add_definitions(-DUSE_GDBM) +else(DB_FOUND) + find_package(BerkeleyDB REQUIRED) +endif(DB_FOUND) +include_directories(${DB_INCLUDE_DIR}) + +add_library(persistence-binding MODULE persistence-binding.c) +target_link_libraries(persistence-binding ${DB_LIBRARY}) + +set_target_properties(persistence-binding PROPERTIES + PREFIX "afb-" + LABELS "BINDING" + LINK_FLAGS ${BINDINGS_LINK_FLAG} + OUTPUT_NAME ${TARGET_NAME}) + +add_custom_command(TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/../package/htdocs + COMMAND cp -rv ${CMAKE_CURRENT_SOURCE_DIR}/../htdocs ${CMAKE_CURRENT_BINARY_DIR}/../package/) + +install(TARGETS persistence-binding + RUNTIME DESTINATION bin + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib) \ No newline at end of file diff --git a/src/export.map b/src/export.map new file mode 100644 index 0000000..ee2f413 --- /dev/null +++ b/src/export.map @@ -0,0 +1 @@ +{ global: afbBindingV*; local: *; }; diff --git a/src/persistence-binding.c b/src/persistence-binding.c new file mode 100644 index 0000000..e16b93b --- /dev/null +++ b/src/persistence-binding.c @@ -0,0 +1,469 @@ +/* + * Copyright 2017 IoT.bzh + * + * author: Loïc Collignon + * author: Jose Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include + +#define AFB_BINDING_VERSION 2 +#include + +#if !defined(TO_STRING_FLAGS) +# if !defined(JSON_C_TO_STRING_NOSLASHESCAPE) +# define JSON_C_TO_STRING_NOSLASHESCAPE (1<<4) +# endif +# define TO_STRING_FLAGS (JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE) +#endif + +#if defined(USE_BERKELEY_DB) +# undef USE_BERKELEY_DB +#endif + +#if defined(USE_GDBM) +# undef USE_GDBM +# define USE_GDBM 1 +# define USE_BERKELEY_DB 0 +#else +# define USE_GDBM 0 +# define USE_BERKELEY_DB 1 +#endif + +// ----- Berkeley database ----- +#if USE_BERKELEY_DB + +#include + +#define DBFILE "ll-database-binding.db" +#define DATA DBT +#define DATA_SET(k,d,s) do{memset((k),0,sizeof*(k));(k)->data=(void*)d;(k)->size=(uint32_t)s;}while(0) +#define DATA_PTR(k) ((void*)((k).data)) +#define DATA_STR(k) ((char*)((k).data)) +#define DATA_SZ(k) ((size_t)((k).size)) + +static DB *database; + +static int xdb_open(const char *path) +{ + int ret; + + ret = db_create(&database, NULL, 0); + if (ret != 0) + { + AFB_ERROR("Failed to create database: %s.", db_strerror(ret)); + return -1; + } + + ret = database->open(database, NULL, path, NULL, DB_BTREE, DB_CREATE, 0600); + if (ret != 0) + { + AFB_ERROR("Failed to open the '%s' database: %s.", path, db_strerror(ret)); + database->close(database, 0); + return -1; + } + return 0; +} + +static void xdb_put(struct afb_req req, DBT *key, DBT *data, int replace) +{ + int ret; + + ret = database->put(database, NULL, key, data, replace ? 0 : DB_NOOVERWRITE); + if (ret == 0) + afb_req_success(req, NULL, NULL); + else + { + AFB_ERROR("can't %s key %s with %s", replace ? "replace" : "insert", DATA_STR(*key), DATA_STR(*data)); + afb_req_fail_f(req, "failed", "%s", db_strerror(ret)); + } +} + +static void xdb_delete(struct afb_req req, DBT *key) +{ + int ret; + + ret = database->del(database, NULL, key, 0); + if (ret == 0) + afb_req_success_f(req, NULL, NULL); + else + { + AFB_ERROR("can't delete key %s", DATA_STR(*key)); + afb_req_fail_f(req, "failed", "%s", db_strerror(ret)); + } + + free(DATA_PTR(key)); +} + +static void verb_read(struct afb_req req) +{ + DATA key; + DATA data; + int ret; + + char value[4096]; + + struct json_object* result; + struct json_object* val; + + + if (get_key(req, &key)) + return; + + AFB_INFO("read: key=%s", DATA_STR(key)); + + memset(&data, 0, sizeof data); + data.data = value; + data.ulen = 4096; + data.flags = DB_DBT_USERMEM; + + ret = database->get(database, NULL, &key, &data, 0); + if (ret == 0) + { + result = json_object_new_object(); + val = json_tokener_parse(DATA_STR(data)); + json_object_object_add(result, "value", val ? val : json_object_new_string(DATA_STR(data))); + + afb_req_success_f(req, result, "db success: read %s=%s.", DATA_STR(key), DATA_STR(data)); + } + else + afb_req_fail_f(req, "Failed to read datas.", "db fail: read %s - %s", DATA_STR(key), db_strerror(ret)); + + free(DATA_PTR(key)); +} + +#endif + +// ----- gdbm database ----- +#if USE_GDBM + +#include +#include + +#define DBFILE "ll-database-binding.dbm" +#define DATA datum +#define DATA_SET(k,d,s) do{(k)->dptr=(char*)d;(k)->dsize=(int)s;}while(0) +#define DATA_PTR(k) ((void*)((k).dptr)) +#define DATA_STR(k) ((char*)((k).dptr)) +#define DATA_SZ(k) ((size_t)((k).dsize)) + +#if GDBM_VERSION_MAJOR > 1 || (GDBM_VERSION_MAJOR == 1 && GDBM_VERSION_MINOR >= 13) +# define IFSYS(yes,no) (gdbm_syserr[gdbm_errno] ? (yes) : (no)) +#else +# define IFSYS(yes,no) (no) +#endif + +static GDBM_FILE database; + +static void onfatal(const char *text) +{ + AFB_ERROR("fatal gdbm message: %s", text); +} + +static int xdb_open(const char *path) +{ + database = gdbm_open(path, 512, GDBM_WRCREAT|GDBM_SYNC, 0600, onfatal); + if (!database) + { + AFB_ERROR("Fail to open/create database: %s%s%s", + gdbm_errlist[gdbm_errno], + IFSYS(", ", ""), + IFSYS(strerror(errno), "")); + return -1; + + } + return 0; +} + +static void xdb_put(struct afb_req req, datum *key, datum *data, int replace) +{ + int ret; + + ret = gdbm_store(database, *key, *data, replace ? GDBM_REPLACE : GDBM_INSERT); + if (ret == 0) + afb_req_success(req, NULL, NULL); + else + { + AFB_ERROR("can't %s key %s with %s: %s%s%s", + replace ? "replace" : "insert", + DATA_STR(*key), + DATA_STR(*data), + gdbm_errlist[gdbm_errno], + IFSYS(", ", ""), + IFSYS(strerror(errno), "")); + afb_req_fail_f(req, "failed", "%s", ret > 0 ? "key already exists" : gdbm_errlist[gdbm_errno]); + } +} + +static void xdb_delete(struct afb_req req, datum *key) +{ + int ret; + + ret = gdbm_delete(database, *key); + if (ret == 0) + afb_req_success_f(req, NULL, NULL); + else + { + AFB_ERROR("can't delete key %s: %s%s%s", + DATA_STR(*key), + gdbm_errlist[gdbm_errno], + IFSYS(", ", ""), + IFSYS(strerror(errno), "")); + afb_req_fail_f(req, "failed", "%s", gdbm_errlist[gdbm_errno]); + } +} + +static void xdb_get(struct afb_req req, datum *key) +{ + struct json_object* obj; + datum result; + + result = gdbm_fetch(database, *key); + if (result.dptr) + { + obj = json_object_new_object(); + json_object_object_add(obj, "value", json_tokener_parse(result.dptr)); + afb_req_success(req, obj, NULL); + free(result.dptr); + } + else + { + AFB_ERROR("can't get key %s: %s%s%s", + DATA_STR(*key), + gdbm_errlist[gdbm_errno], + IFSYS(", ", ""), + IFSYS(strerror(errno), "")); + afb_req_fail_f(req, "failed", "%s", gdbm_errlist[gdbm_errno]); + } +} +#endif + +// ----- Binding's implementations ----- + +/** + * @brief Get the path to the database + */ +static int get_database_path(char *buffer, size_t size) +{ + static const char dbfile[] = DBFILE; + + char *home, *config; + int rc; + + config = secure_getenv("XDG_CONFIG_HOME"); + if (config) + rc = snprintf(buffer, size, "%s/%s", config, dbfile); + else + { + home = secure_getenv("HOME"); + if (home) + rc = snprintf(buffer, size, "%s/.config/%s", home, dbfile); + else + { + uid_t uid = getuid(); + struct passwd *pwd = getpwuid(uid); + if (pwd) + rc = snprintf(buffer, size, "%s/.config/%s", pwd->pw_dir, dbfile); + else + rc = snprintf(buffer, size, "/home/%d/.config/%s", (int)uid, dbfile); + } + } + return rc; +} + +/** + * @brief Initialize the binding. + * @return Exit code, zero if success. + */ +static int ll_database_binding_init() +{ + char path[PATH_MAX]; + int ret; + + ret = get_database_path(path, sizeof path); + if (ret < 0 || (int)ret >= (int)(sizeof path)) + { + AFB_ERROR("Can't compute the database filename"); + return -1; + } + + AFB_INFO("opening database %s", path); + return xdb_open(path); +} + +/** + * Returns the database key for the 'req' + */ +static int get_key(struct afb_req req, DATA *key) +{ + char *appid, *data; + const char *jkey; + + size_t ljkey, lappid, size; + + struct json_object* args; + struct json_object* item; + + /* get the key */ + args = afb_req_json(req); + if (!json_object_object_get_ex(args, "key", &item)) + { + afb_req_fail(req, "no-key", NULL); + return -1; + } + if (!item + || !(jkey = json_object_to_json_string_ext(item, JSON_C_TO_STRING_PLAIN)) + || !(ljkey = strlen(jkey))) + { + afb_req_fail(req, "bad-key", NULL); + return -1; + } + + /* get the appid */ + appid = afb_req_get_application_id(req); +#if 1 + if (!appid) + appid = strdup("#UNKNOWN-APP#"); +#endif + if (!appid) + { + afb_req_fail(req, "bad-context", NULL); + return -1; + } + + /* make the db-key */ + lappid = strlen(appid); + size = lappid + ljkey + 2; + data = realloc(appid, size); + if (!data) + { + free(appid); + afb_req_fail(req, "out-of-memory", NULL); + return -1; + } + data[lappid] = ':'; + memcpy(&data[lappid + 1], jkey, ljkey + 1); + + /* return the key */ + DATA_SET(key, data, size); + return 0; +} + +static void put(struct afb_req req, int replace) +{ + DATA key; + DATA data; + + const char* value; + + struct json_object* args; + struct json_object* item; + + /* get the value */ + args = afb_req_json(req); + if (!json_object_object_get_ex(args, "value", &item)) + { + afb_req_fail(req, "no-value", NULL); + return; + } + value = json_object_to_json_string_ext(item, TO_STRING_FLAGS); + if (!value) + { + afb_req_fail(req, "out-of-memory", NULL); + return; + } + DATA_SET(&data, value, strlen(value) + 1); /* includes the tailing null */ + + /* get the key */ + if (get_key(req, &key)) + return; + + AFB_INFO("put: key=%s, value=%s", DATA_STR(key), DATA_STR(data)); + xdb_put(req, &key, &data, replace); + free(DATA_PTR(key)); +} + +static void verb_insert(struct afb_req req) +{ + put(req, 0); +} + +static void verb_update(struct afb_req req) +{ + put(req, 1); +} + +static void verb_delete(struct afb_req req) +{ + DATA key; + + if (get_key(req, &key)) + return; + + AFB_INFO("delete: key=%s", DATA_STR(key)); + xdb_delete(req, &key); + free(DATA_PTR(key)); +} + +static void verb_read(struct afb_req req) +{ + DATA key; + + if (get_key(req, &key)) + return; + + AFB_INFO("read: key=%s", DATA_STR(key)); + xdb_get(req, &key); + free(DATA_PTR(key)); +} + +// ----- Binding's configuration ----- +/* +static const struct afb_auth ll_database_binding_auths[] = { +}; +*/ + +#define VERB(name_,auth_,info_,sess_) {\ + .verb = #name_, \ + .callback = verb_##name_, \ + .auth = auth_, \ + .info = info_, \ + .session = sess_ } + +static const afb_verb_v2 ll_database_binding_verbs[]= { + VERB(insert, NULL, NULL, AFB_SESSION_NONE_V2), + VERB(update, NULL, NULL, AFB_SESSION_NONE_V2), + VERB(delete, NULL, NULL, AFB_SESSION_NONE_V2), + VERB(read, NULL, NULL, AFB_SESSION_NONE_V2), + { .verb = NULL} +}; + +const struct afb_binding_v2 afbBindingV2 = { + .api = "persistence", + .specification = NULL, + .verbs = ll_database_binding_verbs, + .preinit = NULL, + .init = ll_database_binding_init, + .onevent = NULL, + .noconcurrency = 0 +}; -- cgit 1.2.3-korg