Imported version 0.2-1
[mstardict] / src / lib / stardict_client.cpp
1 /*
2  * This file part of StarDict - A international dictionary for GNOME.
3  * http://stardict.sourceforge.net
4  * Copyright (C) 2006 Hu Zheng <huzheng_001@163.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24
25 #include <glib.h>
26 #include <glib/gi18n.h>
27
28 #include "sockets.hpp"
29 #include "md5.h"
30 #include "getuint32.h"
31
32 #include "stardict_client.hpp"
33
34 #define PROTOCOL_VERSION "0.3"
35
36 #define CODE_HELLO                   220 /* text msg-id */
37 #define CODE_GOODBYE                 221 /* Closing Connection */
38 #define CODE_OK                      250 /* ok */
39 #define CODE_TEMPORARILY_UNAVAILABLE 420 /* server unavailable */
40 #define CODE_SYNTAX_ERROR            500 /* syntax, command not recognized */
41 #define CODE_DENIED                  521
42 #define CODE_DICTMASK_NOTSET         522
43
44 unsigned int STARDICT::Cmd::next_seq = 1;
45
46 sigc::signal<void, const char *> StarDictClient::on_error_;
47 sigc::signal<void, const struct STARDICT::LookupResponse *, unsigned int> StarDictClient::on_lookup_end_;
48 sigc::signal<void, const struct STARDICT::LookupResponse *, unsigned int> StarDictClient::on_floatwin_lookup_end_;
49 sigc::signal<void, const char *> StarDictClient::on_register_end_;
50 sigc::signal<void, const char *> StarDictClient::on_getdictmask_end_;
51 sigc::signal<void, const char *> StarDictClient::on_dirinfo_end_;
52 sigc::signal<void, const char *> StarDictClient::on_dictinfo_end_;
53 sigc::signal<void, int> StarDictClient::on_maxdictcount_end_;
54 sigc::signal<void, std::list<char *> *> StarDictClient::on_previous_end_;
55 sigc::signal<void, std::list<char *> *> StarDictClient::on_next_end_;
56
57 static void arg_escape(std::string &earg, const char *arg)
58 {
59     earg.clear();
60     while (*arg) {
61         if (*arg=='\\') {
62             earg+="\\\\";
63         } else if (*arg==' ') {
64             earg+="\\ ";
65         } else if (*arg=='\n') {
66             earg+="\\n";
67         } else {
68             earg+=*arg;
69         }
70         arg++;
71     }
72 }
73
74 STARDICT::Cmd::Cmd(int cmd, ...)
75 {
76         this->seq = this->next_seq;
77         this->next_seq++;
78         this->reading_status = 0;
79         this->command = cmd;
80         va_list    ap;
81         va_start( ap, cmd );
82         switch (cmd) {
83         case CMD_CLIENT:
84         {
85                 const char *client_name = va_arg( ap, const char * );
86                 std::string earg1, earg2;
87                 arg_escape(earg1, PROTOCOL_VERSION);
88                 arg_escape(earg2, client_name);
89                 this->data = g_strdup_printf("client %s %s\n", earg1.c_str(), earg2.c_str());
90                 break;
91         }
92         case CMD_REGISTER:
93         {
94                 const char *user = va_arg( ap, const char * );
95                 const char *passwd = va_arg( ap, const char * );
96                 const char *email = va_arg( ap, const char * );
97                 std::string earg1, earg2, earg3;
98                 arg_escape(earg1, user);
99                 arg_escape(earg2, passwd);
100                 arg_escape(earg3, email);
101                 this->data = g_strdup_printf("register %s %s %s\n", earg1.c_str(), earg2.c_str(), earg3.c_str());
102                 break;
103         }
104         /*case CMD_CHANGE_PASSWD:
105         {
106                 const char *user = va_arg( ap, const char * );
107                 const char *old_passwd = va_arg( ap, const char * );
108                 const char *new_passwd = va_arg( ap, const char * );
109                 std::string earg1, earg2, earg3;
110                 arg_escape(earg1, user);
111                 arg_escape(earg2, old_passwd);
112                 arg_escape(earg3, new_passwd);
113                 this->data = g_strdup_printf("change_password %s %s %s\n", earg1.c_str(), earg2.c_str(), earg3.c_str());
114                 break;
115         }*/
116         case CMD_AUTH:
117         this->auth = new AuthInfo();
118                 this->auth->user = va_arg( ap, const char * );
119                 this->auth->passwd = va_arg( ap, const char * );
120                 break;
121         case CMD_LOOKUP:
122         {
123                 std::string earg;
124                 arg_escape(earg, va_arg( ap, const char * ));
125                 this->data = g_strdup_printf("lookup %s 30\n", earg.c_str());
126                 this->lookup_response = NULL;
127                 break;
128         }
129         case CMD_PREVIOUS:
130         {
131                 std::string earg;
132                 arg_escape(earg, va_arg( ap, const char * ));
133                 this->data = g_strdup_printf("previous %s 15\n", earg.c_str());
134         this->wordlist_response = NULL;
135                 break;
136         }
137         case CMD_NEXT:
138         {
139                 std::string earg;
140                 arg_escape(earg, va_arg( ap, const char * ));
141                 this->data = g_strdup_printf("next %s 30\n", earg.c_str());
142                 this->wordlist_response = NULL;
143                 break;
144         }
145         /*case CMD_QUERY:
146         {
147                 std::string earg;
148                 arg_escape(earg, va_arg( ap, const char * ));
149                 this->data = g_strdup_printf("query %s\n", earg.c_str());
150                 this->lookup_response = NULL;
151                 break;
152         }*/
153         case CMD_SELECT_QUERY:
154         {
155                 std::string earg;
156                 arg_escape(earg, va_arg( ap, const char * ));
157                 this->data = g_strdup_printf("selectquery %s\n", earg.c_str());
158                 this->lookup_response = NULL;
159                 break;
160         }
161         case CMD_SMART_QUERY:
162         {
163                 std::string earg;
164                 arg_escape(earg, va_arg( ap, const char * ));
165                 int BeginPos = va_arg( ap, int );
166                 this->data = g_strdup_printf("smartquery %s %d\n", earg.c_str(), BeginPos);
167                 this->lookup_response = NULL;
168                 break;
169         }
170         case CMD_DEFINE:
171         {
172                 std::string earg;
173                 arg_escape(earg, va_arg( ap, const char * ));
174                 this->data = g_strdup_printf("define %s\n", earg.c_str());
175                 this->lookup_response = NULL;
176                 break;
177         }
178         case CMD_SET_DICT_MASK:
179         {
180                 std::string earg;
181                 arg_escape(earg, va_arg( ap, const char * ));
182                 this->data = g_strdup_printf("setdictmask %s\n", earg.c_str());
183                 break;
184         }
185         case CMD_GET_DICT_MASK:
186                 this->data = g_strdup("getdictmask\n");
187                 break;
188         /*case CMD_SET_COLLATE_FUNC:
189         {
190                 std::string earg;
191                 arg_escape(earg, va_arg( ap, const char * ));
192                 this->data = g_strdup_printf("setcollatefunc %s\n", earg.c_str());
193                 break;
194         }
195         case CMD_GET_COLLATE_FUNC:
196                 this->data = g_strdup("getcollatefunc\n");
197                 break;
198         case CMD_SET_LANGUAGE:
199         {
200                 std::string earg;
201                 arg_escape(earg, va_arg( ap, const char * ));
202                 this->data = g_strdup_printf("setlanguage %s\n", earg.c_str());
203                 break;
204         }
205         case CMD_GET_LANGUAGE:
206                 this->data = g_strdup("getlanguage\n");
207                 break;
208         case CMD_SET_EMAIL:
209         {
210                 std::string earg;
211                 arg_escape(earg, va_arg( ap, const char * ));
212                 this->data = g_strdup_printf("setemail %s\n", earg.c_str());
213                 break;
214         }
215         case CMD_GET_EMAIL:
216                 this->data = g_strdup("getemail\n");
217                 break;
218         case CMD_GET_USER_LEVEL:
219                 this->data = g_strdup("getuserlevel\n");
220                 break;*/
221         case CMD_MAX_DICT_COUNT:
222                 this->data = g_strdup("maxdictcount\n");
223                 break;
224         case CMD_DIR_INFO:
225         {
226                 std::string earg;
227                 arg_escape(earg, va_arg( ap, const char * ));
228                 this->data = g_strdup_printf("dirinfo %s\n", earg.c_str());
229                 break;
230         }
231         case CMD_DICT_INFO:
232         {
233                 std::string earg;
234                 arg_escape(earg, va_arg( ap, const char * ));
235                 this->data = g_strdup_printf("dictinfo %s\n", earg.c_str());
236                 break;
237         }
238         /*case CMD_USER_LEVEL:
239         {
240                 std::string earg1, earg2, earg3;
241                 arg_escape(earg1, va_arg( ap, const char * ));
242                 arg_escape(earg2, va_arg( ap, const char * ));
243                 arg_escape(earg3, va_arg( ap, const char * ));
244                 this->data = g_strdup_printf("userlevel %s %s %s\n", earg1.c_str(), earg2.c_str(), earg3.c_str());
245                 break;
246         }*/
247         case CMD_QUIT:
248                 this->data = g_strdup("quit\n");
249                 break;
250         }
251         va_end( ap );
252 }
253
254 STARDICT::LookupResponse::DictResponse::DictResponse()
255 {
256         oword = NULL;
257 }
258
259 STARDICT::LookupResponse::DictResponse::~DictResponse()
260 {
261         g_free(oword);
262         for (std::list<DictResult *>::iterator i = dict_result_list.begin(); i != dict_result_list.end(); ++i) {
263                 delete *i;
264         }
265 }
266
267 STARDICT::LookupResponse::DictResponse::DictResult::DictResult()
268 {
269         bookname = NULL;
270 }
271
272 STARDICT::LookupResponse::DictResponse::DictResult::~DictResult()
273 {
274         g_free(bookname);
275         for (std::list<WordResult *>::iterator i = word_result_list.begin(); i != word_result_list.end(); ++i) {
276                 delete *i;
277         }
278 }
279
280 STARDICT::LookupResponse::DictResponse::DictResult::WordResult::WordResult()
281 {
282         word = NULL;
283 }
284
285 STARDICT::LookupResponse::DictResponse::DictResult::WordResult::~WordResult()
286 {
287         g_free(word);
288         for (std::list<char *>::iterator i = datalist.begin(); i != datalist.end(); ++i) {
289                 g_free(*i);
290         }
291 }
292
293 STARDICT::LookupResponse::~LookupResponse()
294 {
295     if (listtype == ListType_List) {
296         for (std::list<char *>::iterator i = wordlist->begin(); i != wordlist->end(); ++i) {
297                 g_free(*i);
298             }
299         delete wordlist;
300     } else if (listtype == ListType_Tree) {
301         for (std::list<WordTreeElement *>::iterator i = wordtree->begin(); i != wordtree->end(); ++i) {
302             g_free((*i)->bookname);
303             for (std::list<char *>::iterator j = (*i)->wordlist.begin(); j != (*i)->wordlist.end(); ++j) {
304                 g_free(*j);
305             }
306             delete *i;
307         }
308         delete wordtree;
309     }
310 }
311
312 STARDICT::Cmd::~Cmd()
313 {
314     if (this->command == CMD_AUTH) {
315         delete this->auth;
316     } else {
317         g_free(this->data);
318     }
319     if (this->command == CMD_LOOKUP || this->command == CMD_DEFINE || this->command == CMD_SELECT_QUERY || this->command == CMD_SMART_QUERY) {
320         delete this->lookup_response;
321     } else if (this->command == CMD_PREVIOUS || this->command == CMD_NEXT) {
322         if (this->wordlist_response) {
323             for (std::list<char *>::iterator i = this->wordlist_response->begin(); i != this->wordlist_response->end(); ++i) {
324                 g_free(*i);
325             }
326             delete this->wordlist_response;
327         }
328     }
329 }
330
331 StarDictCache::StarDictCache()
332 {
333     str_pool.resize(str_pool_size);
334     for (size_t i = 0; i< str_pool_size; i++) {
335         str_pool[i] = NULL;
336     }
337     cur_str_pool_pos = 0;
338
339     lookup_response_pool.resize(lookup_response_pool_size);
340     for (size_t i = 0; i< lookup_response_pool_size; i++) {
341         lookup_response_pool[i] = NULL;
342     }
343     cur_lookup_response_pool_pos = 0;
344 }
345
346 StarDictCache::~StarDictCache()
347 {
348     clean_all_cache();
349 }
350
351 void StarDictCache::clean_all_cache()
352 {
353     for (std::vector<StrElement *>::iterator i = str_pool.begin(); i != str_pool.end(); ++i) {
354         if (*i) {
355             g_free((*i)->data);
356             delete *i;
357             *i = NULL;
358         }
359     }
360     clean_cache_lookup_response();
361 }
362
363 void StarDictCache::clean_cache_lookup_response()
364 {
365     for (std::vector<LookupResponseElement *>::iterator i = lookup_response_pool.begin(); i != lookup_response_pool.end(); ++i) {
366         if (*i) {
367             delete ((*i)->lookup_response);
368             delete *i;
369             *i = NULL;
370         }
371     }
372 }
373
374 char *StarDictCache::get_cache_str(const char *key_str)
375 {
376     for (std::vector<StrElement *>::iterator i = str_pool.begin(); i != str_pool.end(); ++i) {
377         if (*i) {
378             if ((*i)->key == key_str)
379                 return (*i)->data;
380         }
381     }
382     return NULL;
383 }
384
385 STARDICT::LookupResponse *StarDictCache::get_cache_lookup_response(const char *key_str)
386 {
387     for (std::vector<LookupResponseElement *>::iterator i = lookup_response_pool.begin(); i != lookup_response_pool.end(); ++i) {
388         if (*i) {
389             if ((*i)->key == key_str)
390                 return (*i)->lookup_response;
391         }
392     }
393     return NULL;
394 }
395
396 void StarDictCache::clean_cache_str(const char *key_str)
397 {
398     for (std::vector<StrElement *>::iterator i = str_pool.begin(); i != str_pool.end(); ++i) {
399         if (*i) {
400             if ((*i)->key == key_str) {
401                 g_free((*i)->data);
402                 delete *i;
403                 *i = NULL;
404                 //return;
405             }
406         }
407     }
408 }
409
410 void StarDictCache::save_cache_str(const char *key_str, char *data)
411 {
412     if (str_pool[cur_str_pool_pos]) {
413         g_free(str_pool[cur_str_pool_pos]->data);
414         delete str_pool[cur_str_pool_pos];
415     }
416     str_pool[cur_str_pool_pos] = new StrElement();
417     str_pool[cur_str_pool_pos]->key = key_str;
418     str_pool[cur_str_pool_pos]->data = data;
419     cur_str_pool_pos++;
420     if (cur_str_pool_pos == str_pool_size)
421         cur_str_pool_pos = 0;
422 }
423
424 void StarDictCache::save_cache_lookup_response(const char *key_str, STARDICT::LookupResponse *lookup_response)
425 {
426     if (lookup_response_pool[cur_lookup_response_pool_pos]) {
427         delete lookup_response_pool[cur_lookup_response_pool_pos]->lookup_response;
428         delete lookup_response_pool[cur_lookup_response_pool_pos];
429     }
430     lookup_response_pool[cur_lookup_response_pool_pos] = new LookupResponseElement();
431     lookup_response_pool[cur_lookup_response_pool_pos]->key = key_str;
432     lookup_response_pool[cur_lookup_response_pool_pos]->lookup_response = lookup_response;
433     cur_lookup_response_pool_pos++;
434     if (cur_lookup_response_pool_pos == lookup_response_pool_size)
435         cur_lookup_response_pool_pos = 0;
436 }
437
438 StarDictClient::StarDictClient()
439 {
440         sd_ = -1;
441     channel_ = NULL;
442     in_source_id_ = 0;
443     out_source_id_ = 0;
444     is_connected_ = false;
445 }
446
447 StarDictClient::~StarDictClient()
448 {
449     disconnect();
450 }
451
452 void StarDictClient::set_server(const char *host, int port)
453 {
454     if (host_ != host || port_ != port) {
455         host_ = host;
456         port_ = port;
457         host_resolved = false;
458         clean_all_cache();
459     }
460 }
461
462 void StarDictClient::set_auth(const char *user, const char *md5passwd)
463 {
464     if (user_ != user || md5passwd_ != md5passwd) {
465         user_ = user;
466         md5passwd_ = md5passwd;
467         clean_all_cache();
468     }
469 }
470
471 bool StarDictClient::try_cache(STARDICT::Cmd *c)
472 {
473     if (c->command == STARDICT::CMD_LOOKUP || c->command == STARDICT::CMD_DEFINE || c->command == STARDICT::CMD_SELECT_QUERY || c->command == STARDICT::CMD_SMART_QUERY) {
474         STARDICT::LookupResponse *res = get_cache_lookup_response(c->data);
475         if (res) {
476             if (c->command == STARDICT::CMD_LOOKUP || c->command == STARDICT::CMD_DEFINE)
477                 on_lookup_end_.emit(res, 0);
478             else if (c->command == STARDICT::CMD_SELECT_QUERY || c->command == STARDICT::CMD_SMART_QUERY)
479                 on_floatwin_lookup_end_.emit(res, 0);
480             delete c;
481             return true;
482         } else {
483             return false;
484         }
485     }
486     if (c->command == STARDICT::CMD_PREVIOUS || c->command == STARDICT::CMD_NEXT) {
487         // Not implemented yet.
488         return false;
489     }
490     char *data = get_cache_str(c->data);
491     if (data) {
492         switch (c->command) {
493             case STARDICT::CMD_DIR_INFO:
494                 on_dirinfo_end_.emit(data);
495                 break;
496             case STARDICT::CMD_DICT_INFO:
497                 on_dictinfo_end_.emit(data);
498                 break;
499             case STARDICT::CMD_GET_DICT_MASK:
500                 on_getdictmask_end_.emit(data);
501                 break;
502             case STARDICT::CMD_MAX_DICT_COUNT:
503                 on_maxdictcount_end_.emit(atoi(data));
504                 break;
505         }
506         delete c;
507         return true;
508     } else {
509         return false;
510     }
511 }
512
513 void StarDictClient::send_commands(int num, ...)
514 {
515     STARDICT::Cmd *c;
516     if (!is_connected_) {
517 #ifdef _WIN32
518         c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Windows");
519 #else
520         c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Linux");
521 #endif
522         cmdlist.push_back(c);
523         if (!user_.empty() && !md5passwd_.empty()) {
524             c = new STARDICT::Cmd(STARDICT::CMD_AUTH, user_.c_str(), md5passwd_.c_str());
525             cmdlist.push_back(c);
526         }
527     }
528     va_list    ap;
529     va_start( ap, num);
530     for (int i = 0; i< num; i++) {
531         c = va_arg( ap, STARDICT::Cmd *);
532             cmdlist.push_back(c);
533     }
534     va_end( ap );
535     if (!is_connected_) {
536         waiting_banner_ = true;
537         connect();
538     }
539 }
540
541 void StarDictClient::try_cache_or_send_commands(int num, ...)
542 {
543     STARDICT::Cmd *c;
544     std::list<STARDICT::Cmd *> send_cmdlist;
545     va_list    ap;
546     va_start( ap, num);
547     for (int i = 0; i< num; i++) {
548         c = va_arg( ap, STARDICT::Cmd *);
549         if (!try_cache(c)) {
550             send_cmdlist.push_back(c);
551         }
552     }
553     va_end( ap );
554     if (send_cmdlist.empty())
555         return;
556
557     if (!is_connected_) {
558 #ifdef _WIN32
559         c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Windows");
560 #else
561         c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Linux");
562 #endif
563         cmdlist.push_back(c);
564         if (!user_.empty() && !md5passwd_.empty()) {
565             c = new STARDICT::Cmd(STARDICT::CMD_AUTH, user_.c_str(), md5passwd_.c_str());
566             cmdlist.push_back(c);
567         }
568     }
569     for (std::list<STARDICT::Cmd *>::iterator i = send_cmdlist.begin(); i!= send_cmdlist.end(); ++i) {
570             cmdlist.push_back(*i);
571     }
572     if (!is_connected_) {
573         waiting_banner_ = true;
574         connect();
575     }
576 }
577
578 void StarDictClient::write_str(const char *str, GError **err)
579 {
580     int len = strlen(str);
581     int left_byte = len;
582     GIOStatus res;
583     gsize bytes_written;
584     while (left_byte) {
585         res = g_io_channel_write_chars(channel_, str+(len - left_byte), left_byte, &bytes_written, err);
586         if (res == G_IO_STATUS_ERROR) {
587             disconnect();
588             return;
589         }
590         left_byte -= bytes_written;
591     }
592     res = g_io_channel_flush(channel_, err);
593     if (res == G_IO_STATUS_ERROR) {
594         disconnect();
595     }
596         if (out_source_id_ == 0)
597                 out_source_id_ = g_io_add_watch(channel_, GIOCondition(G_IO_OUT), on_io_out_event, this);
598 }
599
600 void StarDictClient::request_command()
601 {
602     reading_type_ = READ_LINE;
603     if (cmdlist.empty()) {
604         cmdlist.push_back(new STARDICT::Cmd(STARDICT::CMD_QUIT));
605     }
606     STARDICT::Cmd *c = cmdlist.front();
607         switch (c->command) {
608         case STARDICT::CMD_AUTH:
609                 {
610                         struct MD5Context ctx;
611                         unsigned char digest[16];
612                         char hex[33];
613                         int i;
614                         MD5Init(&ctx);
615                         MD5Update(&ctx, (const unsigned char*)cmd_reply.daemonStamp.c_str(), cmd_reply.daemonStamp.length());
616                         MD5Update(&ctx, (const unsigned char*)(c->auth->passwd.c_str()), c->auth->passwd.length());
617                         MD5Final(digest, &ctx );
618                         for (i = 0; i < 16; i++)
619                                 sprintf( hex+2*i, "%02x", digest[i] );
620                         hex[32] = '\0';
621                         std::string earg1, earg2;
622                         arg_escape(earg1, c->auth->user.c_str());
623                         arg_escape(earg2, hex);
624                         char *data = g_strdup_printf("auth %s %s\n", earg1.c_str(), earg2.c_str());
625             GError *err = NULL;
626             write_str(data, &err);
627                         g_free(data);
628                         if (err) {
629                 on_error_.emit(err->message);
630                 g_error_free(err);
631                                 return;
632                         }
633                         break;
634                 }
635                 default:
636         {
637             GError *err = NULL;
638             write_str(c->data, &err);
639                         if (err) {
640                 on_error_.emit(err->message);
641                 g_error_free(err);
642                                 return;
643             }
644                         break;
645         }
646         }
647         return;
648 }
649
650 void StarDictClient::clean_command()
651 {
652         for (std::list<STARDICT::Cmd *>::iterator i=cmdlist.begin(); i!=cmdlist.end(); ++i) {
653                 delete *i;
654         }
655         cmdlist.clear();
656 }
657
658 void StarDictClient::connect()
659 {
660     if (host_resolved) {
661             on_resolved(this, true, sa);
662     } else {
663         Socket::resolve(host_, this, on_resolved);
664     }
665 }
666
667 void StarDictClient::on_resolved(gpointer data, bool resolved, in_addr_t sa_)
668 {
669     StarDictClient *oStarDictClient = (StarDictClient *)data;
670     if (!resolved) {
671         static bool showed_once = false;
672         if (!showed_once) {
673                 showed_once = true;
674                 gchar *mes = g_strdup_printf("Can not reslove %s: %s\n",
675                          oStarDictClient->host_.c_str(), Socket::get_error_msg().c_str());
676                 on_error_.emit(mes);
677                 g_free(mes);
678         }
679         return;
680     }
681
682     if (oStarDictClient->host_resolved == false) {
683             oStarDictClient->sa = sa_;
684             oStarDictClient->host_resolved = true;
685     }
686
687     oStarDictClient->sd_ = Socket::socket();
688
689     if (oStarDictClient->sd_ == -1) {
690         std::string str = "Can not create socket: " + Socket::get_error_msg();
691         on_error_.emit(str.c_str());
692         return;
693     }
694     Socket::connect(oStarDictClient->sd_, sa_, oStarDictClient->port_, oStarDictClient, on_connected);
695 }
696
697 void StarDictClient::on_connected(gpointer data, bool succeeded)
698 {
699     StarDictClient *oStarDictClient = (StarDictClient *)data;
700     if (!succeeded) {
701         static bool showed_once = false;
702         if (!showed_once) {
703                 showed_once = true;
704                 gchar *mes = g_strdup_printf("Can not connect to %s: %s\n",
705                          oStarDictClient->host_.c_str(), Socket::get_error_msg().c_str());
706                 on_error_.emit(mes);
707                 g_free(mes);
708         }
709         return;
710     }
711 #ifdef _WIN32
712     oStarDictClient->channel_ = g_io_channel_win32_new_socket(oStarDictClient->sd_);
713 #else
714     oStarDictClient->channel_ = g_io_channel_unix_new(oStarDictClient->sd_);
715 #endif
716
717     g_io_channel_set_encoding(oStarDictClient->channel_, NULL, NULL);
718
719     /* make sure that the channel is non-blocking */
720     int flags = g_io_channel_get_flags(oStarDictClient->channel_);
721     flags |= G_IO_FLAG_NONBLOCK;
722     GError *err = NULL;
723     g_io_channel_set_flags(oStarDictClient->channel_, GIOFlags(flags), &err);
724     if (err) {
725         g_io_channel_unref(oStarDictClient->channel_);
726         oStarDictClient->channel_ = NULL;
727         gchar *str = g_strdup_printf("Unable to set the channel as non-blocking: %s", err->message);
728         on_error_.emit(str);
729         g_free(str);
730         g_error_free(err);
731         return;
732     }
733
734     oStarDictClient->is_connected_ = true;
735     oStarDictClient->waiting_banner_ = true;
736     oStarDictClient->reading_type_ = READ_LINE;
737     oStarDictClient->in_source_id_ = g_io_add_watch(oStarDictClient->channel_, GIOCondition(G_IO_IN | G_IO_ERR), on_io_in_event, oStarDictClient);
738 }
739
740 void StarDictClient::disconnect()
741 {
742     clean_command();
743     if (in_source_id_) {
744         g_source_remove(in_source_id_);
745         in_source_id_ = 0;
746     }
747     if (out_source_id_) {
748         g_source_remove(out_source_id_);
749         out_source_id_ = 0;
750     }
751
752     if (channel_) {
753         g_io_channel_shutdown(channel_, TRUE, NULL);
754         g_io_channel_unref(channel_);
755         channel_ = NULL;
756     }
757         if (sd_ != -1) {
758                 Socket::close(sd_);
759                 sd_ = -1;
760         }
761     is_connected_ = false;
762 }
763
764 gboolean StarDictClient::on_io_out_event(GIOChannel *ch, GIOCondition cond,
765                  gpointer user_data)
766 {
767         StarDictClient *stardict_client = static_cast<StarDictClient *>(user_data);
768         GError *err = NULL;
769         GIOStatus res = g_io_channel_flush(stardict_client->channel_, &err);
770         if (res == G_IO_STATUS_AGAIN) {
771                 return TRUE;
772         } else if (err) {
773                 on_error_.emit(err->message);
774                 g_error_free(err);
775         }
776         stardict_client->out_source_id_ = 0;
777         return FALSE;
778 }
779
780 gboolean StarDictClient::on_io_in_event(GIOChannel *ch, GIOCondition cond,
781                  gpointer user_data)
782 {
783     StarDictClient *stardict_client = static_cast<StarDictClient *>(user_data);
784
785     if (!stardict_client->channel_) {
786         //g_warning("No channel available\n");
787         return FALSE;
788     }
789     if (cond & G_IO_ERR) {
790         /*gchar *mes =
791             g_strdup_printf("Connection failed to the dictionary server at %s:%d",
792                     stardict_client->host_.c_str(), stardict_client->port_);
793         on_error_.emit(mes);
794         g_free(mes);*/
795         stardict_client->disconnect();
796         return FALSE;
797     }
798     GError *err = NULL;
799     gsize term, len;
800     gchar *line;
801     GIOStatus res;
802
803     for (;;) {
804         if (!stardict_client->channel_)
805             break;
806         bool result;
807         if (stardict_client->reading_type_ == READ_SIZE) {
808             gsize bytes_read;
809             res = g_io_channel_read_chars(stardict_client->channel_, stardict_client->size_data+(stardict_client->size_count-stardict_client->size_left), stardict_client->size_left, &bytes_read, &err);
810             if (res == G_IO_STATUS_ERROR || res == G_IO_STATUS_EOF) {
811                 if (err) {
812                     gchar *str = g_strdup_printf("Error while reading reply from server: %s", err->message);
813                     on_error_.emit(str);
814                     g_free(str);
815                     g_error_free(err);
816                 }
817                 stardict_client->disconnect();
818
819                 return FALSE;
820             }
821             stardict_client->size_left -= bytes_read;
822             if (stardict_client->size_left == 0)
823                 result = stardict_client->parse(stardict_client->size_data);
824             else
825                 break;
826         } else {
827             if (stardict_client->reading_type_ == READ_LINE)
828                 g_io_channel_set_line_term(stardict_client->channel_, "\n", 1);
829             else if (stardict_client->reading_type_ == READ_STRING)
830                 g_io_channel_set_line_term(stardict_client->channel_, "", 1);
831
832             res = g_io_channel_read_line(stardict_client->channel_, &line,
833                              &len, &term, &err);
834             if (res == G_IO_STATUS_ERROR || res == G_IO_STATUS_EOF) {
835                 if (err) {
836                     gchar *str = g_strdup_printf("Error while reading reply from server: %s", err->message);
837                     on_error_.emit(str);
838                     g_free(str);
839                     g_error_free(err);
840                 }
841                 stardict_client->disconnect();
842
843                 return FALSE;
844             }
845
846             if (!len)
847                 break;
848
849             //truncate the line terminator before parsing
850             line[term] = '\0';
851             result = stardict_client->parse(line);
852         }
853         if (!result) {
854             stardict_client->disconnect();
855             return FALSE;
856         }
857     }
858
859     return TRUE;
860 }
861
862 int StarDictClient::parse_banner(gchar *line)
863 {
864     int status;
865     status = atoi(line);
866     if (status != CODE_HELLO) {
867         if (status == CODE_TEMPORARILY_UNAVAILABLE) {
868             printf("Server temporarily unavailable!\n");
869         } else {
870             printf("Unexpected status code %d\n", status);
871         }
872         return 0;
873     }
874     char *p;
875     p = strrchr(line, ' ');
876     if (p) {
877         p++;
878         cmd_reply.daemonStamp = p;
879     }
880     return 1;
881 }
882
883 int StarDictClient::parse_command_client(gchar *line)
884 {
885     int status;
886     status = atoi(line);
887     if (status != CODE_OK) {
888         gchar *str = g_strdup_printf("Client denied: %s", line);
889         on_error_.emit(str);
890         g_free(str);
891         return 0;
892     }
893     return 1;
894 }
895
896 int StarDictClient::parse_command_auth(gchar *line)
897 {
898     int status;
899     status = atoi(line);
900     if (status != CODE_OK) {
901         gchar *str = g_strdup_printf(_("Authentication denied: %s"), line);
902         on_error_.emit(str);
903         g_free(str);
904         return 0;
905     }
906     return 1;
907 }
908
909 int StarDictClient::parse_command_register(gchar *line)
910 {
911     int status;
912     status = atoi(line);
913     if (status != CODE_OK) {
914         gchar *str = g_strdup_printf(_("Register failed: %s"), line);
915         on_error_.emit(str);
916         g_free(str);
917         return 0;
918     }
919     on_register_end_.emit(_("Register success!"));
920     return 1;
921 }
922
923 int StarDictClient::parse_command_quit(gchar *line)
924 {
925     int status;
926     status = atoi(line);
927     if (status != CODE_GOODBYE) {
928     }
929     return 0;
930 }
931
932 int StarDictClient::parse_command_setdictmask(gchar *line)
933 {
934     int status;
935     status = atoi(line);
936     if (status != CODE_OK) {
937         gchar *str = g_strdup_printf("Set Dict Mask failed: %s", line);
938         on_error_.emit(str);
939         g_free(str);
940         return 0;
941     }
942     clean_cache_str("getdictmask\n");
943     clean_cache_lookup_response();
944     return 1;
945 }
946
947 int StarDictClient::parse_command_getdictmask(STARDICT::Cmd* cmd, gchar *buf)
948 {
949     if (cmd->reading_status == 0) {
950         int status;
951         status = atoi(buf);
952         if (status != CODE_OK) {
953             g_free(buf);
954             on_error_.emit(_("You haven't setup the account. Please open the \"Net Dict\" page in the Preferences dialog and register an account first."));
955             return 0;
956         }
957         g_free(buf);
958         cmd->reading_status = 1;
959         reading_type_ = READ_STRING;
960     } else if (cmd->reading_status == 1) {
961         on_getdictmask_end_.emit(buf);
962         save_cache_str(cmd->data, buf);
963         return 1;
964     }
965     return 2;
966 }
967
968 int StarDictClient::parse_command_dirinfo(STARDICT::Cmd* cmd, gchar *buf)
969 {
970     if (cmd->reading_status == 0) {
971         int status;
972         status = atoi(buf);
973         if (status != CODE_OK) {
974             gchar *str = g_strdup_printf("Get dir info failed: %s", buf);
975             g_free(buf);
976             on_error_.emit(str);
977             g_free(str);
978             return 0;
979         }
980         g_free(buf);
981         cmd->reading_status = 1;
982         reading_type_ = READ_STRING;
983     } else if (cmd->reading_status == 1) {
984         on_dirinfo_end_.emit(buf);
985         save_cache_str(cmd->data, buf);
986         return 1;
987     }
988     return 2;
989 }
990
991 int StarDictClient::parse_command_dictinfo(STARDICT::Cmd* cmd, gchar *buf)
992 {
993     if (cmd->reading_status == 0) {
994         int status;
995         status = atoi(buf);
996         if (status != CODE_OK) {
997             gchar *str = g_strdup_printf("Get dict info failed: %s", buf);
998             g_free(buf);
999             on_error_.emit(str);
1000             g_free(str);
1001             return 0;
1002         }
1003         g_free(buf);
1004         cmd->reading_status = 1;
1005         reading_type_ = READ_STRING;
1006     } else if (cmd->reading_status == 1) {
1007         on_dictinfo_end_.emit(buf);
1008         save_cache_str(cmd->data, buf);
1009         return 1;
1010     }
1011     return 2;
1012 }
1013
1014 int StarDictClient::parse_command_maxdictcount(STARDICT::Cmd* cmd, gchar *buf)
1015 {
1016     if (cmd->reading_status == 0) {
1017         int status;
1018         status = atoi(buf);
1019         if (status != CODE_OK) {
1020             gchar *str = g_strdup_printf("Get max dict count failed: %s", buf);
1021             g_free(buf);
1022             on_error_.emit(str);
1023             g_free(str);
1024             return 0;
1025         }
1026         g_free(buf);
1027         cmd->reading_status = 1;
1028         reading_type_ = READ_STRING;
1029     } else if (cmd->reading_status == 1) {
1030         on_maxdictcount_end_.emit(atoi(buf));
1031         save_cache_str(cmd->data, buf);
1032         return 1;
1033     }
1034     return 2;
1035 }
1036
1037 int StarDictClient::parse_wordlist(STARDICT::Cmd* cmd, gchar *buf)
1038 {
1039     if (cmd->reading_status == 0) { // Read code.
1040         int status;
1041         status = atoi(buf);
1042         g_free(buf);
1043         if (status != CODE_OK) {
1044             return 0;
1045         }
1046         cmd->wordlist_response = new std::list<char *>;
1047         cmd->reading_status = 1;
1048         reading_type_ = READ_STRING;
1049     } else if (cmd->reading_status == 1) {
1050         if (*buf == '\0') {
1051             g_free(buf);
1052             if (cmd->command == STARDICT::CMD_PREVIOUS) {
1053                 on_previous_end_.emit(cmd->wordlist_response);
1054                 return 1;
1055             } else {
1056                 on_next_end_.emit(cmd->wordlist_response);
1057                 return 1;
1058             }
1059         } else {
1060             cmd->wordlist_response->push_back(buf);
1061         }
1062     }
1063     return 2;
1064 }
1065
1066 int StarDictClient::parse_dict_result(STARDICT::Cmd* cmd, gchar *buf)
1067 {
1068     if (cmd->reading_status == 0) { // Read code.
1069         int status;
1070         status = atoi(buf);
1071         g_free(buf);
1072         if (status != CODE_OK) {
1073             if (status == CODE_DICTMASK_NOTSET) {
1074                 on_error_.emit(_("You haven't chosen any dictionaries, please choose some by clicking \"Manage Dict\"->\"Network dictionaries\"->\"Add\"."));
1075                 return 1;
1076             } else {
1077                 return 0;
1078             }
1079         }
1080         cmd->lookup_response = new STARDICT::LookupResponse();
1081         cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_None;
1082         cmd->reading_status = 1;
1083         reading_type_ = READ_STRING;
1084     } else if (cmd->reading_status == 1) { // Read original word.
1085         cmd->lookup_response->dict_response.oword = buf;
1086         cmd->reading_status = 2;
1087     } else if (cmd->reading_status == 2) { // Read book name.
1088         if (*buf == '\0') {
1089             g_free(buf);
1090             if (cmd->command == STARDICT::CMD_DEFINE) {
1091                 on_lookup_end_.emit(cmd->lookup_response, cmd->seq);
1092                 save_cache_lookup_response(cmd->data, cmd->lookup_response);
1093                 cmd->lookup_response = NULL;
1094                 return 1;
1095             } else if ( cmd->command == STARDICT::CMD_SELECT_QUERY || cmd->command == STARDICT::CMD_SMART_QUERY) {
1096                 on_floatwin_lookup_end_.emit(cmd->lookup_response, cmd->seq);
1097                 save_cache_lookup_response(cmd->data, cmd->lookup_response);
1098                 cmd->lookup_response = NULL;
1099                 return 1;
1100             }
1101             cmd->reading_status = 6;
1102             reading_type_ = READ_STRING;
1103         } else {
1104             struct STARDICT::LookupResponse::DictResponse::DictResult *dict_result = new STARDICT::LookupResponse::DictResponse::DictResult();
1105             dict_result->bookname = buf;
1106             cmd->lookup_response->dict_response.dict_result_list.push_back(dict_result);
1107             cmd->reading_status = 3;
1108         }
1109     } else if (cmd->reading_status == 3) { // Read word.
1110         if (*buf == '\0') {
1111             g_free(buf);
1112             cmd->reading_status = 2;
1113         } else {
1114             struct STARDICT::LookupResponse::DictResponse::DictResult::WordResult *word_result = new STARDICT::LookupResponse::DictResponse::DictResult::WordResult();
1115             word_result->word = buf;
1116             cmd->lookup_response->dict_response.dict_result_list.back()->word_result_list.push_back(word_result);;
1117             cmd->reading_status = 4;
1118             reading_type_ = READ_SIZE;
1119             size_data = (char *)g_malloc(sizeof(guint32));
1120             size_count = size_left = sizeof(guint32);
1121         }
1122     } else if (cmd->reading_status == 4) {
1123         guint32 datasize = g_ntohl(get_uint32(buf));
1124         memcpy(buf, &datasize, sizeof(guint32));
1125         if (datasize == 0) {
1126             g_free(buf);
1127             cmd->reading_status = 3;
1128             reading_type_ = READ_STRING;
1129         } else {
1130             cmd->reading_status = 5;
1131             size_data = (char *)g_realloc(buf, datasize + sizeof(guint32));
1132             size_count = datasize + sizeof(guint32);
1133             size_left = datasize;
1134         }
1135     } else if (cmd->reading_status == 5) {
1136             cmd->lookup_response->dict_response.dict_result_list.back()->word_result_list.back()->datalist.push_back(buf);
1137             cmd->reading_status = 4;
1138             size_data = (char *)g_malloc(sizeof(guint32));
1139             size_count = size_left = sizeof(guint32);
1140     } else if (cmd->reading_status == 6) {
1141         if (strcmp(buf, "d") == 0) {
1142             cmd->reading_status = 8;
1143             cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Tree;
1144             cmd->lookup_response->wordtree = new std::list<STARDICT::LookupResponse::WordTreeElement *>;
1145         } else {
1146             cmd->reading_status = 7;
1147             if (strcmp(buf, "r") == 0)
1148                 cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Rule_List;
1149             else if (strcmp(buf, "g") == 0)
1150                 cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Regex_List;
1151             else if (strcmp(buf, "f") == 0)
1152                 cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Fuzzy_List;
1153             else 
1154                 cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_List;
1155             cmd->lookup_response->wordlist = new std::list<char *>;
1156         }
1157         g_free(buf);
1158     } else if (cmd->reading_status == 7) {
1159         if (*buf == '\0') {
1160             g_free(buf);
1161             on_lookup_end_.emit(cmd->lookup_response, cmd->seq);
1162             save_cache_lookup_response(cmd->data, cmd->lookup_response);
1163             cmd->lookup_response = NULL;
1164             return 1;
1165         } else {
1166             cmd->lookup_response->wordlist->push_back(buf);
1167         }
1168     } else if (cmd->reading_status == 8) {
1169         if (*buf == '\0') {
1170             g_free(buf);
1171             on_lookup_end_.emit(cmd->lookup_response, cmd->seq);
1172             save_cache_lookup_response(cmd->data, cmd->lookup_response);
1173             cmd->lookup_response = NULL;
1174             return 1;
1175         } else {
1176             STARDICT::LookupResponse::WordTreeElement *element = new STARDICT::LookupResponse::WordTreeElement();
1177             element->bookname = buf;
1178             cmd->lookup_response->wordtree->push_back(element);
1179             cmd->reading_status = 9;
1180         }
1181     } else if (cmd->reading_status == 9) {
1182         if (*buf == '\0') {
1183             g_free(buf);
1184             cmd->reading_status = 8;
1185         } else {
1186             cmd->lookup_response->wordtree->back()->wordlist.push_back(buf);
1187         }
1188     }
1189     return 2;
1190 }
1191
1192 bool StarDictClient::parse(gchar *line)
1193 {
1194     int result;
1195     if (waiting_banner_) {
1196         waiting_banner_ = false;
1197         result = parse_banner(line);
1198         g_free(line);
1199         if (!result)
1200             return false;
1201         request_command();
1202         return true;
1203     }
1204     STARDICT::Cmd* cmd = cmdlist.front();
1205     switch (cmd->command) {
1206         case STARDICT::CMD_CLIENT:
1207             result = parse_command_client(line);
1208             g_free(line);
1209             break;
1210         case STARDICT::CMD_AUTH:
1211             result = parse_command_auth(line);
1212             g_free(line);
1213             break;
1214         case STARDICT::CMD_REGISTER:
1215             result = parse_command_register(line);
1216             g_free(line);
1217             break;
1218         case STARDICT::CMD_GET_DICT_MASK:
1219             result = parse_command_getdictmask(cmd, line);
1220             break;
1221         case STARDICT::CMD_SET_DICT_MASK:
1222             result = parse_command_setdictmask(line);
1223             break;
1224         case STARDICT::CMD_DIR_INFO:
1225             result = parse_command_dirinfo(cmd, line);
1226             break;
1227         case STARDICT::CMD_DICT_INFO:
1228             result = parse_command_dictinfo(cmd, line);
1229             break;
1230         case STARDICT::CMD_MAX_DICT_COUNT:
1231             result = parse_command_maxdictcount(cmd, line);
1232             break;
1233         case STARDICT::CMD_DEFINE:
1234         case STARDICT::CMD_LOOKUP:
1235         case STARDICT::CMD_SELECT_QUERY:
1236         case STARDICT::CMD_SMART_QUERY:
1237             result = parse_dict_result(cmd, line);
1238             break;
1239         case STARDICT::CMD_PREVIOUS:
1240         case STARDICT::CMD_NEXT:
1241             result = parse_wordlist(cmd, line);
1242             break;
1243         case STARDICT::CMD_QUIT:
1244             result = parse_command_quit(line);
1245             g_free(line);
1246             break;
1247         default:
1248             result = 0;
1249             g_free(line);
1250             break;
1251     }
1252     if (result == 0)
1253         return false;
1254     if (result == 1) {
1255         delete cmd;
1256         cmdlist.pop_front();
1257         if (cmdlist.empty()) {
1258             cmdlist.push_back(new STARDICT::Cmd(STARDICT::CMD_QUIT));
1259         }
1260         request_command();
1261     }
1262     return true;
1263 }