Files @ 64f0883512d7
Branch filter:

Location: libtransport.git/backends/smstools3/main.cpp - annotation

Vitaly Takmazov
Merge branch 'master' of https://github.com/hanzz/libtransport
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
a06a47ed110e
099857ad59ce
d8e60cea9507
d8e60cea9507
d8e60cea9507
d8e60cea9507
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
7c93aee6f49a
a67bc0fa96a5
57963cf18ec0
57963cf18ec0
099857ad59ce
099857ad59ce
d8e60cea9507
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
57963cf18ec0
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
a67bc0fa96a5
a67bc0fa96a5
099857ad59ce
099857ad59ce
099857ad59ce
57963cf18ec0
1be2472596ac
1be2472596ac
1be2472596ac
1be2472596ac
1be2472596ac
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
a67bc0fa96a5
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
57963cf18ec0
099857ad59ce
099857ad59ce
099857ad59ce
57963cf18ec0
57963cf18ec0
099857ad59ce
099857ad59ce
57963cf18ec0
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
a67bc0fa96a5
a67bc0fa96a5
a67bc0fa96a5
db6f7e6c8f86
a67bc0fa96a5
a67bc0fa96a5
099857ad59ce
099857ad59ce
099857ad59ce
a67bc0fa96a5
a67bc0fa96a5
a67bc0fa96a5
a67bc0fa96a5
a67bc0fa96a5
a67bc0fa96a5
a67bc0fa96a5
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
57963cf18ec0
099857ad59ce
099857ad59ce
099857ad59ce
a67bc0fa96a5
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
db6f7e6c8f86
1be2472596ac
db6f7e6c8f86
db6f7e6c8f86
db6f7e6c8f86
db6f7e6c8f86
1be2472596ac
db6f7e6c8f86
db6f7e6c8f86
db6f7e6c8f86
db6f7e6c8f86
db6f7e6c8f86
db6f7e6c8f86
db6f7e6c8f86
db6f7e6c8f86
db6f7e6c8f86
db6f7e6c8f86
db6f7e6c8f86
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
d8e60cea9507
d8e60cea9507
d8e60cea9507
d8e60cea9507
d8e60cea9507
d8e60cea9507
d8e60cea9507
d8e60cea9507
1be2472596ac
d8e60cea9507
d8e60cea9507
d8e60cea9507
d8e60cea9507
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
1be2472596ac
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
1be2472596ac
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
57963cf18ec0
a67bc0fa96a5
57963cf18ec0
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
d8e60cea9507
a67bc0fa96a5
d8e60cea9507
d8e60cea9507
d8e60cea9507
d8e60cea9507
d8e60cea9507
d8e60cea9507
d8e60cea9507
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
a06a47ed110e
099857ad59ce
48f2d9d6b146
a06a47ed110e
48f2d9d6b146
a06a47ed110e
a06a47ed110e
a06a47ed110e
a06a47ed110e
d8e60cea9507
a06a47ed110e
48f2d9d6b146
48f2d9d6b146
d8e60cea9507
d8e60cea9507
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
099857ad59ce
/*
 * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com
 *
 * This example is free, and not covered by LGPL license. There is no
 * restriction applied to their modification, redistribution, using and so on.
 * You can study them, modify them, use them in your own program - either
 * completely or partially. By using it you may give me some credits in your
 * program, but you don't have to.
 */

#include "transport/config.h"
#include "transport/logging.h"
#include "transport/networkplugin.h"
#include "transport/sqlite3backend.h"
#include "transport/mysqlbackend.h"
#include "transport/pqxxbackend.h"
#include "transport/storagebackend.h"
#include "Swiften/Swiften.h"
#include <boost/filesystem.hpp>
#include "unistd.h"
#include "signal.h"
#include "sys/wait.h"
#include "sys/signal.h"
#include <fstream>
#include <streambuf>

Swift::SimpleEventLoop *loop_;

#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>

using namespace boost::filesystem;

using namespace boost::program_options;
using namespace Transport;

DEFINE_LOGGER(logger, "SMSNetworkPlugin");

#define INTERNAL_USER "/sms@backend@internal@user"

class SMSNetworkPlugin;
SMSNetworkPlugin * np = NULL;
StorageBackend *storageBackend;

class SMSNetworkPlugin : public NetworkPlugin {
	public:
		Swift::BoostNetworkFactories *m_factories;
		Swift::BoostIOServiceThread m_boostIOServiceThread;
		boost::shared_ptr<Swift::Connection> m_conn;
		Swift::Timer::ref m_timer;
		int m_internalUser;

		SMSNetworkPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin() {
			this->config = config;
			m_factories = new Swift::BoostNetworkFactories(loop);
			m_conn = m_factories->getConnectionFactory()->createConnection();
			m_conn->onDataRead.connect(boost::bind(&SMSNetworkPlugin::_handleDataRead, this, _1));
			m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port));
// 			m_conn->onConnectFinished.connect(boost::bind(&FrotzNetworkPlugin::_handleConnected, this, _1));
// 			m_conn->onDisconnected.connect(boost::bind(&FrotzNetworkPlugin::handleDisconnected, this));

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

			m_timer = m_factories->getTimerFactory()->createTimer(5000);
			m_timer->onTick.connect(boost::bind(&SMSNetworkPlugin::handleSMSDir, this));
			m_timer->start();

			// We're reusing our database model here. Buddies of user with JID INTERNAL_USER are there
			// to match received GSM messages from number N with the XMPP users who sent message to number N.
			// BuddyName = GSM number
			// Alias = XMPP user JID to which the messages from this number is sent to.
			// TODO: This should be per Modem!!!
			UserInfo info;
			info.jid = INTERNAL_USER;
			info.password = "";
			storageBackend->setUser(info);
			storageBackend->getUser(INTERNAL_USER, info);
			m_internalUser = info.id;
		}


		void handleSMS(const std::string &sms) {
			LOG4CXX_INFO(logger, "Handling SMS " << sms << ".")
			std::ifstream t(sms.c_str());
			std::string str;

			t.seekg(0, std::ios::end);
			str.reserve(t.tellg());
			t.seekg(0, std::ios::beg);

			str.assign((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());

			std::string from = "";
			std::string msg = "";
			while(str.find("\n") != std::string::npos) {
				std::string line = str.substr(0, str.find("\n"));
				if (line.find("From: ") == 0) {
					from = line.substr(strlen("From: "));
				}
				else if (line.empty()) {
					msg = str.substr(1);
					break;
				}
				str = str.substr(str.find("\n") + 1);
			}

			std::list<BuddyInfo> roster;
			storageBackend->getBuddies(m_internalUser, roster);

			std::string to;
			BOOST_FOREACH(BuddyInfo &b, roster) {
				if (b.legacyName == from) {
					to = b.alias;
				}
			}

			if (to.empty()) {
				LOG4CXX_WARN(logger, "Received SMS from " << from << ", but this number is not associated with any XMPP user.");
			}

			LOG4CXX_INFO(logger, "Forwarding SMS from " << from << " to " << to << ".");
			handleMessage(to, from, msg);
		}

		void handleSMSDir() {
			std::string dir = "/var/spool/sms/incoming/";
			if (config->getUnregistered().find("backend.incoming_dir") != config->getUnregistered().end()) {
				dir = config->getUnregistered().find("backend.incoming_dir")->second;
			}
			LOG4CXX_INFO(logger, "Checking directory " << dir << " for incoming SMS.");

			path p(dir);
			directory_iterator end_itr;
			for (directory_iterator itr(p); itr != end_itr; ++itr) {

				try {
					if (is_regular(itr->path())) {
						handleSMS(itr->path().string());
						remove(itr->path());
					}
				}
				catch (const filesystem_error& ex) {
					LOG4CXX_ERROR(logger, "Error when removing the SMS: " << ex.what() << ".");
				}
			}
			m_timer->start();
		}

		void sendSMS(const std::string &to, const std::string &msg) {
			// TODO: Probably 
			std::string data = "To: " + to + "\n";
			data += "\n";
			data += msg;

			// generate random string here...
			std::string bucket = "abcdefghijklmnopqrstuvwxyz";
			std::string uuid;
			for (int i = 0; i < 10; i++) {
				uuid += bucket[rand() % bucket.size()];
			}
			std::ofstream myfile;
			myfile.open (std::string("/var/spool/sms/outgoing/spectrum." + uuid).c_str());
			myfile << data;
			myfile.close();
		}

		void sendData(const std::string &string) {
			m_conn->write(Swift::createSafeByteArray(string));
		}

		void _handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data) {
			std::string d(data->begin(), data->end());
			handleDataRead(d);
		}

		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
			UserInfo info;
			if (!storageBackend->getUser(user, info)) {
				handleDisconnected(user, 0, "Not registered user.");
				return;
			}
			std::list<BuddyInfo> roster;
			storageBackend->getBuddies(info.id, roster);

			// Send available presence to every number in the roster.
			BOOST_FOREACH(BuddyInfo &b, roster) {
				handleBuddyChanged(user, b.legacyName, b.alias, b.groups, pbnetwork::STATUS_ONLINE);
			}

			np->handleConnected(user);
		}

		void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
		}

		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml = "") {
			// Remove trailing +, because smstools doesn't use it in "From: " field for received messages.
			std::string n = legacyName;
			if (n.find("+") == 0) {
				n = n.substr(1);
			}

			// Create GSM Number - XMPP user pair to match the potential response and send it to the proper JID.
			BuddyInfo info;
			info.legacyName = n;
			info.alias = user;
			info.id = -1;
			info.subscription = "both";
			info.flags = 0;
			storageBackend->addBuddy(m_internalUser, info);

			LOG4CXX_INFO(logger, "Sending SMS from " << user << " to " << n << ".");
			sendSMS(n, message);
		}

		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) {
		}

		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) {
			LOG4CXX_INFO(logger, user << ": Added buddy " << buddyName << ".");
			handleBuddyChanged(user, buddyName, alias, groups, pbnetwork::STATUS_ONLINE);
		}

		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) {

		}


	private:
		
		Config *config;
};

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);
	}
}


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

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

	boost::program_options::options_description desc("Usage: spectrum [OPTIONS] <config_file.cfg>\nAllowed options");
	desc.add_options()
		("host,h", value<std::string>(&host), "host")
		("port,p", value<int>(&port), "port")
		;
	try
	{
		boost::program_options::variables_map vm;
		boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
		boost::program_options::notify(vm);
	}
	catch (std::runtime_error& e)
	{
		std::cout << desc << "\n";
		exit(1);
	}
	catch (...)
	{
		std::cout << desc << "\n";
		exit(1);
	}


	if (argc < 5) {
		return 1;
	}

// 	QStringList channels;
// 	for (int i = 3; i < argc; ++i)
// 	{
// 		channels.append(argv[i]);
// 	}
// 
// 	MyIrcSession session;
// 	session.setNick(argv[2]);
// 	session.setAutoJoinChannels(channels);
// 	session.connectToServer(argv[1], 6667);

	Config config;
	if (!config.load(argv[5])) {
		std::cerr << "Can't open " << argv[1] << " configuration file.\n";
		return 1;
	}

	Logging::initBackendLogging(&config);

	std::string error;
	StorageBackend *storageBackend = StorageBackend::createBackend(&config, error);
	if (storageBackend == NULL) {
		if (!error.empty()) {
			std::cerr << error << "\n";
			return -2;
		}
	}
	else if (!storageBackend->connect()) {
		std::cerr << "Can't connect to database. Check the log to find out the reason.\n";
		return -1;
	}

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

	return 0;
}