2 * Copyright 2015 - 2016 Samsung Electronics Co., Ltd. All.
4 * Licensed under the Flora License, Version 1.1 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://floralicense.org/license
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
21 #include <types_internal.h>
22 #include <dbus_listener_iface.h>
24 #include "request_mgr.h"
25 #include "request_callback.h"
26 #include "smart_reply_daemon.h"
31 static GDBusConnection *dbus_connection = NULL;
32 static guint dbus_owner_id = 0;
33 static GDBusNodeInfo *dbus_node_info = NULL;
35 static const gchar introspection_xml[] =
37 " <interface name='" DBUS_IFACE "'>"
38 " <method name='" METHOD_REQUEST "'>"
39 " <arg type='i' name='" ARG_REQTYPE "' direction='in'/>"
40 " <arg type='s' name='" ARG_COOKIE "' direction='in'/>"
41 " <arg type='i' name='" ARG_REQID "' direction='in'/>"
42 " <arg type='s' name='" ARG_SUBJECT "' direction='in'/>"
43 " <arg type='s' name='" ARG_INPUT "' direction='in'/>"
44 " <arg type='i' name='" ARG_RESULT_ERR "' direction='out'/>"
45 " <arg type='s' name='" ARG_RESULT_ADD "' direction='out'/>"
46 " <arg type='s' name='" ARG_OUTPUT "' direction='out'/>"
51 const vector<pair<string, int>> emoticons = { {"ㅜㅜ", 0x1f622}, {"orz", 0x1f622}, {"ㄷㄷㄷ", 0x1f631},
52 {"ㅋㅋㅋ", 0x1f601}, {"^^", 0x1f60a}, {"♡", 0x1f60d}, {"ㅎㄷㄷ", 0x1f632}, { "ㅡㅡ", 0x1f625} };
54 static const char* req_type_to_str(int req_type)
59 case REQ_REMOVE_ENGINE:
60 return "RemoveEngine";
64 return "Get user generated feedback";
65 case REQ_IS_SUPPORTED:
66 return "Get know supported requested language";
68 return "Get know currently loaded engine's language";
69 case REQ_PREPARE_ENGINE :
70 return "Prepare Engine";
76 static void split(const string &s, char delim, vector<string> &out) {
79 while (getline(ss, item, delim)) {
84 void smr::request_mgr::handle_request(const char *sender, GVariant *param, GDBusMethodInvocation *invocation)
86 _I("handle_request %s", sender);
88 const gchar *cookie = NULL;
90 const gchar *subject = NULL;
91 const gchar *input = NULL;
94 g_variant_get(param, "(i&si&s&s)", &req_type, &cookie, &req_id, &subject, &input);
95 IF_FAIL_VOID_TAG(req_type > 0 && req_id > 0 && cookie && subject && input, _E, "Invalid request");
97 _SD("Cookie: %s", cookie);
98 _SI("[%s] ReqId: %d, Subject: %s", req_type_to_str(req_type), req_id, subject);
100 //TODO: Parameter validation
102 request_info* request = new(std::nothrow) smr::client_request(req_type, sender, req_id, subject, input, cookie);
104 _E("Memory allocation failed");
105 g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", SMARTREPLY_ERROR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT));
109 //NOTE: request->get_cookie() and request->get_app_id() can be used to retrieve the decoded cookie and its app-id.
110 //TODO: Privacy check with the cookie & req_type
112 request_mgr::get_instance().assign_request(invocation, request);
116 static void handle_method_call(GDBusConnection *conn, const gchar *sender,
117 const gchar *obj_path, const gchar *iface, const gchar *method_name,
118 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
120 _I(GREEN("handle_method"));
121 IF_FAIL_VOID_TAG(STR_EQ(obj_path, DBUS_PATH), _W, "Invalid path: %s", obj_path);
122 IF_FAIL_VOID_TAG(STR_EQ(iface, DBUS_IFACE), _W, "Invalid interface: %s", obj_path);
124 if (STR_EQ(method_name, METHOD_REQUEST)) {
125 request_mgr::handle_request(sender, param, invocation);
127 _W("Invalid method: %s", method_name);
131 static void on_bus_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data)
133 GDBusInterfaceVTable vtable;
134 vtable.method_call = handle_method_call;
135 vtable.get_property = NULL;
136 vtable.set_property = NULL;
138 guint reg_id = g_dbus_connection_register_object(conn, DBUS_PATH,
139 dbus_node_info->interfaces[0], &vtable, NULL, NULL, NULL);
142 _E("Failed to acquire dbus");
146 dbus_connection = conn;
149 static void on_name_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data)
151 _SI("Dbus name acquired: %s", name);
154 static void on_name_lost(GDBusConnection *conn, const gchar *name, gpointer user_data)
156 _E("Dbus name lost");
160 static void handle_signal_received(GDBusConnection *conn, const gchar *sender,
161 const gchar *obj_path, const gchar *iface, const gchar *signal_name,
162 GVariant *param, gpointer user_data)
164 IF_FAIL_VOID_TAG(user_data, _W, "user_data cannot be null");
165 smr::dbus_listener_iface *listener = static_cast<smr::dbus_listener_iface*>(user_data);
166 listener->on_signal_received(sender, obj_path, iface, signal_name, param);
169 smr::request_mgr::request_mgr() {
173 smr::request_mgr::~request_mgr() {
176 bool smr::request_mgr::start(request_callback* callback) {
177 _callback = callback;
179 dbus_server::set_instance(this);
180 IF_FAIL_RETURN_TAG(dbus_node_info == NULL, false, _E, "Re-initialization");
182 dbus_node_info = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
183 IF_FAIL_RETURN_TAG(dbus_node_info != NULL, false, _E, "Initialization failed");
185 dbus_owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, DBUS_DEST, G_BUS_NAME_OWNER_FLAGS_NONE,
186 on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL);
191 bool smr::request_mgr::stop() {
192 if (dbus_connection) {
193 g_dbus_connection_flush_sync(dbus_connection, NULL, NULL);
196 if (dbus_owner_id > 0) {
197 g_bus_unown_name(dbus_owner_id);
201 if (dbus_connection) {
202 g_dbus_connection_close_sync(dbus_connection, NULL, NULL);
203 g_object_unref(dbus_connection);
204 dbus_connection = NULL;
207 if (dbus_node_info) {
208 g_dbus_node_info_unref(dbus_node_info);
209 dbus_node_info = NULL;
215 void smr::request_mgr::publish(const char* dest, int req_id, const char* subject, int error, const char* data)
217 IF_FAIL_VOID_TAG(dest && subject && data, _E, "Parameter null");
219 _SI("Publish: %s, %d, %s, %#x, %s", dest, req_id, subject, error, data);
221 GVariant *param = g_variant_new("(isis)", req_id, subject, error, data);
222 IF_FAIL_VOID_TAG(param, _E, "Memory allocation failed");
225 g_dbus_connection_call(dbus_connection, dest, DBUS_PATH, DBUS_IFACE,
226 METHOD_RESPOND, param, NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, NULL, &err);
230 int64_t smr::request_mgr::signal_subscribe(const char* sender, const char* path, const char* iface, const char* name, smr::dbus_listener_iface* listener)
232 IF_FAIL_RETURN_TAG(dbus_connection, -1, _E, "Dbus not connected");
233 guint sid = g_dbus_connection_signal_subscribe(dbus_connection,
234 sender, iface, name, path, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
235 handle_signal_received, listener, NULL);
236 return static_cast<int64_t>(sid);
239 void smr::request_mgr::signal_unsubscribe(int64_t subscription_id)
241 IF_FAIL_VOID_TAG(dbus_connection, _E, "Dbus not connected");
242 IF_FAIL_VOID_TAG(subscription_id >= 0, _W, "Invalid parameter");
243 g_dbus_connection_signal_unsubscribe(dbus_connection, static_cast<guint>(subscription_id));
246 void smr::request_mgr::string_to_unicode(vector<string>& replies) {
247 for (vector<string>::iterator it = replies.begin(); it != replies.end(); ++it) {
248 for (auto emoticon : emoticons) {
249 size_t idx = it->find(emoticon.first);
250 if (idx != string::npos) {
252 const Eina_Unicode unicode_event[2] = { (Eina_Unicode)emoticon.second, 0 };
253 char* utf_8 = eina_unicode_unicode_to_utf8(unicode_event, &length);
254 it->replace(idx, emoticon.first.length(), string(utf_8));
255 _SI("[utf-8]Before : %s, After : %s", emoticon.first.c_str(), utf_8);
262 bool utf8_check_is_valid(const string& string)
265 for (i=0, ix=string.length(); i < ix; i++)
267 c = (unsigned char) string[i];
268 //if (c==0x09 || c==0x0a || c==0x0d || (0x20 <= c && c <= 0x7e) ) n = 0; // is_printable_ascii
269 if (0x00 <= c && c <= 0x7f) n=0; // 0bbbbbbb
270 else if ((c & 0xE0) == 0xC0) n=1; // 110bbbbb
271 else if ( c == 0xed && i < (ix-1) && ((unsigned char)string[i+1] & 0xa0) == 0xa0) return false; //U+d800 to U+dfff
272 else if ((c & 0xF0) == 0xE0) n=2; // 1110bbbb
273 else if ((c & 0xF8) == 0xF0) n=3; // 11110bbb
274 //else if (($c & 0xFC) == 0xF8) n=4; // 111110bb //byte 5, unnecessary in 4 byte UTF-8
275 //else if (($c & 0xFE) == 0xFC) n=5; // 1111110b //byte 6, unnecessary in 4 byte UTF-8
277 for (j=0; j < n && i < ix; j++) { // n bytes matching 10bbbbbb follow ?
278 if ((++i == ix) || (( (unsigned char)string[i] & 0xC0) != 0x80))
285 void smr::request_mgr::assign_request(GDBusMethodInvocation *invocation, request_info* request)
287 int req_type = request->get_type();
290 string lang(request->get_subject());
291 _I("request %d", req_type);
293 case REQ_IS_SUPPORTED:
294 _I("LANG : %s", lang.c_str());
295 result = _callback->on_query_is_supported(lang);
296 reply(invocation, result);
301 _I(GREEN("Get Reply"));
303 string sender_id, app_id;
304 string message(request->get_description());
305 get_params_from_request(request->get_subject(), app_id, sender_id, true);
307 vector<string> replies;
308 _callback->on_request_find_reply(app_id, sender_id, message, replies);
311 string_to_unicode(replies);
313 smr::json result_str;
314 get_result_str(replies, result_str);
315 _SI("Generated replies in json : %s", (char*) result_str.dup_cstr());
317 reply(invocation, SMARTREPLY_ERROR_NONE, result_str);
322 _I(GREEN("Feedback"));
324 string replied_str(request->get_subject()); //subject is replied string
325 string message(request->get_description());
326 string sender_id, app_id;
328 if (utf8_check_is_valid(message) == false) {
331 get_params_from_request(message, app_id, sender_id, false);
332 _callback->on_request_notify_reply(app_id, sender_id, message, replied_str);
335 case REQ_PREPARE_ENGINE:
336 _I(GREEN("Prepare Engine"));
338 _callback->on_request_prepare_engine();
344 void smr::request_mgr::get_result_str(const vector<string>& replies, smr::json& result_str) {
346 for (auto it = replies.begin(); it != replies.end(); ++it) {
347 _SI("Smart Reply :%s ", (char*) ((*it).c_str()));
348 if ( (strncmp((*it).c_str(), "select", 6) == 0) || (strncmp((*it).c_str(), "Select", 6) == 0) ) {
351 result.append(*it).append("|");
354 result_str.set(NULL, "replies", result);
357 void smr::request_mgr::get_params_from_request(const string& data, string& app_id, string& sender_id, bool is_request) {
358 std::vector<string> temp;
359 if (is_request == true) {
360 split(data, '|', temp);
362 split(data, '@', temp);
368 bool smr::request_mgr::reply(GDBusMethodInvocation *invocation, int error)
370 IF_FAIL_RETURN(invocation, true);
372 _I("Server sended Reply: %#x", error);
374 g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT));
379 bool smr::request_mgr::reply(GDBusMethodInvocation *invocation, int error, smr::json& request_result)
381 IF_FAIL_RETURN(invocation, true);
382 //IF_FAIL_RETURN(_type != REQ_GET_REPLY, true);
384 char *result = request_result.dup_cstr();
385 IF_FAIL_RETURN_TAG(result, false, _E, "Memory allocation failed");
387 _I("Reply %#x", error);
388 _SD("Result: %s", result);
390 g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, result, EMPTY_JSON_OBJECT));
397 bool smr::request_mgr::reply(GDBusMethodInvocation *invocation, int error, smr::json& request_result, smr::json& data_read, request_info* request)
399 if (invocation == NULL) {
400 return publish(error, data_read, request);
402 IF_FAIL_RETURN(request->get_type() != REQ_GET_REPLY, true);
407 result = request_result.dup_cstr();
408 IF_FAIL_CATCH_TAG(result, _E, "Memory allocation failed");
410 data = data_read.dup_cstr();
411 IF_FAIL_CATCH_TAG(data, _E, "Memory allocation failed");
413 _I("Reply %#x", error);
414 _SD("Result: %s", result);
415 _SD("Data: %s", data);
417 g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, result, data));
431 bool smr::request_mgr::publish(int error, smr::json& data, request_info* request)
434 char *data_str = data.dup_cstr();
435 IF_FAIL_RETURN_TAG(data_str, false, _E, "Memory allocation failed");
436 const char* _client = request->get_client();
437 int _req_id = request->get_id();
438 const char* _subject = request->get_subject();
439 publish(_client, _req_id, _subject, error, data_str);