Files @ b9d0590c67f8
Branch filter:

Location: libtransport.git/backends/swiften/main.cpp

Vitaly Takmazov
fix tests WIP
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
// Transport includes
#include "transport/config.h"
#include "transport/networkplugin.h"
#include "transport/logging.h"

#include "boost/date_time/posix_time/posix_time.hpp"

// Swiften
#include "Swiften/Swiften.h"
#include <Swiften/Version.h>
#define HAVE_SWIFTEN_3  SWIFTEN_VERSION >= 0x030000

#ifndef WIN32
// for signal handler
#include "unistd.h"
#include "signal.h"
#include "sys/wait.h"
#include "sys/signal.h"
#endif

#ifndef __FreeBSD__
#ifndef __MACH__
// malloc_trim
#include "malloc.h"
#endif
#endif

// Boost
#include <boost/algorithm/string.hpp>
using namespace boost::filesystem;
using namespace boost::program_options;
using namespace Transport;

DEFINE_LOGGER(logger, "Swiften");
DEFINE_LOGGER(logger_xml, "backend.xml");

// eventloop
Swift::SimpleEventLoop *loop_;

// Plugins
class SwiftenPlugin;
NetworkPlugin *np = NULL;
Swift::XMPPSerializer *serializer;

class ForwardIQHandler : public Swift::IQHandler {
	public:
		std::map <std::string, std::string> m_id2resource;

		ForwardIQHandler(NetworkPlugin *np, const std::string &user) {
			m_np = np;
			m_user = user;
		}

		bool handleIQ(boost::shared_ptr<Swift::IQ> iq) {
			if (iq->getPayload<Swift::RosterPayload>() != NULL) {
				return false;
			}
			if (iq->getType() == Swift::IQ::Get) {
				m_id2resource[iq->getID()] = iq->getFrom().getResource();
			}

			iq->setTo(m_user);
			std::string xml = safeByteArrayToString(serializer->serializeElement(iq));
			m_np->sendRawXML(xml);
			return true;
		}

	private:
		NetworkPlugin *m_np;
		std::string m_user;
		
};

class SwiftenPlugin : public NetworkPlugin, Swift::XMPPParserClient {
	public:
		Swift::BoostNetworkFactories *m_factories;
		Swift::BoostIOServiceThread m_boostIOServiceThread;
		boost::shared_ptr<Swift::Connection> m_conn;
		bool m_firstPing;
		
		Swift::FullPayloadSerializerCollection collection;
		Swift::XMPPParser *m_xmppParser;
		Swift::FullPayloadParserFactoryCollection m_collection2;

		SwiftenPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin() {
			this->config = config;
			m_firstPing = true;
			m_factories = new Swift::BoostNetworkFactories(loop);
			m_conn = m_factories->getConnectionFactory()->createConnection();
			m_conn->onDataRead.connect(boost::bind(&SwiftenPlugin::_handleDataRead, this, _1));
			m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port));
#if HAVE_SWIFTEN_3
			serializer = new Swift::XMPPSerializer(&collection, Swift::ClientStreamType, false);
#else
			serializer = new Swift::XMPPSerializer(&collection, Swift::ClientStreamType);
#endif
			m_xmppParser = new Swift::XMPPParser(this, &m_collection2, m_factories->getXMLParserFactory());
			m_xmppParser->parse("<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='localhost' version='1.0'>");

			LOG4CXX_INFO(logger, "Starting the plugin.");
		}

		// NetworkPlugin uses this method to send the data to networkplugin server
		void sendData(const std::string &string) {
			m_conn->write(Swift::createSafeByteArray(string));
		}

		// This method has to call handleDataRead with all received data from network plugin server
		void _handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data) {
			if (m_firstPing) {
				m_firstPing = false;
				NetworkPlugin::PluginConfig cfg;
				cfg.setRawXML(true);
				sendConfig(cfg);
			}
			std::string d(data->begin(), data->end());
			handleDataRead(d);
		}

		void handleStreamStart(const Swift::ProtocolHeader&) {}
#if HAVE_SWIFTEN_3
		void handleElement(boost::shared_ptr<Swift::ToplevelElement> element) {
#else
		void handleElement(boost::shared_ptr<Swift::Element> element) {
#endif
			boost::shared_ptr<Swift::Stanza> stanza = boost::dynamic_pointer_cast<Swift::Stanza>(element);
			if (!stanza) {
				return;
			}

			std::string user = stanza->getFrom().toBare();

			boost::shared_ptr<Swift::Client> client = m_users[user];
			if (!client)
				return;

			stanza->setFrom(client->getJID());

			boost::shared_ptr<Swift::Message> message = boost::dynamic_pointer_cast<Swift::Message>(stanza);
			if (message) {
				client->sendMessage(message);
				return;
			}

			boost::shared_ptr<Swift::Presence> presence = boost::dynamic_pointer_cast<Swift::Presence>(stanza);
			if (presence) {
				client->sendPresence(presence);
				return;
			}

			boost::shared_ptr<Swift::IQ> iq = boost::dynamic_pointer_cast<Swift::IQ>(stanza);
			if (iq) {
				if (m_handlers[user]->m_id2resource.find(stanza->getID()) != m_handlers[user]->m_id2resource.end()) {
					std::string resource = m_handlers[user]->m_id2resource[stanza->getID()];
					if (resource.empty()) {
						iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain()));					
					} else {
						iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain(), resource));					
					}
					
					m_handlers[user]->m_id2resource.erase(stanza->getID());
				}
				client->getIQRouter()->sendIQ(iq);
				return;
			}
		}

		void handleStreamEnd() {}

		void handleRawXML(const std::string &xml) {
			m_xmppParser->parse(xml);
		}

		void handleSwiftDisconnected(const std::string &user, const boost::optional<Swift::ClientError> &error) {
			std::string message = "";
			bool reconnect = false;
			if (error) {
				switch(error->getType()) {
					case Swift::ClientError::UnknownError: message = ("Unknown Error"); reconnect = true; break;
					case Swift::ClientError::DomainNameResolveError: message = ("Unable to find server"); break;
					case Swift::ClientError::ConnectionError: message = ("Error connecting to server"); break;
					case Swift::ClientError::ConnectionReadError: message = ("Error while receiving server data"); reconnect = true; break;
					case Swift::ClientError::ConnectionWriteError: message = ("Error while sending data to the server"); reconnect = true; break;
					case Swift::ClientError::XMLError: message = ("Error parsing server data"); reconnect = true; break;
					case Swift::ClientError::AuthenticationFailedError: message = ("Login/password invalid"); break;
					case Swift::ClientError::CompressionFailedError: message = ("Error while compressing stream"); break;
					case Swift::ClientError::ServerVerificationFailedError: message = ("Server verification failed"); break;
					case Swift::ClientError::NoSupportedAuthMechanismsError: message = ("Authentication mechanisms not supported"); break;
					case Swift::ClientError::UnexpectedElementError: message = ("Unexpected response"); break;
					case Swift::ClientError::ResourceBindError: message = ("Error binding resource"); break;
					case Swift::ClientError::SessionStartError: message = ("Error starting session"); break;
					case Swift::ClientError::StreamError: message = ("Stream error"); break;
					case Swift::ClientError::TLSError: message = ("Encryption error"); break;
					case Swift::ClientError::ClientCertificateLoadError: message = ("Error loading certificate (Invalid password?)"); break;
					case Swift::ClientError::ClientCertificateError: message = ("Certificate not authorized"); break;

					case Swift::ClientError::UnknownCertificateError: message = ("Unknown certificate"); break;
					case Swift::ClientError::CertificateExpiredError: message = ("Certificate has expired"); break;
					case Swift::ClientError::CertificateNotYetValidError: message = ("Certificate is not yet valid"); break;
					case Swift::ClientError::CertificateSelfSignedError: message = ("Certificate is self-signed"); break;
					case Swift::ClientError::CertificateRejectedError: message = ("Certificate has been rejected"); break;
					case Swift::ClientError::CertificateUntrustedError: message = ("Certificate is not trusted"); break;
					case Swift::ClientError::InvalidCertificatePurposeError: message = ("Certificate cannot be used for encrypting your connection"); break;
					case Swift::ClientError::CertificatePathLengthExceededError: message = ("Certificate path length constraint exceeded"); break;
					case Swift::ClientError::InvalidCertificateSignatureError: message = ("Invalid certificate signature"); break;
					case Swift::ClientError::InvalidCAError: message = ("Invalid Certificate Authority"); break;
					case Swift::ClientError::InvalidServerIdentityError: message = ("Certificate does not match the host identity"); break;
				}
			}
			LOG4CXX_INFO(logger, user << ": Disconnected " << message);
			handleDisconnected(user, reconnect ? 0 : 3, message);

			boost::shared_ptr<Swift::Client> client = m_users[user];
			if (client) {
				client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
				client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
				client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
				m_users.erase(user);
				m_handlers.erase(user);
			}

#ifndef WIN32
#ifndef __FreeBSD__
#ifndef __MACH__
			// force returning of memory chunks allocated by libxml2 to kernel
			malloc_trim(0);
#endif
#endif
#endif
		}

		void handleSwiftConnected(const std::string &user) {
			LOG4CXX_INFO(logger, user << ": Connected to XMPP server.");
			handleConnected(user);
			m_users[user]->requestRoster();
			Swift::Presence::ref response = Swift::Presence::create();
			response->setFrom(m_users[user]->getJID());
			m_users[user]->sendPresence(response);
		}

		void handleSwiftRosterReceived(const std::string &user) {
			Swift::PresenceOracle *oracle = m_users[user]->getPresenceOracle();
			BOOST_FOREACH(const Swift::XMPPRosterItem &item, m_users[user]->getRoster()->getItems()) {
				Swift::Presence::ref lastPresence = oracle->getLastPresence(item.getJID());
				pbnetwork::StatusType status = lastPresence ? ((pbnetwork::StatusType) lastPresence->getShow()) : pbnetwork::STATUS_NONE;
				handleBuddyChanged(user, item.getJID().toBare().toString(),
								   item.getName(), item.getGroups(), status);
			}
		}

		void handleSwiftPresenceChanged(const std::string &user, Swift::Presence::ref presence) {
// 			boost::shared_ptr<Swift::Client> client = m_users[user];
// 			if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) {
// 				return;
// 			}
// 
// 			if (presence->getPayload<Swift::MUCUserPayload>() != NULL || presence->getPayload<Swift::MUCPayload>() != NULL) {
// 				return;
// 			}
// 
// 			LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed");
// 
// 			std::string message = presence->getStatus();
// 			std::string photo = "";
// 
// 			boost::shared_ptr<Swift::VCardUpdate> update = presence->getPayload<Swift::VCardUpdate>();
// 			if (update) {
// 				photo = update->getPhotoHash();
// 			}
// 
// 			boost::optional<Swift::XMPPRosterItem> item = m_users[user]->getRoster()->getItem(presence->getFrom());
// 			if (item) {
// 				handleBuddyChanged(user, presence->getFrom().toBare().toString(), item->getName(), item->getGroups(), (pbnetwork::StatusType) presence->getShow(), message, photo);
// 			}
// 			else {
// 				std::vector<std::string> groups;
// 				handleBuddyChanged(user, presence->getFrom().toBare().toString(), presence->getFrom().toBare(), groups, (pbnetwork::StatusType) presence->getShow(), message, photo);
// 			}
			presence->setTo(user);
			std::string xml = safeByteArrayToString(serializer->serializeElement(presence));
			sendRawXML(xml);
		}

		void handleSwiftMessageReceived(const std::string &user, Swift::Message::ref message) {
			message->setTo(user);
			std::string xml = safeByteArrayToString(serializer->serializeElement(message));
			sendRawXML(xml);
		}

		void handleSwiftenDataRead(const Swift::SafeByteArray &data) {
			std::string d = safeByteArrayToString(data);
			if (!boost::starts_with(d, "<auth")) {
				LOG4CXX_INFO(logger_xml, "XML IN " << d);
			}
		}

		void handleSwiftenDataWritten(const Swift::SafeByteArray &data) {
			LOG4CXX_INFO(logger_xml, "XML OUT " << safeByteArrayToString(data));
		}

		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
			LOG4CXX_INFO(logger, user << ": connecting as " << legacyName);
			boost::shared_ptr<Swift::Client> client = boost::make_shared<Swift::Client>(Swift::JID(legacyName + "/Spectrum"), password, m_factories);
			m_users[user] = client;
			client->setAlwaysTrustCertificates();
			client->onConnected.connect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
			client->onDisconnected.connect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
			client->onMessageReceived.connect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
			client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user));
			client->getPresenceOracle()->onPresenceChange.connect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1));
			client->onDataRead.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataRead, this, _1));
			client->onDataWritten.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataWritten, this, _1));
			client->getSubscriptionManager()->onPresenceSubscriptionRequest.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRequest, this, user, _1, _2, _3));
			client->getSubscriptionManager()->onPresenceSubscriptionRevoked.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRevoked, this, user, _1, _2));
			Swift::ClientOptions opt;
			opt.allowPLAINWithoutTLS = true;
			client->connect(opt);

			boost::shared_ptr<ForwardIQHandler> handler = boost::make_shared<ForwardIQHandler>(this, user);
			client->getIQRouter()->addHandler(handler);
			m_handlers[user] = handler;
		}

		void handleSubscriptionRequest(const std::string &user, const Swift::JID& jid, const std::string& message, Swift::Presence::ref presence) {
			handleSwiftPresenceChanged(user, presence);
		}

		void handleSubscriptionRevoked(const std::string &user, const Swift::JID& jid, const std::string& message) {
			Swift::Presence::ref presence = Swift::Presence::create();
			presence->setTo(user);
			presence->setFrom(jid);
			presence->setType(Swift::Presence::Unsubscribe);
			handleSwiftPresenceChanged(user, presence);
		}

		void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
			boost::shared_ptr<Swift::Client> client = m_users[user];
			if (client) {
				client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
// 				client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
				client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
				client->getRoster()->onInitialRosterPopulated.disconnect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user));
				client->getPresenceOracle()->onPresenceChange.disconnect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1));
				client->disconnect();
			}
		}

		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &xhtml = "", const std::string &id = "") {
		}

		void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
		}

		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) {
			boost::shared_ptr<Swift::Client> client = m_users[user];
			if (client) {
				LOG4CXX_INFO(logger, user << ": Added/Updated buddy " << buddyName << ".");
				if (!client->getRoster()->containsJID(buddyName) || client->getRoster()->getSubscriptionStateForJID(buddyName) != Swift::RosterItemPayload::Both) {
					Swift::RosterItemPayload item;
					item.setName(alias);
					item.setJID(buddyName);
					item.setGroups(groups);
					boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
					roster->addItem(item);
					Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
// 					request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
					request->send();
					client->getSubscriptionManager()->requestSubscription(buddyName);
				}
				else {
					Swift::JID contact(buddyName);
					Swift::RosterItemPayload item(contact, alias, client->getRoster()->getSubscriptionStateForJID(contact));
					item.setGroups(groups);
					boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
					roster->addItem(item);
					Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
// 					request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
					request->send();
				}

			}
		}

		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) {
			boost::shared_ptr<Swift::Client> client = m_users[user];
			if (client) {
				Swift::RosterItemPayload item(buddyName, "", Swift::RosterItemPayload::Remove);
				boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
				roster->addItem(item);
				Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
// 				request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
				request->send();
			}
		}

		void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) {

		}

		void handleLeaveRoomRequest(const std::string &user, const std::string &room) {

		}

	private:
		Config *config;
		std::map<std::string, boost::shared_ptr<Swift::Client> > m_users;
		std::map<std::string, boost::shared_ptr<ForwardIQHandler> > m_handlers;
};

#ifndef WIN32
static void spectrum_sigchld_handler(int sig)
{
	int status;
	pid_t pid;

	do {
		pid = waitpid(-1, &status, WNOHANG);
	} while (pid != 0 && pid != (pid_t)-1);

	if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
		char errmsg[BUFSIZ];
		snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid);
		perror(errmsg);
	}
}
#endif


int main (int argc, char* argv[]) {
	std::string host;
	int port;

#ifndef WIN32
	if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
		std::cout << "SIGCHLD handler can't be set\n";
		return -1;
	}
#endif

	std::string error;
	Config *cfg = Config::createFromArgs(argc, argv, error, host, port);
	if (cfg == NULL) {
		std::cerr << error;
		return 1;
	}

	Logging::initBackendLogging(cfg);

	Swift::SimpleEventLoop eventLoop;
	loop_ = &eventLoop;
	np = new SwiftenPlugin(cfg, &eventLoop, host, port);
	loop_->run();

	return 0;
}