--- /dev/null
+/*
+ * This file part of StarDict - A international dictionary for GNOME.
+ * http://stardict.sourceforge.net
+ * Copyright (C) 2006 Hu Zheng <huzheng_001@163.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "sockets.hpp"
+#include "md5.h"
+#include "getuint32.h"
+
+#include "stardict_client.hpp"
+
+#define PROTOCOL_VERSION "0.3"
+
+#define CODE_HELLO 220 /* text msg-id */
+#define CODE_GOODBYE 221 /* Closing Connection */
+#define CODE_OK 250 /* ok */
+#define CODE_TEMPORARILY_UNAVAILABLE 420 /* server unavailable */
+#define CODE_SYNTAX_ERROR 500 /* syntax, command not recognized */
+#define CODE_DENIED 521
+#define CODE_DICTMASK_NOTSET 522
+
+unsigned int STARDICT::Cmd::next_seq = 1;
+
+sigc::signal<void, const char *> StarDictClient::on_error_;
+sigc::signal<void, const struct STARDICT::LookupResponse *, unsigned int> StarDictClient::on_lookup_end_;
+sigc::signal<void, const struct STARDICT::LookupResponse *, unsigned int> StarDictClient::on_floatwin_lookup_end_;
+sigc::signal<void, const char *> StarDictClient::on_register_end_;
+sigc::signal<void, const char *> StarDictClient::on_getdictmask_end_;
+sigc::signal<void, const char *> StarDictClient::on_dirinfo_end_;
+sigc::signal<void, const char *> StarDictClient::on_dictinfo_end_;
+sigc::signal<void, int> StarDictClient::on_maxdictcount_end_;
+sigc::signal<void, std::list<char *> *> StarDictClient::on_previous_end_;
+sigc::signal<void, std::list<char *> *> StarDictClient::on_next_end_;
+
+static void arg_escape(std::string &earg, const char *arg)
+{
+ earg.clear();
+ while (*arg) {
+ if (*arg=='\\') {
+ earg+="\\\\";
+ } else if (*arg==' ') {
+ earg+="\\ ";
+ } else if (*arg=='\n') {
+ earg+="\\n";
+ } else {
+ earg+=*arg;
+ }
+ arg++;
+ }
+}
+
+STARDICT::Cmd::Cmd(int cmd, ...)
+{
+ this->seq = this->next_seq;
+ this->next_seq++;
+ this->reading_status = 0;
+ this->command = cmd;
+ va_list ap;
+ va_start( ap, cmd );
+ switch (cmd) {
+ case CMD_CLIENT:
+ {
+ const char *client_name = va_arg( ap, const char * );
+ std::string earg1, earg2;
+ arg_escape(earg1, PROTOCOL_VERSION);
+ arg_escape(earg2, client_name);
+ this->data = g_strdup_printf("client %s %s\n", earg1.c_str(), earg2.c_str());
+ break;
+ }
+ case CMD_REGISTER:
+ {
+ const char *user = va_arg( ap, const char * );
+ const char *passwd = va_arg( ap, const char * );
+ const char *email = va_arg( ap, const char * );
+ std::string earg1, earg2, earg3;
+ arg_escape(earg1, user);
+ arg_escape(earg2, passwd);
+ arg_escape(earg3, email);
+ this->data = g_strdup_printf("register %s %s %s\n", earg1.c_str(), earg2.c_str(), earg3.c_str());
+ break;
+ }
+ /*case CMD_CHANGE_PASSWD:
+ {
+ const char *user = va_arg( ap, const char * );
+ const char *old_passwd = va_arg( ap, const char * );
+ const char *new_passwd = va_arg( ap, const char * );
+ std::string earg1, earg2, earg3;
+ arg_escape(earg1, user);
+ arg_escape(earg2, old_passwd);
+ arg_escape(earg3, new_passwd);
+ this->data = g_strdup_printf("change_password %s %s %s\n", earg1.c_str(), earg2.c_str(), earg3.c_str());
+ break;
+ }*/
+ case CMD_AUTH:
+ this->auth = new AuthInfo();
+ this->auth->user = va_arg( ap, const char * );
+ this->auth->passwd = va_arg( ap, const char * );
+ break;
+ case CMD_LOOKUP:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("lookup %s 30\n", earg.c_str());
+ this->lookup_response = NULL;
+ break;
+ }
+ case CMD_PREVIOUS:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("previous %s 15\n", earg.c_str());
+ this->wordlist_response = NULL;
+ break;
+ }
+ case CMD_NEXT:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("next %s 30\n", earg.c_str());
+ this->wordlist_response = NULL;
+ break;
+ }
+ /*case CMD_QUERY:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("query %s\n", earg.c_str());
+ this->lookup_response = NULL;
+ break;
+ }*/
+ case CMD_SELECT_QUERY:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("selectquery %s\n", earg.c_str());
+ this->lookup_response = NULL;
+ break;
+ }
+ case CMD_SMART_QUERY:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ int BeginPos = va_arg( ap, int );
+ this->data = g_strdup_printf("smartquery %s %d\n", earg.c_str(), BeginPos);
+ this->lookup_response = NULL;
+ break;
+ }
+ case CMD_DEFINE:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("define %s\n", earg.c_str());
+ this->lookup_response = NULL;
+ break;
+ }
+ case CMD_SET_DICT_MASK:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("setdictmask %s\n", earg.c_str());
+ break;
+ }
+ case CMD_GET_DICT_MASK:
+ this->data = g_strdup("getdictmask\n");
+ break;
+ /*case CMD_SET_COLLATE_FUNC:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("setcollatefunc %s\n", earg.c_str());
+ break;
+ }
+ case CMD_GET_COLLATE_FUNC:
+ this->data = g_strdup("getcollatefunc\n");
+ break;
+ case CMD_SET_LANGUAGE:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("setlanguage %s\n", earg.c_str());
+ break;
+ }
+ case CMD_GET_LANGUAGE:
+ this->data = g_strdup("getlanguage\n");
+ break;
+ case CMD_SET_EMAIL:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("setemail %s\n", earg.c_str());
+ break;
+ }
+ case CMD_GET_EMAIL:
+ this->data = g_strdup("getemail\n");
+ break;
+ case CMD_GET_USER_LEVEL:
+ this->data = g_strdup("getuserlevel\n");
+ break;*/
+ case CMD_MAX_DICT_COUNT:
+ this->data = g_strdup("maxdictcount\n");
+ break;
+ case CMD_DIR_INFO:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("dirinfo %s\n", earg.c_str());
+ break;
+ }
+ case CMD_DICT_INFO:
+ {
+ std::string earg;
+ arg_escape(earg, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("dictinfo %s\n", earg.c_str());
+ break;
+ }
+ /*case CMD_USER_LEVEL:
+ {
+ std::string earg1, earg2, earg3;
+ arg_escape(earg1, va_arg( ap, const char * ));
+ arg_escape(earg2, va_arg( ap, const char * ));
+ arg_escape(earg3, va_arg( ap, const char * ));
+ this->data = g_strdup_printf("userlevel %s %s %s\n", earg1.c_str(), earg2.c_str(), earg3.c_str());
+ break;
+ }*/
+ case CMD_QUIT:
+ this->data = g_strdup("quit\n");
+ break;
+ }
+ va_end( ap );
+}
+
+STARDICT::LookupResponse::DictResponse::DictResponse()
+{
+ oword = NULL;
+}
+
+STARDICT::LookupResponse::DictResponse::~DictResponse()
+{
+ g_free(oword);
+ for (std::list<DictResult *>::iterator i = dict_result_list.begin(); i != dict_result_list.end(); ++i) {
+ delete *i;
+ }
+}
+
+STARDICT::LookupResponse::DictResponse::DictResult::DictResult()
+{
+ bookname = NULL;
+}
+
+STARDICT::LookupResponse::DictResponse::DictResult::~DictResult()
+{
+ g_free(bookname);
+ for (std::list<WordResult *>::iterator i = word_result_list.begin(); i != word_result_list.end(); ++i) {
+ delete *i;
+ }
+}
+
+STARDICT::LookupResponse::DictResponse::DictResult::WordResult::WordResult()
+{
+ word = NULL;
+}
+
+STARDICT::LookupResponse::DictResponse::DictResult::WordResult::~WordResult()
+{
+ g_free(word);
+ for (std::list<char *>::iterator i = datalist.begin(); i != datalist.end(); ++i) {
+ g_free(*i);
+ }
+}
+
+STARDICT::LookupResponse::~LookupResponse()
+{
+ if (listtype == ListType_List) {
+ for (std::list<char *>::iterator i = wordlist->begin(); i != wordlist->end(); ++i) {
+ g_free(*i);
+ }
+ delete wordlist;
+ } else if (listtype == ListType_Tree) {
+ for (std::list<WordTreeElement *>::iterator i = wordtree->begin(); i != wordtree->end(); ++i) {
+ g_free((*i)->bookname);
+ for (std::list<char *>::iterator j = (*i)->wordlist.begin(); j != (*i)->wordlist.end(); ++j) {
+ g_free(*j);
+ }
+ delete *i;
+ }
+ delete wordtree;
+ }
+}
+
+STARDICT::Cmd::~Cmd()
+{
+ if (this->command == CMD_AUTH) {
+ delete this->auth;
+ } else {
+ g_free(this->data);
+ }
+ if (this->command == CMD_LOOKUP || this->command == CMD_DEFINE || this->command == CMD_SELECT_QUERY || this->command == CMD_SMART_QUERY) {
+ delete this->lookup_response;
+ } else if (this->command == CMD_PREVIOUS || this->command == CMD_NEXT) {
+ if (this->wordlist_response) {
+ for (std::list<char *>::iterator i = this->wordlist_response->begin(); i != this->wordlist_response->end(); ++i) {
+ g_free(*i);
+ }
+ delete this->wordlist_response;
+ }
+ }
+}
+
+StarDictCache::StarDictCache()
+{
+ str_pool.resize(str_pool_size);
+ for (size_t i = 0; i< str_pool_size; i++) {
+ str_pool[i] = NULL;
+ }
+ cur_str_pool_pos = 0;
+
+ lookup_response_pool.resize(lookup_response_pool_size);
+ for (size_t i = 0; i< lookup_response_pool_size; i++) {
+ lookup_response_pool[i] = NULL;
+ }
+ cur_lookup_response_pool_pos = 0;
+}
+
+StarDictCache::~StarDictCache()
+{
+ clean_all_cache();
+}
+
+void StarDictCache::clean_all_cache()
+{
+ for (std::vector<StrElement *>::iterator i = str_pool.begin(); i != str_pool.end(); ++i) {
+ if (*i) {
+ g_free((*i)->data);
+ delete *i;
+ *i = NULL;
+ }
+ }
+ clean_cache_lookup_response();
+}
+
+void StarDictCache::clean_cache_lookup_response()
+{
+ for (std::vector<LookupResponseElement *>::iterator i = lookup_response_pool.begin(); i != lookup_response_pool.end(); ++i) {
+ if (*i) {
+ delete ((*i)->lookup_response);
+ delete *i;
+ *i = NULL;
+ }
+ }
+}
+
+char *StarDictCache::get_cache_str(const char *key_str)
+{
+ for (std::vector<StrElement *>::iterator i = str_pool.begin(); i != str_pool.end(); ++i) {
+ if (*i) {
+ if ((*i)->key == key_str)
+ return (*i)->data;
+ }
+ }
+ return NULL;
+}
+
+STARDICT::LookupResponse *StarDictCache::get_cache_lookup_response(const char *key_str)
+{
+ for (std::vector<LookupResponseElement *>::iterator i = lookup_response_pool.begin(); i != lookup_response_pool.end(); ++i) {
+ if (*i) {
+ if ((*i)->key == key_str)
+ return (*i)->lookup_response;
+ }
+ }
+ return NULL;
+}
+
+void StarDictCache::clean_cache_str(const char *key_str)
+{
+ for (std::vector<StrElement *>::iterator i = str_pool.begin(); i != str_pool.end(); ++i) {
+ if (*i) {
+ if ((*i)->key == key_str) {
+ g_free((*i)->data);
+ delete *i;
+ *i = NULL;
+ //return;
+ }
+ }
+ }
+}
+
+void StarDictCache::save_cache_str(const char *key_str, char *data)
+{
+ if (str_pool[cur_str_pool_pos]) {
+ g_free(str_pool[cur_str_pool_pos]->data);
+ delete str_pool[cur_str_pool_pos];
+ }
+ str_pool[cur_str_pool_pos] = new StrElement();
+ str_pool[cur_str_pool_pos]->key = key_str;
+ str_pool[cur_str_pool_pos]->data = data;
+ cur_str_pool_pos++;
+ if (cur_str_pool_pos == str_pool_size)
+ cur_str_pool_pos = 0;
+}
+
+void StarDictCache::save_cache_lookup_response(const char *key_str, STARDICT::LookupResponse *lookup_response)
+{
+ if (lookup_response_pool[cur_lookup_response_pool_pos]) {
+ delete lookup_response_pool[cur_lookup_response_pool_pos]->lookup_response;
+ delete lookup_response_pool[cur_lookup_response_pool_pos];
+ }
+ lookup_response_pool[cur_lookup_response_pool_pos] = new LookupResponseElement();
+ lookup_response_pool[cur_lookup_response_pool_pos]->key = key_str;
+ lookup_response_pool[cur_lookup_response_pool_pos]->lookup_response = lookup_response;
+ cur_lookup_response_pool_pos++;
+ if (cur_lookup_response_pool_pos == lookup_response_pool_size)
+ cur_lookup_response_pool_pos = 0;
+}
+
+StarDictClient::StarDictClient()
+{
+ sd_ = -1;
+ channel_ = NULL;
+ in_source_id_ = 0;
+ out_source_id_ = 0;
+ is_connected_ = false;
+}
+
+StarDictClient::~StarDictClient()
+{
+ disconnect();
+}
+
+void StarDictClient::set_server(const char *host, int port)
+{
+ if (host_ != host || port_ != port) {
+ host_ = host;
+ port_ = port;
+ host_resolved = false;
+ clean_all_cache();
+ }
+}
+
+void StarDictClient::set_auth(const char *user, const char *md5passwd)
+{
+ if (user_ != user || md5passwd_ != md5passwd) {
+ user_ = user;
+ md5passwd_ = md5passwd;
+ clean_all_cache();
+ }
+}
+
+bool StarDictClient::try_cache(STARDICT::Cmd *c)
+{
+ if (c->command == STARDICT::CMD_LOOKUP || c->command == STARDICT::CMD_DEFINE || c->command == STARDICT::CMD_SELECT_QUERY || c->command == STARDICT::CMD_SMART_QUERY) {
+ STARDICT::LookupResponse *res = get_cache_lookup_response(c->data);
+ if (res) {
+ if (c->command == STARDICT::CMD_LOOKUP || c->command == STARDICT::CMD_DEFINE)
+ on_lookup_end_.emit(res, 0);
+ else if (c->command == STARDICT::CMD_SELECT_QUERY || c->command == STARDICT::CMD_SMART_QUERY)
+ on_floatwin_lookup_end_.emit(res, 0);
+ delete c;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ if (c->command == STARDICT::CMD_PREVIOUS || c->command == STARDICT::CMD_NEXT) {
+ // Not implemented yet.
+ return false;
+ }
+ char *data = get_cache_str(c->data);
+ if (data) {
+ switch (c->command) {
+ case STARDICT::CMD_DIR_INFO:
+ on_dirinfo_end_.emit(data);
+ break;
+ case STARDICT::CMD_DICT_INFO:
+ on_dictinfo_end_.emit(data);
+ break;
+ case STARDICT::CMD_GET_DICT_MASK:
+ on_getdictmask_end_.emit(data);
+ break;
+ case STARDICT::CMD_MAX_DICT_COUNT:
+ on_maxdictcount_end_.emit(atoi(data));
+ break;
+ }
+ delete c;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void StarDictClient::send_commands(int num, ...)
+{
+ STARDICT::Cmd *c;
+ if (!is_connected_) {
+#ifdef _WIN32
+ c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Windows");
+#else
+ c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Linux");
+#endif
+ cmdlist.push_back(c);
+ if (!user_.empty() && !md5passwd_.empty()) {
+ c = new STARDICT::Cmd(STARDICT::CMD_AUTH, user_.c_str(), md5passwd_.c_str());
+ cmdlist.push_back(c);
+ }
+ }
+ va_list ap;
+ va_start( ap, num);
+ for (int i = 0; i< num; i++) {
+ c = va_arg( ap, STARDICT::Cmd *);
+ cmdlist.push_back(c);
+ }
+ va_end( ap );
+ if (!is_connected_) {
+ waiting_banner_ = true;
+ connect();
+ }
+}
+
+void StarDictClient::try_cache_or_send_commands(int num, ...)
+{
+ STARDICT::Cmd *c;
+ std::list<STARDICT::Cmd *> send_cmdlist;
+ va_list ap;
+ va_start( ap, num);
+ for (int i = 0; i< num; i++) {
+ c = va_arg( ap, STARDICT::Cmd *);
+ if (!try_cache(c)) {
+ send_cmdlist.push_back(c);
+ }
+ }
+ va_end( ap );
+ if (send_cmdlist.empty())
+ return;
+
+ if (!is_connected_) {
+#ifdef _WIN32
+ c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Windows");
+#else
+ c = new STARDICT::Cmd(STARDICT::CMD_CLIENT, "StarDict Linux");
+#endif
+ cmdlist.push_back(c);
+ if (!user_.empty() && !md5passwd_.empty()) {
+ c = new STARDICT::Cmd(STARDICT::CMD_AUTH, user_.c_str(), md5passwd_.c_str());
+ cmdlist.push_back(c);
+ }
+ }
+ for (std::list<STARDICT::Cmd *>::iterator i = send_cmdlist.begin(); i!= send_cmdlist.end(); ++i) {
+ cmdlist.push_back(*i);
+ }
+ if (!is_connected_) {
+ waiting_banner_ = true;
+ connect();
+ }
+}
+
+void StarDictClient::write_str(const char *str, GError **err)
+{
+ int len = strlen(str);
+ int left_byte = len;
+ GIOStatus res;
+ gsize bytes_written;
+ while (left_byte) {
+ res = g_io_channel_write_chars(channel_, str+(len - left_byte), left_byte, &bytes_written, err);
+ if (res == G_IO_STATUS_ERROR) {
+ disconnect();
+ return;
+ }
+ left_byte -= bytes_written;
+ }
+ res = g_io_channel_flush(channel_, err);
+ if (res == G_IO_STATUS_ERROR) {
+ disconnect();
+ }
+ if (out_source_id_ == 0)
+ out_source_id_ = g_io_add_watch(channel_, GIOCondition(G_IO_OUT), on_io_out_event, this);
+}
+
+void StarDictClient::request_command()
+{
+ reading_type_ = READ_LINE;
+ if (cmdlist.empty()) {
+ cmdlist.push_back(new STARDICT::Cmd(STARDICT::CMD_QUIT));
+ }
+ STARDICT::Cmd *c = cmdlist.front();
+ switch (c->command) {
+ case STARDICT::CMD_AUTH:
+ {
+ struct MD5Context ctx;
+ unsigned char digest[16];
+ char hex[33];
+ int i;
+ MD5Init(&ctx);
+ MD5Update(&ctx, (const unsigned char*)cmd_reply.daemonStamp.c_str(), cmd_reply.daemonStamp.length());
+ MD5Update(&ctx, (const unsigned char*)(c->auth->passwd.c_str()), c->auth->passwd.length());
+ MD5Final(digest, &ctx );
+ for (i = 0; i < 16; i++)
+ sprintf( hex+2*i, "%02x", digest[i] );
+ hex[32] = '\0';
+ std::string earg1, earg2;
+ arg_escape(earg1, c->auth->user.c_str());
+ arg_escape(earg2, hex);
+ char *data = g_strdup_printf("auth %s %s\n", earg1.c_str(), earg2.c_str());
+ GError *err = NULL;
+ write_str(data, &err);
+ g_free(data);
+ if (err) {
+ on_error_.emit(err->message);
+ g_error_free(err);
+ return;
+ }
+ break;
+ }
+ default:
+ {
+ GError *err = NULL;
+ write_str(c->data, &err);
+ if (err) {
+ on_error_.emit(err->message);
+ g_error_free(err);
+ return;
+ }
+ break;
+ }
+ }
+ return;
+}
+
+void StarDictClient::clean_command()
+{
+ for (std::list<STARDICT::Cmd *>::iterator i=cmdlist.begin(); i!=cmdlist.end(); ++i) {
+ delete *i;
+ }
+ cmdlist.clear();
+}
+
+void StarDictClient::connect()
+{
+ if (host_resolved) {
+ on_resolved(this, true, sa);
+ } else {
+ Socket::resolve(host_, this, on_resolved);
+ }
+}
+
+void StarDictClient::on_resolved(gpointer data, bool resolved, in_addr_t sa_)
+{
+ StarDictClient *oStarDictClient = (StarDictClient *)data;
+ if (!resolved) {
+ static bool showed_once = false;
+ if (!showed_once) {
+ showed_once = true;
+ gchar *mes = g_strdup_printf("Can not reslove %s: %s\n",
+ oStarDictClient->host_.c_str(), Socket::get_error_msg().c_str());
+ on_error_.emit(mes);
+ g_free(mes);
+ }
+ return;
+ }
+
+ if (oStarDictClient->host_resolved == false) {
+ oStarDictClient->sa = sa_;
+ oStarDictClient->host_resolved = true;
+ }
+
+ oStarDictClient->sd_ = Socket::socket();
+
+ if (oStarDictClient->sd_ == -1) {
+ std::string str = "Can not create socket: " + Socket::get_error_msg();
+ on_error_.emit(str.c_str());
+ return;
+ }
+ Socket::connect(oStarDictClient->sd_, sa_, oStarDictClient->port_, oStarDictClient, on_connected);
+}
+
+void StarDictClient::on_connected(gpointer data, bool succeeded)
+{
+ StarDictClient *oStarDictClient = (StarDictClient *)data;
+ if (!succeeded) {
+ static bool showed_once = false;
+ if (!showed_once) {
+ showed_once = true;
+ gchar *mes = g_strdup_printf("Can not connect to %s: %s\n",
+ oStarDictClient->host_.c_str(), Socket::get_error_msg().c_str());
+ on_error_.emit(mes);
+ g_free(mes);
+ }
+ return;
+ }
+#ifdef _WIN32
+ oStarDictClient->channel_ = g_io_channel_win32_new_socket(oStarDictClient->sd_);
+#else
+ oStarDictClient->channel_ = g_io_channel_unix_new(oStarDictClient->sd_);
+#endif
+
+ g_io_channel_set_encoding(oStarDictClient->channel_, NULL, NULL);
+
+ /* make sure that the channel is non-blocking */
+ int flags = g_io_channel_get_flags(oStarDictClient->channel_);
+ flags |= G_IO_FLAG_NONBLOCK;
+ GError *err = NULL;
+ g_io_channel_set_flags(oStarDictClient->channel_, GIOFlags(flags), &err);
+ if (err) {
+ g_io_channel_unref(oStarDictClient->channel_);
+ oStarDictClient->channel_ = NULL;
+ gchar *str = g_strdup_printf("Unable to set the channel as non-blocking: %s", err->message);
+ on_error_.emit(str);
+ g_free(str);
+ g_error_free(err);
+ return;
+ }
+
+ oStarDictClient->is_connected_ = true;
+ oStarDictClient->waiting_banner_ = true;
+ oStarDictClient->reading_type_ = READ_LINE;
+ oStarDictClient->in_source_id_ = g_io_add_watch(oStarDictClient->channel_, GIOCondition(G_IO_IN | G_IO_ERR), on_io_in_event, oStarDictClient);
+}
+
+void StarDictClient::disconnect()
+{
+ clean_command();
+ if (in_source_id_) {
+ g_source_remove(in_source_id_);
+ in_source_id_ = 0;
+ }
+ if (out_source_id_) {
+ g_source_remove(out_source_id_);
+ out_source_id_ = 0;
+ }
+
+ if (channel_) {
+ g_io_channel_shutdown(channel_, TRUE, NULL);
+ g_io_channel_unref(channel_);
+ channel_ = NULL;
+ }
+ if (sd_ != -1) {
+ Socket::close(sd_);
+ sd_ = -1;
+ }
+ is_connected_ = false;
+}
+
+gboolean StarDictClient::on_io_out_event(GIOChannel *ch, GIOCondition cond,
+ gpointer user_data)
+{
+ StarDictClient *stardict_client = static_cast<StarDictClient *>(user_data);
+ GError *err = NULL;
+ GIOStatus res = g_io_channel_flush(stardict_client->channel_, &err);
+ if (res == G_IO_STATUS_AGAIN) {
+ return TRUE;
+ } else if (err) {
+ on_error_.emit(err->message);
+ g_error_free(err);
+ }
+ stardict_client->out_source_id_ = 0;
+ return FALSE;
+}
+
+gboolean StarDictClient::on_io_in_event(GIOChannel *ch, GIOCondition cond,
+ gpointer user_data)
+{
+ StarDictClient *stardict_client = static_cast<StarDictClient *>(user_data);
+
+ if (!stardict_client->channel_) {
+ //g_warning("No channel available\n");
+ return FALSE;
+ }
+ if (cond & G_IO_ERR) {
+ /*gchar *mes =
+ g_strdup_printf("Connection failed to the dictionary server at %s:%d",
+ stardict_client->host_.c_str(), stardict_client->port_);
+ on_error_.emit(mes);
+ g_free(mes);*/
+ stardict_client->disconnect();
+ return FALSE;
+ }
+ GError *err = NULL;
+ gsize term, len;
+ gchar *line;
+ GIOStatus res;
+
+ for (;;) {
+ if (!stardict_client->channel_)
+ break;
+ bool result;
+ if (stardict_client->reading_type_ == READ_SIZE) {
+ gsize bytes_read;
+ 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);
+ if (res == G_IO_STATUS_ERROR || res == G_IO_STATUS_EOF) {
+ if (err) {
+ gchar *str = g_strdup_printf("Error while reading reply from server: %s", err->message);
+ on_error_.emit(str);
+ g_free(str);
+ g_error_free(err);
+ }
+ stardict_client->disconnect();
+
+ return FALSE;
+ }
+ stardict_client->size_left -= bytes_read;
+ if (stardict_client->size_left == 0)
+ result = stardict_client->parse(stardict_client->size_data);
+ else
+ break;
+ } else {
+ if (stardict_client->reading_type_ == READ_LINE)
+ g_io_channel_set_line_term(stardict_client->channel_, "\n", 1);
+ else if (stardict_client->reading_type_ == READ_STRING)
+ g_io_channel_set_line_term(stardict_client->channel_, "", 1);
+
+ res = g_io_channel_read_line(stardict_client->channel_, &line,
+ &len, &term, &err);
+ if (res == G_IO_STATUS_ERROR || res == G_IO_STATUS_EOF) {
+ if (err) {
+ gchar *str = g_strdup_printf("Error while reading reply from server: %s", err->message);
+ on_error_.emit(str);
+ g_free(str);
+ g_error_free(err);
+ }
+ stardict_client->disconnect();
+
+ return FALSE;
+ }
+
+ if (!len)
+ break;
+
+ //truncate the line terminator before parsing
+ line[term] = '\0';
+ result = stardict_client->parse(line);
+ }
+ if (!result) {
+ stardict_client->disconnect();
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+int StarDictClient::parse_banner(gchar *line)
+{
+ int status;
+ status = atoi(line);
+ if (status != CODE_HELLO) {
+ if (status == CODE_TEMPORARILY_UNAVAILABLE) {
+ printf("Server temporarily unavailable!\n");
+ } else {
+ printf("Unexpected status code %d\n", status);
+ }
+ return 0;
+ }
+ char *p;
+ p = strrchr(line, ' ');
+ if (p) {
+ p++;
+ cmd_reply.daemonStamp = p;
+ }
+ return 1;
+}
+
+int StarDictClient::parse_command_client(gchar *line)
+{
+ int status;
+ status = atoi(line);
+ if (status != CODE_OK) {
+ gchar *str = g_strdup_printf("Client denied: %s", line);
+ on_error_.emit(str);
+ g_free(str);
+ return 0;
+ }
+ return 1;
+}
+
+int StarDictClient::parse_command_auth(gchar *line)
+{
+ int status;
+ status = atoi(line);
+ if (status != CODE_OK) {
+ gchar *str = g_strdup_printf(_("Authentication denied: %s"), line);
+ on_error_.emit(str);
+ g_free(str);
+ return 0;
+ }
+ return 1;
+}
+
+int StarDictClient::parse_command_register(gchar *line)
+{
+ int status;
+ status = atoi(line);
+ if (status != CODE_OK) {
+ gchar *str = g_strdup_printf(_("Register failed: %s"), line);
+ on_error_.emit(str);
+ g_free(str);
+ return 0;
+ }
+ on_register_end_.emit(_("Register success!"));
+ return 1;
+}
+
+int StarDictClient::parse_command_quit(gchar *line)
+{
+ int status;
+ status = atoi(line);
+ if (status != CODE_GOODBYE) {
+ }
+ return 0;
+}
+
+int StarDictClient::parse_command_setdictmask(gchar *line)
+{
+ int status;
+ status = atoi(line);
+ if (status != CODE_OK) {
+ gchar *str = g_strdup_printf("Set Dict Mask failed: %s", line);
+ on_error_.emit(str);
+ g_free(str);
+ return 0;
+ }
+ clean_cache_str("getdictmask\n");
+ clean_cache_lookup_response();
+ return 1;
+}
+
+int StarDictClient::parse_command_getdictmask(STARDICT::Cmd* cmd, gchar *buf)
+{
+ if (cmd->reading_status == 0) {
+ int status;
+ status = atoi(buf);
+ if (status != CODE_OK) {
+ g_free(buf);
+ on_error_.emit(_("You haven't setup the account. Please open the \"Net Dict\" page in the Preferences dialog and register an account first."));
+ return 0;
+ }
+ g_free(buf);
+ cmd->reading_status = 1;
+ reading_type_ = READ_STRING;
+ } else if (cmd->reading_status == 1) {
+ on_getdictmask_end_.emit(buf);
+ save_cache_str(cmd->data, buf);
+ return 1;
+ }
+ return 2;
+}
+
+int StarDictClient::parse_command_dirinfo(STARDICT::Cmd* cmd, gchar *buf)
+{
+ if (cmd->reading_status == 0) {
+ int status;
+ status = atoi(buf);
+ if (status != CODE_OK) {
+ gchar *str = g_strdup_printf("Get dir info failed: %s", buf);
+ g_free(buf);
+ on_error_.emit(str);
+ g_free(str);
+ return 0;
+ }
+ g_free(buf);
+ cmd->reading_status = 1;
+ reading_type_ = READ_STRING;
+ } else if (cmd->reading_status == 1) {
+ on_dirinfo_end_.emit(buf);
+ save_cache_str(cmd->data, buf);
+ return 1;
+ }
+ return 2;
+}
+
+int StarDictClient::parse_command_dictinfo(STARDICT::Cmd* cmd, gchar *buf)
+{
+ if (cmd->reading_status == 0) {
+ int status;
+ status = atoi(buf);
+ if (status != CODE_OK) {
+ gchar *str = g_strdup_printf("Get dict info failed: %s", buf);
+ g_free(buf);
+ on_error_.emit(str);
+ g_free(str);
+ return 0;
+ }
+ g_free(buf);
+ cmd->reading_status = 1;
+ reading_type_ = READ_STRING;
+ } else if (cmd->reading_status == 1) {
+ on_dictinfo_end_.emit(buf);
+ save_cache_str(cmd->data, buf);
+ return 1;
+ }
+ return 2;
+}
+
+int StarDictClient::parse_command_maxdictcount(STARDICT::Cmd* cmd, gchar *buf)
+{
+ if (cmd->reading_status == 0) {
+ int status;
+ status = atoi(buf);
+ if (status != CODE_OK) {
+ gchar *str = g_strdup_printf("Get max dict count failed: %s", buf);
+ g_free(buf);
+ on_error_.emit(str);
+ g_free(str);
+ return 0;
+ }
+ g_free(buf);
+ cmd->reading_status = 1;
+ reading_type_ = READ_STRING;
+ } else if (cmd->reading_status == 1) {
+ on_maxdictcount_end_.emit(atoi(buf));
+ save_cache_str(cmd->data, buf);
+ return 1;
+ }
+ return 2;
+}
+
+int StarDictClient::parse_wordlist(STARDICT::Cmd* cmd, gchar *buf)
+{
+ if (cmd->reading_status == 0) { // Read code.
+ int status;
+ status = atoi(buf);
+ g_free(buf);
+ if (status != CODE_OK) {
+ return 0;
+ }
+ cmd->wordlist_response = new std::list<char *>;
+ cmd->reading_status = 1;
+ reading_type_ = READ_STRING;
+ } else if (cmd->reading_status == 1) {
+ if (*buf == '\0') {
+ g_free(buf);
+ if (cmd->command == STARDICT::CMD_PREVIOUS) {
+ on_previous_end_.emit(cmd->wordlist_response);
+ return 1;
+ } else {
+ on_next_end_.emit(cmd->wordlist_response);
+ return 1;
+ }
+ } else {
+ cmd->wordlist_response->push_back(buf);
+ }
+ }
+ return 2;
+}
+
+int StarDictClient::parse_dict_result(STARDICT::Cmd* cmd, gchar *buf)
+{
+ if (cmd->reading_status == 0) { // Read code.
+ int status;
+ status = atoi(buf);
+ g_free(buf);
+ if (status != CODE_OK) {
+ if (status == CODE_DICTMASK_NOTSET) {
+ on_error_.emit(_("You haven't chosen any dictionaries, please choose some by clicking \"Manage Dict\"->\"Network dictionaries\"->\"Add\"."));
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ cmd->lookup_response = new STARDICT::LookupResponse();
+ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_None;
+ cmd->reading_status = 1;
+ reading_type_ = READ_STRING;
+ } else if (cmd->reading_status == 1) { // Read original word.
+ cmd->lookup_response->dict_response.oword = buf;
+ cmd->reading_status = 2;
+ } else if (cmd->reading_status == 2) { // Read book name.
+ if (*buf == '\0') {
+ g_free(buf);
+ if (cmd->command == STARDICT::CMD_DEFINE) {
+ on_lookup_end_.emit(cmd->lookup_response, cmd->seq);
+ save_cache_lookup_response(cmd->data, cmd->lookup_response);
+ cmd->lookup_response = NULL;
+ return 1;
+ } else if ( cmd->command == STARDICT::CMD_SELECT_QUERY || cmd->command == STARDICT::CMD_SMART_QUERY) {
+ on_floatwin_lookup_end_.emit(cmd->lookup_response, cmd->seq);
+ save_cache_lookup_response(cmd->data, cmd->lookup_response);
+ cmd->lookup_response = NULL;
+ return 1;
+ }
+ cmd->reading_status = 6;
+ reading_type_ = READ_STRING;
+ } else {
+ struct STARDICT::LookupResponse::DictResponse::DictResult *dict_result = new STARDICT::LookupResponse::DictResponse::DictResult();
+ dict_result->bookname = buf;
+ cmd->lookup_response->dict_response.dict_result_list.push_back(dict_result);
+ cmd->reading_status = 3;
+ }
+ } else if (cmd->reading_status == 3) { // Read word.
+ if (*buf == '\0') {
+ g_free(buf);
+ cmd->reading_status = 2;
+ } else {
+ struct STARDICT::LookupResponse::DictResponse::DictResult::WordResult *word_result = new STARDICT::LookupResponse::DictResponse::DictResult::WordResult();
+ word_result->word = buf;
+ cmd->lookup_response->dict_response.dict_result_list.back()->word_result_list.push_back(word_result);;
+ cmd->reading_status = 4;
+ reading_type_ = READ_SIZE;
+ size_data = (char *)g_malloc(sizeof(guint32));
+ size_count = size_left = sizeof(guint32);
+ }
+ } else if (cmd->reading_status == 4) {
+ guint32 datasize = g_ntohl(get_uint32(buf));
+ memcpy(buf, &datasize, sizeof(guint32));
+ if (datasize == 0) {
+ g_free(buf);
+ cmd->reading_status = 3;
+ reading_type_ = READ_STRING;
+ } else {
+ cmd->reading_status = 5;
+ size_data = (char *)g_realloc(buf, datasize + sizeof(guint32));
+ size_count = datasize + sizeof(guint32);
+ size_left = datasize;
+ }
+ } else if (cmd->reading_status == 5) {
+ cmd->lookup_response->dict_response.dict_result_list.back()->word_result_list.back()->datalist.push_back(buf);
+ cmd->reading_status = 4;
+ size_data = (char *)g_malloc(sizeof(guint32));
+ size_count = size_left = sizeof(guint32);
+ } else if (cmd->reading_status == 6) {
+ if (strcmp(buf, "d") == 0) {
+ cmd->reading_status = 8;
+ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Tree;
+ cmd->lookup_response->wordtree = new std::list<STARDICT::LookupResponse::WordTreeElement *>;
+ } else {
+ cmd->reading_status = 7;
+ if (strcmp(buf, "r") == 0)
+ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Rule_List;
+ else if (strcmp(buf, "g") == 0)
+ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Regex_List;
+ else if (strcmp(buf, "f") == 0)
+ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_Fuzzy_List;
+ else
+ cmd->lookup_response->listtype = STARDICT::LookupResponse::ListType_List;
+ cmd->lookup_response->wordlist = new std::list<char *>;
+ }
+ g_free(buf);
+ } else if (cmd->reading_status == 7) {
+ if (*buf == '\0') {
+ g_free(buf);
+ on_lookup_end_.emit(cmd->lookup_response, cmd->seq);
+ save_cache_lookup_response(cmd->data, cmd->lookup_response);
+ cmd->lookup_response = NULL;
+ return 1;
+ } else {
+ cmd->lookup_response->wordlist->push_back(buf);
+ }
+ } else if (cmd->reading_status == 8) {
+ if (*buf == '\0') {
+ g_free(buf);
+ on_lookup_end_.emit(cmd->lookup_response, cmd->seq);
+ save_cache_lookup_response(cmd->data, cmd->lookup_response);
+ cmd->lookup_response = NULL;
+ return 1;
+ } else {
+ STARDICT::LookupResponse::WordTreeElement *element = new STARDICT::LookupResponse::WordTreeElement();
+ element->bookname = buf;
+ cmd->lookup_response->wordtree->push_back(element);
+ cmd->reading_status = 9;
+ }
+ } else if (cmd->reading_status == 9) {
+ if (*buf == '\0') {
+ g_free(buf);
+ cmd->reading_status = 8;
+ } else {
+ cmd->lookup_response->wordtree->back()->wordlist.push_back(buf);
+ }
+ }
+ return 2;
+}
+
+bool StarDictClient::parse(gchar *line)
+{
+ int result;
+ if (waiting_banner_) {
+ waiting_banner_ = false;
+ result = parse_banner(line);
+ g_free(line);
+ if (!result)
+ return false;
+ request_command();
+ return true;
+ }
+ STARDICT::Cmd* cmd = cmdlist.front();
+ switch (cmd->command) {
+ case STARDICT::CMD_CLIENT:
+ result = parse_command_client(line);
+ g_free(line);
+ break;
+ case STARDICT::CMD_AUTH:
+ result = parse_command_auth(line);
+ g_free(line);
+ break;
+ case STARDICT::CMD_REGISTER:
+ result = parse_command_register(line);
+ g_free(line);
+ break;
+ case STARDICT::CMD_GET_DICT_MASK:
+ result = parse_command_getdictmask(cmd, line);
+ break;
+ case STARDICT::CMD_SET_DICT_MASK:
+ result = parse_command_setdictmask(line);
+ break;
+ case STARDICT::CMD_DIR_INFO:
+ result = parse_command_dirinfo(cmd, line);
+ break;
+ case STARDICT::CMD_DICT_INFO:
+ result = parse_command_dictinfo(cmd, line);
+ break;
+ case STARDICT::CMD_MAX_DICT_COUNT:
+ result = parse_command_maxdictcount(cmd, line);
+ break;
+ case STARDICT::CMD_DEFINE:
+ case STARDICT::CMD_LOOKUP:
+ case STARDICT::CMD_SELECT_QUERY:
+ case STARDICT::CMD_SMART_QUERY:
+ result = parse_dict_result(cmd, line);
+ break;
+ case STARDICT::CMD_PREVIOUS:
+ case STARDICT::CMD_NEXT:
+ result = parse_wordlist(cmd, line);
+ break;
+ case STARDICT::CMD_QUIT:
+ result = parse_command_quit(line);
+ g_free(line);
+ break;
+ default:
+ result = 0;
+ g_free(line);
+ break;
+ }
+ if (result == 0)
+ return false;
+ if (result == 1) {
+ delete cmd;
+ cmdlist.pop_front();
+ if (cmdlist.empty()) {
+ cmdlist.push_back(new STARDICT::Cmd(STARDICT::CMD_QUIT));
+ }
+ request_command();
+ }
+ return true;
+}