diff --git a/backends/libyahoo2/main.cpp b/backends/libyahoo2/main.cpp index 9b6560da..c99bb14c 100644 --- a/backends/libyahoo2/main.cpp +++ b/backends/libyahoo2/main.cpp @@ -10,6 +10,9 @@ #include #include +#include "yahoohandler.h" +#include "yahoolocalaccount.h" + // Swiften #include "Swiften/Swiften.h" #include "Swiften/TLS/OpenSSL/OpenSSLContextFactory.h" @@ -26,28 +29,39 @@ using namespace boost::filesystem; using namespace boost::program_options; using namespace Transport; - -typedef struct { - int handler_tag; - int conn_tag; - void *data; - yahoo_input_condition cond; -} yahoo_handler; - -typedef struct { - int id; - std::map > conns; - int conn_tag; - std::map handlers; - std::map > handlers_per_conn; - int handler_tag; - int status; - std::string msg; - std::string buffer; -} yahoo_local_account; +class YahooHandler; +class YahooLocalAccount; static std::string *currently_read_data; -static yahoo_local_account *currently_writting_account; +static YahooLocalAccount *currently_writting_account; + +YahooHandler::YahooHandler(YahooLocalAccount *account, int conn_tag, int handler_tag, void *data, yahoo_input_condition cond) : + handler_tag(handler_tag), conn_tag(conn_tag), data(data), cond(cond), remove_later(false), account(account) {} + +YahooHandler::~YahooHandler() {} + +void YahooHandler::ready(std::string *buffer) { + if (cond == YAHOO_INPUT_WRITE) { + YahooLocalAccount *old = currently_writting_account; + currently_writting_account = account; + yahoo_write_ready(account->id, (void *) conn_tag, data); + currently_writting_account = old; + } + else { + if (!buffer) { + return; + } + // yahoo_read_ready calls ext_yahoo_read(...) in a loop, so we just have to choose proper buffer from which will + // that method read. We do that by static currently_read_data pointer. + currently_read_data = buffer; + // libyahoo2 reads data per 1024 bytes, so if we still have some data after the first ext_yahoo_read call, + // we have to call yahoo_read_ready again... + do { + yahoo_read_ready(account->id, (void *) conn_tag, data); + } while (currently_read_data->size() != 0); + } +} + typedef struct { std::string yahoo_id; @@ -109,29 +123,30 @@ class YahooPlugin : public NetworkPlugin { } void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) { - yahoo_local_account *account = new yahoo_local_account; - account->conn_tag = 1; - account->handler_tag = 1; + YahooLocalAccount *account = new YahooLocalAccount(user, legacyName, password); m_users[user] = account; - - account->id = yahoo_init_with_attributes(legacyName.c_str(), password.c_str(), - "local_host", "", - "pager_port", 5050, - NULL); m_ids[account->id] = user; - account->status = YAHOO_STATUS_OFFLINE; LOG4CXX_INFO(logger, user << ": Logging in the user as " << legacyName << " with id=" << account->id); - yahoo_login(account->id, YAHOO_STATUS_AVAILABLE); + account->login(); } void handleLogoutRequest(const std::string &user, const std::string &legacyName) { + YahooLocalAccount *account = m_users[user]; + if (account) { + yahoo_logoff(account->id); + m_ids.erase(account->id); + m_users.erase(user); + delete account; + } } void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml = "") { - LOG4CXX_INFO(logger, "Sending message from " << user << " to " << legacyName << "."); - if (legacyName == "echo") { - handleMessage(user, legacyName, message); + YahooLocalAccount *account = m_users[user]; + if (account) { + LOG4CXX_INFO(logger, "Sending message from " << user << " to " << legacyName << ": " << message << "."); + yahoo_send_im(account->id, NULL, legacyName.c_str(), message.c_str(), 0, 0); + _yahoo_write_ready(account); } } @@ -144,55 +159,84 @@ class YahooPlugin : public NetworkPlugin { } - yahoo_local_account *getAccount(int id) { + YahooLocalAccount *getAccount(int id) { return m_users[m_ids[id]]; } - void _yahoo_connect_finished(yahoo_local_account *account, yahoo_connect_callback callback, void *data, int conn_tag, bool error) { + void _yahoo_remove_account(YahooLocalAccount *account) { + m_ids.erase(account->id); + m_users.erase(account->user); + delete account; + } + + void _yahoo_connect_finished(YahooLocalAccount *account, yahoo_connect_callback callback, void *data, int conn_tag, bool error) { + currently_writting_account = account; if (error) { - LOG4CXX_ERROR(logger, "Connection error!"); + LOG4CXX_ERROR(logger, account->user << ": Connection error!"); callback(NULL, 0, data); // np->handleDisconnected(user, 0, "Connection error."); } else { - LOG4CXX_INFO(logger, "Connected"); + LOG4CXX_INFO(logger, account->user << ": Connected"); // We will have dangling pointer here, but we can't pass boost::shared_ptr here... callback((void *) conn_tag, 0, data); } } - void _yahoo_data_read(yahoo_local_account *account, int conn_tag, boost::shared_ptr data) { - // yahoo_read_ready calls ext_yahoo_read(...) in a loop, so we just have to choose proper buffer from which will - // that method read. We do that by static currently_read_data pointer. + void _yahoo_write_ready(YahooLocalAccount *account) { + // Find all WRITE handlers and inform that they really can write. + for (std::map::iterator it = account->handlers.begin(); it != account->handlers.end(); it++) { + if (it->second->cond == YAHOO_INPUT_WRITE && !it->second->remove_later) { + it->second->ready(); + } + } + } + + void _yahoo_data_read(YahooLocalAccount *account, int conn_tag, boost::shared_ptr data) { std::string d(data->begin(), data->end()); - LOG4CXX_INFO(logger, "data to read"); - for (std::map::iterator it = account->handlers_per_conn[conn_tag].begin(); it != account->handlers_per_conn[conn_tag].end(); it++) { - if (it->second->cond == YAHOO_INPUT_READ) { - LOG4CXX_INFO(logger, "found handler"); + // Find the handler that handles READing for this conn_tag + for (std::map::iterator it = account->handlers_per_conn[conn_tag].begin(); it != account->handlers_per_conn[conn_tag].end(); it++) { + if (it->second->cond == YAHOO_INPUT_READ && !it->second->remove_later) { std::string cpy(d); - currently_read_data = &cpy; - yahoo_read_ready(account->id, (void *) conn_tag, it->second->data); + it->second->ready(&cpy); + + // Look like libyahoo2 needs to be informed it can write to socket after the read + // even we have informed it before... + _yahoo_write_ready(account); + break; } } + + account->removeOldHandlers(); } - void _yahoo_data_written(yahoo_local_account *account, int conn_tag) { + void _yahoo_data_written(YahooLocalAccount *account, int conn_tag) { LOG4CXX_INFO(logger, "data written"); - currently_writting_account = account; - for (std::map::iterator it = account->handlers_per_conn[conn_tag].begin(); it != account->handlers_per_conn[conn_tag].end(); it++) { + for (std::map::iterator it = account->handlers_per_conn[conn_tag].begin(); it != account->handlers_per_conn[conn_tag].end(); it++) { if (it->second->cond == YAHOO_INPUT_WRITE) { - yahoo_write_ready(account->id, (void *) conn_tag, it->second->data); + it->second->ready(); } } + + account->removeOldHandlers(); } - void _yahoo_disconnected(yahoo_local_account *account, int conn_tag, const boost::optional &error) { - LOG4CXX_INFO(logger, "Disconnected " << error); + void _yahoo_disconnected(YahooLocalAccount *account, int conn_tag, const boost::optional &error) { + for (std::map::iterator it = account->handlers_per_conn[conn_tag].begin(); it != account->handlers_per_conn[conn_tag].end(); it++) { + if (it->second->cond == YAHOO_INPUT_READ && !it->second->remove_later) { + std::string cpy; + it->second->ready(&cpy); + _yahoo_write_ready(account); + break; + } + } + + account->removeConn(conn_tag); } int _yahoo_connect_async(int id, const char *host, int port, yahoo_connect_callback callback, void *data, int use_ssl) { - yahoo_local_account *account = getAccount(id); + YahooLocalAccount *account = getAccount(id); if (!account) { LOG4CXX_ERROR(logger, "Unknown account id=" << id); return -1; @@ -225,7 +269,7 @@ class YahooPlugin : public NetworkPlugin { private: Config *config; - std::map m_users; + std::map m_users; std::map m_ids; }; @@ -276,27 +320,64 @@ static void ext_yahoo_chat_message(int id, const char *me, const char *who, cons } static void ext_yahoo_status_changed(int id, const char *who, int stat, const char *msg, int away, int idle, int mobile) { + YahooLocalAccount *account = np->getAccount(id); + if (!account) { + return; + } + + LOG4CXX_INFO(logger, account->user << ": " << who << " status changed"); + + pbnetwork::StatusType status = pbnetwork::STATUS_NONE; + switch (stat) { + case YAHOO_STATUS_AVAILABLE: + status = pbnetwork::STATUS_ONLINE; + break; + case YAHOO_STATUS_NOTATHOME: + case YAHOO_STATUS_NOTATDESK: + case YAHOO_STATUS_NOTINOFFICE: + case YAHOO_STATUS_ONPHONE: + case YAHOO_STATUS_ONVACATION: + case YAHOO_STATUS_OUTTOLUNCH: + case YAHOO_STATUS_STEPPEDOUT: + status = pbnetwork::STATUS_AWAY; + break; + case YAHOO_STATUS_BRB: + status = pbnetwork::STATUS_XA; + break; + case YAHOO_STATUS_BUSY: + status = pbnetwork::STATUS_DND; + break; + case YAHOO_STATUS_OFFLINE: + status = pbnetwork::STATUS_NONE; + break; + default: + status = pbnetwork::STATUS_ONLINE; + break; + } + + yahoo_buddyicon_request(id, who); + + np->handleBuddyChanged(account->user, who, "", std::vector(), status, msg ? msg : ""); } static void ext_yahoo_got_buddies(int id, YList * buds) { -// while(buddies) { -// FREE(buddies->data); -// buddies = buddies->next; -// if(buddies) -// FREE(buddies->prev); -// } -// for(; buds; buds = buds->next) { -// yahoo_account *ya = y_new0(yahoo_account, 1); -// struct yahoo_buddy *bud = buds->data; -// strncpy(ya->yahoo_id, bud->id, 255); -// if(bud->real_name) -// strncpy(ya->name, bud->real_name, 255); -// strncpy(ya->group, bud->group, 255); -// ya->status = YAHOO_STATUS_OFFLINE; -// buddies = y_list_append(buddies, ya); -// -// /* print_message(("%s is %s", bud->id, bud->real_name));*/ -// } + YahooLocalAccount *account = np->getAccount(id); + if (!account) { + return; + } + + LOG4CXX_INFO(logger, account->user << ": Got buddy list"); + for(; buds; buds = buds->next) { + struct yahoo_buddy *bud = (struct yahoo_buddy *) buds->data; + + std::vector groups; + groups.push_back(bud->group); + np->handleBuddyChanged(account->user, bud->id, bud->real_name ? bud->real_name : "", groups, pbnetwork::STATUS_NONE); + } + + yahoo_set_away(id, YAHOO_STATUS_AVAILABLE, "", 1); + np->_yahoo_write_ready(account); + np->handleConnected(account->user); } static void ext_yahoo_got_ignore(int id, YList * igns) @@ -307,6 +388,12 @@ static void ext_yahoo_got_buzz(int id, const char *me, const char *who, long tm) } static void ext_yahoo_got_im(int id, const char *me, const char *who, const char *msg, long tm, int stat, int utf8) { + YahooLocalAccount *account = np->getAccount(id); + if (!account) { + return; + } + + np->handleMessage(account->user, who, msg); } static void ext_yahoo_rejected(int id, const char *who, const char *msg) { @@ -350,39 +437,36 @@ static void ext_yahoo_got_cookies(int id) { } static void ext_yahoo_login_response(int id, int succ, const char *url) { - LOG4CXX_INFO(logger, "login_response"); -// char buff[1024]; -// -// if(succ == YAHOO_LOGIN_OK) { -// ylad->status = yahoo_current_status(id); -// print_message(("logged in")); -// return; -// -// } else if(succ == YAHOO_LOGIN_UNAME) { -// -// snprintf(buff, sizeof(buff), "Could not log into Yahoo service - username not recognised. Please verify that your username is correctly typed."); -// } else if(succ == YAHOO_LOGIN_PASSWD) { -// -// snprintf(buff, sizeof(buff), "Could not log into Yahoo service - password incorrect. Please verify that your password is correctly typed."); -// -// } else if(succ == YAHOO_LOGIN_LOCK) { -// -// snprintf(buff, sizeof(buff), "Could not log into Yahoo service. Your account has been locked.\nVisit %s to reactivate it.", url); -// -// } else if(succ == YAHOO_LOGIN_DUPL) { -// -// snprintf(buff, sizeof(buff), "You have been logged out of the yahoo service, possibly due to a duplicate login."); -// } else if(succ == YAHOO_LOGIN_SOCK) { -// -// snprintf(buff, sizeof(buff), "The server closed the socket."); -// } else { -// snprintf(buff, sizeof(buff), "Could not log in, unknown reason: %d.", succ); -// } -// -// ylad->status = YAHOO_STATUS_OFFLINE; -// print_message((buff)); -// yahoo_logout(); -// poll_loop=0; + YahooLocalAccount *account = np->getAccount(id); + if (!account) { + return; + } + + if (succ == YAHOO_LOGIN_OK) { + account->status = yahoo_current_status(id); + // We will fire handleConnected in Got Buddy List. + return; + } + else if (succ == YAHOO_LOGIN_UNAME) { + np->handleDisconnected(account->user, 0, "Could not log into Yahoo service - username not recognised. Please verify that your username is correctly typed."); + } + else if (succ == YAHOO_LOGIN_PASSWD) { + np->handleDisconnected(account->user, 0, "Could not log into Yahoo service - password incorrect. Please verify that your password is correctly typed."); + } + else if (succ == YAHOO_LOGIN_LOCK) { + np->handleDisconnected(account->user, 0, std::string("Could not log into Yahoo service. Your account has been locked. Visit ") + url + " to reactivate it."); + } + else if (succ == YAHOO_LOGIN_DUPL) { + np->handleDisconnected(account->user, 0, "You have been logged out of the yahoo service, possibly due to a duplicate login."); + } + else if (succ == YAHOO_LOGIN_SOCK) { + np->handleDisconnected(account->user, 0, "The server closed the socket."); + } + else { + np->handleDisconnected(account->user, 0, "Could not log in, unknown reason."); + } + + np->handleLogoutRequest(account->user, ""); } static void ext_yahoo_error(int id, const char *_err, int fatal, int num) { @@ -416,8 +500,16 @@ static void ext_yahoo_error(int id, const char *_err, int fatal, int num) { break; } LOG4CXX_ERROR(logger, msg); -// if(fatal) -// yahoo_logout(); + + YahooLocalAccount *account = np->getAccount(id); + if (!account) { + return; + } + + if(fatal) { + np->handleDisconnected(account->user, 0, msg); + np->handleLogoutRequest(account->user, ""); + } } static int ext_yahoo_connect(const char *host, int port) { @@ -425,36 +517,23 @@ static int ext_yahoo_connect(const char *host, int port) { } static int ext_yahoo_add_handler(int id, void *fd, yahoo_input_condition cond, void *data) { - yahoo_local_account *account = np->getAccount(id); + YahooLocalAccount *account = np->getAccount(id); if (!account) { return -1; } - LOG4CXX_INFO(logger, "Adding handler " << cond); - int conn_tag = (unsigned long) fd; - yahoo_handler *handler = new yahoo_handler; + YahooHandler *handler = new YahooHandler(account, conn_tag, account->handler_tag++, data, cond); + account->addHandler(handler); - handler->conn_tag = conn_tag; - handler->handler_tag = account->handler_tag++; - handler->data = data; - handler->cond = cond; - - if (cond == YAHOO_INPUT_WRITE) { - yahoo_local_account *old = currently_writting_account; - currently_writting_account = account; - yahoo_write_ready(id, fd, data); - currently_writting_account = old; - } - - account->handlers[handler->handler_tag] = handler; - account->handlers_per_conn[conn_tag][handler->handler_tag] = handler; + // We are ready to write right now, so why not... + handler->ready(); return handler->handler_tag; } static void ext_yahoo_remove_handler(int id, int tag) { - yahoo_local_account *account = np->getAccount(id); + YahooLocalAccount *account = np->getAccount(id); if (!account) { return; } @@ -463,18 +542,13 @@ static void ext_yahoo_remove_handler(int id, int tag) { return; } - yahoo_handler *handler = account->handlers[tag]; - account->handlers.erase(tag); - account->handlers_per_conn[handler->conn_tag].erase(tag); - delete handler; + YahooHandler *handler = account->handlers[tag]; + handler->remove_later = true; } static int ext_yahoo_write(void *fd, char *buf, int len) { - LOG4CXX_INFO(logger, "Writting " << len); - int conn_tag = (unsigned long) fd; - - yahoo_local_account *account = currently_writting_account; + YahooLocalAccount *account = currently_writting_account; std::string string(buf, len); account->conns[conn_tag]->write(Swift::createSafeByteArray(string)); @@ -486,13 +560,13 @@ static int ext_yahoo_read(void *fd, char *buf, int len) { if (currently_read_data->size() < len) { len = currently_read_data->size(); } - LOG4CXX_INFO(logger, "Reading " << len); memcpy(buf, currently_read_data->c_str(), len); currently_read_data->erase(0, len); return len; } static void ext_yahoo_close(void *fd) { + // No need to do anything here. We close it properly in _yahoo_disconnected(...); } static int ext_yahoo_connect_async(int id, const char *host, int port, yahoo_connect_callback callback, void *data, int use_ssl) { @@ -534,6 +608,7 @@ static void ext_yahoo_got_buddy_change_group(int id, const char *me, const char } static void ext_yahoo_got_buddyicon(int id, const char *a, const char *b, const char *c, int checksum) { + LOG4CXX_INFO(logger, "got buddyicon " << c); } static void ext_yahoo_buddyicon_uploaded(int id, const char *url) { @@ -545,12 +620,18 @@ static void ext_yahoo_got_buddyicon_request(int id, const char *me, const char * static int ext_yahoo_log(const char *fmt,...) { static char log[8192]; + static std::string buffered; va_list ap; va_start(ap, fmt); vsnprintf(log, 8191, fmt, ap); - LOG4CXX_INFO(logger, log); + buffered += log; + if (buffered.find('\n') != std::string::npos) { + buffered.erase(buffered.find('\n'), 1); + LOG4CXX_INFO(logger, buffered); + buffered.clear(); + } fflush(stderr); va_end(ap); return 0; @@ -658,7 +739,6 @@ int main (int argc, char* argv[]) { return 1; } - Swift::logging=true; Logging::initBackendLogging(&config); register_callbacks(); diff --git a/backends/libyahoo2/yahoohandler.h b/backends/libyahoo2/yahoohandler.h new file mode 100644 index 00000000..94a07eb5 --- /dev/null +++ b/backends/libyahoo2/yahoohandler.h @@ -0,0 +1,48 @@ +#pragma once + +// Transport includes +#include "transport/config.h" +#include "transport/networkplugin.h" +#include "transport/logging.h" + +// Yahoo2 +#include +#include +#include +#include +#include + +// Swiften +#include "Swiften/Swiften.h" +#include "Swiften/TLS/OpenSSL/OpenSSLContextFactory.h" + +// for signal handler +#include "unistd.h" +#include "signal.h" +#include "sys/wait.h" +#include "sys/signal.h" + +// Boost +#include + +using namespace boost::filesystem; +using namespace boost::program_options; +using namespace Transport; + +class YahooLocalAccount; + +class YahooHandler { + public: + YahooHandler(YahooLocalAccount *account, int conn_tag, int handler_tag, void *data, yahoo_input_condition cond); + virtual ~YahooHandler(); + + void ready(std::string *buffer = NULL); + + int handler_tag; + int conn_tag; + void *data; + yahoo_input_condition cond; + bool remove_later; + YahooLocalAccount *account; +}; + diff --git a/backends/libyahoo2/yahoolocalaccount.cpp b/backends/libyahoo2/yahoolocalaccount.cpp new file mode 100644 index 00000000..49b88c94 --- /dev/null +++ b/backends/libyahoo2/yahoolocalaccount.cpp @@ -0,0 +1,60 @@ + +#include "yahoolocalaccount.h" +#include "yahoohandler.h" + +YahooLocalAccount::YahooLocalAccount(const std::string &user, const std::string &legacyName, const std::string &password) : user(user), id(0), conn_tag(1), handler_tag(1), status(YAHOO_STATUS_OFFLINE), msg(""), buffer("") { + id = yahoo_init_with_attributes(legacyName.c_str(), password.c_str(), + "local_host", "", + "pager_port", 5050, + NULL); +} + +YahooLocalAccount::~YahooLocalAccount() { + // remove handlers + for (std::map::iterator it = handlers.begin(); it != handlers.end(); it++) { + delete it->second; + } + + // remove conns + for (std::map >::iterator it = conns.begin(); it != conns.end(); it++) { + it->second->onConnectFinished.disconnect_all_slots(); + it->second->onDisconnected.disconnect_all_slots(); + it->second->onDataRead.disconnect_all_slots(); + it->second->onDataWritten.disconnect_all_slots(); + } +} + +void YahooLocalAccount::login() { + yahoo_login(id, YAHOO_STATUS_AVAILABLE); +} + +void YahooLocalAccount::addHandler(YahooHandler *handler) { + handlers[handler->handler_tag] = handler; + handlers_per_conn[handler->conn_tag][handler->handler_tag] = handler; +} + +void YahooLocalAccount::removeOldHandlers() { + std::vector handlers_to_remove; + for (std::map::iterator it = handlers.begin(); it != handlers.end(); it++) { + if (it->second->remove_later) { + handlers_to_remove.push_back(it->first); + } + } + + BOOST_FOREACH(int tag, handlers_to_remove) { + YahooHandler *handler = handlers[tag]; + handlers.erase(tag); + handlers_per_conn[handler->conn_tag].erase(tag); + delete handler; + } +} + +void YahooLocalAccount::removeConn(int conn_tag) { + for (std::map::iterator it = handlers_per_conn[conn_tag].begin(); it != handlers_per_conn[conn_tag].end(); it++) { + it->second->remove_later = true; + } + + removeOldHandlers(); + + conns.erase(conn_tag); +} diff --git a/backends/libyahoo2/yahoolocalaccount.h b/backends/libyahoo2/yahoolocalaccount.h new file mode 100644 index 00000000..ab6c1b17 --- /dev/null +++ b/backends/libyahoo2/yahoolocalaccount.h @@ -0,0 +1,55 @@ +#pragma once + +// Transport includes +#include "transport/config.h" +#include "transport/networkplugin.h" +#include "transport/logging.h" + +// Yahoo2 +#include +#include +#include +#include +#include + +// Swiften +#include "Swiften/Swiften.h" +#include "Swiften/TLS/OpenSSL/OpenSSLContextFactory.h" + +// for signal handler +#include "unistd.h" +#include "signal.h" +#include "sys/wait.h" +#include "sys/signal.h" + +// Boost +#include + +using namespace boost::filesystem; +using namespace boost::program_options; +using namespace Transport; + +class YahooHandler; + +class YahooLocalAccount { + public: + YahooLocalAccount(const std::string &user, const std::string &legacyName, const std::string &password); + virtual ~YahooLocalAccount(); + + void login(); + + void addHandler(YahooHandler *handler); + void removeOldHandlers(); + void removeConn(int conn_tag); + + std::string user; + int id; + std::map > conns; + int conn_tag; + std::map handlers; + std::map > handlers_per_conn; + int handler_tag; + int status; + std::string msg; + std::string buffer; +}; diff --git a/include/Swiften/Server/ServerStanzaChannel.h b/include/Swiften/Server/ServerStanzaChannel.h index 7da771b6..627c3f09 100644 --- a/include/Swiften/Server/ServerStanzaChannel.h +++ b/include/Swiften/Server/ServerStanzaChannel.h @@ -14,6 +14,7 @@ #include "Swiften/Elements/Message.h" #include "Swiften/Elements/IQ.h" #include "Swiften/Elements/Presence.h" +#include "Swiften/TLS/Certificate.h" namespace Swift { class Error;