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