From 82b0433192ab41933af9a16b2831508fd170162c Mon Sep 17 00:00:00 2001 From: Drew Fisher Date: Sat, 12 Apr 2014 22:52:15 -0700 Subject: [PATCH] Add a TCP socket server TODO: actually make said server do anything useful. --- ribbon/main.cpp | 6 ++- ribbon/ribbon.pro | 10 ++++- ribbon/ribbonmanager.cpp | 96 +++++++++++++++++++++++++--------------- ribbon/ribbonmanager.h | 18 ++++++-- ribbon/ribbonserver.cpp | 49 ++++++++++++++++++++ ribbon/ribbonserver.h | 33 ++++++++++++++ ribbon/ribbonsocket.cpp | 35 +++++++++++++++ ribbon/ribbonsocket.h | 25 +++++++++++ 8 files changed, 231 insertions(+), 41 deletions(-) create mode 100644 ribbon/ribbonserver.cpp create mode 100644 ribbon/ribbonserver.h create mode 100644 ribbon/ribbonsocket.cpp create mode 100644 ribbon/ribbonsocket.h diff --git a/ribbon/main.cpp b/ribbon/main.cpp index ae507c1..3f1f7ac 100644 --- a/ribbon/main.cpp +++ b/ribbon/main.cpp @@ -8,6 +8,7 @@ #include #include "ribbonmanager.h" +#include "ribbonserver.h" int main(int argc, char** argv) @@ -26,13 +27,16 @@ int main(int argc, char** argv) b->show(); RibbonManager* purple = new RibbonManager(); - purple->init_libpurple(); + purple->init(); qDebug() << "libpurple initialized, version" << purple->get_version(); QObject::connect(b, SIGNAL(clicked()), purple, SLOT(sign_in_user())); + RibbonServer* server = new RibbonServer(); + server->listen(QHostAddress::Any, 8888); + return app->exec(); } diff --git a/ribbon/ribbon.pro b/ribbon/ribbon.pro index 03e93f3..f1cea56 100644 --- a/ribbon/ribbon.pro +++ b/ribbon/ribbon.pro @@ -6,14 +6,22 @@ TEMPLATE = app TARGET = ribbon DEPENDPATH += . INCLUDEPATH += . +QT += network unix { CONFIG += link_pkgconfig PKGCONFIG += glib-2.0 purple + # PKGCONFIG += QJson } +QMAKE_CXXFLAGS += -Werror -W + # Input -HEADERS += ribbonmanager.h +HEADERS += ribbonmanager.h \ + ribbonserver.h \ + ribbonsocket.h SOURCES += ribbonmanager.cpp \ + ribbonserver.cpp \ + ribbonsocket.cpp \ main.cpp diff --git a/ribbon/ribbonmanager.cpp b/ribbon/ribbonmanager.cpp index 304fa03..fff8d56 100644 --- a/ribbon/ribbonmanager.cpp +++ b/ribbon/ribbonmanager.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -77,27 +78,27 @@ static PurpleEventLoopUiOps glib_eventloops = /* End verbatim copied code */ /* noops for now, add features later */ -static PurpleConversationUiOps ribbon_conversation_callbacks = { - NULL, /* create_conversation */ - NULL, /* destroy_conversation */ - NULL, /* write_chat */ - NULL, /* write_im */ - NULL, /* write_conv */ - NULL, /* chat_add_users */ - NULL, /* chat_rename_user */ - NULL, /* chat_remove_users */ - NULL, /* chat_update_user */ - NULL, /* present */ - NULL, /* has_focus */ - NULL, /* custom_smiley_add */ - NULL, /* custom_smiley_write */ - NULL, /* custom_smiley_close */ - NULL, /* send_confirm */ - NULL, - NULL, - NULL, - NULL -}; +//static PurpleConversationUiOps ribbon_conversation_callbacks = { +// NULL, /* create_conversation */ +// NULL, /* destroy_conversation */ +// NULL, /* write_chat */ +// NULL, /* write_im */ +// NULL, /* write_conv */ +// NULL, /* chat_add_users */ +// NULL, /* chat_rename_user */ +// NULL, /* chat_remove_users */ +// NULL, /* chat_update_user */ +// NULL, /* present */ +// NULL, /* has_focus */ +// NULL, /* custom_smiley_add */ +// NULL, /* custom_smiley_write */ +// NULL, /* custom_smiley_close */ +// NULL, /* send_confirm */ +// NULL, +// NULL, +// NULL, +// NULL +//}; static void connect_progress(PurpleConnection *gc, const char* text, size_t step, size_t step_count) { @@ -169,29 +170,44 @@ static PurpleCoreUiOps ribbon_core_callbacks = { NULL }; -static void signed_on(PurpleConnection *gc) +// TODO: note that the void* data can be set at purple_signal_connect time, and +// set it to the class instance to allow for convenient upcalls +// Then, actually do upcalls and emit the appropriate signals. +static void signed_on(PurpleConnection *gc, void* data) { + Q_UNUSED(data); PurpleAccount *account = purple_connection_get_account(gc); printf("Account connected: \"%s\" (%s)\n", purple_account_get_username(account), purple_account_get_protocol_id(account)); } static void received_im_msg(PurpleAccount *account, char *sender, char *message, - PurpleConversation *conv, PurpleMessageFlags flags) + PurpleConversation *conv, PurpleMessageFlags flags, void* data) { - Q_UNUSED(flags); - if (conv==NULL) { - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); - } - printf("(%s) %s (%s): %s\n", purple_utf8_strftime("%H:%M:%S", NULL), sender, purple_conversation_get_name(conv), message); + Q_UNUSED(data); + RibbonManager* r = static_cast(account->ui_data); + r->received_im_msg(account, sender, message, conv, flags); +} + +static void buddy_signed_on(PurpleBuddy* buddy, void* data) +{ + Q_UNUSED(data); + qDebug() << purple_buddy_get_contact_alias(buddy) << "signed on"; } -void connect_to_signals(void) +static void connect_to_signals(void) { static int handle; - purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, + void *blist_handle = purple_blist_get_handle(); + void *connections_handle = purple_connections_get_handle(); + void *conversations_handle = purple_conversations_get_handle(); + + purple_signal_connect(connections_handle, "signed-on", &handle, PURPLE_CALLBACK(signed_on), NULL); - purple_signal_connect(purple_conversations_get_handle(), "received-im-msg", &handle, + purple_signal_connect(conversations_handle, "received-im-msg", &handle, PURPLE_CALLBACK(received_im_msg), NULL); + purple_signal_connect(blist_handle, "buddy-signed-on", &handle, + PURPLE_CALLBACK(buddy_signed_on), NULL); + //purple_signal_connect( } RibbonManager::RibbonManager(QObject* parent) : QObject(parent) @@ -202,7 +218,7 @@ RibbonManager::~RibbonManager() { } -void RibbonManager::init_libpurple(void) +void RibbonManager::init(void) { /* optional: purple_util_set_user_dir(CUSTOM_USER_DIRECTORY); @@ -237,6 +253,15 @@ const char* RibbonManager::get_version(void) return purple_core_get_version(); } +void RibbonManager::received_im_msg(PurpleAccount *account, char *sender, char *message, + PurpleConversation *conv, PurpleMessageFlags flags) { + Q_UNUSED(flags); + if (conv==NULL) { + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); + } + qDebug() << QDateTime::currentDateTime().toString("hh:mm:ss") << sender << purple_conversation_get_name(conv) << message; +} + void RibbonManager::sign_in_user(void) { QFile file("account.txt"); @@ -244,13 +269,14 @@ void RibbonManager::sign_in_user(void) qDebug() << "Couldn't load account information from account.txt!"; return; } - QByteArray protocol = file.readLine().trimmed(); + QByteArray protocol = file.readLine().trimmed();//this could be prpl-aim, prpl-yahoo, prpl-msn, prpl-icq, etc. QByteArray username = file.readLine().trimmed(); QByteArray password = file.readLine().trimmed(); // Allocate and enable an account. - // PurpleAccount *account = purple_account_new("YOUR_IM_ACCOUNTS_USERNAME_HERE", "prpl-IM_NETWORK_HERE"); //this could be prpl-aim, prpl-yahoo, prpl-msn, prpl-icq, etc. PurpleAccount *account = purple_account_new(username.data(), protocol.data()); - // purple_account_set_password(account, "YOUR_IM_ACCOUNTS_PASSWORD_HERE"); + // ui_data needed for upcalls + account->ui_data = this; + purple_account_set_password(account, password.data()); purple_accounts_add(account); purple_account_set_enabled(account, UI_ID, TRUE); diff --git a/ribbon/ribbonmanager.h b/ribbon/ribbonmanager.h index a9d15f9..0e808e6 100644 --- a/ribbon/ribbonmanager.h +++ b/ribbon/ribbonmanager.h @@ -2,21 +2,31 @@ #define RIBBONMANAGER_H #include +#include -#define UI_ID "ribbon" +#define UI_ID "ribbon" class RibbonManager : public QObject { Q_OBJECT public: RibbonManager(QObject* parent = 0); ~RibbonManager(); - void init_libpurple(void); - //void connect_to_signals(void); + + // Called to configure libpurple and connect to purple signals + void init(void); + + // Exposes purple_get_version() const char* get_version(void); + + // Event handlers + void received_im_msg(PurpleAccount* account, char* sender, char* message, PurpleConversation* conv, PurpleMessageFlags flags); + public slots: void sign_in_user(void); -private: +private: + // TODO: add a map of currently-open conversations? + Q_DISABLE_COPY(RibbonManager) }; #endif /* RIBBONMANAGER_H */ diff --git a/ribbon/ribbonserver.cpp b/ribbon/ribbonserver.cpp new file mode 100644 index 0000000..ec3d902 --- /dev/null +++ b/ribbon/ribbonserver.cpp @@ -0,0 +1,49 @@ +#include "ribbonserver.h" +#include "ribbonsocket.h" + +#include +#include +#include + +RibbonServer::RibbonServer(QObject* parent) : + QObject(parent) +{ + server = new QTcpServer(this); + QObject::connect(server, SIGNAL(newConnection()), + this, SLOT(onNewConnection())); +} + +RibbonServer::~RibbonServer() +{ +} + +bool RibbonServer::listen(const QHostAddress& address, quint16 port) +{ + _addr = address; + _port = port; + return server->listen(_addr, _port); +} + +void RibbonServer::onNewConnection() +{ + QTcpSocket* sock = server->nextPendingConnection(); + qDebug() << "Accepted connection from" << sock->peerAddress() << ":" << sock->peerPort(); + RibbonSocket* rsock = new RibbonSocket(sock, this); + sockets.append(rsock); + if (sockets.length() >= 1) { + server->close(); + qDebug() << "Server closed"; + } + QObject::connect(rsock, SIGNAL(disconnected(RibbonSocket*)), + this, SLOT(onDisconnection(RibbonSocket*))); +} + +void RibbonServer::onDisconnection(RibbonSocket* r) +{ + sockets.removeOne(r); + qDebug() << "Socket disconnected:" << r->s(); + if (!server->isListening()) { + server->listen(_addr, _port); + qDebug() << "Server listening on" << _addr << ":" << _port; + } +} diff --git a/ribbon/ribbonserver.h b/ribbon/ribbonserver.h new file mode 100644 index 0000000..f5fd8f2 --- /dev/null +++ b/ribbon/ribbonserver.h @@ -0,0 +1,33 @@ +#ifndef RIBBONSERVER_H +#define RIBBONSERVER_H + +#include +#include +#include + +// forward declarations +class QTcpServer; +class QTcpSocket; +class RibbonSocket; + +class RibbonServer : public QObject { + Q_OBJECT +public: + RibbonServer(QObject* parent = 0); + ~RibbonServer(); + bool listen(const QHostAddress& address = QHostAddress::Any, quint16 port = 0); +public slots: + //void dispatch(QByteArray ba); +signals: + void newConnection(); +private: + QTcpServer* server; + QHostAddress _addr; + quint16 _port; + QList sockets; +private slots: + void onNewConnection(); + void onDisconnection(RibbonSocket* r); +}; + +#endif /* RIBBONSERVER_H */ diff --git a/ribbon/ribbonsocket.cpp b/ribbon/ribbonsocket.cpp new file mode 100644 index 0000000..722cdd1 --- /dev/null +++ b/ribbon/ribbonsocket.cpp @@ -0,0 +1,35 @@ +#include "ribbonsocket.h" + +RibbonSocket::RibbonSocket(QTcpSocket* sock, QObject* parent) : QObject(parent), socket(sock) +{ + QObject::connect(socket, SIGNAL(connected()), + this, SLOT(onConnect())); + QObject::connect(socket, SIGNAL(disconnected()), + this, SLOT(onDisconnect())); + QObject::connect(socket, SIGNAL(readyRead()), + this, SLOT(onReadyRead())); +} + +RibbonSocket::~RibbonSocket() +{ +} + +const QTcpSocket* RibbonSocket::s() +{ + return socket; +} + +void RibbonSocket::onConnect() +{ + emit connected(this); +} + +void RibbonSocket::onDisconnect() +{ + emit disconnected(this); +} + +void RibbonSocket::onReadyRead() +{ + emit readyRead(this); +} diff --git a/ribbon/ribbonsocket.h b/ribbon/ribbonsocket.h new file mode 100644 index 0000000..32327ae --- /dev/null +++ b/ribbon/ribbonsocket.h @@ -0,0 +1,25 @@ +#ifndef RIBBONSOCKET_H +#define RIBBONSOCKET_H + +#include + +class RibbonSocket : public QObject { + Q_OBJECT +public: + RibbonSocket(QTcpSocket* socket, QObject* parent=0); + // Consider s to be final. Don't modify it. + const QTcpSocket* s(); +signals: + void connected(RibbonSocket* s); + void disconnected(RibbonSocket* s); + void readyRead(RibbonSocket* s); +private: + virtual ~RibbonSocket(); + QTcpSocket* socket; +private slots: + void onConnect(); + void onDisconnect(); + void onReadyRead(); +}; + +#endif /* RIBBONSOCKET_H */ -- 2.39.5