Fix typo
[platform/core/uifw/smartreply-service.git] / src / request_mgr.cpp
1 /*
2  *  Copyright 2015 - 2016 Samsung Electronics Co., Ltd. All.
3  *
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
7  *
8  *      http://floralicense.org/license
9  *
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.
15  */
16
17 #include <signal.h>
18 #include <glib.h>
19 #include <gio/gio.h>
20 #include <sstream>
21 #include <types_internal.h>
22 #include <dbus_listener_iface.h>
23 #include <Eina.h>
24 #include "request_mgr.h"
25 #include "request_callback.h"
26 #include "smart_reply_daemon.h"
27
28 using namespace smr;
29 using namespace std;
30
31 static GDBusConnection *dbus_connection = NULL;
32 static guint dbus_owner_id = 0;
33 static GDBusNodeInfo *dbus_node_info = NULL;
34
35 static const gchar introspection_xml[] =
36         "<node>"
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'/>"
47         "               </method>"
48         "       </interface>"
49         "</node>";
50
51 const vector<pair<string, int>> emoticons = { {"ㅜㅜ", 0x1f622}, {"orz", 0x1f622}, {"ㄷㄷㄷ", 0x1f631},
52                 {"ㅋㅋㅋ", 0x1f601}, {"^^", 0x1f60a}, {"♡", 0x1f60d}, {"ㅎㄷㄷ", 0x1f632}, { "ㅡㅡ", 0x1f625} };
53
54 static const char* req_type_to_str(int req_type)
55 {
56         switch (req_type) {
57                 case REQ_ADD_ENGINE:
58                         return "AddEngine";
59                 case REQ_REMOVE_ENGINE:
60                         return "RemoveEngine";
61                 case REQ_GET_REPLY:
62                         return "GetReply";
63                 case REQ_FEEDBACK:
64                         return "Get user generated feedback";
65                 case REQ_IS_SUPPORTED:
66                         return "Get know supported requested language";
67                 case REQ_IS_LOADED :
68                         return "Get know currently loaded engine's language";
69                 case REQ_PREPARE_ENGINE :
70                         return "Prepare Engine";
71                 default:
72                         return NULL;
73         }
74 }
75
76 static void split(const string &s, char delim, vector<string> &out) {
77         stringstream ss(s);
78         string item;
79         while (getline(ss, item, delim)) {
80                 out.push_back(item);
81         }
82 }
83
84 void smr::request_mgr::handle_request(const char *sender, GVariant *param, GDBusMethodInvocation *invocation)
85 {
86         _I("handle_request %s", sender);
87         gint req_type = 0;
88         const gchar *cookie = NULL;
89         gint req_id = 0;
90         const gchar *subject = NULL;
91         const gchar *input = NULL;
92
93         //TODO . modify?
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");
96
97         _SD("Cookie: %s", cookie);
98         _SI("[%s] ReqId: %d, Subject: %s", req_type_to_str(req_type), req_id, subject);
99
100         //TODO: Parameter validation
101
102         request_info* request = new(std::nothrow) smr::client_request(req_type, sender, req_id, subject, input, cookie);
103         if (!request) {
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));
106                 return;
107         }
108
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
111
112         request_mgr::get_instance().assign_request(invocation, request);
113         delete request;
114 }
115
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)
119 {
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);
123
124         if (STR_EQ(method_name, METHOD_REQUEST)) {
125                 request_mgr::handle_request(sender, param, invocation);
126         } else {
127                 _W("Invalid method: %s", method_name);
128         }
129 }
130
131 static void on_bus_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data)
132 {
133         GDBusInterfaceVTable vtable;
134         vtable.method_call = handle_method_call;
135         vtable.get_property = NULL;
136         vtable.set_property = NULL;
137
138         guint reg_id = g_dbus_connection_register_object(conn, DBUS_PATH,
139                         dbus_node_info->interfaces[0], &vtable, NULL, NULL, NULL);
140
141         if (reg_id <= 0) {
142                 _E("Failed to acquire dbus");
143                 raise(SIGTERM);
144         }
145
146         dbus_connection = conn;
147 }
148
149 static void on_name_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data)
150 {
151         _SI("Dbus name acquired: %s", name);
152 }
153
154 static void on_name_lost(GDBusConnection *conn, const gchar *name, gpointer user_data)
155 {
156         _E("Dbus name lost");
157         raise(SIGTERM);
158 }
159
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)
163 {
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);
167 }
168
169 smr::request_mgr::request_mgr() {
170         _callback = nullptr;
171 }
172
173 smr::request_mgr::~request_mgr() {
174 }
175
176 bool smr::request_mgr::start(request_callback* callback) {
177         _callback = callback;
178
179         dbus_server::set_instance(this);
180         IF_FAIL_RETURN_TAG(dbus_node_info == NULL, false, _E, "Re-initialization");
181
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");
184
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);
187
188         return true;
189 }
190
191 bool smr::request_mgr::stop() {
192         if (dbus_connection) {
193                 g_dbus_connection_flush_sync(dbus_connection, NULL, NULL);
194         }
195
196         if (dbus_owner_id > 0) {
197                 g_bus_unown_name(dbus_owner_id);
198                 dbus_owner_id = 0;
199         }
200
201         if (dbus_connection) {
202                 g_dbus_connection_close_sync(dbus_connection, NULL, NULL);
203                 g_object_unref(dbus_connection);
204                 dbus_connection = NULL;
205         }
206
207         if (dbus_node_info) {
208                 g_dbus_node_info_unref(dbus_node_info);
209                 dbus_node_info = NULL;
210         }
211
212         return true;
213 }
214
215 void smr::request_mgr::publish(const char* dest, int req_id, const char* subject, int error, const char* data)
216 {
217         IF_FAIL_VOID_TAG(dest && subject && data, _E, "Parameter null");
218
219         _SI("Publish: %s, %d, %s, %#x, %s", dest, req_id, subject, error, data);
220
221         GVariant *param = g_variant_new("(isis)", req_id, subject, error, data);
222         IF_FAIL_VOID_TAG(param, _E, "Memory allocation failed");
223
224         GError *err = NULL;
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);
227         HANDLE_GERROR(err);
228 }
229
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)
231 {
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);
237 }
238
239 void smr::request_mgr::signal_unsubscribe(int64_t subscription_id)
240 {
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));
244 }
245
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) {
251                                 int length;
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);
256                                 free(utf_8);
257                         }
258                 }
259         }
260 }
261
262 bool utf8_check_is_valid(const string& string)
263 {
264     int c, i, ix, n, j;
265     for (i=0, ix=string.length(); i < ix; i++)
266     {
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
276         else return false;
277         for (j=0; j < n && i < ix; j++) { // n bytes matching 10bbbbbb follow ?
278             if ((++i == ix) || (( (unsigned char)string[i] & 0xC0) != 0x80))
279                 return false;
280         }
281     }
282     return true;
283 }
284
285 void smr::request_mgr::assign_request(GDBusMethodInvocation *invocation, request_info* request)
286 {
287         int req_type = request->get_type();
288         int result = -1;
289
290         string lang(request->get_subject());
291         _I("request %d", req_type);
292         switch (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);
297                         break;
298                 case REQ_IS_LOADED:
299                         break;
300                 case REQ_GET_REPLY:
301                         _I(GREEN("Get Reply"));
302                         {
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);
306
307                                 vector<string> replies;
308                                 _callback->on_request_find_reply(app_id, sender_id, message, replies);
309
310 #ifdef  USE_EMOTICON
311                                 string_to_unicode(replies);
312 #endif
313                                 smr::json result_str;
314                                 get_result_str(replies, result_str);
315                                 _SI("Generated replies in json : %s", (char*) result_str.dup_cstr());
316
317                                 reply(invocation, SMARTREPLY_ERROR_NONE, result_str);
318                         }
319
320                         break;
321                 case REQ_FEEDBACK:
322                         _I(GREEN("Feedback"));
323                         {
324                                 string replied_str(request->get_subject());     //subject is replied string
325                                 string message(request->get_description());
326                                 string sender_id, app_id;
327
328                                 if (utf8_check_is_valid(message) == false) {
329                                         return;
330                                 }
331                                 get_params_from_request(message, app_id, sender_id, false);
332                                 _callback->on_request_notify_reply(app_id, sender_id, message, replied_str);
333                         }
334                         break;
335                 case REQ_PREPARE_ENGINE:
336                         _I(GREEN("Prepare Engine"));
337                         {
338                                 _callback->on_request_prepare_engine();
339                         }
340                         break;
341         }
342 }
343
344 void smr::request_mgr::get_result_str(const vector<string>& replies, smr::json& result_str) {
345         string result;
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) ) {
349                         continue;
350                 }
351                 result.append(*it).append("|");
352         }
353
354         result_str.set(NULL, "replies", result);
355 }
356
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);
361         } else {
362                 split(data, '@', temp);
363         }
364         sender_id = temp[0];
365         app_id = temp[1];
366 }
367
368 bool smr::request_mgr::reply(GDBusMethodInvocation *invocation, int error)
369 {
370         IF_FAIL_RETURN(invocation, true);
371
372         _I("Server sended Reply: %#x", error);
373
374         g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT));
375         invocation = NULL;
376         return true;
377 }
378
379 bool smr::request_mgr::reply(GDBusMethodInvocation *invocation, int error, smr::json& request_result)
380 {
381         IF_FAIL_RETURN(invocation, true);
382         //IF_FAIL_RETURN(_type != REQ_GET_REPLY, true);
383
384         char *result = request_result.dup_cstr();
385         IF_FAIL_RETURN_TAG(result, false, _E, "Memory allocation failed");
386
387         _I("Reply %#x", error);
388         _SD("Result: %s", result);
389
390         g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, result, EMPTY_JSON_OBJECT));
391         invocation = NULL;
392
393         g_free(result);
394         return true;
395 }
396
397 bool smr::request_mgr::reply(GDBusMethodInvocation *invocation, int error, smr::json& request_result, smr::json& data_read, request_info* request)
398 {
399         if (invocation == NULL) {
400                 return publish(error, data_read, request);
401         }
402         IF_FAIL_RETURN(request->get_type() != REQ_GET_REPLY, true);
403
404         char *result = NULL;
405         char *data = NULL;
406
407         result = request_result.dup_cstr();
408         IF_FAIL_CATCH_TAG(result, _E, "Memory allocation failed");
409
410         data = data_read.dup_cstr();
411         IF_FAIL_CATCH_TAG(data, _E, "Memory allocation failed");
412
413         _I("Reply %#x", error);
414         _SD("Result: %s", result);
415         _SD("Data: %s", data);
416
417         g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, result, data));
418         invocation = NULL;
419
420         g_free(result);
421         g_free(data);
422         return true;
423
424 CATCH:
425         g_free(result);
426         g_free(data);
427         return false;
428 }
429
430
431 bool smr::request_mgr::publish(int error, smr::json& data, request_info* request)
432 {
433         _I("Publish");
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);
440
441         g_free(data_str);
442
443         return true;
444 }