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