From c4d03c6dbf93a74c4fb41a1e2db398b10777ee3d Mon Sep 17 00:00:00 2001
From: Adrien Beraud <adrien.beraud@savoirfairelinux.com>
Date: Mon, 15 May 2017 11:15:54 -0400
Subject: [PATCH] nodejs: add initial module

Change-Id: I12310bd4a5826e5658e5b7fe72a8a5ae6efe82d0
---
 bin/nodejs/Makefile.am            |  10 ++
 bin/nodejs/binding.gyp            |  10 ++
 bin/nodejs/callmanager.i          | 137 +++++++++++++++
 bin/nodejs/configurationmanager.i | 226 ++++++++++++++++++++++++
 bin/nodejs/index.js               |  21 +++
 bin/nodejs/jni_interface.i        | 275 ++++++++++++++++++++++++++++++
 bin/nodejs/managerimpl.i          |  39 +++++
 bin/nodejs/presencemanager.i      |  54 ++++++
 bin/nodejs/ringservice.c.template |  73 ++++++++
 bin/nodejs/videomanager.i         |  69 ++++++++
 configure.ac                      |  14 ++
 11 files changed, 928 insertions(+)
 create mode 100644 bin/nodejs/Makefile.am
 create mode 100644 bin/nodejs/binding.gyp
 create mode 100644 bin/nodejs/callmanager.i
 create mode 100644 bin/nodejs/configurationmanager.i
 create mode 100644 bin/nodejs/index.js
 create mode 100644 bin/nodejs/jni_interface.i
 create mode 100644 bin/nodejs/managerimpl.i
 create mode 100644 bin/nodejs/presencemanager.i
 create mode 100644 bin/nodejs/ringservice.c.template
 create mode 100644 bin/nodejs/videomanager.i

diff --git a/bin/nodejs/Makefile.am b/bin/nodejs/Makefile.am
new file mode 100644
index 0000000000..f2b1e82c19
--- /dev/null
+++ b/bin/nodejs/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/globals.mk
+
+BUILT_SOURCES=                      \
+	ring_wrapper.cpp
+
+ring_wrapper.cpp: jni_interface.i configurationmanager.i managerimpl.i
+	$(SWIG) -v -c++ -javascript -node -o ring_wrapper.cpp jni_interface.i
+
+CLEANFILES= \
+	$(BUILT_SOURCES)
diff --git a/bin/nodejs/binding.gyp b/bin/nodejs/binding.gyp
new file mode 100644
index 0000000000..900a555c7d
--- /dev/null
+++ b/bin/nodejs/binding.gyp
@@ -0,0 +1,10 @@
+{
+  "targets": [
+    {
+      "target_name": "dring",
+      "sources": [ "ring_wrapper.cpp" ],
+      'cflags!': [ '-fno-exceptions' ],
+      'cflags_cc!': [ '-fno-exceptions' ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/bin/nodejs/callmanager.i b/bin/nodejs/callmanager.i
new file mode 100644
index 0000000000..39c1bf09df
--- /dev/null
+++ b/bin/nodejs/callmanager.i
@@ -0,0 +1,137 @@
+/*
+ *  Copyright (C) 2004-2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Emeric Vigier <emeric.vigier@savoirfairelinux.com>
+ *          Alexandre Lision <alexnadre.L@savoirfairelinux.com>
+ *          Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+%header %{
+
+#include "dring/dring.h"
+#include "dring/callmanager_interface.h"
+
+class Callback {
+public:
+    virtual ~Callback() {}
+    virtual void callStateChanged(const std::string& call_id, const std::string& state, int detail_code){}
+    virtual void transferFailed(void){}
+    virtual void transferSucceeded(void){}
+    virtual void recordPlaybackStopped(const std::string& path){}
+    virtual void voiceMailNotify(const std::string& call_id, int nd_msg){}
+    virtual void incomingMessage(const std::string& id, const std::string& from, const std::map<std::string, std::string>& messages){}
+    virtual void incomingCall(const std::string& account_id, const std::string& call_id, const std::string& from){}
+    virtual void recordPlaybackFilepath(const std::string& id, const std::string& filename){}
+    virtual void conferenceCreated(const std::string& conf_id){}
+    virtual void conferenceChanged(const std::string& conf_id, const std::string& state){}
+    virtual void conferenceRemoved(const std::string& conf_id){}
+    virtual void newCallCreated(const std::string& call_id, const std::string&, const std::string&){}
+    virtual void updatePlaybackScale(const std::string& filepath, int position, int scale){}
+    virtual void conferenceRemove(const std::string& conf_id){}
+    virtual void newCall(const std::string& account_id, const std::string& call_id, const std::string& to){}
+    virtual void sipCallStateChange(const std::string& call_id, const std::string& state, int code){}
+    virtual void recordingStateChanged(const std::string& call_id, int code){}
+    virtual void recordStateChange(const std::string& call_id, int state){}
+    virtual void onRtcpReportReceived(const std::string& call_id, const std::map<std::string, int>& stats){}
+    virtual void peerHold(const std::string& call_id, bool holding){}
+};
+
+
+%}
+
+%feature("director") Callback;
+
+namespace DRing {
+
+/* Call related methods */
+std::string placeCall(const std::string& accountID, const std::string& to);
+
+bool refuse(const std::string& callID);
+bool accept(const std::string& callID);
+bool hangUp(const std::string& callID);
+bool hold(const std::string& callID);
+bool unhold(const std::string& callID);
+bool muteLocalMedia(const std::string& callid, const std::string& mediaType, bool mute);
+bool transfer(const std::string& callID, const std::string& to);
+bool attendedTransfer(const std::string& transferID, const std::string& targetID);
+std::map<std::string, std::string> getCallDetails(const std::string& callID);
+std::vector<std::string> getCallList();
+
+/* Conference related methods */
+void removeConference(const std::string& conference_id);
+bool joinParticipant(const std::string& sel_callID, const std::string& drag_callID);
+void createConfFromParticipantList(const std::vector<std::string>& participants);
+bool isConferenceParticipant(const std::string& call_id);
+bool addParticipant(const std::string& callID, const std::string& confID);
+bool addMainParticipant(const std::string& confID);
+bool detachParticipant(const std::string& callID);
+bool joinConference(const std::string& sel_confID, const std::string& drag_confID);
+bool hangUpConference(const std::string& confID);
+bool holdConference(const std::string& confID);
+bool unholdConference(const std::string& confID);
+std::vector<std::string> getConferenceList();
+std::vector<std::string> getParticipantList(const std::string& confID);
+std::vector<std::string> getDisplayNames(const std::string& confID);
+std::string getConferenceId(const std::string& callID);
+std::map<std::string, std::string> getConferenceDetails(const std::string& callID);
+
+/* File Playback methods */
+bool startRecordedFilePlayback(const std::string& filepath);
+void stopRecordedFilePlayback(const std::string& filepath);
+
+/* General audio methods */
+bool toggleRecording(const std::string& callID);
+/* DEPRECATED */
+void setRecording(const std::string& callID);
+
+void recordPlaybackSeek(double value);
+bool getIsRecording(const std::string& callID);
+std::string getCurrentAudioCodecName(const std::string& callID);
+void playDTMF(const std::string& key);
+void startTone(int32_t start, int32_t type);
+
+bool switchInput(const std::string& callID, const std::string& resource);
+
+/* Instant messaging */
+void sendTextMessage(const std::string& callID, const std::map<std::string, std::string>& messages, const std::string& from, const bool& isMixed);
+
+}
+
+class Callback {
+public:
+    virtual ~Callback() {}
+    virtual void callStateChanged(const std::string& call_id, const std::string& state, int detail_code){}
+    virtual void transferFailed(void){}
+    virtual void transferSucceeded(void){}
+    virtual void recordPlaybackStopped(const std::string& path){}
+    virtual void voiceMailNotify(const std::string& call_id, int nd_msg){}
+    virtual void incomingMessage(const std::string& id, const std::string& from, const std::map<std::string, std::string>& messages){}
+    virtual void incomingCall(const std::string& account_id, const std::string& call_id, const std::string& from){}
+    virtual void recordPlaybackFilepath(const std::string& id, const std::string& filename){}
+    virtual void conferenceCreated(const std::string& conf_id){}
+    virtual void conferenceChanged(const std::string& conf_id, const std::string& state){}
+    virtual void conferenceRemoved(const std::string& conf_id){}
+    virtual void newCallCreated(const std::string& call_id, const std::string&, const std::string&){}
+    virtual void updatePlaybackScale(const std::string& filepath, int position, int scale){}
+    virtual void conferenceRemove(const std::string& conf_id){}
+    virtual void newCall(const std::string& account_id, const std::string& call_id, const std::string& to){}
+    virtual void sipCallStateChange(const std::string& call_id, const std::string& state, int code){}
+    virtual void recordingStateChanged(const std::string& call_id, int code){}
+    virtual void recordStateChange(const std::string& call_id, int state){}
+    virtual void onRtcpReportReceived(const std::string& call_id, const std::map<std::string, int>& stats){}
+    virtual void peerHold(const std::string& call_id, bool holding){}
+};
diff --git a/bin/nodejs/configurationmanager.i b/bin/nodejs/configurationmanager.i
new file mode 100644
index 0000000000..b27a3f97c3
--- /dev/null
+++ b/bin/nodejs/configurationmanager.i
@@ -0,0 +1,226 @@
+/*
+ *  Copyright (C) 2004-2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+%header %{
+#include "dring/dring.h"
+#include "dring/configurationmanager_interface.h"
+
+class ConfigurationCallback {
+public:
+    virtual ~ConfigurationCallback(){}
+    virtual void volumeChanged(const std::string& device, int value){}
+    virtual void accountsChanged(void){}
+    virtual void historyChanged(void){}
+    virtual void stunStatusFailure(const std::string& account_id){}
+    virtual void registrationStateChanged(const std::string& account_id, const std::string& state, int code, const std::string& detail_str){}
+    virtual void volatileAccountDetailsChanged(const std::string& account_id, const std::map<std::string, std::string>& details){}
+    virtual void incomingAccountMessage(const std::string& /*account_id*/, const std::string& /*from*/, const std::map<std::string, std::string>& /*payload*/){}
+    virtual void accountMessageStatusChanged(const std::string& /*account_id*/, uint64_t /*message_id*/, const std::string& /*to*/, int /*state*/){}
+    virtual void knownDevicesChanged(const std::string& /*account_id*/, const std::map<std::string, std::string>& /*devices*/){}
+    virtual void exportOnRingEnded(const std::string& /*account_id*/, int /*state*/, const std::string& /*pin*/){}
+
+    virtual void incomingTrustRequest(const std::string& /*account_id*/, const std::string& /*from*/, const std::vector<uint8_t>& /*payload*/, time_t received){}
+    virtual void contactAdded(const std::string& /*account_id*/, const std::string& /*uri*/, bool confirmed){}
+    virtual void contactRemoved(const std::string& /*account_id*/, const std::string& /*uri*/, bool banned){}
+
+    virtual void certificatePinned(const std::string& /*certId*/){}
+    virtual void certificatePathPinned(const std::string& /*path*/, const std::vector<std::string>& /*certId*/){}
+    virtual void certificateExpired(const std::string& /*certId*/){}
+    virtual void certificateStateChanged(const std::string& /*account_id*/, const std::string& /*certId*/, const std::string& /*state*/){}
+
+    virtual void errorAlert(int alert){}
+
+    virtual void nameRegistrationEnded(const std::string& /*account_id*/, int state, const std::string& /*name*/){}
+    virtual void registeredNameFound(const std::string& /*account_id*/, int state, const std::string& /*address*/, const std::string& /*name*/){}
+
+    virtual void migrationEnded(const std::string& /*accountId*/, const std::string& /*state*/){}
+    virtual void deviceRevocationEnded(const std::string& /*accountId*/, const std::string& /*device*/, int /*status*/){}
+};
+%}
+
+%feature("director") ConfigurationCallback;
+
+namespace DRing {
+
+std::map<std::string, std::string> getAccountDetails(const std::string& accountID);
+std::map<std::string, std::string> getVolatileAccountDetails(const std::string& accountID);
+void setAccountDetails(const std::string& accountID, const std::map<std::string, std::string>& details);
+void setAccountActive(const std::string& accountID, bool active);
+std::map<std::string, std::string> getAccountTemplate(const std::string& accountType);
+std::string addAccount(const std::map<std::string, std::string>& details);
+void removeAccount(const std::string& accountID);
+std::vector<std::string> getAccountList();
+void sendRegister(const std::string& accountID, bool enable);
+void registerAllAccounts(void);
+uint64_t sendAccountTextMessage(const std::string& accountID, const std::string& to, const std::map<std::string, std::string>& message);
+int getMessageStatus(uint64_t id);
+
+bool lookupName(const std::string& account, const std::string& nameserver, const std::string& name);
+bool lookupAddress(const std::string& account, const std::string& nameserver, const std::string& address);
+bool registerName(const std::string& account, const std::string& password, const std::string& name);
+
+std::map<std::string, std::string> getTlsDefaultSettings();
+
+std::vector<unsigned> getCodecList();
+std::vector<std::string> getSupportedTlsMethod();
+std::vector<std::string> getSupportedCiphers(const std::string& accountID);
+std::map<std::string, std::string> getCodecDetails(const std::string& accountID, const unsigned& codecId);
+bool setCodecDetails(const std::string& accountID, const unsigned& codecId, const std::map<std::string, std::string>& details);
+std::vector<unsigned> getActiveCodecList(const std::string& accountID);
+std::string exportOnRing(const std::string& accountID, const std::string& password);
+
+std::map<std::string, std::string> getKnownRingDevices(const std::string& accountID);
+bool revokeDevice(const std::string& accountID, const std::string& password, const std::string& deviceID);
+
+void setActiveCodecList(const std::string& accountID, const std::vector<unsigned>& list);
+
+std::vector<std::string> getAudioPluginList();
+void setAudioPlugin(const std::string& audioPlugin);
+std::vector<std::string> getAudioOutputDeviceList();
+void setAudioOutputDevice(int32_t index);
+void setAudioInputDevice(int32_t index);
+void setAudioRingtoneDevice(int32_t index);
+std::vector<std::string> getAudioInputDeviceList();
+std::vector<std::string> getCurrentAudioDevicesIndex();
+int32_t getAudioInputDeviceIndex(const std::string& name);
+int32_t getAudioOutputDeviceIndex(const std::string& name);
+std::string getCurrentAudioOutputPlugin();
+bool getNoiseSuppressState();
+void setNoiseSuppressState(bool state);
+
+bool isAgcEnabled();
+void setAgcState(bool enabled);
+
+void muteDtmf(bool mute);
+bool isDtmfMuted();
+
+bool isCaptureMuted();
+void muteCapture(bool mute);
+bool isPlaybackMuted();
+void mutePlayback(bool mute);
+bool isRingtoneMuted();
+void muteRingtone(bool mute);
+
+std::string getAudioManager();
+bool setAudioManager(const std::string& api);
+
+std::string getRecordPath();
+void setRecordPath(const std::string& recPath);
+bool getIsAlwaysRecording();
+void setIsAlwaysRecording(bool rec);
+
+void setHistoryLimit(int32_t days);
+int32_t getHistoryLimit();
+
+void setAccountsOrder(const std::string& order);
+
+std::map<std::string, std::string> getHookSettings();
+void setHookSettings(const std::map<std::string, std::string>& settings);
+
+std::vector<std::map<std::string, std::string> > getCredentials(const std::string& accountID);
+void setCredentials(const std::string& accountID, const std::vector<std::map<std::string, std::string> >& details);
+
+std::string getAddrFromInterfaceName(const std::string& interface);
+
+std::vector<std::string> getAllIpInterface();
+std::vector<std::string> getAllIpInterfaceByName();
+
+std::map<std::string, std::string> getShortcuts();
+void setShortcuts(const std::map<std::string, std::string> &shortcutsMap);
+
+void setVolume(const std::string& device, double value);
+double getVolume(const std::string& device);
+
+/*
+ * Security
+ */
+std::map<std::string, std::string> validateCertificatePath(const std::string& accountId,
+                                                       const std::string& certificate,
+                                                       const std::string& privateKey,
+                                                       const std::string& privateKeyPassword,
+                                                       const std::string& caList);
+
+std::map<std::string, std::string> validateCertificate(const std::string& accountId, const std::string& certificate);
+
+std::map<std::string, std::string> getCertificateDetails(const std::string& certificate);
+std::map<std::string, std::string> getCertificateDetailsPath(const std::string& certificate, const std::string& privateKey, const std::string& privateKeyPass);
+
+std::vector<std::string> getPinnedCertificates();
+
+std::vector<std::string> pinCertificate(const std::vector<uint8_t>& certificate, bool local);
+bool unpinCertificate(const std::string& certId);
+
+void pinCertificatePath(const std::string& path);
+unsigned unpinCertificatePath(const std::string& path);
+
+bool pinRemoteCertificate(const std::string& accountId, const std::string& certId);
+bool setCertificateStatus(const std::string& account, const std::string& certId, const std::string& status);
+std::vector<std::string> getCertificatesByStatus(const std::string& account, const std::string& status);
+
+/* contact requests */
+std::vector<std::map<std::string, std::string>> getTrustRequests(const std::string& accountId);
+bool acceptTrustRequest(const std::string& accountId, const std::string& from);
+bool discardTrustRequest(const std::string& accountId, const std::string& from);
+void sendTrustRequest(const std::string& accountId, const std::string& to, const std::vector<uint8_t>& payload);
+
+/* Contacts */
+
+void addContact(const std::string& accountId, const std::string& uri);
+void removeContact(const std::string& accountId, const std::string& uri);
+std::vector<std::map<std::string, std::string>> getContacts(const std::string& accountId);
+
+int exportAccounts(std::vector<std::string> accountIDs, std::string toDir, std::string password);
+int importAccounts(std::string archivePath, std::string password);
+
+void connectivityChanged();
+}
+
+class ConfigurationCallback {
+public:
+    virtual ~ConfigurationCallback(){}
+    virtual void volumeChanged(const std::string& device, int value){}
+    virtual void accountsChanged(void){}
+    virtual void historyChanged(void){}
+    virtual void stunStatusFailure(const std::string& account_id){}
+    virtual void registrationStateChanged(const std::string& account_id, const std::string& state, int code, const std::string& detail_str){}
+    virtual void volatileAccountDetailsChanged(const std::string& account_id, const std::map<std::string, std::string>& details){}
+    virtual void incomingAccountMessage(const std::string& /*account_id*/, const std::string& /*from*/, const std::map<std::string, std::string>& /*payload*/){}
+    virtual void accountMessageStatusChanged(const std::string& /*account_id*/, uint64_t /*message_id*/, const std::string& /*to*/, int /*state*/){}
+    virtual void knownDevicesChanged(const std::string& /*account_id*/, const std::map<std::string, std::string>& /*devices*/){}
+    virtual void exportOnRingEnded(const std::string& /*account_id*/, int /*state*/, const std::string& /*pin*/){}
+
+    virtual void incomingTrustRequest(const std::string& /*account_id*/, const std::string& /*from*/, const std::vector<uint8_t>& /*payload*/, time_t received){}
+    virtual void contactAdded(const std::string& /*account_id*/, const std::string& /*uri*/, bool confirmed){}
+    virtual void contactRemoved(const std::string& /*account_id*/, const std::string& /*uri*/, bool banned){}
+
+    virtual void certificatePinned(const std::string& /*certId*/){}
+    virtual void certificatePathPinned(const std::string& /*path*/, const std::vector<std::string>& /*certId*/){}
+    virtual void certificateExpired(const std::string& /*certId*/){}
+    virtual void certificateStateChanged(const std::string& /*account_id*/, const std::string& /*certId*/, const std::string& /*state*/){}
+
+    virtual void errorAlert(int alert){}
+
+    virtual void nameRegistrationEnded(const std::string& /*account_id*/, int state, const std::string& /*name*/){}
+    virtual void registeredNameFound(const std::string& /*account_id*/, int state, const std::string& /*address*/, const std::string& /*name*/){}
+
+    virtual void migrationEnded(const std::string& /*accountId*/, const std::string& /*state*/){}
+    virtual void deviceRevocationEnded(const std::string& /*accountId*/, const std::string& /*device*/, int /*status*/){}
+};
diff --git a/bin/nodejs/index.js b/bin/nodejs/index.js
new file mode 100644
index 0000000000..6a0e4fc3ac
--- /dev/null
+++ b/bin/nodejs/index.js
@@ -0,0 +1,21 @@
+/*
+ *  Copyright (c) 2017 Savoir-faire Linux Inc.
+ *
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+'use strict';
+
+require("./build/Release/dring")
diff --git a/bin/nodejs/jni_interface.i b/bin/nodejs/jni_interface.i
new file mode 100644
index 0000000000..d927ad6f13
--- /dev/null
+++ b/bin/nodejs/jni_interface.i
@@ -0,0 +1,275 @@
+/*
+ *  Copyright (C) 2004-2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Emeric Vigier <emeric.vigier@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* File : jni_interface.i */
+%module (directors="1") Ringservice
+
+#define SWIG_JAVA_ATTACH_CURRENT_THREAD_AS_DAEMON
+%include "typemaps.i"
+%include "std_string.i" /* std::string typemaps */
+%include "arrays_javascript.i";
+%include "carrays.i";
+%include "std_map.i";
+%include "std_vector.i";
+%include "stdint.i";
+
+/* void* shall be handled as byte arrays */
+%typemap(jni) void * "void *"
+%typemap(jtype) void * "byte[]"
+%typemap(jstype) void * "byte[]"
+%typemap(javain) void * "$javainput"
+%typemap(in) void * %{
+    $1 = (void*)$input;
+%}
+%typemap(javadirectorin) void * "$jniinput"
+%typemap(out) void * %{
+    $result = $1;
+%}
+%typemap(javaout) void * {
+    return $jnicall;
+}
+
+/* Avoid uint64_t to be converted to BigInteger */
+%apply int64_t { uint64_t };
+
+namespace std {
+
+%typemap(javacode) map<string, string> %{
+  public static $javaclassname toSwig(java.util.Map<String,String> in) {
+    $javaclassname n = new $javaclassname();
+    for (java.util.Map.Entry<String, String> entry : in.entrySet()) {
+      if (entry.getValue() != null) {
+        n.set(entry.getKey(), entry.getValue());
+      }
+    }
+    return n;
+  }
+  public java.util.HashMap<String,String> toNative() {
+    java.util.HashMap<String,String> out = new java.util.HashMap<>((int)size());
+    StringVect keys = keys();
+    for (String s : keys) {
+        out.put(s, get(s));
+    }
+    return out;
+  }
+  public java.util.HashMap<String,String> toNativeFromUtf8() {
+      java.util.HashMap<String,String> out = new java.util.HashMap<>((int)size());
+      StringVect keys = keys();
+      for (String s : keys) {
+        out.put(s, getRaw(s).toJavaString());
+      }
+      return out;
+  }
+%}
+%extend map<string, string> {
+    std::vector<std::string> keys() const {
+        std::vector<std::string> k;
+        k.reserve($self->size());
+        for (const auto& i : *$self) {
+            k.push_back(i.first);
+        }
+        return k;
+    }
+    void setRaw(std::string key, const vector<uint8_t>& value) {
+        (*$self)[key] = std::string(value.data(), value.data()+value.size());
+    }
+    std::vector<uint8_t> getRaw(std::string key) {
+        auto& v = $self->at(key);
+        return {v.begin(), v.end()};
+    }
+}
+%template(StringMap) map<string, string>;
+
+%typemap(javabase) vector<string> "java.util.AbstractList<String>"
+%typemap(javainterface) vector<string> "java.util.RandomAccess"
+%extend vector<string> {
+  value_type set(int i, const value_type& in) throw (std::out_of_range) {
+    const std::string old = $self->at(i);
+    $self->at(i) = in;
+    return old;
+  }
+  bool add(const value_type& in) {
+    $self->push_back(in);
+    return true;
+  }
+  int32_t size() const {
+    return $self->size();
+  }
+}
+%template(StringVect) vector<string>;
+
+%typemap(javacode) vector< map<string,string> > %{
+  public java.util.ArrayList<java.util.Map<String, String>> toNative() {
+    java.util.ArrayList<java.util.Map<String, String>> out = new java.util.ArrayList<>();
+    for (int i = 0; i < size(); ++i) {
+        out.add(get(i).toNative());
+    }
+    return out;
+  }
+%}
+%template(VectMap) vector< map<string,string> >;
+%template(IntegerMap) map<string,int>;
+%template(IntVect) vector<int32_t>;
+%template(UintVect) vector<uint32_t>;
+
+%typemap(javacode) vector<uint8_t> %{
+  public static Blob fromString(String in) {
+    byte[] dat;
+    try {
+      dat = in.getBytes("UTF-8");
+    } catch (java.io.UnsupportedEncodingException e) {
+      dat = in.getBytes();
+    }
+    Blob n = new Blob(dat.length);
+    for (int i=0; i<dat.length; i++) {
+      n.set(i, dat[i]);
+    }
+    return n;
+  }
+  public String toJavaString() {
+    byte[] dat = new byte[(int)size()];
+    for (int i=0; i<dat.length; i++) {
+        dat[i] = (byte)get(i);
+    }
+    try {
+        return new String(dat, "utf-8");
+    } catch (java.io.UnsupportedEncodingException e) {
+        return "";
+    }
+  }
+%}
+%template(Blob) vector<uint8_t>;
+%template(FloatVect) vector<float>;
+}
+
+/* not parsed by SWIG but needed by generated C files */
+%header %{
+
+#include <functional>
+
+%}
+
+/* parsed by SWIG to generate all the glue */
+/* %include "../managerimpl.h" */
+/* %include <client/callmanager.h> */
+
+%include "managerimpl.i"
+%include "callmanager.i"
+%include "configurationmanager.i"
+%include "presencemanager.i"
+%include "videomanager.i"
+
+#include "dring/callmanager_interface.h"
+
+%inline %{
+/* some functions that need to be declared in *_wrap.cpp
+ * that are not declared elsewhere in the c++ code
+ */
+
+void init(ConfigurationCallback* confM, Callback* callM, PresenceCallback* presM, VideoCallback* videoM) {
+    using namespace std::placeholders;
+
+    using std::bind;
+    using DRing::exportable_callback;
+    using DRing::CallSignal;
+    using DRing::ConfigurationSignal;
+    using DRing::PresenceSignal;
+    using DRing::VideoSignal;
+
+    using SharedCallback = std::shared_ptr<DRing::CallbackWrapperBase>;
+
+    // Call event handlers
+    const std::map<std::string, SharedCallback> callEvHandlers = {
+        exportable_callback<CallSignal::StateChange>(bind(&Callback::callStateChanged, callM, _1, _2, _3)),
+        exportable_callback<CallSignal::TransferFailed>(bind(&Callback::transferFailed, callM)),
+        exportable_callback<CallSignal::TransferSucceeded>(bind(&Callback::transferSucceeded, callM)),
+        exportable_callback<CallSignal::RecordPlaybackStopped>(bind(&Callback::recordPlaybackStopped, callM, _1)),
+        exportable_callback<CallSignal::VoiceMailNotify>(bind(&Callback::voiceMailNotify, callM, _1, _2)),
+        exportable_callback<CallSignal::IncomingMessage>(bind(&Callback::incomingMessage, callM, _1, _2, _3)),
+        exportable_callback<CallSignal::IncomingCall>(bind(&Callback::incomingCall, callM, _1, _2, _3)),
+        exportable_callback<CallSignal::RecordPlaybackFilepath>(bind(&Callback::recordPlaybackFilepath, callM, _1, _2)),
+        exportable_callback<CallSignal::ConferenceCreated>(bind(&Callback::conferenceCreated, callM, _1)),
+        exportable_callback<CallSignal::ConferenceChanged>(bind(&Callback::conferenceChanged, callM, _1, _2)),
+        exportable_callback<CallSignal::UpdatePlaybackScale>(bind(&Callback::updatePlaybackScale, callM, _1, _2, _3)),
+        exportable_callback<CallSignal::ConferenceRemoved>(bind(&Callback::conferenceRemoved, callM, _1)),
+        exportable_callback<CallSignal::NewCallCreated>(bind(&Callback::newCallCreated, callM, _1, _2, _3)),
+        exportable_callback<CallSignal::RecordingStateChanged>(bind(&Callback::recordingStateChanged, callM, _1, _2)),
+        exportable_callback<CallSignal::RtcpReportReceived>(bind(&Callback::onRtcpReportReceived, callM, _1, _2)),
+        exportable_callback<CallSignal::PeerHold>(bind(&Callback::peerHold, callM, _1, _2))
+    };
+
+    // Configuration event handlers
+    const std::map<std::string, SharedCallback> configEvHandlers = {
+        exportable_callback<ConfigurationSignal::VolumeChanged>(bind(&ConfigurationCallback::volumeChanged, confM, _1, _2)),
+        exportable_callback<ConfigurationSignal::AccountsChanged>(bind(&ConfigurationCallback::accountsChanged, confM)),
+        exportable_callback<ConfigurationSignal::StunStatusFailed>(bind(&ConfigurationCallback::stunStatusFailure, confM, _1)),
+        exportable_callback<ConfigurationSignal::RegistrationStateChanged>(bind(&ConfigurationCallback::registrationStateChanged, confM, _1, _2, _3, _4)),
+        exportable_callback<ConfigurationSignal::VolatileDetailsChanged>(bind(&ConfigurationCallback::volatileAccountDetailsChanged, confM, _1, _2)),
+        exportable_callback<ConfigurationSignal::KnownDevicesChanged>(bind(&ConfigurationCallback::knownDevicesChanged, confM, _1, _2)),
+        exportable_callback<ConfigurationSignal::ExportOnRingEnded>(bind(&ConfigurationCallback::exportOnRingEnded, confM, _1, _2, _3)),
+        exportable_callback<ConfigurationSignal::Error>(bind(&ConfigurationCallback::errorAlert, confM, _1)),
+        exportable_callback<ConfigurationSignal::IncomingAccountMessage>(bind(&ConfigurationCallback::incomingAccountMessage, confM, _1, _2, _3 )),
+        exportable_callback<ConfigurationSignal::AccountMessageStatusChanged>(bind(&ConfigurationCallback::accountMessageStatusChanged, confM, _1, _2, _3, _4 )),
+        exportable_callback<ConfigurationSignal::IncomingTrustRequest>(bind(&ConfigurationCallback::incomingTrustRequest, confM, _1, _2, _3, _4 )),
+        exportable_callback<ConfigurationSignal::ContactAdded>(bind(&ConfigurationCallback::contactAdded, confM, _1, _2, _3 )),
+        exportable_callback<ConfigurationSignal::ContactRemoved>(bind(&ConfigurationCallback::contactRemoved, confM, _1, _2, _3 )),
+        exportable_callback<ConfigurationSignal::CertificatePinned>(bind(&ConfigurationCallback::certificatePinned, confM, _1 )),
+        exportable_callback<ConfigurationSignal::CertificatePathPinned>(bind(&ConfigurationCallback::certificatePathPinned, confM, _1, _2 )),
+        exportable_callback<ConfigurationSignal::CertificateExpired>(bind(&ConfigurationCallback::certificateExpired, confM, _1 )),
+        exportable_callback<ConfigurationSignal::CertificateStateChanged>(bind(&ConfigurationCallback::certificateStateChanged, confM, _1, _2, _3 )),
+        exportable_callback<ConfigurationSignal::RegisteredNameFound>(bind(&ConfigurationCallback::registeredNameFound, confM, _1, _2, _3, _4 )),
+        exportable_callback<ConfigurationSignal::NameRegistrationEnded>(bind(&ConfigurationCallback::nameRegistrationEnded, confM, _1, _2, _3 )),
+        exportable_callback<ConfigurationSignal::MigrationEnded>(bind(&ConfigurationCallback::migrationEnded, confM, _1, _2)),
+        exportable_callback<ConfigurationSignal::DeviceRevocationEnded>(bind(&ConfigurationCallback::deviceRevocationEnded, confM, _1, _2, _3))
+    };
+
+    // Presence event handlers
+    const std::map<std::string, SharedCallback> presenceEvHandlers = {
+        exportable_callback<PresenceSignal::NewServerSubscriptionRequest>(bind(&PresenceCallback::newServerSubscriptionRequest, presM, _1 )),
+        exportable_callback<PresenceSignal::ServerError>(bind(&PresenceCallback::serverError, presM, _1, _2, _3 )),
+        exportable_callback<PresenceSignal::NewBuddyNotification>(bind(&PresenceCallback::newBuddyNotification, presM, _1, _2, _3, _4 )),
+        exportable_callback<PresenceSignal::SubscriptionStateChanged>(bind(&PresenceCallback::subscriptionStateChanged, presM, _1, _2, _3 ))
+    };
+
+    /*const std::map<std::string, SharedCallback> videoEvHandlers = {
+        exportable_callback<VideoSignal::GetCameraInfo>(bind(&VideoCallback::getCameraInfo, videoM, _1, _2, _3, _4)),
+        exportable_callback<VideoSignal::SetParameters>(bind(&VideoCallback::setParameters, videoM, _1, _2, _3, _4, _5)),
+        exportable_callback<VideoSignal::StartCapture>(bind(&VideoCallback::startCapture, videoM, _1)),
+        exportable_callback<VideoSignal::StopCapture>(bind(&VideoCallback::stopCapture, videoM)),
+        exportable_callback<VideoSignal::DecodingStarted>(bind(&VideoCallback::decodingStarted, videoM, _1, _2, _3, _4, _5)),
+        exportable_callback<VideoSignal::DecodingStopped>(bind(&VideoCallback::decodingStopped, videoM, _1, _2, _3)),
+    };*/
+
+    if (!DRing::init(static_cast<DRing::InitFlag>(DRing::DRING_FLAG_DEBUG)))
+        return;
+
+    registerCallHandlers(callEvHandlers);
+    registerConfHandlers(configEvHandlers);
+    registerPresHandlers(presenceEvHandlers);
+    //registerVideoHandlers(videoEvHandlers);
+
+    DRing::start();
+}
+
+
+%}
+#ifndef SWIG
+/* some bad declarations */
+#endif
diff --git a/bin/nodejs/managerimpl.i b/bin/nodejs/managerimpl.i
new file mode 100644
index 0000000000..d24a425721
--- /dev/null
+++ b/bin/nodejs/managerimpl.i
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (C) 2004-2016 Savoir-faire Linux Inc.
+ *
+ *  Author: Emeric Vigier <emeric.vigier@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* %nodefaultctor ManagerImpl;
+%nodefaultdtor ManagerImpl; */
+%header %{
+#include "dring/dring.h"
+%}
+
+namespace DRing {
+
+/**
+ * Finalizes libsflphone, freeing any resource allocated by the library.
+ */
+void fini(void);
+
+/**
+ * Poll for events
+ */
+void pollEvents(void);
+
+}
diff --git a/bin/nodejs/presencemanager.i b/bin/nodejs/presencemanager.i
new file mode 100644
index 0000000000..5fe2302e6c
--- /dev/null
+++ b/bin/nodejs/presencemanager.i
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (C) 2017 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Bonnet <aline.bonnet@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+%header %{
+
+#include "dring/dring.h"
+#include "dring/presencemanager_interface.h"
+
+class PresenceCallback {
+public:
+    virtual ~PresenceCallback(){}
+    virtual void newServerSubscriptionRequest(const std::string& /*remote*/){}
+    virtual void serverError(const std::string& /*account_id*/, const std::string& /*error*/, const std::string& /*msg*/){}
+    virtual void newBuddyNotification(const std::string& /*account_id*/, const std::string& /*buddy_uri*/, int /*status*/, const std::string& /*line_status*/){}
+    virtual void subscriptionStateChanged(const std::string& /*account_id*/, const std::string& /*buddy_uri*/, int /*state*/){}
+};
+%}
+
+%feature("director") PresenceCallback;
+
+namespace DRing {
+
+/* Presence subscription/Notification. */
+void publish(const std::string& accountID, bool status, const std::string& note);
+void answerServerRequest(const std::string& uri, bool flag);
+void subscribeBuddy(const std::string& accountID, const std::string& uri, bool flag);
+std::vector<std::map<std::string, std::string>> getSubscriptions(const std::string& accountID);
+void setSubscriptions(const std::string& accountID, const std::vector<std::string>& uris);
+}
+
+class PresenceCallback {
+public:
+    virtual ~PresenceCallback(){}
+    virtual void newServerSubscriptionRequest(const std::string& /*remote*/){}
+    virtual void serverError(const std::string& /*account_id*/, const std::string& /*error*/, const std::string& /*msg*/){}
+    virtual void newBuddyNotification(const std::string& /*account_id*/, const std::string& /*buddy_uri*/, int /*status*/, const std::string& /*line_status*/){}
+    virtual void subscriptionStateChanged(const std::string& /*account_id*/, const std::string& /*buddy_uri*/, int /*state*/){}
+};
\ No newline at end of file
diff --git a/bin/nodejs/ringservice.c.template b/bin/nodejs/ringservice.c.template
new file mode 100644
index 0000000000..61c4e61262
--- /dev/null
+++ b/bin/nodejs/ringservice.c.template
@@ -0,0 +1,73 @@
+#include "logger.h"
+
+JavaVM *gJavaVM;
+const char *kringservicePath = "cx/ring/daemon/RingserviceJNI";
+
+void deinitClassHelper(JNIEnv *env, jobject obj) {
+	RING_INFO("deinitClassHelper");
+
+	/* delete cached object instances */
+    env->DeleteGlobalRef(obj);
+	RING_INFO("deinitClassHelper: object %x deleted", obj);
+}
+
+JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+	JNIEnv *env;
+	jclass clazz;
+	jint r;
+
+    RING_INFO("JNI_OnLoad");
+
+	//Assume it is c++
+	r = vm->GetEnv ((void **) &env, JNI_VERSION_1_6);
+    if (r != JNI_OK) {
+		RING_ERR("JNI_OnLoad: failed to get the environment using GetEnv()");
+        return -1;
+    }
+	RING_INFO("JNI_Onload: GetEnv %p", env);
+
+	clazz = env->FindClass (kringservicePath);
+	if (!clazz) {
+        RING_ERR("JNI_Onload: whoops, %s class not found!", kringservicePath);
+	}
+	gJavaVM = vm;
+	RING_INFO("JNI_Onload: JavaVM %p", gJavaVM);
+
+	/* put instances of class object we need into cache */
+    //initClassHelper(env, kManagerPath, &gManagerObject);
+
+	JNINativeMethod methods[] = {
+
+	$defs
+
+	};
+
+	r = env->RegisterNatives (clazz, methods, (int) (sizeof(methods) / sizeof(methods[0])));
+	return JNI_VERSION_1_6;
+}
+
+void JNI_OnUnLoad(JavaVM* vm, void* reserved) {
+    JNIEnv* env;
+	jclass clazz;
+
+	RING_INFO("JNI_OnUnLoad");
+
+	/* get env */
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+		RING_ERR("JNI_OnUnLoad: failed to get the environment using GetEnv()");
+        return;
+    }
+	RING_INFO("JNI_OnUnLoad: GetEnv %p", env);
+
+    /* Get jclass with env->FindClass */
+	clazz = env->FindClass(kringservicePath);
+	if (!clazz) {
+        RING_ERR("JNI_OnUnLoad: whoops, %s class not found!", kringservicePath);
+	}
+
+	/* remove instances of class object we need into cache */
+    //deinitClassHelper(env, gManagerObject);
+
+	env->UnregisterNatives(clazz);
+	RING_INFO("JNI_OnUnLoad: Native functions unregistered");
+}
diff --git a/bin/nodejs/videomanager.i b/bin/nodejs/videomanager.i
new file mode 100644
index 0000000000..87a0cc4267
--- /dev/null
+++ b/bin/nodejs/videomanager.i
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (C) 2015-2016 Savoir-faire Linux Inc.
+ *
+ *  Authors: Damien Riegel <damien.riegel@savoirfairelinux.com>
+ *           Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *           Ciro Santilli <ciro.santilli@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+%header %{
+#include <functional>
+#include <list>
+#include <mutex>
+
+#include "dring/dring.h"
+#include "dring/videomanager_interface.h"
+
+class VideoCallback {
+public:
+    virtual ~VideoCallback(){}
+    virtual void getCameraInfo(const std::string& device, std::vector<int> *formats, std::vector<unsigned> *sizes, std::vector<unsigned> *rates) {}
+    virtual void setParameters(const std::string, const int format, const int width, const int height, const int rate) {}
+    virtual void startCapture(const std::string& camid) {}
+    virtual void stopCapture() {}
+    virtual void decodingStarted(const std::string& id, const std::string& shm_path, int w, int h, bool is_mixer) {}
+    virtual void decodingStopped(const std::string& id, const std::string& shm_path, bool is_mixer) {}
+};
+%}
+
+%feature("director") VideoCallback;
+
+namespace DRing {
+
+void setDefaultDevice(const std::string& name);
+std::string getDefaultDevice();
+
+void startCamera();
+void stopCamera();
+bool hasCameraStarted();
+bool switchInput(const std::string& resource);
+bool switchToCamera();
+std::map<std::string, std::string> getSettings(const std::string& name);
+void applySettings(const std::string& name, const std::map<std::string, std::string>& settings);
+
+void registerSinkTarget(const std::string& sinkId, const DRing::SinkTarget& target);
+}
+
+class VideoCallback {
+public:
+    virtual ~VideoCallback(){}
+    virtual void getCameraInfo(const std::string& device, std::vector<int> *formats, std::vector<unsigned> *sizes, std::vector<unsigned> *rates){}
+    virtual void setParameters(const std::string, const int format, const int width, const int height, const int rate) {}
+    virtual void startCapture(const std::string& camid) {}
+    virtual void stopCapture() {}
+    virtual void decodingStarted(const std::string& id, const std::string& shm_path, int w, int h, bool is_mixer) {}
+    virtual void decodingStopped(const std::string& id, const std::string& shm_path, bool is_mixer) {}
+};
diff --git a/configure.ac b/configure.ac
index 3ee14ce5bb..a10c41b35d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -390,6 +390,20 @@ AC_ARG_ENABLE([ringns], AS_HELP_STRING([--disable-ringns], [Enable Ring Name Ser
 AM_CONDITIONAL([RINGNS], test "x$enable_ringns" != "xno", [Define if you use the Ring Name Service])
 AC_DEFINE_UNQUOTED([HAVE_RINGNS], `if test "x$enable_ringns" != "xno"; then echo 1; else echo 0; fi`, [Define if you use the Ring Name Service])
 
+dnl Ring nodejs module
+AC_ARG_WITH([nodejs], AS_HELP_STRING([--with-nodejs], [Enable Ring NodeJS module]))
+AM_CONDITIONAL([RING_NODEJS], test "x$enable_nodejs" != "xno", [Define if you use the Ring NodeJS module])
+AC_DEFINE_UNQUOTED([HAVE_NODEJS], `if test "x$enable_ringns" != "xno"; then echo 1; else echo 0; fi`, [Define if you use the Ring NodeJS module])
+AS_IF([test "x$with_nodejs" = "xyes"], [
+    AC_PATH_PROG(SWIG, swig, "")
+    AS_AC_EXPAND(SBINDIR, $sbindir)
+    AC_SUBST(SBINDIR)
+    AC_CONFIG_FILES([bin/nodejs/Makefile])
+    AM_CONDITIONAL(RING_NODEJS, true)
+  ],
+  AM_CONDITIONAL(RING_NODEJS, false)
+);
+
 # Rest C++ with restbed
 AC_ARG_WITH([restcpp],
     AS_HELP_STRING([--with-restcpp], [enable rest support with C++]))
-- 
GitLab