Update package version to 0.9.10
[platform/core/uifw/libscl-core.git] / src / websocket.cpp
1 /*
2  * Copyright (c) 2012 - 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (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://www.apache.org/licenses/LICENSE-2.0
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
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <getopt.h>
22 #include <string.h>
23 #include <sys/time.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <assert.h>
27 #include <Ecore.h>
28 #include <Ecore_IMF.h>
29 #include <dlog.h>
30
31 #include "websocket.h"
32
33 #include <syslog.h>
34 #include <signal.h>
35
36 #include <libwebsockets.h>
37
38 #ifdef WAYLAND
39 #define EFL_BETA_API_SUPPORT
40 #include <Ecore_Wl2.h>
41 #endif
42
43 #define WEBSOCKET_PORT 7681
44
45 #define RECVED_MESSAGE "recved"
46 #define MESSAGE_LEFT "left"
47
48 pthread_t g_ws_server_thread = (pthread_t)NULL;
49 pthread_mutex_t g_ws_server_mutex = PTHREAD_MUTEX_INITIALIZER;
50
51 pthread_cond_t g_ws_query_condition = PTHREAD_COND_INITIALIZER;
52 pthread_mutex_t g_ws_query_mutex = PTHREAD_MUTEX_INITIALIZER;
53
54 bool g_ws_server_exit = false;
55 struct lws_context *g_ws_server_context = NULL;
56
57 CWebHelperAgentWebSocket* CWebHelperAgentWebSocket::m_current_instance = NULL;
58
59 int force_exit = 0;
60
61 enum protocols {
62     /* always first */
63     PROTOCOL_HTTP = 0,
64
65     PROTOCOL_KEYBOARD,
66
67     /* always last */
68     MAX_PROTOCOL_COUNT
69 };
70
71 struct per_session_data__http {
72     int fd;
73 };
74
75 static int callback_http(struct lws *wsi,
76         enum lws_callback_reasons reason,
77         void *user, void *in, size_t len)
78 {
79     return 0;
80 }
81
82 struct per_session_data__keyboard {
83     int session_id;
84     int valid;
85     int need_init;
86     int initialized;
87 };
88
89 static int callback_keyboard(struct lws *wsi,
90         enum lws_callback_reasons reason,
91         void *user, void *in, size_t len);
92
93 static struct lws_protocols protocols[] = {
94     {
95         "http-only",
96         callback_http,
97         sizeof(struct per_session_data__http),
98         0,
99     },
100     {
101         "keyboard-protocol",
102         callback_keyboard,
103         sizeof(struct per_session_data__keyboard),
104         32,
105     },
106     { NULL, NULL, 0, 0 }
107 };
108
109
110 static int callback_client(struct lws *wsi,
111     enum lws_callback_reasons reason,
112     void *user, void *in, size_t len)
113 {
114     switch (reason) {
115     case LWS_CALLBACK_CLIENT_ESTABLISHED:
116         LOGD("[ClientTest] Connection established");
117         break;
118
119     case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
120         LOGD("[ClientTest] Connection error");
121         break;
122
123     case LWS_CALLBACK_CLOSED:
124         LOGD("[ClientTest] Connection closed");
125         break;
126
127     default:
128         break;
129     }
130
131     return 0;
132 }
133
134 int test_client_connection(void)
135 {
136     struct lws_context *context = NULL;
137     struct lws_context_creation_info context_info;
138     struct lws_client_connect_info connect_info;
139     struct lws *wsi = NULL;
140
141     memset(&context_info, 0, sizeof context_info);
142     memset(&connect_info, 0, sizeof(connect_info));
143
144     const int protocols_num = sizeof(protocols) / sizeof(lws_protocols);
145     static struct lws_protocols client_protocols[protocols_num];
146
147     memcpy(&client_protocols, protocols, sizeof(protocols));
148     for (int loop = 0; loop < protocols_num - 1; loop++) {
149         client_protocols[loop].callback = callback_client;
150     }
151
152     context_info.port = CONTEXT_PORT_NO_LISTEN;
153     context_info.protocols = protocols;
154     context_info.gid = -1;
155     context_info.uid = -1;
156
157     context = lws_create_context(&context_info);
158     LOGD("[ClientTest] create_context : %p", context);
159     if (context == NULL) {
160         return -1;
161     }
162
163     connect_info.address = "localhost";
164     connect_info.port = WEBSOCKET_PORT;
165     connect_info.path = "/";
166     connect_info.context = context;
167     connect_info.ssl_connection = 0;
168     connect_info.host = connect_info.address;
169     connect_info.origin = connect_info.address;
170     connect_info.ietf_version_or_minus_one = -1;
171     connect_info.protocol = "keyboard-protocol";
172
173     wsi = lws_client_connect_via_info(&connect_info);
174     LOGD("[ClientTest] wsi created : %p", wsi);
175
176     if (wsi) {
177         lws_service(context, 50);
178     }
179
180     lws_context_destroy(context);
181
182     return 0;
183 }
184
185 static Ecore_Timer *g_flush_server_recv_buffer_timer = NULL;
186 static std::string server_recv_buffer;
187 static Eina_Bool flush_server_recv_buffer_func(void *user)
188 {
189     LOGD("flushing recv buffer");
190     CWebHelperAgentWebSocket *agent = CWebHelperAgentWebSocket::get_current_instance();
191     struct per_session_data__keyboard *pss = (struct per_session_data__keyboard *)user;
192
193     if (g_flush_server_recv_buffer_timer)
194         ecore_timer_del(g_flush_server_recv_buffer_timer);
195     g_flush_server_recv_buffer_timer = NULL;
196
197     if (!agent || !pss) return ECORE_CALLBACK_CANCEL;
198
199     pthread_mutex_lock(&g_ws_server_mutex);
200     ISE_MESSAGE message = CISEMessageSerializer::deserialize(server_recv_buffer);
201     server_recv_buffer.clear();
202     pthread_mutex_unlock(&g_ws_server_mutex);
203
204     if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_LOGIN]) == 0) {
205         /*
206            if (message.values.at(0).compare(CMagicKeyManager::get_magic_key()) == 0) {
207            LOGD("LOGIN successful, validating client");
208            pss->valid = true;
209            } else {
210            LOGD("LOGIN failed, invalidating client");
211            pss->valid = false;
212            }
213            */
214         pss->valid = true;
215
216         if (agent->initialized()) {
217             pss->need_init = true;
218             ecore_pipe_write(agent->get_message_pipe(), MESSAGE_LEFT, strlen(MESSAGE_LEFT));
219         }
220     }
221
222     /* Ignore valid check since the magic key is not used anymore */
223     if (!pss->valid) pss->valid = true;
224
225     if (pss->valid) {
226         pthread_mutex_lock(&g_ws_server_mutex);
227         std::queue<ISE_MESSAGE>& messages = agent->get_recv_message_queue();
228         messages.push(message);
229         pthread_mutex_unlock(&g_ws_server_mutex);
230
231         ecore_pipe_write(agent->get_message_pipe(), RECVED_MESSAGE, strlen(RECVED_MESSAGE));
232
233         /* If we received reply message, let's send signal to wake up our main thread */
234         if (message.type.compare(ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY]) == 0) {
235             pthread_mutex_lock(&g_ws_query_mutex);
236             pthread_cond_signal(&g_ws_query_condition);
237             pthread_mutex_unlock(&g_ws_query_mutex);
238         }
239     } else {
240         LOGD("Ignoring data received since client is not valid %d", pss->session_id);
241     }
242
243     return ECORE_CALLBACK_CANCEL;
244 }
245
246 static int callback_keyboard(struct lws *wsi,
247         enum lws_callback_reasons reason,
248         void *user, void *in, size_t len)
249 {
250     static int last_session_id = 0;
251     const int bufsize = 512;
252     int n = 0;
253     unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + bufsize +
254                           LWS_SEND_BUFFER_POST_PADDING];
255     unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
256     struct per_session_data__keyboard *pss = (struct per_session_data__keyboard *)user;
257     CWebHelperAgentWebSocket *agent = CWebHelperAgentWebSocket::get_current_instance();
258
259     switch (reason) {
260     case LWS_CALLBACK_ESTABLISHED:
261         pss->session_id = ++last_session_id;
262         LOGD("LWS_CALLBACK_ESTABLISHED : %p %d", g_ws_server_context, pss->session_id);
263         pss->valid = false;
264         pss->need_init = false;
265         pss->initialized = false;
266         if (g_ws_server_context) {
267             ecore_pipe_write(agent->get_message_pipe(), MESSAGE_LEFT, strlen(MESSAGE_LEFT));
268         }
269         break;
270
271     case LWS_CALLBACK_CLOSED:
272         LOGD("LWS_CALLBACK_CLOSED : %d", pss->session_id);
273         break;
274
275     case LWS_CALLBACK_SERVER_WRITEABLE:
276         if (agent) {
277             /* Ignore valid check since the magic key is not used anymore */
278             if (!pss->valid) pss->valid = true;
279
280             /* We allow data tranmission only if this client is guaranteed to be valid */
281             if (pss->valid) {
282                 pthread_mutex_lock(&g_ws_server_mutex);
283                 std::queue<ISE_MESSAGE>& messages = agent->get_send_message_queue();
284
285                 if (pss->need_init && !pss->initialized) {
286                     ISE_MESSAGE message;
287                     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
288                     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_INIT];
289                     std::string str = CISEMessageSerializer::serialize(message);
290                     SECURE_LOGD("SEND_WEBSOCKET_MESSAGE : %d %s", pss->session_id, str.c_str());
291                     n = snprintf((char *)p, bufsize, "%s", str.c_str());
292                     /* too small for partial */
293                     n = lws_write(wsi, p, n, LWS_WRITE_TEXT);
294                     pss->need_init = false;
295                     pss->initialized = true;
296                 } else {
297                     /* One write allowed per one writable callback */
298                     if (messages.size() > 0) {
299                         ISE_MESSAGE &message = messages.front();
300                         bool drop = false;
301                         if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_INIT]) == 0) {
302                             if (pss->initialized) {
303                                 drop = true;
304                             } else {
305                                 pss->initialized = true;
306                             }
307                         }
308                         if (!drop) {
309                             std::string str = CISEMessageSerializer::serialize(message);
310                             SECURE_LOGD("SEND_WEBSOCKET_MESSAGE : %d %s", pss->session_id, str.c_str());
311                             n = snprintf((char *)p, bufsize, "%s", str.c_str());
312                             /* too small for partial */
313                             n = lws_write(wsi, p, n, LWS_WRITE_TEXT);
314                         }
315                         messages.pop();
316                     }
317                 }
318                 if (messages.size() > 0) {
319                     ecore_pipe_write(agent->get_message_pipe(), MESSAGE_LEFT, strlen(MESSAGE_LEFT));
320                 }
321                 pthread_mutex_unlock(&g_ws_server_mutex);
322
323                 if (n < 0) {
324                     LOGE("ERROR %d writing to di socket %d", n, pss->session_id);
325                 }
326
327                 if (messages.size() > 0) {
328                     lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
329                 }
330             } else {
331                 LOGD("Rejecting data transmission since client is not valid : %d", pss->session_id);
332             }
333         }
334         break;
335
336     case LWS_CALLBACK_RECEIVE:
337         if (in) {
338             if (g_flush_server_recv_buffer_timer) {
339                 ecore_thread_main_loop_begin();
340                 ecore_timer_del(g_flush_server_recv_buffer_timer);
341                 g_flush_server_recv_buffer_timer = NULL;
342                 ecore_thread_main_loop_end();
343             }
344
345             std::string str = std::string((const char *)in, len);
346             if (CISEMessageSerializer::valid(str)) {
347                 LOGD("A valid new message received, flush previous buffer");
348                 flush_server_recv_buffer_func((void*)pss);
349             }
350
351             pthread_mutex_lock(&g_ws_server_mutex);
352             server_recv_buffer += str;
353             SECURE_LOGD("RECEIVE callback : [%zu] [%s], [%s]", len, str.c_str(), server_recv_buffer.c_str());
354             pthread_mutex_unlock(&g_ws_server_mutex);
355
356             ecore_thread_main_loop_begin();
357             g_flush_server_recv_buffer_timer = ecore_timer_add(0.05, flush_server_recv_buffer_func, (void*)pss);
358             SECURE_LOGD("flush timer registered : %p", g_flush_server_recv_buffer_timer);
359             ecore_thread_main_loop_end();
360         }
361
362         break;
363     default:
364         break;
365     }
366
367     return 0;
368 }
369
370 void *process_ws_server(void *data)
371 {
372     while (!force_exit && !g_ws_server_exit) {
373         struct timeval tv;
374         gettimeofday(&tv, NULL);
375
376         if (g_ws_server_context) {
377             lws_service(g_ws_server_context, 50);
378         } else {
379             LOGD("WARNING : g_ws_server_context is NULL");
380         }
381     }
382     LOGD("process_ws_server exits now");
383     return NULL;
384 }
385
386 void log_func(int level, const char *line)
387 {
388     if (line) {
389         LOGD("LEVEL : %d , %s", level, line);
390     }
391 }
392
393 CWebHelperAgentWebSocket::CWebHelperAgentWebSocket()
394 {
395     if (m_current_instance != NULL) {
396         LOGD("WARNING : m_current_instance is NOT NULL");
397     }
398     m_current_instance = this;
399     m_message_pipe = NULL;
400     m_initialized = false;
401 }
402
403 CWebHelperAgentWebSocket::~CWebHelperAgentWebSocket()
404 {
405     if (m_current_instance == this) {
406         m_current_instance = NULL;
407     }
408
409     if (m_message_pipe) {
410         ecore_pipe_del(m_message_pipe);
411         m_message_pipe = NULL;
412     }
413 }
414
415 static void message_pipe_handler(void *data, void *buffer, unsigned int nbyte)
416 {
417     if (buffer) {
418         if (strncmp((const char*)buffer, RECVED_MESSAGE, strlen(RECVED_MESSAGE)) == 0) {
419             CWebHelperAgentWebSocket *agent = CWebHelperAgentWebSocket::get_current_instance();
420             if (agent) {
421                 agent->process_recved_messages();
422             }
423         } else {
424             if (g_ws_server_context) {
425                 lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
426             } else {
427                 LOGD("WARNING : g_ws_server_context is NULL");
428             }
429         }
430     }
431 }
432
433 bool CWebHelperAgentWebSocket::init()
434 {
435     bool ret = true;
436
437     struct lws_context_creation_info info;
438
439     memset(&info, 0, sizeof info);
440     info.port = WEBSOCKET_PORT;
441
442     int log_level = LLL_ERR | LLL_WARN | LLL_DEBUG;
443     lws_set_log_level(log_level, log_func);
444
445     info.iface = NULL;
446     info.protocols = protocols;
447     info.extensions = NULL;
448     info.ssl_cert_filepath = NULL;
449     info.ssl_private_key_filepath = NULL;
450     info.gid = -1;
451     info.uid = -1;
452     info.options = LWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND;
453
454     ecore_init();
455
456     /* The WebSocket server is running on a separate thread, and let the thread send a message
457         through this pipe to guarantee thread safety */
458     m_message_pipe = ecore_pipe_add(message_pipe_handler, NULL);
459
460     /* Let's retry creating server context for a certain number of times */
461     const int max_retry_num = 30;
462     int retry_num = 0;
463
464     do {
465         g_ws_server_context = lws_create_context(&info);
466         LOGD("libwebsocket context : %p", g_ws_server_context);
467         usleep(100 * 1000);
468     } while (g_ws_server_context == NULL && retry_num++ < max_retry_num);
469
470     pthread_mutex_init(&g_ws_server_mutex, NULL);
471
472     pthread_mutex_init(&g_ws_query_mutex, NULL);
473     pthread_cond_init(&g_ws_query_condition, NULL);
474
475     m_initialized = true;
476
477     return ret;
478 }
479
480 bool CWebHelperAgentWebSocket::exit()
481 {
482     if (g_flush_server_recv_buffer_timer)
483         ecore_timer_del(g_flush_server_recv_buffer_timer);
484     g_flush_server_recv_buffer_timer = NULL;
485
486     on_exit();
487
488     g_ws_server_exit = true;
489     if (g_ws_server_context) {
490         lws_cancel_service(g_ws_server_context);
491     }
492
493     if (m_message_pipe) {
494         ecore_pipe_del(m_message_pipe);
495         m_message_pipe = NULL;
496     }
497
498     if (g_ws_server_thread) {
499         pthread_join(g_ws_server_thread, NULL);
500     }
501
502     pthread_cond_destroy(&g_ws_query_condition);
503     pthread_mutex_destroy(&g_ws_query_mutex);
504
505     pthread_mutex_destroy(&g_ws_server_mutex);
506
507     if (g_ws_server_context) {
508         lws_context_destroy(g_ws_server_context);
509         g_ws_server_context = NULL;
510     }
511
512     ecore_shutdown();
513
514     return true;
515 }
516
517 bool CWebHelperAgentWebSocket::run()
518 {
519     if (!m_initialized) {
520         LOGE("Not initialized");
521         return false;
522     }
523
524     bool ret = false;
525     if (g_ws_server_context) {
526         if (pthread_create(&g_ws_server_thread, NULL, &process_ws_server, NULL) != 0) {
527             g_ws_server_thread = (pthread_t)NULL;
528             ret = false;
529
530             on_init();
531
532             test_client_connection();
533         }
534     } else {
535         LOGE("Failed creating server context : %p", g_ws_server_context);
536         ret = false;
537     }
538     return ret;
539 }
540
541 void CWebHelperAgentWebSocket::signal(int sig)
542 {
543     force_exit = 1;
544 }
545
546 template<class T>
547 std::string to_string(T i)
548 {
549     std::stringstream ss;
550     std::string s;
551     ss << i;
552     s = ss.str();
553
554     return s;
555 }
556
557 static void send_ise_message(std::queue<ISE_MESSAGE> *message_queue, ISE_MESSAGE message)
558 {
559     pthread_mutex_lock(&g_ws_server_mutex);
560     message_queue->push(message);
561     pthread_mutex_unlock(&g_ws_server_mutex);
562
563     if (g_ws_server_context) {
564         lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
565     } else {
566         LOGD("WARNING : g_ws_server_context is NULL");
567     }
568 }
569
570 void CWebHelperAgentWebSocket::on_init()
571 {
572     ISE_MESSAGE message;
573     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
574     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_INIT];
575
576     send_ise_message(&m_send_message_queue, message);
577 }
578
579 void CWebHelperAgentWebSocket::on_exit()
580 {
581     ISE_MESSAGE message;
582     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
583     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_EXIT];
584
585     send_ise_message(&m_send_message_queue, message);
586 }
587
588 void CWebHelperAgentWebSocket::on_focus_in(int ic)
589 {
590     ISE_MESSAGE message;
591     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
592     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_FOCUS_IN];
593     message.values.push_back(to_string(ic));
594
595     send_ise_message(&m_send_message_queue, message);
596 }
597
598 void CWebHelperAgentWebSocket::on_focus_out(int ic)
599 {
600     ISE_MESSAGE message;
601     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
602     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_FOCUS_OUT];
603     message.values.push_back(to_string(ic));
604
605     send_ise_message(&m_send_message_queue, message);
606 }
607
608 void CWebHelperAgentWebSocket::on_show(int ic)
609 {
610     ISE_MESSAGE message;
611     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
612     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SHOW];
613     message.values.push_back(to_string(ic));
614
615     send_ise_message(&m_send_message_queue, message);
616
617     LOGD("put into send message buffer");
618 }
619
620 void CWebHelperAgentWebSocket::on_hide(int ic)
621 {
622     ISE_MESSAGE message;
623     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
624     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_HIDE];
625     message.values.push_back(to_string(ic));
626
627     send_ise_message(&m_send_message_queue, message);
628 }
629
630 void CWebHelperAgentWebSocket::on_set_rotation(int degree)
631 {
632     ISE_MESSAGE message;
633     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
634     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_ROTATION];
635     message.values.push_back(to_string(degree));
636
637     send_ise_message(&m_send_message_queue, message);
638 }
639
640 void CWebHelperAgentWebSocket::on_update_cursor_position(int ic, int cursor_pos)
641 {
642     ISE_MESSAGE message;
643     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
644     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_CURSOR_POSITION];
645     message.values.push_back(to_string(ic));
646     message.values.push_back(to_string(cursor_pos));
647
648     send_ise_message(&m_send_message_queue, message);
649 }
650
651 void CWebHelperAgentWebSocket::on_update_surrounding_text(int ic, const char *text, int cursor)
652 {
653     ISE_MESSAGE message;
654     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY];
655     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_SURROUNDING_TEXT];
656     message.values.push_back(to_string(cursor));
657     message.values.push_back(text);
658
659     send_ise_message(&m_send_message_queue, message);
660 }
661
662 void CWebHelperAgentWebSocket::on_update_selection(int ic, const char *text)
663 {
664     ISE_MESSAGE message;
665     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY];
666     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_SELECTION];
667     message.values.push_back(text);
668
669     send_ise_message(&m_send_message_queue, message);
670 }
671
672 void CWebHelperAgentWebSocket::on_set_language(unsigned int language)
673 {
674     ISE_MESSAGE message;
675     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
676     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_LANGUAGE];
677
678     bool found = false;
679     for (unsigned int loop = 0;loop < sizeof(ISE_LANGUAGE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
680         if (language == (unsigned int)ISE_LANGUAGE_TYPES[loop].type_value) {
681             message.values.push_back(ISE_LANGUAGE_TYPES[loop].type_string);
682             found = true;
683         }
684     }
685
686     if (found) {
687         send_ise_message(&m_send_message_queue, message);
688     }
689 }
690
691 void CWebHelperAgentWebSocket::on_set_imdata(char *buf, unsigned int len)
692 {
693     ISE_MESSAGE message;
694     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
695     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_IMDATA];
696     message.values.push_back(buf);
697
698     send_ise_message(&m_send_message_queue, message);
699 }
700
701 void CWebHelperAgentWebSocket::on_get_imdata(char **buf, unsigned int *len)
702 {
703     ISE_MESSAGE message;
704     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
705     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_IMDATA];
706
707     send_ise_message(&m_send_message_queue, message);
708
709     wait_for_reply_message();
710
711     std::vector<std::string> values;
712     /* Check if we received reply for GET_IMDATA message */
713     if (process_recved_messages_until_reply_found(
714         ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_IMDATA], values)) {
715             if (values.size() > 0 && buf && len) {
716                 int string_length = values.at(0).length();
717                 (*buf) = new char[string_length + 1];
718                 if (*buf) {
719                     strncpy(*buf, values.at(0).c_str(), string_length);
720                     /* Make sure this is a null-terminated string */
721                     *(*buf + string_length) = '\0';
722                     *len = string_length;
723                 }
724             }
725     } else {
726         LOGD("process_recved_messages_until_reply_found returned FALSE");
727     }
728     /* Now process the rest in the recv buffer */
729     process_recved_messages();
730 }
731
732 void CWebHelperAgentWebSocket::on_set_return_key_type(unsigned int type)
733 {
734     ISE_MESSAGE message;
735     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
736     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_RETURN_KEY_TYPE];
737
738     bool found = false;
739     for (unsigned int loop = 0;loop < sizeof(ISE_RETURN_KEY_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
740         if (type == (unsigned int)ISE_RETURN_KEY_TYPES[loop].type_value) {
741             message.values.push_back(ISE_RETURN_KEY_TYPES[loop].type_string);
742             found = true;
743         }
744     }
745
746     if (found) {
747         send_ise_message(&m_send_message_queue, message);
748     }
749 }
750
751 void CWebHelperAgentWebSocket::on_get_return_key_type(unsigned int *type)
752 {
753     ISE_MESSAGE message;
754     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
755     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_TYPE];
756
757     send_ise_message(&m_send_message_queue, message);
758
759     wait_for_reply_message();
760
761     std::vector<std::string> values;
762     /* Check if we received reply for GET_RETURN_KEY_TYPE message */
763     if (process_recved_messages_until_reply_found(
764         ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_TYPE], values)) {
765             if (type) {
766                 for (unsigned int loop = 0;loop < sizeof(ISE_RETURN_KEY_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
767                     if (values.at(0).compare(ISE_RETURN_KEY_TYPES[loop].type_string) == 0) {
768                         *type = ISE_RETURN_KEY_TYPES[loop].type_value;
769                     }
770                 }
771             }
772     }
773     /* Now process the rest in the recv buffer */
774     process_recved_messages();
775 }
776
777 void CWebHelperAgentWebSocket::on_set_return_key_disable(unsigned int disabled)
778 {
779     ISE_MESSAGE message;
780     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
781     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_RETURN_KEY_DISABLE];
782
783     bool found = false;
784     for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
785         if (disabled == (unsigned int)ISE_TRUEFALSE_TYPES[loop].type_value) {
786             message.values.push_back(ISE_TRUEFALSE_TYPES[loop].type_string);
787             found = true;
788         }
789     }
790
791     if (found) {
792         send_ise_message(&m_send_message_queue, message);
793     }
794 }
795
796 void CWebHelperAgentWebSocket::on_get_return_key_disable(unsigned int *disabled)
797 {
798     ISE_MESSAGE message;
799     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
800     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_DISABLE];
801
802     send_ise_message(&m_send_message_queue, message);
803
804     wait_for_reply_message();
805
806     std::vector<std::string> values;
807     /* Check if we received reply for GET_RETURN_KEY_DISABLE message */
808     if (process_recved_messages_until_reply_found(
809         ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_RETURN_KEY_DISABLE], values)) {
810             if (disabled) {
811                 for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
812                     if (values.at(0).compare(ISE_TRUEFALSE_TYPES[loop].type_string) == 0) {
813                         *disabled = ISE_TRUEFALSE_TYPES[loop].type_value;
814                     }
815                 }
816             }
817     }
818     /* Now process the rest in the recv buffer */
819     process_recved_messages();
820 }
821
822 void CWebHelperAgentWebSocket::on_set_layout(unsigned int layout)
823 {
824     ISE_MESSAGE message;
825     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
826     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_LAYOUT];
827
828     bool found = false;
829     for (unsigned int loop = 0;loop < sizeof(ISE_LAYOUT_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
830         if (layout == (unsigned int)ISE_LAYOUT_TYPES[loop].type_value) {
831             message.values.push_back(ISE_LAYOUT_TYPES[loop].type_string);
832             found = true;
833         }
834     }
835
836     if (found) {
837         send_ise_message(&m_send_message_queue, message);
838     }
839 }
840
841 void CWebHelperAgentWebSocket::on_get_layout(unsigned int *layout)
842 {
843     ISE_MESSAGE message;
844     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
845     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_LAYOUT];
846
847     send_ise_message(&m_send_message_queue, message);
848
849     wait_for_reply_message();
850
851     std::vector<std::string> values;
852     /* Check if we received reply for GET_LAYOUT message */
853     if (process_recved_messages_until_reply_found(
854         ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_LAYOUT], values)) {
855             if (layout) {
856                 for (unsigned int loop = 0;loop < sizeof(ISE_LAYOUT_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
857                     if (values.at(0).compare(ISE_LAYOUT_TYPES[loop].type_string) == 0) {
858                         *layout = ISE_LAYOUT_TYPES[loop].type_value;
859                     }
860                 }
861             }
862     }
863     /* Now process the rest in the recv buffer */
864     process_recved_messages();
865 }
866
867 void CWebHelperAgentWebSocket::on_reset_input_context(int ic)
868 {
869     ISE_MESSAGE message;
870     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_PLAIN];
871     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_RESET_INPUT_CONTEXT];
872     message.values.push_back(to_string(ic));
873
874     send_ise_message(&m_send_message_queue, message);
875 }
876
877 void CWebHelperAgentWebSocket::on_process_key_event(unsigned int code, unsigned int mask, unsigned int layout, unsigned int *ret)
878 {
879     ISE_MESSAGE message;
880     message.type = ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_QUERY];
881     message.command = ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_PROCESS_KEY_EVENT];
882     message.values.push_back(to_string(code));
883     message.values.push_back(to_string(mask));
884     message.values.push_back(to_string(layout));
885
886     send_ise_message(&m_send_message_queue, message);
887
888     wait_for_reply_message();
889
890     std::vector<std::string> values;
891     /* Check if we received reply for PROCESS_KEY_EVENT message */
892     if (process_recved_messages_until_reply_found(
893         ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_PROCESS_KEY_EVENT], values)) {
894             if (ret) {
895                 for (unsigned int loop = 0;loop < sizeof(ISE_TRUEFALSE_TYPES) / sizeof(ISE_TYPE_VALUE_STRING);loop++) {
896                     if (values.at(0).compare(ISE_TRUEFALSE_TYPES[loop].type_string) == 0) {
897                         *ret = ISE_TRUEFALSE_TYPES[loop].type_value;
898                     }
899                 }
900             }
901     }
902     /* Now process the rest in the recv buffer */
903     process_recved_messages();
904 }
905
906 CWebHelperAgentWebSocket* CWebHelperAgentWebSocket::get_current_instance()
907 {
908     return m_current_instance;
909 }
910
911 std::queue<ISE_MESSAGE>& CWebHelperAgentWebSocket::get_send_message_queue()
912 {
913     return m_send_message_queue;
914 }
915
916 std::queue<ISE_MESSAGE>& CWebHelperAgentWebSocket::get_recv_message_queue()
917 {
918     return m_recv_message_queue;
919 }
920
921 Ecore_Pipe* CWebHelperAgentWebSocket::get_message_pipe()
922 {
923     return m_message_pipe;
924 }
925
926 void CWebHelperAgentWebSocket::wait_for_reply_message()
927 {
928     /* Let's wait for at most REPLY_TIMEOUT */
929     struct timeval now;
930     struct timespec timeout;
931     gettimeofday(&now, NULL);
932     timeout.tv_sec = now.tv_sec + REPLY_TIMEOUT.tv_sec;
933     timeout.tv_nsec = (now.tv_usec + REPLY_TIMEOUT.tv_usec) * 1000;
934     pthread_mutex_lock(&g_ws_query_mutex);
935     pthread_cond_timedwait(&g_ws_query_condition, &g_ws_query_mutex, &timeout);
936     pthread_mutex_unlock(&g_ws_query_mutex);
937 }
938
939 void CWebHelperAgentWebSocket::process_recved_messages()
940 {
941     pthread_mutex_lock(&g_ws_server_mutex);
942
943     while (m_recv_message_queue.size() > 0) {
944         ISE_MESSAGE &message = m_recv_message_queue.front();
945
946         handle_recved_message(message);
947
948         m_recv_message_queue.pop();
949     }
950
951     pthread_mutex_unlock(&g_ws_server_mutex);
952 }
953
954 bool CWebHelperAgentWebSocket::process_recved_messages_until_reply_found(std::string command, std::vector<std::string> &values)
955 {
956     bool ret = false;
957
958     pthread_mutex_lock(&g_ws_server_mutex);
959
960     while (ret == false && m_recv_message_queue.size() > 0) {
961         ISE_MESSAGE &message = m_recv_message_queue.front();
962
963         if (message.command.compare(command) == 0 &&
964             message.type.compare(ISE_MESSAGE_TYPE_STRINGS[ISE_MESSAGE_TYPE_REPLY]) == 0) {
965             ret = true;
966             values = message.values;
967         }
968         handle_recved_message(message);
969
970         m_recv_message_queue.pop();
971     }
972
973     pthread_mutex_unlock(&g_ws_server_mutex);
974
975     return ret;
976 }
977
978 void CWebHelperAgentWebSocket::handle_recved_message(ISE_MESSAGE &message)
979 {
980     static bool _key_event_processing = false;
981     if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_LOG]) == 0) {
982         std::string str = "";
983         for (unsigned int loop = 0;loop < message.values.size();loop++) {
984             str += message.values.at(loop).c_str();
985             if (loop < message.values.size() - 1) {
986                 str += " ";
987             }
988         }
989         log(str.c_str());
990     } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_COMMIT_STRING]) == 0) {
991         std::string str = "";
992         for (unsigned int loop = 0;loop < message.values.size();loop++) {
993             str += message.values.at(loop).c_str();
994             if (loop < message.values.size() - 1) {
995                 str += " ";
996             }
997         }
998         if (_key_event_processing) {
999             struct timeval tv;
1000             tv.tv_sec = 0;
1001             tv.tv_usec = 50000;
1002             select(0, NULL, NULL, NULL, &tv);
1003             _key_event_processing = false;
1004         }
1005         commit_string(str.c_str());
1006     } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_UPDATE_PREEDIT_STRING]) == 0) {
1007         std::string str = "";
1008         for (unsigned int loop = 0;loop < message.values.size();loop++) {
1009             str += message.values.at(loop).c_str();
1010             if (loop < message.values.size() - 1) {
1011                 str += " ";
1012             }
1013         }
1014         update_preedit_string(str.c_str());
1015     } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SEND_KEY_EVENT]) == 0) {
1016         if (message.values.size() == 1) {
1017             forward_key_event(atoi(message.values.at(0).c_str()));
1018             _key_event_processing = true;
1019         }
1020     } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES]) == 0) {
1021         LOGD("ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES");
1022         int portrait_width, portrait_height;
1023         int landscape_width, landscape_height;
1024
1025         if (message.values.size() == 4 || message.values.size() == 2) {
1026             portrait_width = atoi(message.values.at(0).c_str());
1027             portrait_height = atoi(message.values.at(1).c_str());
1028             if (message.values.size() == 2) {
1029                 landscape_width = portrait_width;
1030                 landscape_height = portrait_height;
1031             } else {
1032                 landscape_width = atoi(message.values.at(2).c_str());
1033                 landscape_height = atoi(message.values.at(3).c_str());
1034             }
1035
1036             LOGD("ISE_MESSAGE_COMMAND_SET_KEYBOARD_SIZES : %d %d %d %d",
1037                 portrait_width, portrait_height, landscape_width, landscape_height);
1038
1039 #ifdef WAYLAND
1040             /* Since the full screen IME makes the client application fully obscured,
1041              * when it hides the client receives resume command and try to show IME again.
1042              * So here we are adjusting the height value when the requested keyboard size
1043              * is the same with the screen size, as a workaround */
1044             int scr_w = 0, scr_h = 0;
1045             ecore_wl2_sync();
1046             Ecore_Wl2_Display *ewd = NULL;
1047             if ((ewd = ecore_wl2_connected_display_get(NULL))) {
1048                 ecore_wl2_display_screen_size_get(ewd, &scr_w, &scr_h);
1049
1050                 if (scr_w == portrait_width && scr_h == portrait_height) {
1051                     portrait_height -= 1;
1052                 }
1053                 if (scr_h == landscape_width && scr_w == landscape_height) {
1054                     landscape_height -= 1;
1055                 }
1056             }
1057 #endif
1058
1059             set_keyboard_sizes(
1060                 portrait_width, portrait_height, landscape_width, landscape_height);
1061         }
1062     } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_SET_SELECTION]) == 0) {
1063         LOGD("ISE_MESSAGE_COMMAND_SET_SELECTION");
1064         if (message.values.size() == 2) {
1065             LOGD("ISE_MESSAGE_COMMAND_SET_SELECTION : %d %d",
1066                 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1067             set_selection(
1068                 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1069         }
1070     } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_SELECTION]) == 0) {
1071         if (message.values.size() == 0) {
1072             get_selection();
1073         }
1074     } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT]) == 0) {
1075         LOGD("ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT");
1076         if (message.values.size() == 2) {
1077             LOGD("ISE_MESSAGE_COMMAND_GET_SURROUNDING_TEXT : %d %d",
1078                 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1079             get_surrounding_text(
1080                 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1081         }
1082     } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT]) == 0) {
1083         LOGD("ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT");
1084         if (message.values.size() == 2) {
1085             LOGD("ISE_MESSAGE_COMMAND_DELETE_SURROUNDING_TEXT : %d %d",
1086                 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1087             delete_surrounding_text(
1088                 atoi(message.values.at(0).c_str()), atoi(message.values.at(1).c_str()));
1089         }
1090     } else if (message.command.compare(ISE_MESSAGE_COMMAND_STRINGS[ISE_MESSAGE_COMMAND_LOGIN]) == 0) {
1091         if (g_ws_server_context) {
1092             lws_callback_on_writable_all_protocol(g_ws_server_context, &protocols[PROTOCOL_KEYBOARD]);
1093         } else {
1094             LOGD("WARNING : g_ws_server_context is NULL");
1095         }
1096     }
1097 }