* Copyright (C) 2006 Hu Zheng <huzheng001@gmail.com>
* Copyright 2011 kubtek <kubtek@mail.com>
*
* This file is part of StarDict.
*
* StarDict 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.
*
* StarDict 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with StarDict. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <glib.h>
#include <glib/gi18n.h>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include "sockets.h"
#include "md5.h"
#include "rsa.h"
#include "utils.h"
#include "stardict_client.h"
#define PROTOCOL_VERSION "0.4"
#define CODE_HELLO 220
#define CODE_GOODBYE 221
#define CODE_OK 250
#define CODE_TEMPORARILY_UNAVAILABLE 420
#define CODE_SYNTAX_ERROR 500
#define CODE_DENIED 521
#define CODE_DICTMASK_NOTSET 522
#define CODE_USER_NOT_REGISTER 523
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_changepassword_end_;
sigc::signal<void, const char *> StarDictClient::on_getdictmask_end_;
sigc::signal<void, const char *> StarDictClient::on_getadinfo_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 * );
int *RSA_Public_Key_e = va_arg( ap, int * );
int *RSA_Public_Key_n = va_arg( ap, int * );
struct MD5Context ctx;
unsigned char digest[16];
char hex[33];
MD5Init(&ctx);
MD5Update(&ctx, (const unsigned char*)"LoveCher", 8);
MD5Update(&ctx, (const unsigned char*)passwd, strlen(passwd));
MD5Update(&ctx, (const unsigned char*)"StarDict", 8);
MD5Final(digest, &ctx);
for (int i = 0; i < 16; i++)
sprintf( hex+2*i, "%02x", digest[i] );
hex[32] = '\0';
std::string passwd1 = hex;
std::vector<unsigned char> v;
string_to_vector(passwd1, v);
std::vector<unsigned char> v2;
rsa_encrypt(v, v2, RSA_Public_Key_e, RSA_Public_Key_n);
std::string v2_passwd;
vector_to_string(v2, v2_passwd);
gchar *str_base64 = g_base64_encode((const guchar *)(v2_passwd.c_str()), v2_passwd.length());
std::string earg1, earg2, earg3;
arg_escape(earg1, user);
arg_escape(earg2, str_base64);
g_free(str_base64);
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 * );
int *RSA_Public_Key_e = va_arg( ap, int * );
int *RSA_Public_Key_n = va_arg( ap, int * );
std::string old_passwd1 = old_passwd;
std::vector<unsigned char> v;
string_to_vector(old_passwd1, v);
std::vector<unsigned char> v2;
rsa_encrypt(v, v2, RSA_Public_Key_e, RSA_Public_Key_n);
std::string v2_passwd;
vector_to_string(v2, v2_passwd);
gchar *str_base64 = g_base64_encode((const guchar *)(v2_passwd.c_str()), v2_passwd.length());
std::string new_passwd1 = new_passwd;
string_to_vector(new_passwd1, v);
rsa_encrypt(v, v2, RSA_Public_Key_e, RSA_Public_Key_n);
vector_to_string(v2, v2_passwd);
gchar *str_base64_2 = g_base64_encode((const guchar *)(v2_passwd.c_str()), v2_passwd.length());
std::string earg1, earg2, earg3;
arg_escape(earg1, user);
arg_escape(earg2, str_base64);
g_free(str_base64);
arg_escape(earg3, str_base64_2);
g_free(str_base64_2);
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->md5saltpasswd = 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;
}
{
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;
{
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_FROMTO:
this->data = g_strdup("fromto\n");
break;
case CMD_TMP_DICTMASK:
{
std::string earg;
arg_escape(earg, va_arg( ap, const char * ));
this->data = g_strdup_printf("tmpdictmask %s\n", earg.c_str());
break;
}
case CMD_DICTS_LIST:
{
std::string earg;
arg_escape(earg, va_arg( ap, const char * ));
this->data = g_strdup_printf("dictslist %s\n", earg.c_str());
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;
}
{
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_GET_ADINFO:
this->data = g_strdup("getadinfo\n");
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;
}
}
}
}
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 *md5saltpasswd)
{
if (user_ != user || md5saltpasswd_ != md5saltpasswd) {
user_ = user;
md5saltpasswd_ = md5saltpasswd;
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) {
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;
case STARDICT::CMD_GET_ADINFO:
on_getadinfo_end_.emit(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() && !md5saltpasswd_.empty()) {
c = new STARDICT::Cmd(STARDICT::CMD_AUTH, user_.c_str(), md5saltpasswd_.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() && !md5saltpasswd_.empty()) {
c = new STARDICT::Cmd(STARDICT::CMD_AUTH, user_.c_str(), md5saltpasswd_.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];
MD5Init(&ctx);
MD5Update(&ctx, (const unsigned char*)cmd_reply.daemonStamp.c_str(), cmd_reply.daemonStamp.length());
MD5Update(&ctx, (const unsigned char*)(c->auth->md5saltpasswd.c_str()), c->auth->md5saltpasswd.length());
MD5Final(digest, &ctx );
for (int 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 resolve %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);
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_) {
return FALSE;
}
if (cond & G_IO_ERR) {
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;
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) {
g_print(_("Server temporarily unavailable!\n"));
} else {
g_print(_("Unexpected status code %d\n"), status);
}
return 0;
}
char *p, *p1, *p2;
p = strstr(line, "<auth>");
if (p) {
p += 6;
p1 = strchr(p, '<');
if (p1) {
p2 = strchr(p1, '>');
if (p2) {
cmd_reply.daemonStamp.assign(p1, p2+1-p1);
}
}
}
std::string RSA_Public_Key;
p = strstr(line, "<rsa_public_key>");
if (p) {
p += 16;
p1 = strchr(p, '<');
if (p1) {
p1++;
p2 = strchr(p1, '>');
if (p2) {
RSA_Public_Key.assign(p1, p2-p1);
rsa_public_key_str_to_bin(RSA_Public_Key, RSA_Public_Key_e, RSA_Public_Key_n);
}
}
}
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_changepassword(gchar *line)
{
int status;
status = atoi(line);
if (status != CODE_OK) {
gchar *str = g_strdup_printf(_("Change password failed: %s"), line);
on_error_.emit(str);
g_free(str);
return 0;
}
on_changepassword_end_.emit(_("Change password 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) {
if (status == CODE_USER_NOT_REGISTER) {
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 1;
} else {
gchar *str = g_strdup_printf(_("Get Dict Mask 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_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_getadinfo(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 ad 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_getadinfo_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) {
if (status == CODE_USER_NOT_REGISTER) {
on_maxdictcount_end_.emit(0);
save_cache_str(cmd->data, g_strdup("0"));
g_free(buf);
return 1;
} else {
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) {
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 {
if (cmd->command == STARDICT::CMD_PREVIOUS) {
if (cmd->wordlist_response->size() >= 50) {
} else {
cmd->wordlist_response->push_back(buf);
}
} else {
if (cmd->wordlist_response->size() >= 100) {
} else {
cmd->wordlist_response->push_back(buf);
}
}
}
}
return 2;
}
int StarDictClient::parse_dict_result(STARDICT::Cmd* cmd, gchar *buf)
{
if (cmd->reading_status == 0) {
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) {
cmd->lookup_response->dict_response.oword = buf;
cmd->reading_status = 2;
} else if (cmd->reading_status == 2) {
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) {
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 {
if (datasize > 4*1024*1024) {
g_print(_("Drop data for security. Data too big! More than 4M!\n"));
return 1;
} 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_CHANGE_PASSWD:
result = parse_command_changepassword(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_GET_ADINFO:
result = parse_command_getadinfo(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;
}