From: Drew Fisher <drew.m.fisher@gmail.com>
Date: Sun, 13 Apr 2014 05:52:15 +0000 (-0700)
Subject: Add a TCP socket server
X-Git-Url: https://git.zarvox.org/?a=commitdiff_plain;h=82b0433192ab41933af9a16b2831508fd170162c;p=imoo.git

Add a TCP socket server

TODO: actually make said server do anything useful.
---

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 <signal.h>
 
 #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 <QDebug>
 #include <QFile>
+#include <QDateTime>
 #include <libpurple/purple.h>
 #include <cstdio>
 
@@ -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<RibbonManager*>(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 <QObject>
+#include <libpurple/purple.h>
 
-#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 <QtNetwork/QTcpServer>
+#include <QtNetwork/QTcpSocket>
+#include <QDebug>
+
+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 <QObject>
+#include <QList>
+#include <QtNetwork/QHostAddress>
+
+// 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<RibbonSocket*> 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 <QtNetwork/QTcpSocket>
+
+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 */