summaryrefslogtreecommitdiffstats
path: root/kopete/plugins/smpppdcs/libsmpppdclient
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitbcb704366cb5e333a626c18c308c7e0448a8e69f (patch)
treef0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kopete/plugins/smpppdcs/libsmpppdclient
downloadtdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz
tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kopete/plugins/smpppdcs/libsmpppdclient')
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/Makefile.am10
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.cpp104
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.h80
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.cpp104
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.h49
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.cpp76
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.h58
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.cpp153
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.h49
9 files changed, 683 insertions, 0 deletions
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/Makefile.am b/kopete/plugins/smpppdcs/libsmpppdclient/Makefile.am
new file mode 100644
index 00000000..9fc9258c
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/Makefile.am
@@ -0,0 +1,10 @@
+AM_CPPFLAGS = $(all_includes)
+
+noinst_LTLIBRARIES = libsmpppdclient.la
+libsmpppdclient_la_LDFLAGS = -avoid-version $(all_libraries)
+
+noinst_HEADERS = smpppdclient.h smpppdstate.h smpppdready.h smpppdunsettled.h
+libsmpppdclient_la_SOURCES = smpppdclient.cpp smpppdstate.cpp smpppdready.cpp \
+ smpppdunsettled.cpp
+
+libsmpppdclient_la_LIBADD = -lcrypto
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.cpp b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.cpp
new file mode 100644
index 00000000..d386b669
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.cpp
@@ -0,0 +1,104 @@
+/*
+ smpppdclient.cpp
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <kstreamsocket.h>
+
+#include "smpppdunsettled.h"
+#include "smpppdclient.h"
+
+using namespace SMPPPD;
+
+Client::Client()
+ : m_state(NULL), m_sock(NULL), m_serverID(QString::null), m_serverVer(QString::null), m_password(QString::null) {
+ changeState(Unsettled::instance());
+}
+
+Client::~Client() {
+ disconnect();
+}
+
+bool Client::connect(const QString& server, uint port) {
+ return m_state->connect(this, server, port);
+}
+
+void Client::disconnect() {
+ m_state->disconnect(this);
+}
+
+QStringList Client::getInterfaceConfigurations() {
+ return m_state->getInterfaceConfigurations(this);
+}
+
+bool Client::statusInterface(const QString& ifcfg) {
+ return m_state->statusInterface(this, ifcfg);
+}
+
+QString Client::serverID() const {
+ return m_serverID;
+}
+
+QString Client::serverVersion() const {
+ return m_serverVer;
+}
+
+QStringList Client::read() const {
+ QStringList qsl;
+
+ if(isReady()) {
+ QDataStream stream(m_sock);
+ char s[1024];
+
+ stream.readRawBytes(s, 1023);
+ char *sp = s;
+
+ for(int i = 0; i < 1024; i++) {
+ if(s[i] == '\n') {
+ s[i] = 0;
+ qsl.push_back(sp);
+ sp = &(s[i+1]);
+ }
+ }
+ }
+
+ return qsl;
+}
+
+void Client::write(const char * cmd) {
+ if(isReady()) {
+ QDataStream stream(m_sock);
+ stream.writeRawBytes(cmd, strlen(cmd));
+ stream.writeRawBytes("\n", strlen("\n"));
+ m_sock->flush();
+ }
+}
+
+bool Client::isReady() const {
+ return m_sock && m_sock->state() == KNetwork::KStreamSocket::Connected;
+}
+
+bool Client::isOnline() {
+
+ if(isReady()) {
+ QStringList ifcfgs = getInterfaceConfigurations();
+ for(uint i = 0; i < ifcfgs.count(); i++) {
+ if(statusInterface(ifcfgs[i])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.h b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.h
new file mode 100644
index 00000000..a123cd4c
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.h
@@ -0,0 +1,80 @@
+/*
+ smpppdclient.h
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDCLIENT_H
+#define SMPPPDCLIENT_H
+
+#include <qstringlist.h>
+
+namespace KNetwork {
+class KStreamSocket;
+};
+
+namespace SMPPPD {
+
+class State;
+
+/**
+ @author Heiko Schaefer <heiko@rangun.de>
+*/
+class Client {
+ Client(const Client&);
+ Client& operator=(const Client&);
+
+public:
+ Client();
+ ~Client();
+
+ bool isReady() const;
+
+ bool connect(const QString& server, uint port = 3185);
+ void disconnect();
+
+ QStringList getInterfaceConfigurations();
+ bool statusInterface(const QString& ifcfg);
+
+ bool isOnline();
+ QString serverID() const;
+ QString serverVersion() const;
+
+ void setPassword(const QString& password);
+
+private:
+ friend class State;
+
+ void changeState(State * newState);
+ QStringList read() const;
+ void write(const char * cmd);
+
+private:
+ State * m_state;
+ KNetwork::KStreamSocket * m_sock;
+ QString m_serverID;
+ QString m_serverVer;
+ QString m_password;
+};
+
+inline void Client::changeState(State * newState) {
+ m_state = newState;
+}
+
+inline void Client::setPassword(const QString& password) {
+ m_password = password;
+}
+
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.cpp b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.cpp
new file mode 100644
index 00000000..421914bb
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.cpp
@@ -0,0 +1,104 @@
+/*
+ smpppdready.cpp
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kstreamsocket.h>
+
+#include "smpppdunsettled.h"
+#include "smpppdclient.h"
+#include "smpppdready.h"
+
+using namespace SMPPPD;
+
+Ready * Ready::m_instance = NULL;
+
+Ready::Ready() {}
+
+Ready::~Ready() {}
+
+Ready * Ready::instance() {
+ if(!m_instance) {
+ m_instance = new Ready;
+ }
+
+ return m_instance;
+}
+
+void Ready::disconnect(Client * client) {
+ kdDebug(14312) << k_funcinfo << endl;
+ if(socket(client)) {
+ socket(client)->flush();
+ socket(client)->close();
+
+ delete socket(client);
+ setSocket(client, NULL);
+
+ setServerID(client, QString::null);
+ setServerVersion(client, QString::null);
+ }
+
+ changeState(client, Unsettled::instance());
+}
+
+QStringList Ready::getInterfaceConfigurations(Client * client) {
+
+ QStringList ifcfgs;
+
+ // we want all ifcfgs
+ kdDebug(14312) << k_funcinfo << "smpppd req: list-ifcfgs" << endl;
+ write(client, "list-ifcfgs");
+ QStringList stream = read(client);
+ kdDebug(14312) << k_funcinfo << "smpppd ack: " << stream[0] << endl;
+ if(stream[0].startsWith("ok")) {
+ // we have now a QStringList with all ifcfgs
+ // we extract them and put them in the global ifcfgs-list
+ // stream[1] tells us how many ifcfgs are coming next
+ QRegExp numIfcfgsRex("^BEGIN IFCFGS ([0-9]+).*");
+ if(numIfcfgsRex.exactMatch(stream[1])) {
+ int count_ifcfgs = numIfcfgsRex.cap(1).toInt();
+ kdDebug(14312) << k_funcinfo << "ifcfgs: " << count_ifcfgs << endl;
+
+ for(int i = 0; i < count_ifcfgs; i++) {
+ QRegExp ifcfgRex("^i \"(ifcfg-[a-zA-Z]+[0-9]+)\".*");
+ if(ifcfgRex.exactMatch(stream[i+2])) {
+ ifcfgs.push_back(ifcfgRex.cap(1));
+ }
+ }
+ }
+ }
+
+ return ifcfgs;
+}
+
+bool Ready::statusInterface(Client * client, const QString& ifcfg) {
+
+ QString cmd = "list-status " + ifcfg;
+
+ write(client, cmd.latin1());
+ socket(client)->waitForMore(0);
+
+ QStringList stream = read(client);
+
+ if(stream[0].startsWith("ok")) {
+ if(stream[2].startsWith("status connected")) {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.h b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.h
new file mode 100644
index 00000000..9ec3ab93
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.h
@@ -0,0 +1,49 @@
+/*
+ smpppdready.h
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDREADY_H
+#define SMPPPDREADY_H
+
+#include "smpppdstate.h"
+
+namespace SMPPPD {
+
+/**
+ @author Heiko Schaefer <heiko@rangun.de>
+*/
+class Ready : public State
+{
+ Ready(const Ready&);
+ Ready& operator=(const Ready&);
+
+ Ready();
+
+public:
+ virtual ~Ready();
+
+ static Ready * instance();
+
+ virtual void disconnect(Client * client);
+ virtual QStringList getInterfaceConfigurations(Client * client);
+ virtual bool statusInterface(Client * client, const QString& ifcfg);
+
+private:
+ static Ready * m_instance;
+};
+
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.cpp b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.cpp
new file mode 100644
index 00000000..9e4bd508
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.cpp
@@ -0,0 +1,76 @@
+/*
+ smpppdstate.cpp
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <kstreamsocket.h>
+
+#include "smpppdclient.h"
+#include "smpppdstate.h"
+
+using namespace SMPPPD;
+
+State::State() {}
+
+State::~State() {}
+
+QStringList State::read(Client * client) const {
+ return client->read();
+}
+
+void State::write(Client * client, const char * cmd) {
+ client->write(cmd);
+}
+
+void State::changeState(Client * client, State * state) {
+ client->changeState(state);
+}
+
+KNetwork::KStreamSocket * State::socket(Client * client) const {
+ return client->m_sock;
+}
+
+QString State::password(Client * client) const {
+ return client->m_password;
+}
+
+void State::setPassword(Client * client, const QString& pass) {
+ client->m_password = pass;
+}
+
+void State::setServerID(Client * client, const QString& id) {
+ client->m_serverID = id;
+}
+
+void State::setServerVersion(Client * client, const QString& ver) {
+ client->m_serverVer = ver;
+}
+
+void State::setSocket(Client * client, KNetwork::KStreamSocket * sock) {
+ client->m_sock = sock;
+}
+
+bool State::connect(Client * /* client */, const QString& /* server */, uint /* port */) {
+ return false;
+}
+
+void State::disconnect(Client * /* client */) {}
+
+QStringList State::getInterfaceConfigurations(Client * /* client */) {
+ return QStringList();
+}
+
+bool State::statusInterface(Client * /* client */, const QString& /* ifcfg */) {
+ return false;
+}
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.h b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.h
new file mode 100644
index 00000000..0e7d393b
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.h
@@ -0,0 +1,58 @@
+/*
+ smpppdstate.h
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDSTATE_H
+#define SMPPPDSTATE_H
+
+#include <qstringlist.h>
+
+namespace SMPPPD {
+
+class Client;
+
+/**
+ @author Heiko Schaefer <heiko@rangun.de>
+*/
+class State {
+ State(const State&);
+ State& operator=(const State&);
+
+public:
+ State();
+ virtual ~State();
+
+ virtual bool connect(Client * client, const QString& server, uint port = 3185);
+ virtual void disconnect(Client * client);
+
+ virtual QStringList getInterfaceConfigurations(Client * client);
+ virtual bool statusInterface(Client * client, const QString& ifcfg);
+
+protected:
+ QStringList read(Client * client) const;
+ void write(Client * client, const char * cmd);
+ void changeState(Client * client, State * state);
+ KNetwork::KStreamSocket * socket(Client * client) const;
+ void setSocket(Client * client, KNetwork::KStreamSocket * sock);
+ QString password(Client * client) const;
+ void setPassword(Client * client, const QString& pass);
+ void setServerID(Client * client, const QString& id);
+ void setServerVersion(Client * client, const QString& ver);
+
+};
+
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.cpp b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.cpp
new file mode 100644
index 00000000..7ed5f516
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.cpp
@@ -0,0 +1,153 @@
+/*
+ smpppdunsettled.cpp
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <cstdlib>
+#include <openssl/md5.h>
+
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kstreamsocket.h>
+
+#include "smpppdready.h"
+#include "smpppdunsettled.h"
+
+using namespace SMPPPD;
+
+Unsettled * Unsettled::m_instance = NULL;
+
+Unsettled::Unsettled() {}
+
+Unsettled::~Unsettled() {}
+
+Unsettled * Unsettled::instance() {
+ if(!m_instance) {
+ m_instance = new Unsettled();
+ }
+
+ return m_instance;
+}
+
+bool Unsettled::connect(Client * client, const QString& server, uint port) {
+ if(!socket(client) ||
+ socket(client)->state() != KNetwork::KStreamSocket::Connected ||
+ socket(client)->state() != KNetwork::KStreamSocket::Connecting) {
+
+ QString resolvedServer = server;
+
+ changeState(client, Ready::instance());
+ disconnect(client);
+
+ // since a lookup on a non-existant host can take a lot of time we
+ // try to get the IP of server before and we do the lookup ourself
+ KNetwork::KResolver resolver(server);
+ resolver.start();
+ if(resolver.wait(500)) {
+ KNetwork::KResolverResults results = resolver.results();
+ if(!results.empty()) {
+ QString ip = results[0].address().asInet().ipAddress().toString();
+ kdDebug(14312) << k_funcinfo << "Found IP-Address for " << server << ": " << ip << endl;
+ resolvedServer = ip;
+ } else {
+ kdWarning(14312) << k_funcinfo << "No IP-Address found for " << server << endl;
+ return false;
+ }
+ } else {
+ kdWarning(14312) << k_funcinfo << "Looking up hostname timed out, consider to use IP or correct host" << endl;
+ return false;
+ }
+
+ setSocket(client, new KNetwork::KStreamSocket(resolvedServer, QString::number(port)));
+ socket(client)->setBlocking(TRUE);
+
+ if(!socket(client)->connect()) {
+ kdDebug(14312) << k_funcinfo << "Socket Error: " << KNetwork::KStreamSocket::errorString(socket(client)->error()) << endl;
+ } else {
+ kdDebug(14312) << k_funcinfo << "Successfully connected to smpppd \"" << server << ":" << port << "\"" << endl;
+
+ static QString verRex = "^SuSE Meta pppd \\(smpppd\\), Version (.*)$";
+ static QString clgRex = "^challenge = (.*)$";
+
+ QRegExp ver(verRex);
+ QRegExp clg(clgRex);
+
+ QString response = read(client)[0];
+
+ if(response != QString::null &&
+ ver.exactMatch(response)) {
+ setServerID(client, response);
+ setServerVersion(client, ver.cap(1));
+ changeState(client, Ready::instance());
+ return true;
+ } else if(response != QString::null &&
+ clg.exactMatch(response)) {
+ if(password(client) != QString::null) {
+ // we are challenged, ok, respond
+ write(client, QString("response = %1\n").arg(make_response(clg.cap(1).stripWhiteSpace(), password(client))).latin1());
+ response = read(client)[0];
+ if(ver.exactMatch(response)) {
+ setServerID(client, response);
+ setServerVersion(client, ver.cap(1));
+ return true;
+ } else {
+ kdWarning(14312) << k_funcinfo << "SMPPPD responded: " << response << endl;
+ changeState(client, Ready::instance());
+ disconnect(client);
+ }
+ } else {
+ kdWarning(14312) << k_funcinfo << "SMPPPD requested a challenge, but no password was supplied!" << endl;
+ changeState(client, Ready::instance());
+ disconnect(client);
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+QString Unsettled::make_response(const QString& chex, const QString& password) const {
+
+ int size = chex.length ();
+ if (size & 1)
+ return "error";
+ size >>= 1;
+
+ // convert challenge from hex to bin
+ QString cbin;
+ for (int i = 0; i < size; i++) {
+ QString tmp = chex.mid (2 * i, 2);
+ cbin.append ((char) strtol (tmp.ascii (), 0, 16));
+ }
+
+ // calculate response
+ unsigned char rbin[MD5_DIGEST_LENGTH];
+ MD5state_st md5;
+ MD5_Init (&md5);
+ MD5_Update (&md5, cbin.ascii (), size);
+ MD5_Update (&md5, password.ascii(), password.length ());
+ MD5_Final (rbin, &md5);
+
+ // convert response from bin to hex
+ QString rhex;
+ for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
+ char buffer[3];
+ snprintf (buffer, 3, "%02x", rbin[i]);
+ rhex.append (buffer);
+ }
+
+ return rhex;
+}
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.h b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.h
new file mode 100644
index 00000000..57a83752
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.h
@@ -0,0 +1,49 @@
+/*
+ smpppdunsettled.h
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDSMPPPDUNSETTLED_H
+#define SMPPPDSMPPPDUNSETTLED_H
+
+#include "smpppdstate.h"
+
+namespace SMPPPD {
+
+/**
+ @author Heiko Schaefer <heiko@rangun.de>
+*/
+class Unsettled : public State
+{
+ Unsettled(const Unsettled&);
+ Unsettled& operator=(const Unsettled&);
+
+ Unsettled();
+public:
+ virtual ~Unsettled();
+
+ static Unsettled * instance();
+
+ virtual bool connect(Client * client, const QString& server, uint port = 3185);
+
+private:
+ QString make_response(const QString& chex, const QString& password) const;
+
+private:
+ static Unsettled * m_instance;
+};
+
+}
+
+#endif