From 18bfa12042360dba3bde180315d19a27098b5c4c Mon Sep 17 00:00:00 2001 From: John Maguire Date: Thu, 7 Apr 2011 13:38:27 +0000 Subject: [PATCH] Add the beginnings of an ICE socket implementation. Currently, it can be demoed using --stun-test and the icedemo from pjsip as the other client. TODOs: Add ability to do a full demo using only 2 clementine instances. Complete session initiation over XMPP. Add local & port forwarded sockets as options. --- 3rdparty/libxrme/connection.cpp | 37 ++++- 3rdparty/libxrme/connection.h | 16 ++ 3rdparty/libxrme/extensions.cpp | 2 + 3rdparty/libxrme/extensions.h | 6 + CMakeLists.txt | 12 +- src/CMakeLists.txt | 10 +- src/core/commandlineoptions.cpp | 12 +- src/core/commandlineoptions.h | 9 + src/main.cpp | 65 ++++++++ src/remote/icesession.cpp | 280 ++++++++++++++++++++++++++++++++ src/remote/icesession.h | 64 ++++++++ src/remote/remote.cpp | 2 +- 12 files changed, 506 insertions(+), 9 deletions(-) create mode 100644 src/remote/icesession.cpp create mode 100644 src/remote/icesession.h diff --git a/3rdparty/libxrme/connection.cpp b/3rdparty/libxrme/connection.cpp index c99b1dfa7..82f798106 100644 --- a/3rdparty/libxrme/connection.cpp +++ b/3rdparty/libxrme/connection.cpp @@ -47,7 +47,8 @@ struct Connection::Private : public gloox::ConnectionListener, public gloox::LogHandler, public gloox::RosterListener, public gloox::DiscoHandler, - public gloox::MessageHandler { + public gloox::MessageHandler, + public gloox::IqHandler { Private(Connection* parent) : parent_(parent), server_(kDefaultServer), @@ -287,6 +288,9 @@ bool Connection::Connect() { d->client_->registerStanzaExtension(d->media_player_extension_); d->client_->registerStanzaExtension(d->remote_control_extension_); d->client_->registerStanzaExtension(new MediaStorageExtension); + d->client_->registerStanzaExtension(new SIPNegotiation); + + d->client_->registerIqHandler(d.data(), SIPNegotiation::kExtensionType); // Initialise the handlers foreach (Handler* handler, d->handlers_) { @@ -302,7 +306,7 @@ bool Connection::Connect() { caps->setNode("http://tomahawk-player.org/"); d->client_->presence().addExtension(caps); - d->client_->setSASLMechanisms(gloox::SaslMechGoogleToken); + //d->client_->setSASLMechanisms(gloox::SaslMechGoogleToken); // Connect if (!d->client_->connect(false)) { @@ -584,4 +588,33 @@ void Connection::Private::handleMessage(const gloox::Message& message, gloox::Me } } +bool Connection::Private::handleIq(const gloox::IQ& iq) { + gloox::Tag* xrme = iq.tag()->findChild("xrme"); + if (!xrme) { + return false; + } + + gloox::Tag* sip = xrme->findChild("sip"); + const std::string& user_fragment = sip->findAttribute("ufrag"); + const std::string& password = sip->findAttribute("password"); + + gloox::TagList candidates = sip->findChildren("candidate"); + for (gloox::TagList::const_iterator it = candidates.begin(); + it != candidates.end(); ++it) { + gloox::Tag* candidate = *it; + const std::string& address = candidate->findAttribute("address"); + const std::string& port = candidate->findAttribute("port"); + const std::string& type = candidate->findAttribute("type"); + const std::string& component = candidate->findAttribute("component"); + const std::string& priority = candidate->findAttribute("priority"); + const std::string& foundation = candidate->findAttribute("foundation"); + } + + return true; +} + +void Connection::Private::handleIqID(const gloox::IQ& iq, int context) { + +} + } // namespace xrme diff --git a/3rdparty/libxrme/connection.h b/3rdparty/libxrme/connection.h index 2c8da6fa0..e13117855 100644 --- a/3rdparty/libxrme/connection.h +++ b/3rdparty/libxrme/connection.h @@ -18,6 +18,7 @@ #ifndef LIBXRME_CONNECTION_H #define LIBXRME_CONNECTION_H +#include #include #include #include @@ -29,6 +30,21 @@ class MediaPlayerInterface; class MediaStorageInterface; class RemoteControlInterface; +struct SIPInfo { + QString user_fragment; + QString password; + + struct Candidate { + QHostAddress address; + quint16 port; + QString type; + int component; + int priority; + QString foundation; + }; + QList candidates; +}; + class Connection : public QObject { Q_OBJECT diff --git a/3rdparty/libxrme/extensions.cpp b/3rdparty/libxrme/extensions.cpp index 8a5c1e318..afaaca28b 100644 --- a/3rdparty/libxrme/extensions.cpp +++ b/3rdparty/libxrme/extensions.cpp @@ -8,5 +8,7 @@ const char* RemoteControlExtension::kFilterString = "/iq/xrme[@xmlns='http://purplehatstands.com/xmlns/xrme/remotecontrol']"; const char* MediaStorageExtension::kFilterString = "/iq/xrme[@xmlns='http://purplehatstands.com/xmlns/xrme/mediastorage']"; +const char* SIPNegotiation::kFilterString = + "/iq/xrme[@xmlns='http://purplehatstands.com/xmlns/xrme/sipnegotiation']"; } diff --git a/3rdparty/libxrme/extensions.h b/3rdparty/libxrme/extensions.h index f13eaa3a7..712290d89 100644 --- a/3rdparty/libxrme/extensions.h +++ b/3rdparty/libxrme/extensions.h @@ -52,6 +52,12 @@ class MediaStorageExtension : public XRMEExtension { static const char* kFilterString; }; +class SIPNegotiation : public XRMEExtension { + public: + static const int kExtensionType = gloox::ExtUser + 4; + static const char* kFilterString; +}; + template XRMEExtension::XRMEExtension() : StanzaExtension(T::kExtensionType), diff --git a/CMakeLists.txt b/CMakeLists.txt index e0a0deac5..a95b7af7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -236,11 +236,15 @@ endif(ENABLE_VISUALISATIONS) option(STATIC_SQLITE "Compile and use a static sqlite3 library" ON) pkg_check_modules(GNUTLS gnutls) -if (GNUTLS_LIBRARIES) +if(GNUTLS_LIBRARIES) set(HAVE_GNUTLS 1) -endif (GNUTLS_LIBRARIES) +endif(GNUTLS_LIBRARIES) +pkg_check_modules(PJSIP libpjproject>=1.8) +if(PJSIP_LIBRARIES) + set(HAVE_PJSIP 1) +endif(PJSIP_LIBRARIES) -if(ENABLE_REMOTE AND HAVE_GNUTLS) +if(ENABLE_REMOTE AND HAVE_GNUTLS AND HAVE_PJSIP) set(HAVE_REMOTE ON) add_subdirectory(3rdparty/gloox) add_subdirectory(3rdparty/libxrme) @@ -248,7 +252,7 @@ if(ENABLE_REMOTE AND HAVE_GNUTLS) include_directories(3rdparty) include_directories(3rdparty/libxrme) include_directories(3rdparty/libportfwd/include) -endif(ENABLE_REMOTE AND HAVE_GNUTLS) +endif(ENABLE_REMOTE AND HAVE_GNUTLS AND HAVE_PJSIP) set(HAVE_STATIC_SQLITE ${STATIC_SQLITE}) if(STATIC_SQLITE) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 05cfb51ce..bf27ecb22 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,6 +43,10 @@ if(HAVE_BREAKPAD) include_directories(../3rdparty/google-breakpad) endif(HAVE_BREAKPAD) +if(HAVE_REMOTE) + include_directories(${PJSIP_INCLUDE_DIRS}) +endif(HAVE_REMOTE) + cmake_policy(SET CMP0011 NEW) include(../cmake/ParseArguments.cmake) include(../cmake/Translations.cmake) @@ -792,11 +796,13 @@ endif(HAVE_SCRIPTING_PYTHON) if(HAVE_REMOTE) list(APPEND HEADERS + remote/icesession.h remote/portforwarder.h remote/remote.h remote/remoteconfig.h ) list(APPEND SOURCES + remote/icesession.cpp remote/portforwarder.h remote/remote.cpp remote/remoteconfig.cpp @@ -964,10 +970,12 @@ endif(HAVE_SCRIPTING_PYTHON) if(HAVE_REMOTE) target_link_libraries(clementine_lib ${GLOOX_LIBRARIES}) + link_directories(${GLOOX_LIBRARY_DIRS}) target_link_libraries(clementine_lib xrme) target_link_libraries(clementine_lib portfwd) target_link_libraries(clementine_lib qjson) - link_directories(${GLOOX_LIBRARY_DIRS}) + target_link_libraries(clementine_lib ${PJSIP_LIBRARIES}) + link_directories(${PJSIP_LIBRARY_DIRS}) endif(HAVE_REMOTE) if(HAVE_BREAKPAD) diff --git a/src/core/commandlineoptions.cpp b/src/core/commandlineoptions.cpp index 067299f3d..71c8d78cf 100644 --- a/src/core/commandlineoptions.cpp +++ b/src/core/commandlineoptions.cpp @@ -61,7 +61,8 @@ CommandlineOptions::CommandlineOptions(int argc, char** argv) seek_to_(-1), seek_by_(0), play_track_at_(-1), - show_osd_(false) + show_osd_(false), + stun_test_(StunTestNone) { #ifdef Q_OS_DARWIN // Remove -psn_xxx option that Mac passes when opened from Finder. @@ -108,6 +109,8 @@ bool CommandlineOptions::Parse() { {"show-osd", no_argument, 0, 'o'}, {"language", required_argument, 0, 'g'}, + {"stun-test", required_argument, 0, 'z'}, + {0, 0, 0, 0} }; @@ -180,6 +183,13 @@ bool CommandlineOptions::Parse() { if (!ok) play_track_at_ = -1; break; + case 'z': { + // Stun test + QString direction = QString(optarg); + stun_test_ = direction == "offer" ? StunTestOffer : StunTestAccept; + break; + } + case '?': default: return false; diff --git a/src/core/commandlineoptions.h b/src/core/commandlineoptions.h index 4bf2ba84d..259a51e7b 100644 --- a/src/core/commandlineoptions.h +++ b/src/core/commandlineoptions.h @@ -46,6 +46,11 @@ class CommandlineOptions { Player_Previous = 5, Player_Next = 6, }; + enum StunTestDirection { + StunTestNone = 0, + StunTestOffer = 1, + StunTestAccept = 2, + }; bool Parse(); @@ -62,6 +67,9 @@ class CommandlineOptions { QList urls() const { return urls_; } QString language() const { return language_; } + StunTestDirection stun_test() const { return stun_test_; } + + QByteArray Serialize() const; void Load(const QByteArray& serialized); @@ -93,6 +101,7 @@ class CommandlineOptions { int play_track_at_; bool show_osd_; QString language_; + StunTestDirection stun_test_; QList urls_; }; diff --git a/src/main.cpp b/src/main.cpp index 571f376b3..f094ae3b0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -130,6 +130,9 @@ void GLog(const gchar* domain, qDebug() << "GLOG" << message; } +#include +#include "remote/icesession.h" + int main(int argc, char *argv[]) { if (CrashReporting::SendCrashReport(argc, argv)) { return 0; @@ -213,6 +216,8 @@ int main(int argc, char *argv[]) { qRegisterMetaType("GstElement*"); qRegisterMetaType("GstEnginePipeline*"); + qRegisterMetaType("SIPInfo"); + #ifdef HAVE_LIBLASTFM lastfm::ws::ApiKey = LastFMService::kApiKey; lastfm::ws::SharedSecret = LastFMService::kSecret; @@ -248,6 +253,66 @@ int main(int argc, char *argv[]) { UniversalEncodingHandler handler; TagLib::ID3v1::Tag::setStringHandler(&handler); + if (options.stun_test() != CommandlineOptions::StunTestNone) { + QCoreApplication app(argc, argv); + + ICESession::StaticInit(); + ICESession ice; + ice.Init(); + + QEventLoop loop; + QObject::connect(&ice, + SIGNAL(CandidatesAvailable(const SessionInfo&)), + &loop, SLOT(quit())); + loop.exec(); + + const xrme::SIPInfo& candidates = ice.candidates(); + qDebug() << candidates; + + QFile file; + file.open(stdin, QIODevice::ReadOnly); + QTextStream in(&file); + + xrme::SIPInfo remote_session; + qDebug() << "fragment"; + in >> remote_session.user_fragment; + qDebug() << "password"; + in >> remote_session.password; + + xrme::SIPInfo::Candidate cand; + QString address; + qDebug() << "address"; + in >> address; + cand.address = address; + qDebug() << "port"; + in >> cand.port; + QString type; + qDebug() << "type"; + in >> type; + if (type == "host") { + cand.type = PJ_ICE_CAND_TYPE_HOST; + } else if (type == "srflx") { + cand.type = PJ_ICE_CAND_TYPE_SRFLX; + } else if (type == "prflx") { + cand.type = PJ_ICE_CAND_TYPE_PRFLX; + } + qDebug() << "component"; + in >> cand.component; + qDebug() << "priority"; + in >> cand.priority; + qDebug() << "foundation"; + in >> cand.foundation; + + remote_session.candidates << cand; + + qDebug() << "Remote:" << remote_session; + + ice.StartNegotiation(remote_session); + loop.exec(); + + return 0; + } + QtSingleApplication a(argc, argv); #ifdef Q_OS_DARWIN QCoreApplication::setLibraryPaths( diff --git a/src/remote/icesession.cpp b/src/remote/icesession.cpp new file mode 100644 index 000000000..4f4eae463 --- /dev/null +++ b/src/remote/icesession.cpp @@ -0,0 +1,280 @@ +#include "icesession.h" + +#include + +using xrme::SIPInfo; + +const char* ICESession::kStunServer = "stunserver.org"; +int ICESession::sComponentId = 0; +pj_caching_pool ICESession::sCachingPool; +pj_ice_strans_cfg ICESession::sIceConfig; + +pj_pool_t* ICESession::sPool; +pj_thread_t* ICESession::sThread; + +ICESession::ICESession(QObject* parent) + : QObject(parent) { +} + + +bool ICESession::Init() { + // Create instance. + pj_ice_strans_cb ice_cb; + pj_bzero(&ice_cb, sizeof(ice_cb)); + + ice_cb.on_rx_data = &OnReceiveData; + ice_cb.on_ice_complete = &OnICEComplete; + + component_id_ = ++sComponentId; + + pj_status_t status = pj_ice_strans_create( + "clementine", + &sIceConfig, + component_id_, + this, + &ice_cb, + &ice_instance_); + if (status != PJ_SUCCESS) { + qWarning() << "Failed to create ICE instance"; + return false; + } + + // TODO + pj_ice_sess_role role = PJ_ICE_SESS_ROLE_CONTROLLING; + + status = pj_ice_strans_init_ice(ice_instance_, role, NULL, NULL); + + qDebug() << "Init ice:" << status; + + return true; +} + +QString CandidateTypeToString(pj_ice_cand_type type) { + switch (type) { + case PJ_ICE_CAND_TYPE_HOST: + return "host"; + case PJ_ICE_CAND_TYPE_SRFLX: + return "srflx"; + case PJ_ICE_CAND_TYPE_PRFLX: + return "prflx"; + case PJ_ICE_CAND_TYPE_RELAYED: + return "relayed"; + } +} + +pj_ice_cand_type CandidateStringToType(const QString& type) { + if (type == "host") { + return PJ_ICE_CAND_TYPE_HOST; + } else if (type == "srflx") { + return PJ_ICE_CAND_TYPE_SRFLX; + } else if (type == "prflx") { + return PJ_ICE_CAND_TYPE_PRFLX; + } else if (type == "relayed") { + return PJ_ICE_CAND_TYPE_RELAYED; + } + return PJ_ICE_CAND_TYPE_HOST; +} + +void ICESession::InitialisationComplete(pj_status_t status) { + int candidates = pj_ice_strans_get_cands_count(ice_instance_, component_id_); + pj_ice_sess_cand cand[candidates]; + pj_ice_strans_get_def_cand(ice_instance_, component_id_, &cand[0]); + + pj_str_t ufrag; + pj_str_t pwd; + pj_ice_strans_get_ufrag_pwd(ice_instance_, &ufrag, &pwd, NULL, NULL); + + candidates_.user_fragment = QString::fromAscii(ufrag.ptr, ufrag.slen); + candidates_.password = QString::fromAscii(pwd.ptr, pwd.slen); + + for (int i = 0; i < candidates; ++i) { + int port = pj_sockaddr_get_port(&cand[i].addr); + char ipaddr[PJ_INET6_ADDRSTRLEN]; + pj_sockaddr_print(&cand[i].addr, ipaddr, sizeof(ipaddr), 0); + + QHostAddress address(QString::fromAscii(ipaddr)); + + SIPInfo::Candidate candidate; + candidate.address = address; + candidate.port = port; + candidate.type = CandidateTypeToString(cand[i].type); + candidate.component = component_id_; + candidate.priority = cand[i].prio; + candidate.foundation = QString::fromAscii( + cand[i].foundation.ptr, cand[i].foundation.slen); + + candidates_.candidates << candidate; + } + + emit CandidatesAvailable(candidates_); +} + + +void ICESession::StartNegotiation(const xrme::SIPInfo& session) { + pj_str_t remote_ufrag; + pj_str_t remote_password; + + pj_cstr(&remote_ufrag, strdup(session.user_fragment.toAscii().constData())); + pj_cstr(&remote_password, strdup(session.password.toAscii().constData())); + + pj_ice_sess_cand candidates[session.candidates.size()]; + for (int i = 0; i < session.candidates.size(); ++i) { + const xrme::SIPInfo::Candidate c = session.candidates[i]; + pj_ice_sess_cand* candidate = &candidates[i]; + pj_bzero(candidate, sizeof(*candidate)); + + candidate->type = CandidateStringToType(c.type); + candidate->comp_id = c.component; + candidate->prio = c.priority; + int af = c.address.protocol() == QAbstractSocket::IPv6Protocol + ? pj_AF_INET6() + : pj_AF_INET(); + + pj_sockaddr_init(af, &candidate->addr, NULL, 0); + pj_str_t temp_addr; + pj_cstr(&temp_addr, c.address.toString().toAscii().constData()); + pj_sockaddr_set_str_addr(af, &candidate->addr, &temp_addr); + pj_sockaddr_set_port(&candidate->addr, c.port); + + pj_cstr(&candidate->foundation, c.foundation.toAscii().constData()); + } + + pj_status_t status = pj_ice_strans_start_ice( + ice_instance_, + &remote_ufrag, + &remote_password, + session.candidates.size(), + candidates); + + if (status != PJ_SUCCESS) { + qWarning() << "Start negotation failed"; + } else { + qDebug() << "ICE negotiation started"; + } +} + + +void ICESession::OnReceiveData(pj_ice_strans* ice_st, + unsigned comp_id, + void* pkt, + pj_size_t size, + const pj_sockaddr_t* src_addr, + unsigned src_addr_len) { + qDebug() << "Received data"; +} + +void ICESession::OnICEComplete(pj_ice_strans* ice_st, + pj_ice_strans_op op, + pj_status_t status) { + ICESession* me = reinterpret_cast(pj_ice_strans_get_user_data(ice_st)); + const char* op_name = NULL; + switch (op) { + case PJ_ICE_STRANS_OP_INIT: + op_name = "initialisation"; + me->InitialisationComplete(status); + break; + case PJ_ICE_STRANS_OP_NEGOTIATION: + op_name = "negotation"; + break; + default: + op_name = "unknown"; + } + + qDebug() << op_name << (status == PJ_SUCCESS ? "succeeded" : "failed"); + +} + +int ICESession::HandleEvents(unsigned max_msec, unsigned* p_count) { + pj_time_val max_timeout = { 0, 0 }; + max_timeout.msec = max_msec; + + pj_time_val timeout = { 0, 0 }; + int c = pj_timer_heap_poll(sIceConfig.stun_cfg.timer_heap, &timeout); + int count = 0; + if (c > 0) { + count += c; + } + + if (timeout.msec >= 1000) { + timeout.msec = 999; + } + + if (PJ_TIME_VAL_GT(timeout, max_timeout)) { + timeout = max_timeout; + } + + static const int kMaxNetEvents = 1; + int net_event_count = 0; + do { + c = pj_ioqueue_poll(sIceConfig.stun_cfg.ioqueue, &timeout); + if (c < 0) { + pj_status_t err = pj_get_netos_error(); + pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout)); + if (p_count) { + *p_count = count; + } + return err; + } else if (c == 0) { + break; + } else { + net_event_count += c; + timeout.sec = 0; + timeout.msec = 0; + } + + } while (c > 0 && net_event_count < kMaxNetEvents); + + count += net_event_count; + if (p_count) { + *p_count = count; + } + + return PJ_SUCCESS; +} + +int ICESession::WorkerThread(void*) { + forever { + HandleEvents(500, NULL); + } + return 0; +} + +void ICESession::StaticInit() { + pj_init(); + pjlib_util_init(); + pjnath_init(); + + pj_caching_pool_init(&sCachingPool, NULL, 0); + pj_ice_strans_cfg_default(&sIceConfig); + + sIceConfig.stun_cfg.pf = &sCachingPool.factory; + + sPool = pj_pool_create(&sCachingPool.factory, "clementine", 512, 512, NULL); + + pj_timer_heap_create(sPool, 100, &sIceConfig.stun_cfg.timer_heap); + pj_ioqueue_create(sPool, 16, &sIceConfig.stun_cfg.ioqueue); + + pj_thread_create(sPool, "clementine", &WorkerThread, NULL, 0, 0, &sThread); + + sIceConfig.af = pj_AF_INET(); + + sIceConfig.stun.server.ptr = strdup(kStunServer); + sIceConfig.stun.server.slen = strlen(kStunServer); + sIceConfig.stun.port = PJ_STUN_PORT; +} + +QDebug operator<< (QDebug dbg, const xrme::SIPInfo& session) { + dbg.nospace() << "fragment:" << session.user_fragment << "\n"; + dbg.nospace() << "password:" << session.password << "\n"; + + foreach (const xrme::SIPInfo::Candidate& c, session.candidates) { + dbg.space() << "Candidate:" << "\n"; + dbg.space() << c.address.toString() << ":" << c.port << "\n"; + dbg.space() << "type:" << c.type << "\n"; + dbg.space() << "component:" << c.component << "\n"; + dbg.space() << "priority:" << c.priority << "\n"; + dbg.space() << "foundation:" << c.foundation << "\n"; + } + + return dbg.space(); +} diff --git a/src/remote/icesession.h b/src/remote/icesession.h new file mode 100644 index 000000000..500416f83 --- /dev/null +++ b/src/remote/icesession.h @@ -0,0 +1,64 @@ +#ifndef ICESESSION_H +#define ICESESSION_H + +#include +#include +#include + +#include +#include +#include + +#include + + +class ICESession : public QObject { + Q_OBJECT + public: + explicit ICESession(QObject* parent = 0); + + static void StaticInit(); + bool Init(); + + const xrme::SIPInfo& candidates() const { return candidates_; } + + void StartNegotiation(const xrme::SIPInfo& session); + + signals: + void CandidatesAvailable(const xrme::SIPInfo& candidates); + + private: + pj_ice_strans* ice_instance_; + int component_id_; + + xrme::SIPInfo candidates_; + + void InitialisationComplete(pj_status_t status); + + + static int HandleEvents(unsigned max_msec, unsigned* p_count); + static int WorkerThread(void*); + + static void OnReceiveData(pj_ice_strans* ice_st, + unsigned comp_id, + void* pkt, + pj_size_t size, + const pj_sockaddr_t* src_addr, + unsigned src_addr_len); + static void OnICEComplete(pj_ice_strans* ice_st, + pj_ice_strans_op op, + pj_status_t status); + + static pj_pool_t* sPool; + static pj_caching_pool sCachingPool; + static pj_ice_strans_cfg sIceConfig; + static pj_thread_t* sThread; + + static int sComponentId; + + static const char* kStunServer; +}; + +QDebug operator<< (QDebug dbg, const xrme::SIPInfo& session); + +#endif // ICESESSION_H diff --git a/src/remote/remote.cpp b/src/remote/remote.cpp index d70f4b758..effc00eba 100644 --- a/src/remote/remote.cpp +++ b/src/remote/remote.cpp @@ -62,7 +62,7 @@ void Remote::ReloadSettings() { s.beginGroup(RemoteConfig::kSettingsGroup); QString username = s.value("username").toString(); - QString password = s.value("token").toString(); + QString password = s.value("password").toString(); QString agent_name = s.value("agent_name", RemoteConfig::DefaultAgentName()).toString(); // Have the settings changed?