diff --git a/iris/src/irisnet/corelib/netnames.cpp b/iris/src/irisnet/corelib/netnames.cpp index 2ec3185e3..336f2f38a 100644 --- a/iris/src/irisnet/corelib/netnames.cpp +++ b/iris/src/irisnet/corelib/netnames.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Justin Karneges + * Copyright (C) 2009-2010 Dennis Schridde * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,11 +21,21 @@ #include "netnames.h" +#include + //#include #include "irisnetplugin.h" #include "irisnetglobal_p.h" #include "addressresolver.h" + +//#define NETNAMES_DEBUG + +#ifdef NETNAMES_DEBUG +# define NNDEBUG (qDebug() << this << "#" << __FUNCTION__ << ":") +#endif + + namespace XMPP { //---------------------------------------------------------------------------- @@ -75,6 +86,36 @@ NameRecord & NameRecord::operator=(const NameRecord &from) return *this; } +bool NameRecord::operator==(const NameRecord &o) { + if (isNull() != o.isNull() || owner() != o.owner() || ttl() != o.ttl() || type() != o.type()) { + return false; + } + + switch (type()) { + case XMPP::NameRecord::A: + case XMPP::NameRecord::Aaaa: + return address() == o.address(); + case XMPP::NameRecord::Mx: + return name() == o.name() && priority() == o.priority(); + case XMPP::NameRecord::Srv: + return name() == o.name() && port() == o.port() && priority() == o.priority() && weight() == o.weight(); + case XMPP::NameRecord::Cname: + case XMPP::NameRecord::Ptr: + case XMPP::NameRecord::Ns: + return name() == o.name(); + case XMPP::NameRecord::Txt: + return texts() == o.texts(); + case XMPP::NameRecord::Hinfo: + return cpu() == o.cpu() && os() == o.os(); + case XMPP::NameRecord::Null: + return rawData() == o.rawData(); + case XMPP::NameRecord::Any: + return false; + } + + return false; +} + bool NameRecord::isNull() const { return (d ? false : true); @@ -319,12 +360,14 @@ QDebug operator<<(QDebug dbg, const XMPP::NameRecord &record) dbg.nospace() << ", size=" << record.rawData().size(); break; case XMPP::NameRecord::Any: - // can't happen - Q_ASSERT(0); + dbg.nospace() << ", "; + // should not happen + Q_ASSERT(false); break; } dbg.nospace() << ")"; + return dbg; } @@ -435,121 +478,187 @@ class ServiceResolver::Private : public QObject { Q_OBJECT public: - ServiceResolver *q; + Private(ServiceResolver *parent) + : q(parent), dns_sd_resolve_id(0), requestedProtocol(IPv6_IPv4), port(0), protocol(QAbstractSocket::IPv6Protocol) + { + } - int id; + /* DNS-SD interaction with NameManager */ + ServiceResolver *q; //!< Pointing upwards, so NameManager can call its signals + int dns_sd_resolve_id; //!< DNS-SD lookup id, set by NameManager - int mode; - NameResolver dns; - AddressResolver adns; - int port; + /* configuration */ + Protocol requestedProtocol; //!< IP protocol requested by user - class Server - { - public: - QByteArray host; - int port; - int priority; - int weight; - }; + /* state trackers */ + QString domain; //!< Domain we are currently looking up + QString host; //!< Hostname we are currently looking up + QHostAddress address; //!< IP address we are currently looking up + quint16 port; //!< Port we are currently looking up + QAbstractSocket::NetworkLayerProtocol protocol; //!< IP protocol we are currently looking up - QList servers; - QList addrs; + XMPP::WeightedNameRecordList srvList; //!< List of resolved SRV names + QList hostList; //!< List or resolved hostnames for current SRV name + QList resolverList; //!< NameResolvers currently in use, needed for cleanup - Private(ServiceResolver *_q) : q(_q) - { - mode = 3; - connect(&dns, SIGNAL(resultsReady(const QList &)), SLOT(dns_resultsReady(const QList &))); - connect(&dns, SIGNAL(error(XMPP::NameResolver::Error)), SLOT(dns_error(XMPP::NameResolver::Error))); - connect(&adns, SIGNAL(resultsReady(const QList &)), SLOT(adns_resultsReady(const QList &))); - connect(&adns, SIGNAL(error(XMPP::AddressResolver::Error)), SLOT(adns_error(XMPP::AddressResolver::Error))); +}; + + +WeightedNameRecordList::WeightedNameRecordList() + : currentPriorityGroup(priorityGroups.end()) /* void current state */ +{} + +WeightedNameRecordList::WeightedNameRecordList(const QList &list) +{ + append(list); +} + +WeightedNameRecordList::~WeightedNameRecordList() { +} + +bool WeightedNameRecordList::empty() const { + return currentPriorityGroup == priorityGroups.end(); +} + +XMPP::NameRecord WeightedNameRecordList::takeNext() { + /* Find the next useful priority group */ + while (currentPriorityGroup != priorityGroups.end() && currentPriorityGroup->empty()) { + currentPriorityGroup++; + } + /* There are no priority groups left, return failure */ + if (currentPriorityGroup == priorityGroups.end()) { +#ifdef NETNAMES_DEBUG + NNDEBUG << "No more SRV records left"; +#endif + return XMPP::NameRecord(); } - void tryNext() - { - if(mode == 3) - { - QMetaObject::invokeMethod(q, "finished", Qt::QueuedConnection); - } - if(mode == 2) - { - if(!addrs.isEmpty()) - { - QHostAddress addr = addrs.takeFirst(); - QMetaObject::invokeMethod(q, "resultsReady", Qt::QueuedConnection, Q_ARG(QHostAddress, addr), Q_ARG(int, port)); - return; - } + /* Find the new total weight of this priority group */ + int totalWeight = 0; + foreach (const XMPP::NameRecord &record, *currentPriorityGroup) { + totalWeight += record.weight(); + } - if(servers.isEmpty()) - { - QMetaObject::invokeMethod(q, "finished", Qt::QueuedConnection); - return; - } +#ifdef NETNAMES_DEBUG + NNDEBUG << "Total weight:" << totalWeight; +#endif - Server serv = servers.takeFirst(); - port = serv.port; - adns.start(serv.host); - } - else - { - if(addrs.isEmpty()) - { - QMetaObject::invokeMethod(q, "finished", Qt::QueuedConnection); - return; - } + /* Pick a random entry */ + int randomWeight = qrand()/static_cast(RAND_MAX)*totalWeight; + +#ifdef NETNAMES_DEBUG + NNDEBUG << "Picked weight:" << randomWeight; +#endif + + /* Iterate through the priority group until we found the randomly selected entry */ + WeightedNameRecordPriorityGroup::iterator it(currentPriorityGroup->begin()); + for (int currentWeight = it->weight(); currentWeight < randomWeight; currentWeight += (++it)->weight()) {} + Q_ASSERT(it != currentPriorityGroup->end()); + + /* We are going to delete the entry in the list, so save it */ + XMPP::NameRecord result(*it); + +#ifdef NETNAMES_DEBUG + NNDEBUG << "Picked record:" << result; +#endif + + /* Delete the entry from list, to prevent it from being tried multiple times */ + currentPriorityGroup->remove(it->weight(), *it); + + return result; +} + +void WeightedNameRecordList::clear() { + priorityGroups.clear(); + + /* void current state */ + currentPriorityGroup = priorityGroups.end(); +} - QHostAddress addr = addrs.takeFirst(); - QMetaObject::invokeMethod(q, "resultsReady", Qt::QueuedConnection, Q_ARG(QHostAddress, addr), Q_ARG(int, port)); +void WeightedNameRecordList::append(const XMPP::WeightedNameRecordList &list) { + /* Copy over all records from all groups */ + foreach (const WeightedNameRecordPriorityGroup &group, list.priorityGroups) { + foreach(const NameRecord& record, group) { + append(record); } } -private slots: - void dns_resultsReady(const QList &results) - { - mode = 2; - servers.clear(); - for(int n = 0; n < results.count(); ++n) - { - Server serv; - serv.host = results[n].name(); - serv.port = results[n].port(); - serv.priority = results[n].priority(); - serv.weight = results[n].weight(); - servers += serv; + /* Reset to beginning */ + currentPriorityGroup = priorityGroups.begin(); +} + +void WeightedNameRecordList::append(const QList &list) { + foreach (const XMPP::NameRecord &record, list) { + WeightedNameRecordPriorityGroup group(priorityGroups.value(record.priority())); + + group.insert(record.weight(), record); + + if (!priorityGroups.contains(record.priority())) { + priorityGroups.insert(record.priority(), group); } - tryNext(); } - void dns_error(XMPP::NameResolver::Error) - { - if(mode == 0 || mode == 1) - emit q->error(); - else - tryNext(); // FIXME: probably shouldn't share this + /* Reset to beginning */ + currentPriorityGroup = priorityGroups.begin(); +} + +void WeightedNameRecordList::append(const XMPP::NameRecord &record) { + WeightedNameRecordPriorityGroup group(priorityGroups.value(record.priority())); + + group.insert(record.weight(), record); + + if (!priorityGroups.contains(record.priority())) { + priorityGroups.insert(record.priority(), group); } - void adns_resultsReady(const QList &results) - { - if(mode == 1) - { - addrs = results; - tryNext(); - } - else - { - addrs += results; - tryNext(); - } + /* Reset to beginning */ + currentPriorityGroup = priorityGroups.begin(); +} + +void WeightedNameRecordList::append(const QString &hostname, quint16 port) { + NameRecord record(hostname.toLocal8Bit(), std::numeric_limits::max()); + record.setSrv(hostname.toLocal8Bit(), port, std::numeric_limits::max(), std::numeric_limits::max()); + + append(record); + + /* Reset to beginning */ + currentPriorityGroup = priorityGroups.begin(); +} + +XMPP::WeightedNameRecordList& WeightedNameRecordList::operator<<(const XMPP::WeightedNameRecordList &list) { + append(list); + return *this; +} + +WeightedNameRecordList& WeightedNameRecordList::operator<<(const QList &list) { + append(list); + return *this; +} + +XMPP::WeightedNameRecordList& WeightedNameRecordList::operator<<(const XMPP::NameRecord &record) { + append(record); + return *this; +} + + +QDebug operator<<(QDebug dbg, const XMPP::WeightedNameRecordList &list) { + dbg.nospace() << "XMPP::WeightedNameRecordList(\n"; + + /* operator(QDebug, QMap const&) has a bug which makes it crash when trying to print the dereferenced end() iterator */ + if (list.currentPriorityGroup != list.priorityGroups.end()) { + dbg.nospace() << "current=" << *list.currentPriorityGroup << endl; } - void adns_error(XMPP::AddressResolver::Error) - { - if(mode == 0 || mode == 1) - emit q->error(); - else - tryNext(); // FIXME: probably shouldn't share this + dbg.nospace() << "{"; + + foreach(int priority, list.priorityGroups.keys()) { + dbg.nospace() << "\t" << priority << "->" << list.priorityGroups.value(priority) << endl; } -}; + + dbg.nospace() << "})"; + return dbg; +} + class ServiceLocalPublisher::Private { @@ -714,11 +823,10 @@ class NameManager : public QObject connect(p_serv, SIGNAL(resolve_resultsReady(int, const QList &)), SLOT(provider_resolve_resultsReady(int, const QList &)), Qt::QueuedConnection); } - /*np->id = */ - - np->id = p_serv->resolve_start(name); + /* store the id so we can stop it later */ + np->dns_sd_resolve_id = p_serv->resolve_start(name); - sres_instances.insert(np->id, np); + sres_instances.insert(np->dns_sd_resolve_id, np); } void publish_start(ServiceLocalPublisher::Private *np, const QString &instance, const QString &type, int port, const QMap &attribs) @@ -861,7 +969,7 @@ private slots: void provider_resolve_resultsReady(int id, const QList &results) { ServiceResolver::Private *np = sres_instances.value(id); - emit np->q->resultsReady(results[0].address, results[0].port); + emit np->q->resultReady(results[0].address, results[0].port); } void provider_publish_published(int id) @@ -970,6 +1078,7 @@ QDebug operator<<(QDebug dbg, XMPP::NameResolver::Error e) return dbg; } + //---------------------------------------------------------------------------- // ServiceBrowser //---------------------------------------------------------------------------- @@ -993,13 +1102,17 @@ void ServiceBrowser::stop() { } + //---------------------------------------------------------------------------- // ServiceResolver //---------------------------------------------------------------------------- ServiceResolver::ServiceResolver(QObject *parent) -:QObject(parent) + : QObject(parent) { - qRegisterMetaType("QHostAddress"); +#ifdef NETNAMES_DEBUG + NNDEBUG; +#endif + d = new Private(this); } @@ -1008,33 +1121,271 @@ ServiceResolver::~ServiceResolver() delete d; } -void ServiceResolver::startFromInstance(const QByteArray &name) +void ServiceResolver::clear_resolvers() { - NameManager::instance()->resolve_instance_start(d, name); +#ifdef NETNAMES_DEBUG + NNDEBUG; +#endif + + /* cleanup all resolvers */ + foreach (XMPP::NameResolver *resolver, d->resolverList) { + cleanup_resolver(resolver); + } } -void ServiceResolver::startFromDomain(const QString &domain, const QString &type) +void ServiceResolver::cleanup_resolver(XMPP::NameResolver *resolver) { - d->mode = 0; - d->dns.start(type.toLatin1() + '.' + domain.toLatin1(), NameRecord::Srv); +#ifdef NETNAMES_DEBUG + NNDEBUG << "r:" << resolver; +#endif + + if (resolver) { + /* + do not just "delete", because we might have been called from a slot + that was invoked by the resolver, and we do not want to create a mess + there. + */ + disconnect(resolver); + resolver->stop(); + resolver->deleteLater(); + + d->resolverList.removeAll(resolver); + } +} + +ServiceResolver::Protocol ServiceResolver::protocol() const { + return d->requestedProtocol; +} + +void ServiceResolver::setProtocol(ServiceResolver::Protocol p) { + d->requestedProtocol = p; +} + +/* DNS-SD lookup */ +void ServiceResolver::start(const QByteArray &name) { + NameManager::instance()->resolve_instance_start(d, name); } -void ServiceResolver::startFromPlain(const QString &host, int port) +/* normal host lookup */ +void ServiceResolver::start(const QString &host, quint16 port) { - d->mode = 1; +#ifdef NETNAMES_DEBUG + NNDEBUG << "h:" << host << "p:" << port; +#endif + + /* clear host list */ + d->hostList.clear(); + + d->protocol = (d->requestedProtocol == IPv6_IPv4 || d->requestedProtocol == IPv6 ? QAbstractSocket::IPv6Protocol : QAbstractSocket::IPv4Protocol); + d->host = host; d->port = port; - d->adns.start(host.toLatin1()); + +#ifdef NETNAMES_DEBUG + NNDEBUG << "d->p:" << d->protocol; +#endif + + /* initiate the host lookup */ + XMPP::NameRecord::Type querytype = (d->protocol == QAbstractSocket::IPv6Protocol ? XMPP::NameRecord::Aaaa : XMPP::NameRecord::A); + XMPP::NameResolver *resolver = new XMPP::NameResolver; + connect(resolver, SIGNAL(resultsReady(QList)), this, SLOT(handle_host_ready(QList))); + connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, SLOT(handle_host_error(XMPP::NameResolver::Error))); + resolver->start(host.toLocal8Bit(), querytype); + d->resolverList << resolver; } -void ServiceResolver::tryNext() +/* SRV lookup */ +void ServiceResolver::start(const QString &service, const QString &transport, const QString &domain, int port) { - d->tryNext(); +#ifdef NETNAMES_DEBUG + NNDEBUG << "s:" << service << "t:" << transport << "d:" << domain << "p:" << port; +#endif + + QString srv_request("_" + service + "._" + transport + "." + domain + "."); + + /* clear SRV list */ + d->srvList.clear(); + + d->domain = domain; + + /* after we tried all SRV hosts, we shall connect directly (if requested) */ + if (port < std::numeric_limits::max()) { + d->srvList.append(domain.toLocal8Bit(), port); + } + else { + /* The only "valid" port above the valid port range is our specification of an invalid port */ + Q_ASSERT(port == std::numeric_limits::max()); + } + + /* initiate the SRV lookup */ + XMPP::NameResolver *resolver = new XMPP::NameResolver; + connect(resolver, SIGNAL(resultsReady(QList)), this, SLOT(handle_srv_ready(QList))); + connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, SLOT(handle_srv_error(XMPP::NameResolver::Error))); + resolver->start(srv_request.toLocal8Bit(), XMPP::NameRecord::Srv); + d->resolverList << resolver; } -void ServiceResolver::stop() +/* SRV request resolved, now try to connect to the hosts */ +void ServiceResolver::handle_srv_ready(const QList &r) { +#ifdef NETNAMES_DEBUG + NNDEBUG << "sl:" << r; +#endif + + /* cleanup resolver */ + cleanup_resolver(static_cast(sender())); + + /* lookup srv pointers */ + d->srvList << r; + try_next_srv(); } +/* failed the srv lookup, but we might have a fallback host in the srvList */ +void ServiceResolver::handle_srv_error(XMPP::NameResolver::Error e) +{ +#ifdef NETNAMES_DEBUG + NNDEBUG << "e:" << e; +#endif + + /* cleanup resolver */ + cleanup_resolver(static_cast(sender())); + + /* srvList already contains a failsafe host, try that */ + try_next_srv(); +} + +/* hosts resolved, now try to connect to them */ +void ServiceResolver::handle_host_ready(const QList &r) +{ +#ifdef NETNAMES_DEBUG + NNDEBUG << "hl:" << r; +#endif + + /* cleanup resolver */ + cleanup_resolver(static_cast(sender())); + + /* connect to host */ + d->hostList << r; + try_next_host(); +} + +/* failed to lookup the primary record (A or AAAA, depending on user choice) */ +void ServiceResolver::handle_host_error(XMPP::NameResolver::Error e) +{ +#ifdef NETNAMES_DEBUG + NNDEBUG << "e:" << e; +#endif + + /* cleanup resolver */ + cleanup_resolver(static_cast(sender())); + + /* try a fallback lookup if requested*/ + if (!lookup_host_fallback()) { + /* no-fallback should behave the same as a failed fallback */ + handle_host_fallback_error(e); + } +} + +/* failed to lookup the fallback record (A or AAAA, depending on user choice) */ +void ServiceResolver::handle_host_fallback_error(XMPP::NameResolver::Error e) +{ +#ifdef NETNAMES_DEBUG + NNDEBUG << "e:" << e; +#endif + + /* cleanup resolver */ + cleanup_resolver(static_cast(sender())); + + /* lookup next SRV */ + try_next_srv(); +} + +/* check whether a fallback is needed in the current situation */ +bool ServiceResolver::check_protocol_fallback() +{ + return (d->requestedProtocol == IPv6_IPv4 && d->protocol == QAbstractSocket::IPv6Protocol) + || (d->requestedProtocol == IPv4_IPv6 && d->protocol == QAbstractSocket::IPv4Protocol); +} + +/* lookup the fallback host */ +bool ServiceResolver::lookup_host_fallback() { +#ifdef NETNAMES_DEBUG + NNDEBUG; +#endif + + /* if a fallback is desired, otherwise we must fail immediately */ + if (!check_protocol_fallback()) { + return false; + } + + d->protocol = (d->protocol == QAbstractSocket::IPv6Protocol ? QAbstractSocket::IPv4Protocol : QAbstractSocket::IPv6Protocol); + +#ifdef NETNAMES_DEBUG + NNDEBUG << "d->p:" << d->protocol; +#endif + + /* initiate the fallback host lookup */ + XMPP::NameRecord::Type querytype = (d->protocol == QAbstractSocket::IPv6Protocol ? XMPP::NameRecord::Aaaa : XMPP::NameRecord::A); + XMPP::NameResolver *resolver = new XMPP::NameResolver; + connect(resolver, SIGNAL(resultsReady(QList)), this, SLOT(handle_host_ready(QList))); + connect(resolver, SIGNAL(error(XMPP::NameResolver::Error)), this, SLOT(handle_host_fallback_error(XMPP::NameResolver::Error))); + resolver->start(d->host.toLocal8Bit(), querytype); + d->resolverList << resolver; + + return true; +} + +/* notify user about next host */ +bool ServiceResolver::try_next_host() { +#ifdef NETNAMES_DEBUG + NNDEBUG << "hl:" << d->hostList; +#endif + + /* if there is a host left for current protocol (AAAA or A) */ + if (!d->hostList.empty()) { + XMPP::NameRecord record(d->hostList.takeFirst()); + /* emit found address and the port specified earlier */ + emit resultReady(record.address(), d->port); + return true; + } + + /* otherwise try the fallback protocol */ + return lookup_host_fallback(); +} + +/* lookup the next SRV record in line */ +void ServiceResolver::try_next_srv() +{ +#ifdef NETNAMES_DEBUG + NNDEBUG << "sl:" << d->srvList; +#endif + + /* if there are still hosts we did not try */ + if (!d->srvList.empty()) { + XMPP::NameRecord record(d->srvList.takeNext()); + /* lookup host by name and specify port for later use */ + start(record.name(), record.port()); + } + else { +#ifdef NETNAMES_DEBUG + NNDEBUG << "SRV list empty, failing"; +#endif + /* no more SRV hosts to try, fail */ + emit error(NoHostLeft); + } +} + +void ServiceResolver::tryNext() { + /* if the host list cannot help, try the SRV list */ + if (!try_next_host()) { + try_next_srv(); + } +} + +void ServiceResolver::stop() { + clear_resolvers(); +} + + //---------------------------------------------------------------------------- // ServiceLocalPublisher //---------------------------------------------------------------------------- diff --git a/iris/src/irisnet/corelib/netnames.h b/iris/src/irisnet/corelib/netnames.h index 1430c5b82..e8a549256 100644 --- a/iris/src/irisnet/corelib/netnames.h +++ b/iris/src/irisnet/corelib/netnames.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2006,2008 Justin Karneges + * Copyright (C) 2009-2010 Dennis Schridde * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,6 +24,9 @@ #include #include + +#include + #include "irisnetglobal.h" namespace XMPP { @@ -132,6 +136,11 @@ class IRISNET_EXPORT NameRecord */ NameRecord & operator=(const NameRecord &from); + /** + \brief Compares \a other with this object + */ + bool operator==(const NameRecord &other); + /** \brief Returns true if this record object is null, otherwise returns false @@ -296,6 +305,7 @@ class IRISNET_EXPORT NameRecord IRISNET_EXPORT QDebug operator<<(QDebug, XMPP::NameRecord::Type); IRISNET_EXPORT QDebug operator<<(QDebug, const XMPP::NameRecord &); + class IRISNET_EXPORT ServiceInstance { public: @@ -319,12 +329,15 @@ class IRISNET_EXPORT ServiceInstance }; /** - \brief Performs a DNS lookup + \brief Represents a DNS query/lookup NameResolver performs an asynchronous DNS lookup for a given domain name and record type. Call start() to begin. The resultsReady() signal is emitted on success, otherwise error() is emitted. To cancel a lookup, call stop(). Each NameResolver object can only perform one DNS lookup at a time. If start() is called while a lookup is already in progress, then the existing lookup is stopped before starting the new lookup. + Each NameResolver object should be used for just one DNS query and then be deleted. + Otherwise ambiguity might arise when receiving multiple answers to future queries. + For example, here is how to obtain the IPv4 addresses of a domain name: \code NameResolver *resolver; @@ -461,6 +474,39 @@ class IRISNET_EXPORT NameResolver : public QObject IRISNET_EXPORT QDebug operator<<(QDebug, XMPP::NameResolver::Error); + +class IRISNET_EXPORT WeightedNameRecordList +{ + friend QDebug operator<<(QDebug, const WeightedNameRecordList&); + +public: + WeightedNameRecordList(); + WeightedNameRecordList(const QList &list); + ~WeightedNameRecordList(); + bool empty() const; //!< Returns true if the list contains no items; otherwise returns false. + NameRecord takeNext(); //!< Removes the next host to try from the list and returns it. + + void clear(); //!< Removes all items from the list. + void append(const WeightedNameRecordList&); + void append(const QList&); + void append(const NameRecord&); + void append(const QString &hostname, quint16 port); + + WeightedNameRecordList& operator<<(const WeightedNameRecordList&); + WeightedNameRecordList& operator<<(const QList&); + WeightedNameRecordList& operator<<(const NameRecord&); + +private: + typedef QMultiMap WeightedNameRecordPriorityGroup; + typedef QMap WNRL; + + WNRL priorityGroups; + WNRL::iterator currentPriorityGroup; +}; + +QDebug operator<<(QDebug, const XMPP::WeightedNameRecordList&); + + class IRISNET_EXPORT ServiceBrowser : public QObject { Q_OBJECT @@ -491,32 +537,104 @@ class IRISNET_EXPORT ServiceBrowser : public QObject friend class NameManager; }; + +/*! DNS resolver with DNS-SD/mDNS and recursive lookup support */ +/* +Flow: +1) SRV query for server + : answer = host[] + : failure -> (9) + 2) Primary query for host[i] (usually AAAA) + : answer = address[] + : failure -> (5) + 3) Connect to address[j] + : connect -> FINISHED + : failure -> j++, (3) + 4) address[] empty -> (5) + 5) Fallback query for host[i] (usually A) + : answer = address[] + : failure -> i++, (2) + 6) Connect to address[j] + : connect -> FINISHED + : failure -> j++, (6) + 7) address[] empty -> i++, (2) + 8) host[] empty -> (9) +9) Try servername directly +*/ class IRISNET_EXPORT ServiceResolver : public QObject { Q_OBJECT public: - enum Error - { - ErrorGeneric, - ErrorTimeout, - ErrorNoLocal + /*! Error codes for (SRV) lookups */ + enum Error { + ServiceNotFound, //!< There is no service with the specified parameters + NoHostLeft, //!< we did all we could, none of the found host seemed to suffice the users needs + ErrorGeneric, ErrorTimeout, ErrorNoLocal // Stuff that netnames_jdns.cpp needs ... }; + /*! Order of lookup / IP protocols to try */ + enum Protocol { IPv6_IPv4, IPv4_IPv6, IPv6, IPv4 }; + /*! + * Create a new ServiceResolver. + * This resolver can be used for multiple lookups in a row, but not concurrently! + */ ServiceResolver(QObject *parent = 0); ~ServiceResolver(); - void startFromInstance(const QByteArray &name); - void startFromDomain(const QString &domain, const QString &type); - void startFromPlain(const QString &host, int port); // non-SRV + Protocol protocol() const; //!< IP protocol to use, defaults to IPv6_IPv4 + void setProtocol(Protocol); //!< Set IP protocol to use, \sa protocol + + /*! + * Start a DNS-SD lookup + * \param name Instance to lookup + */ + void start(const QByteArray &name); + /*! + * Start a lookup for host directly + * Behaves like a NameResolver with IP protocol fallback + * \param host Hostname to lookup + * \param port Port to signal via resultReady (for convenience) + */ + void start(const QString &host, quint16 port); + /*! + * Start an indirect (SRV) lookup for the service + * \param service Service type, like "ssh" or "ftp" + * \param transport IP transport, like "tcp" or "udp" + * \param domain Domainname to lookup + * \param port Specify a valid port number to make ServiceResolver fallback to domain:port + */ + void start(const QString &service, const QString &transport, const QString &domain, int port = std::numeric_limits::max()); + + /*! Announce the next resolved host, \sa resultReady */ void tryNext(); + /*! Stop the current lookup */ void stop(); signals: - void resultsReady(const QHostAddress &address, int port); - void finished(); - void error(); // SRV lookup failed + /*! + * The lookup succeeded + * \param address Resolved IP address + * \param port Port the service resides on + */ + void resultReady(const QHostAddress &address, quint16 port); + /*! The lookup failed */ + void error(XMPP::ServiceResolver::Error); + +private slots: + void handle_srv_ready(const QList&); + void handle_srv_error(XMPP::NameResolver::Error); + void handle_host_ready(const QList&); + void handle_host_error(XMPP::NameResolver::Error); + void handle_host_fallback_error(XMPP::NameResolver::Error); private: + void clear_resolvers(); + void cleanup_resolver(XMPP::NameResolver*); + bool check_protocol_fallback(); + bool lookup_host_fallback(); + bool try_next_host(); + void try_next_srv(); + class Private; friend class Private; Private *d; diff --git a/iris/src/irisnet/noncore/cutestuff/bsocket.cpp b/iris/src/irisnet/noncore/cutestuff/bsocket.cpp index 273399173..81c6e7f93 100644 --- a/iris/src/irisnet/noncore/cutestuff/bsocket.cpp +++ b/iris/src/irisnet/noncore/cutestuff/bsocket.cpp @@ -1,6 +1,7 @@ /* * bsocket.cpp - QSocket wrapper based on Bytestream with SRV DNS support * Copyright (C) 2003 Justin Karneges + * Copyright (C) 2009-2010 Dennis Schridde * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,18 +23,19 @@ #include #include +#include + #include "bsocket.h" //#include "safedelete.h" -#include "ndns.h" -#include "srvresolver.h" //#define BS_DEBUG #ifdef BS_DEBUG -#include +# define BSDEBUG (qDebug() << this << "#" << __FUNCTION__ << ":") #endif + #define READBUFSIZE 65536 // CS_NAMESPACE_BEGIN @@ -94,36 +96,39 @@ public slots: } }; + class BSocket::Private { public: - Private(BSocket *_q) : - ndns(_q), - srv(_q) + Private() { qsock = 0; qsock_relay = 0; + resolver = 0; } QTcpSocket *qsock; QTcpSocketSignalRelay *qsock_relay; int state; - NDns ndns; - SrvResolver srv; - QString host; - int port; - QHostAddress addr; + QString domain; //!< Domain we are currently connected to + QString host; //!< Hostname we are currently connected to + QHostAddress address; //!< IP address we are currently connected to + quint16 port; //!< Port we are currently connected to + //SafeDelete sd; + + /*! + * Resolver used for lookups, + * will be destroyed and recreated after each lookup + */ + XMPP::ServiceResolver *resolver; }; BSocket::BSocket(QObject *parent) :ByteStream(parent) { - d = new Private(this); - connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done())); - connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done())); - + d = new Private; reset(); } @@ -133,10 +138,24 @@ BSocket::~BSocket() delete d; } +/* +Recreate teh resolver, +a safety measure in case the resolver behaves strange when doing multiple lookups in a row +*/ +void BSocket::recreate_resolver() { + if (d->resolver) { + disconnect(d->resolver); + d->resolver->stop(); + d->resolver->deleteLater(); + } + + d->resolver = new XMPP::ServiceResolver; +} + void BSocket::reset(bool clear) { #ifdef BS_DEBUG - qDebug("BSocket::reset(%s)", clear?"true":"false"); + BSDEBUG << clear; #endif if(d->qsock) { delete d->qsock_relay; @@ -160,12 +179,11 @@ void BSocket::reset(bool clear) clearReadBuffer(); } - if(d->srv.isBusy()) - d->srv.stop(); - if(d->ndns.isBusy()) - d->ndns.stop(); d->state = Idle; - d->addr = QHostAddress(); + d->domain = ""; + d->host = ""; + d->address = QHostAddress(); + d->port = 0; } void BSocket::ensureSocket() @@ -185,30 +203,101 @@ void BSocket::ensureSocket() } } -void BSocket::connectToHost(const QString &host, quint16 port) +/* Connect to an already resolved host */ +void BSocket::connectToHost(const QHostAddress &address, quint16 port) { +#ifdef BS_DEBUG + BSDEBUG << "a:" << address << "p:" << port; +#endif + reset(true); - d->host = host; + d->address = address; d->port = port; - d->state = HostLookup; - d->ndns.resolve(d->host); + d->state = Connecting; + + ensureSocket(); + d->qsock->connectToHost(address, port); } -void BSocket::connectToHost(const QHostAddress &addr, quint16 port) +/* Connect to a host via the specified protocol, or the default protocols if not specified */ +void BSocket::connectToHost(const QString &host, quint16 port, QAbstractSocket::NetworkLayerProtocol protocol) { +#ifdef BS_DEBUG + BSDEBUG << "h:" << host << "p:" << port << "pr:" << protocol; +#endif + reset(true); - d->host = addr.toString(); - d->addr = addr; + d->host = host; d->port = port; - d->state = Connecting; - do_connect(); + d->state = HostLookup; + + /* cleanup resolver for the new query */ + recreate_resolver(); + + switch (protocol) { + case QAbstractSocket::IPv6Protocol: + d->resolver->setProtocol(XMPP::ServiceResolver::IPv6); + break; + case QAbstractSocket::IPv4Protocol: + d->resolver->setProtocol(XMPP::ServiceResolver::IPv4); + break; + case QAbstractSocket::UnknownNetworkLayerProtocol: + /* use ServiceResolver's default in this case */ + break; + } + + connect(d->resolver, SIGNAL(resultReady(const QHostAddress&, quint16)), this, SLOT(handle_dns_ready(const QHostAddress&, quint16))); + connect(d->resolver, SIGNAL(error(XMPP::ServiceResolver::Error)), this, SLOT(handle_dns_error(XMPP::ServiceResolver::Error))); + d->resolver->start(host, port); } -void BSocket::connectToServer(const QString &srv, const QString &type) +/* Connect to the hosts for the specified service */ +void BSocket::connectToHost(const QString &service, const QString &transport, const QString &domain, quint16 port) { +#ifdef BS_DEBUG + BSDEBUG << "s:" << service << "t:" << transport << "d:" << domain; +#endif + reset(true); + d->domain = domain; d->state = HostLookup; - d->srv.resolve(srv, type, "tcp"); + + /* cleanup resolver for the new query */ + recreate_resolver(); + + connect(d->resolver, SIGNAL(resultReady(const QHostAddress&, quint16)), this, SLOT(handle_dns_ready(const QHostAddress&, quint16))); + connect(d->resolver, SIGNAL(error(XMPP::ServiceResolver::Error)), this, SLOT(handle_dns_error(XMPP::ServiceResolver::Error))); + d->resolver->start(service, transport, domain, port); +} + +/* host resolved, now try to connect to it */ +void BSocket::handle_dns_ready(const QHostAddress &address, quint16 port) +{ +#ifdef BS_DEBUG + BSDEBUG << "a:" << address << "p:" << port; +#endif + + connectToHost(address, port); +} + +/* resolver failed the dns lookup */ +void BSocket::handle_dns_error(XMPP::ServiceResolver::Error e) { +#ifdef BS_DEBUG + BSDEBUG << "e:" << e; +#endif + + emit error(ErrHostNotFound); +} + +/* failed to connect to host */ +void BSocket::handle_connect_error(QAbstractSocket::SocketError e) { +#ifdef BS_DEBUG + BSDEBUG << "d->r:" << d->resolver; +#endif + + /* try the next host for this service */ + Q_ASSERT(d->resolver); + d->resolver->tryNext(); } int BSocket::socket() const @@ -260,9 +349,8 @@ void BSocket::write(const QByteArray &a) { if(d->state != Connected) return; -#ifdef BS_DEBUG - QString s = QString::fromUtf8(a); - qDebug("BSocket: writing [%d]: {%s}", a.size(), qPrintable(s)); +#ifdef BS_DEBUG_EXTRA + BSDEBUG << "- [" << a.size() << "]: {" << a << "}"; #endif d->qsock->write(a.data(), a.size()); } @@ -280,9 +368,8 @@ QByteArray BSocket::read(int bytes) else block = ByteStream::read(bytes); -#ifdef BS_DEBUG - QString s = QString::fromUtf8(block); - qDebug("BSocket: read [%d]: {%s}", block.size(), qPrintable(s)); +#ifdef BS_DEBUG_EXTRA + BSDEBUG << "- [" << block.size() << "]: {" << block << "}"; #endif return block; } @@ -334,52 +421,6 @@ quint16 BSocket::peerPort() const return 0; } -void BSocket::srv_done() -{ - if(d->srv.failed()) { -#ifdef BS_DEBUG - qDebug("BSocket: Error resolving hostname."); -#endif - error(ErrHostNotFound); - return; - } - - d->host = d->srv.resultAddress().toString(); - d->port = d->srv.resultPort(); - do_connect(); - //QTimer::singleShot(0, this, SLOT(do_connect())); - //hostFound(); -} - -void BSocket::ndns_done() -{ - if(!d->ndns.result().isNull()) { - d->host = d->ndns.resultString(); - d->state = Connecting; - do_connect(); - //QTimer::singleShot(0, this, SLOT(do_connect())); - //hostFound(); - } - else { -#ifdef BS_DEBUG - qDebug("BSocket: Error resolving hostname."); -#endif - error(ErrHostNotFound); - } -} - -void BSocket::do_connect() -{ -#ifdef BS_DEBUG - qDebug("BSocket: Connecting to %s:%d", qPrintable(d->host), d->port); -#endif - ensureSocket(); - if(!d->addr.isNull()) - d->qsock->connectToHost(d->addr, d->port); - else - d->qsock->connectToHost(d->host, d->port); -} - void BSocket::qs_hostFound() { //SafeDeleteLock s(&d->sd); @@ -389,10 +430,10 @@ void BSocket::qs_connected() { d->state = Connected; #ifdef BS_DEBUG - qDebug("BSocket: Connected."); + BSDEBUG << "Connected"; #endif //SafeDeleteLock s(&d->sd); - connected(); + emit connected(); } void BSocket::qs_closed() @@ -400,60 +441,61 @@ void BSocket::qs_closed() if(d->state == Closing) { #ifdef BS_DEBUG - qDebug("BSocket: Delayed Close Finished."); + BSDEBUG << "Delayed Close Finished"; #endif //SafeDeleteLock s(&d->sd); reset(); - delayedCloseFinished(); + emit delayedCloseFinished(); } } void BSocket::qs_readyRead() { //SafeDeleteLock s(&d->sd); - readyRead(); + emit readyRead(); } void BSocket::qs_bytesWritten(qint64 x64) { int x = x64; -#ifdef BS_DEBUG - qDebug("BSocket: BytesWritten [%d].", x); +#ifdef BS_DEBUG_EXTRA + BSDEBUG << "BytesWritten [" << x << "]"; #endif //SafeDeleteLock s(&d->sd); - bytesWritten(x); + emit bytesWritten(x); } void BSocket::qs_error(QAbstractSocket::SocketError x) { + /* arriving here from connectToHost() */ + if (d->state == Connecting) { + /* We do our own special error handling in this case */ + handle_connect_error(x); + return; + } + if(x == QTcpSocket::RemoteHostClosedError) { #ifdef BS_DEBUG - qDebug("BSocket: Connection Closed."); + BSDEBUG << "Connection Closed"; #endif //SafeDeleteLock s(&d->sd); reset(); - connectionClosed(); + emit connectionClosed(); return; } #ifdef BS_DEBUG - qDebug("BSocket: Error."); + BSDEBUG << "Error"; #endif //SafeDeleteLock s(&d->sd); - // connection error during SRV host connect? try next - if(d->state == HostLookup && (x == QTcpSocket::ConnectionRefusedError || x == QTcpSocket::HostNotFoundError)) { - d->srv.next(); - return; - } - reset(); if(x == QTcpSocket::ConnectionRefusedError) - error(ErrConnectionRefused); + emit error(ErrConnectionRefused); else if(x == QTcpSocket::HostNotFoundError) - error(ErrHostNotFound); + emit error(ErrHostNotFound); else - error(ErrRead); + emit error(ErrRead); } #include "bsocket.moc" diff --git a/iris/src/irisnet/noncore/cutestuff/bsocket.h b/iris/src/irisnet/noncore/cutestuff/bsocket.h index c46389c34..28b28c94c 100644 --- a/iris/src/irisnet/noncore/cutestuff/bsocket.h +++ b/iris/src/irisnet/noncore/cutestuff/bsocket.h @@ -1,6 +1,7 @@ /* * bsocket.h - QSocket wrapper based on Bytestream with SRV DNS support * Copyright (C) 2003 Justin Karneges + * Copyright (C) 2009-2010 Dennis Schridde * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,7 +24,10 @@ #include +#include + #include "bytestream.h" +#include "netnames.h" class QString; class QObject; @@ -31,6 +35,10 @@ class QByteArray; // CS_NAMESPACE_BEGIN + +/*! + Socket with automatic hostname lookups, using SRV, AAAA and A DNS queries. +*/ class BSocket : public ByteStream { Q_OBJECT @@ -40,9 +48,12 @@ class BSocket : public ByteStream BSocket(QObject *parent=0); ~BSocket(); - void connectToHost(const QString &host, quint16 port); - void connectToHost(const QHostAddress &addr, quint16 port); - void connectToServer(const QString &srv, const QString &type); + /*! Connect to an already resolved host */ + void connectToHost(const QHostAddress &address, quint16 port); + /*! Connect to a host via the specified protocol, or the default protocols if not specified */ + void connectToHost(const QString &host, quint16 port, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::UnknownNetworkLayerProtocol); + /*! Connect to the hosts for the specified service */ + void connectToHost(const QString &service, const QString &transport, const QString &domain, quint16 port = std::numeric_limits::max()); int socket() const; void setSocket(int); int state() const; @@ -74,9 +85,10 @@ private slots: void qs_readyRead(); void qs_bytesWritten(qint64); void qs_error(QAbstractSocket::SocketError); - void srv_done(); - void ndns_done(); - void do_connect(); + + void handle_dns_ready(const QHostAddress&, quint16); + void handle_dns_error(XMPP::ServiceResolver::Error e); + void handle_connect_error(QAbstractSocket::SocketError); private: class Private; @@ -84,6 +96,10 @@ private slots: void reset(bool clear=false); void ensureSocket(); + void recreate_resolver(); + bool check_protocol_fallback(); + void dns_srv_try_next(); + bool connect_host_try_next(); }; // CS_NAMESPACE_END diff --git a/iris/src/xmpp/xmpp-core/connector.cpp b/iris/src/xmpp/xmpp-core/connector.cpp index c7eac948e..7009d692f 100644 --- a/iris/src/xmpp/xmpp-core/connector.cpp +++ b/iris/src/xmpp/xmpp-core/connector.cpp @@ -37,17 +37,26 @@ #include #include #include -#include "safedelete.h" + #include "bsocket.h" #include "httpconnect.h" #include "httppoll.h" #include "socks.h" -#include "srvresolver.h" //#define XMPP_DEBUG +#ifdef XMPP_DEBUG +# define XDEBUG (qDebug() << this << "#" << __FUNCTION__ << ":") +#endif + using namespace XMPP; +const int XMPP_DEFAULT_PORT = 5222; +const int XMPP_LEGACY_PORT = 5223; +const QString XMPP_CLIENT_SRV = "xmpp-client"; +const QString XMPP_CLIENT_TRANSPORT = "tcp"; + + //---------------------------------------------------------------------------- // Connector //---------------------------------------------------------------------------- @@ -192,37 +201,25 @@ void AdvancedConnector::Proxy::setPollInterval(int secs) //---------------------------------------------------------------------------- // AdvancedConnector //---------------------------------------------------------------------------- -enum { Idle, Connecting, Connected }; +typedef enum { Idle, Connecting, Connected } Mode; +typedef enum { Force, Probe, Never } LegacySSL; + class AdvancedConnector::Private { public: - int mode; - ByteStream *bs; - AddressResolver dns; - SrvResolver srv; - - QString server; - QStringList opt_hosts; - int opt_port; - bool opt_probe, opt_ssl; - Proxy proxy; - - QStringList hostsToTry; - QString host; - int port; - QList servers; - int errorCode; - QString connectHost; - QList addrList; - QHostAddress curAddr; - - bool multi, using_srv; - bool will_be_ssl; - int probe_mode; - - SafeDelete sd; - - QTimer *connectTimeout; + ByteStream *bs; //!< Socket to use + + /* configuration values / "options" */ + QString opt_host; //!< explicit host from config + quint16 opt_port; //!< explicit port from config + LegacySSL opt_ssl; //!< Whether to use legacy SSL support + Proxy proxy; //!< Proxy configuration + + /* State tracking values */ + Mode mode; //!< Idle, Connecting, Connected + QString host; //!< Host we currently try to connect to, set from connectToServer() + int port; //!< Port we currently try to connect to, set from connectToServer() and bs_error() + int errorCode; //!< Current error, if any }; AdvancedConnector::AdvancedConnector(QObject *parent) @@ -230,14 +227,7 @@ AdvancedConnector::AdvancedConnector(QObject *parent) { d = new Private; d->bs = 0; - d->connectTimeout = new QTimer(this); - connect(&d->dns, SIGNAL(resultsReady(const QList &)), SLOT(dns_resultsReady(const QList &))); - connect(&d->dns, SIGNAL(error(XMPP::AddressResolver::Error)), SLOT(dns_error(XMPP::AddressResolver::Error))); - connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done())); - connect(d->connectTimeout, SIGNAL(timeout()), SLOT(t_timeout())); - d->connectTimeout->setSingleShot(true); - d->opt_probe = false; - d->opt_ssl = false; + d->opt_ssl = Never; cleanup(); d->errorCode = 0; } @@ -245,9 +235,6 @@ AdvancedConnector::AdvancedConnector(QObject *parent) AdvancedConnector::~AdvancedConnector() { cleanup(); - d->connectTimeout->disconnect(this); - d->connectTimeout->setParent(0); - d->connectTimeout->deleteLater(); delete d; } @@ -255,26 +242,10 @@ void AdvancedConnector::cleanup() { d->mode = Idle; - // stop any dns - //if(d->dns.isBusy()) - d->dns.stop(); - if(d->srv.isBusy()) - d->srv.stop(); - - d->connectTimeout->stop(); - // destroy the bytestream, if there is one delete d->bs; d->bs = 0; - d->multi = false; - d->using_srv = false; - d->will_be_ssl = false; - d->probe_mode = -1; - - d->addrList.clear(); - d->curAddr = QHostAddress(); - setUseSSL(false); setPeerAddressNone(); } @@ -286,75 +257,77 @@ void AdvancedConnector::setProxy(const Proxy &proxy) d->proxy = proxy; } -void AdvancedConnector::setOptHostPort(const QString &host, quint16 _port) +void AdvancedConnector::setOptHostPort(const QString &_host, quint16 _port) { +#ifdef XMPP_DEBUG + XDEBUG << "h:" << _host << "p:" << _port; +#endif + if(d->mode != Idle) return; + // empty host means disable explicit host support - if(host.isEmpty()) { - d->opt_hosts.clear(); + if(_host.isEmpty()) { + d->opt_host.clear(); return; } - d->opt_hosts = QStringList() << host; - d->opt_port = _port; -} - -void AdvancedConnector::setOptHostsPort(const QStringList &_hosts, quint16 _port) -{ - if(d->mode != Idle) - return; - d->opt_hosts = _hosts; + d->opt_host = _host; d->opt_port = _port; } void AdvancedConnector::setOptProbe(bool b) { +#ifdef XMPP_DEBUG + XDEBUG << "b:" << b; +#endif + if(d->mode != Idle) return; - d->opt_probe = b; + d->opt_ssl = (b ? Probe : Never); } void AdvancedConnector::setOptSSL(bool b) { +#ifdef XMPP_DEBUG + XDEBUG << "b:" << b; +#endif + if(d->mode != Idle) return; - d->opt_ssl = b; + d->opt_ssl = (b ? Force : Never); } void AdvancedConnector::connectToServer(const QString &server) { +#ifdef XMPP_DEBUG + XDEBUG << "s:" << server; +#endif + if(d->mode != Idle) return; if(server.isEmpty()) return; - d->hostsToTry.clear(); d->errorCode = 0; d->mode = Connecting; - d->connectHost.clear(); // Encode the servername - d->server = QUrl::toAce(server); - //char* server_encoded; - //if (!idna_to_ascii_8z(server.utf8().data(), &server_encoded, 0)) { - // d->server = QString(server_encoded); - // free(server_encoded); - //} - //else { - // d->server = server; - //} + d->host = QUrl::toAce(server); + if (d->host == QByteArray()) { + /* server contains invalid characters for DNS name, but maybe valid characters for connecting, like "::1" */ + d->host = server; + } + d->port = XMPP_DEFAULT_PORT; if(d->proxy.type() == Proxy::HttpPoll) { - // need SHA1 here - //if(!QCA::isSupported(QCA::CAP_SHA1)) - // QCA::insertProvider(createProviderHash()); - HttpPoll *s = new HttpPoll; d->bs = s; + connect(s, SIGNAL(connected()), SLOT(bs_connected())); connect(s, SIGNAL(syncStarted()), SLOT(http_syncStarted())); connect(s, SIGNAL(syncFinished()), SLOT(http_syncFinished())); connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); + if(!d->proxy.user().isEmpty()) s->setAuth(d->proxy.user(), d->proxy.pass()); s->setPollInterval(d->proxy.pollInterval()); @@ -365,34 +338,52 @@ void AdvancedConnector::connectToServer(const QString &server) s->connectToHost(d->proxy.host(), d->proxy.port(), d->proxy.url()); } else if (d->proxy.type() == Proxy::HttpConnect) { - if(!d->opt_hosts.isEmpty()) { - d->hostsToTry = d->opt_hosts; - d->host = d->hostsToTry.takeFirst(); + HttpConnect *s = new HttpConnect; + d->bs = s; + + connect(s, SIGNAL(connected()), SLOT(bs_connected())); + connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); + + if(!d->opt_host.isEmpty()) { + d->host = d->opt_host; d->port = d->opt_port; } - else { - d->host = server; - d->port = 5222; - } - do_connect(); + + if(!d->proxy.user().isEmpty()) + s->setAuth(d->proxy.user(), d->proxy.pass()); + + s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port); + } + else if (d->proxy.type() == Proxy::Socks) { + SocksClient *s = new SocksClient; + d->bs = s; + + connect(s, SIGNAL(connected()), SLOT(bs_connected())); + connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); + + if(!d->proxy.user().isEmpty()) + s->setAuth(d->proxy.user(), d->proxy.pass()); + + s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port); } else { - if(!d->opt_hosts.isEmpty()) { - d->hostsToTry = d->opt_hosts; - d->host = d->hostsToTry.takeFirst(); - d->port = d->opt_port; - do_resolve(); - } - else { - d->multi = true; + BSocket *s = new BSocket; + d->bs = s; +#ifdef XMPP_DEBUG + XDEBUG << "Adding socket:" << s; +#endif - QPointer self = this; - srvLookup(d->server); - if(!self) - return; + connect(s, SIGNAL(connected()), SLOT(bs_connected())); + connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); - d->srv.resolveSrvOnly(d->server, "xmpp-client", "tcp"); + if(!d->opt_host.isEmpty()) { + d->host = d->opt_host; + d->port = d->opt_port; + s->connectToHost(d->host, d->port); + return; } + + s->connectToHost(XMPP_CLIENT_SRV, XMPP_CLIENT_TRANSPORT, d->host, d->port); } } @@ -422,212 +413,34 @@ int AdvancedConnector::errorCode() const return d->errorCode; } -void AdvancedConnector::do_resolve() -{ -#ifdef XMPP_DEBUG - qDebug("resolving [%s]", qPrintable(d->host)); -#endif - d->dns.start(d->host.toLatin1()); -} - -void AdvancedConnector::dns_resultsReady(const QList &results) -{ - if(results.isEmpty()) { -#ifdef XMPP_DEBUG - qDebug("dns1"); -#endif - // using proxy? then try the unresolved host through the proxy - if(d->proxy.type() != Proxy::None) { -#ifdef XMPP_DEBUG - qDebug("dns1.1"); -#endif - do_connect(); - } - else if(d->using_srv) { -#ifdef XMPP_DEBUG - qDebug("dns1.2"); -#endif - if(d->servers.isEmpty()) { -#ifdef XMPP_DEBUG - qDebug("dns1.2.1"); -#endif - cleanup(); - d->errorCode = ErrConnectionRefused; - error(); - } - else { -#ifdef XMPP_DEBUG - qDebug("dns1.2.2"); -#endif - tryNextSrv(); - return; - } - } - else { -#ifdef XMPP_DEBUG - qDebug("dns1.3"); -#endif - if(!d->hostsToTry.isEmpty()) - { - d->host = d->hostsToTry.takeFirst(); - do_resolve(); - return; - } - - cleanup(); - d->errorCode = ErrHostNotFound; - error(); - } - } - else { -#ifdef XMPP_DEBUG - qDebug("dns2"); -#endif - d->addrList = results; - d->connectHost = d->host; - d->curAddr = d->addrList.takeFirst(); - do_connect(); - } -} - -void AdvancedConnector::dns_error(XMPP::AddressResolver::Error) -{ - dns_resultsReady(QList()); -} - -void AdvancedConnector::do_connect() -{ - // 5 seconds to connect - d->connectTimeout->start(5000); - - if(!d->curAddr.isNull()) - d->host = d->curAddr.toString(); - -#ifdef XMPP_DEBUG - qDebug("trying %s:%d", qPrintable(d->host), d->port); -#endif - int t = d->proxy.type(); - if(t == Proxy::None) { -#ifdef XMPP_DEBUG - qDebug("do_connect1"); -#endif - BSocket *s = new BSocket; - d->bs = s; - connect(s, SIGNAL(connected()), SLOT(bs_connected())); - connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); - if(!d->curAddr.isNull()) - s->connectToHost(d->curAddr, d->port); - else - s->connectToHost(d->host, d->port); - } - else if(t == Proxy::HttpConnect) { -#ifdef XMPP_DEBUG - qDebug("do_connect2"); -#endif - HttpConnect *s = new HttpConnect; - d->bs = s; - connect(s, SIGNAL(connected()), SLOT(bs_connected())); - connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); - if(!d->proxy.user().isEmpty()) - s->setAuth(d->proxy.user(), d->proxy.pass()); - s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port); - } - else if(t == Proxy::Socks) { -#ifdef XMPP_DEBUG - qDebug("do_connect3"); -#endif - SocksClient *s = new SocksClient; - d->bs = s; - connect(s, SIGNAL(connected()), SLOT(bs_connected())); - connect(s, SIGNAL(error(int)), SLOT(bs_error(int))); - if(!d->proxy.user().isEmpty()) - s->setAuth(d->proxy.user(), d->proxy.pass()); - s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port); - } -} - -void AdvancedConnector::tryNextSrv() -{ -#ifdef XMPP_DEBUG - qDebug("trying next srv"); -#endif - Q_ASSERT(!d->servers.isEmpty()); - d->host = d->servers.first().name; - d->port = d->servers.first().port; - d->servers.takeFirst(); - do_resolve(); -} - -void AdvancedConnector::srv_done() +void AdvancedConnector::bs_connected() { - QPointer self = this; #ifdef XMPP_DEBUG - qDebug("srv_done1"); + XDEBUG; #endif - d->servers = d->srv.servers(); - if(d->servers.isEmpty()) { - srvResult(false); - if(!self) - return; - -#ifdef XMPP_DEBUG - qDebug("srv_done1.1"); -#endif - // fall back to A record - d->using_srv = false; - d->host = d->server; - if(d->opt_probe) { -#ifdef XMPP_DEBUG - qDebug("srv_done1.1.1"); -#endif - d->probe_mode = 0; - d->port = 5223; - d->will_be_ssl = true; - } - else { -#ifdef XMPP_DEBUG - qDebug("srv_done1.1.2"); -#endif - d->probe_mode = 1; - d->port = 5222; - } - do_resolve(); - return; - } - - srvResult(true); - if(!self) - return; - - d->using_srv = true; - tryNextSrv(); -} - -void AdvancedConnector::bs_connected() -{ - d->connectTimeout->stop(); - if(d->proxy.type() == Proxy::None) { QHostAddress h = (static_cast(d->bs))->peerAddress(); int p = (static_cast(d->bs))->peerPort(); setPeerAddress(h, p); } - // only allow ssl override if proxy==poll or host:port - if((d->proxy.type() == Proxy::HttpPoll || !d->opt_hosts.isEmpty()) && d->opt_ssl) - setUseSSL(true); - else if(d->will_be_ssl) + // only allow ssl override if proxy==poll or host:port or when probing legacy ssl port + if(d->proxy.type() == Proxy::HttpPoll || d->opt_ssl != Never) setUseSSL(true); d->mode = Connected; - connected(); + emit connected(); } void AdvancedConnector::bs_error(int x) { +#ifdef XMPP_DEBUG + XDEBUG << "e:" << x; +#endif + if(d->mode == Connected) { d->errorCode = ErrStream; - error(); + emit error(); return; } @@ -692,56 +505,34 @@ void AdvancedConnector::bs_error(int x) } } - // try next host, if any - if(!d->hostsToTry.isEmpty()) - { - d->host = d->hostsToTry.takeFirst(); - do_resolve(); - return; - } - // no-multi or proxy error means we quit - if(!d->multi || proxyError) { + if(proxyError) { cleanup(); d->errorCode = err; - error(); + emit error(); return; } - if(d->using_srv && !d->servers.isEmpty()) { -#ifdef XMPP_DEBUG - qDebug("bse1.1"); -#endif - tryNextSrv(); - } - else if(!d->using_srv && d->opt_probe && d->probe_mode == 0) { + /* + if we shall probe the ssl legacy port, and we just did that (port=legacy), + then try to connect to the normal port instead + */ + if(d->opt_ssl == Probe && d->port == XMPP_LEGACY_PORT) { #ifdef XMPP_DEBUG qDebug("bse1.2"); #endif - d->probe_mode = 1; - d->port = 5222; - d->will_be_ssl = false; - do_connect(); + BSocket *s = static_cast(d->bs); + d->port = XMPP_DEFAULT_PORT; + s->connectToHost(XMPP_CLIENT_SRV, XMPP_CLIENT_TRANSPORT, d->host, d->port); } + /* otherwise we have no fallbacks and must have failed to connect */ else { #ifdef XMPP_DEBUG qDebug("bse1.3"); #endif - // try next address, if any - if(!d->addrList.isEmpty()) { - if(d->opt_probe && d->probe_mode == 1) { - d->probe_mode = 0; - d->port = 5223; - d->will_be_ssl = true; - } - d->curAddr = d->addrList.takeFirst(); - do_connect(); - return; - } - cleanup(); d->errorCode = ErrConnectionRefused; - error(); + emit error(); } } @@ -757,18 +548,10 @@ void AdvancedConnector::http_syncFinished() void AdvancedConnector::t_timeout() { - // skip to next host, if there is one - if(!d->hostsToTry.isEmpty()) - { - delete d->bs; - d->bs = 0; - - d->host = d->hostsToTry.takeFirst(); - do_resolve(); - } + //bs_error(-1); } QString AdvancedConnector::host() const { - return d->connectHost; + return d->host; } diff --git a/iris/src/xmpp/xmpp-core/xmpp.h b/iris/src/xmpp/xmpp-core/xmpp.h index 9c21c79e9..6942656f2 100644 --- a/iris/src/xmpp/xmpp-core/xmpp.h +++ b/iris/src/xmpp/xmpp-core/xmpp.h @@ -141,7 +141,6 @@ namespace XMPP void setProxy(const Proxy &proxy); void setOptHostPort(const QString &host, quint16 port); - void setOptHostsPort(const QStringList &hosts, quint16 port); void setOptProbe(bool); void setOptSSL(bool); @@ -162,9 +161,6 @@ namespace XMPP void httpSyncFinished(); private slots: - void dns_resultsReady(const QList &results); - void dns_error(XMPP::AddressResolver::Error e); - void srv_done(); void bs_connected(); void bs_error(int); void http_syncStarted(); @@ -176,9 +172,6 @@ namespace XMPP Private *d; void cleanup(); - void do_resolve(); - void do_connect(); - void tryNextSrv(); }; class TLSHandler : public QObject diff --git a/iris/tools/nettool/main.cpp b/iris/tools/nettool/main.cpp index 93b6eeba5..e6019aa5a 100644 --- a/iris/tools/nettool/main.cpp +++ b/iris/tools/nettool/main.cpp @@ -227,8 +227,12 @@ private slots: if(null_dump && list[0].type() == NameRecord::Null) { QByteArray buf = list[0].rawData(); - if(fwrite(buf.data(), buf.size(), 1, stdout) < 1) + if(fwrite(buf.data(), buf.size(), 1, stdout) != static_cast(buf.size())) { + /* FIXME: + C89/Unix allows fwrite() to return without having written everything. + The application should retry in that case. + */ fprintf(stderr, "Error: unable to write raw record to stdout\n"); emit quit(); return; diff --git a/src/plugins/generic/otrplugin/README b/src/plugins/generic/otrplugin/README index 1b6a35848..e7c7ed7fd 100644 --- a/src/plugins/generic/otrplugin/README +++ b/src/plugins/generic/otrplugin/README @@ -1,4 +1,3 @@ - Off-The-Record-Messaging plugin for Psi This is a Off-The-Record-Messaging plugin for the Psi+ instant @@ -20,8 +19,23 @@ TODOs +KNOWN ISSUES +* Currently only works for top priority resources +* System messages are only displayed for existing chat windows, might be + displayed in wrong order (needs to be fixed in Psi+) + + + + CHANGES + 0.9.5pre + -Ignore internal messages, report state changes and notifications + via system messages + -The authentication dialog now differs between 'question and answer' + and 'shared secret', enabling mutual authentication for the latter. + -Add plugin info + 0.9.4 -Add authentication via Socialist Millionaires' Protocol -Fix escaping of plaintext OTR messages @@ -52,7 +66,8 @@ CHANGES -Output accounts by name -Auto-resize table columns in options -Don't send empty messages - -Handle multiple selections in the fingerprint widget, fix selections made without mouse + -Handle multiple selections in the fingerprint widget, + fix selections made without mouse 0.9.1 -OTR data is now stored in the data directory of the current profile @@ -75,14 +90,14 @@ CHANGES 0.7 -Use icons for otr button. -Fixed update of message state on button. - -Default policy is now 'enabled'. + -Default policy is now 'enabled'. -Problem related to key generation fixed. -Enabling and disabling of plugin fixed. -Adapted to Psi+ interface changes. 0.6 -adapt to Psi+ plugin interface - -use HtmlTidy for invalid XHTML in encrypted messages + -use HtmlTidy for invalid XHTML in encrypted messages -changed handling of callbacks -several bugfixes @@ -98,7 +113,7 @@ CHANGES -menu in chatdialog to start OTR session, etc. -incoming messages with invalid html markup were discarded in previous versions. - -problem with 'status' information of known fingerprints resolved + -problem with 'status' information of known fingerprints resolved 0.3 -character encoding in tagged-plaintext-messages @@ -106,7 +121,7 @@ CHANGES -resolved problems with configuration widget 0.2 - -character encoding in encrypted messages + -character encoding in encrypted messages @@ -123,6 +138,30 @@ patches into Psi+. +LICENSE + +The Off-the-Record Messaging plugin for Psi+ is covered by the following +(GPL) license: + + Off-the-Record Messaging plugin for Psi+ + Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + 2011-2012 Florian Fieber + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + + + CONTACT http://public.beuth-hochschule.de/~s30935/ diff --git a/src/plugins/generic/otrplugin/otrplugin.pro b/src/plugins/generic/otrplugin/otrplugin.pro index 9bcaf4312..444e50424 100644 --- a/src/plugins/generic/otrplugin/otrplugin.pro +++ b/src/plugins/generic/otrplugin/otrplugin.pro @@ -1,24 +1,24 @@ -include(../../psiplugin.pri) - -CONFIG += release -LIBS += -lotr -ltidy -lgcrypt -L/usr/local/lib -RESOURCES = psi-otr.qrc -unix { - INCLUDEPATH += /usr/include/tidy -} - -HEADERS += src/PsiOtrPlugin.hpp -HEADERS += src/OtrMessaging.hpp -HEADERS += src/OtrInternal.hpp -HEADERS += src/PsiOtrConfig.hpp -HEADERS += src/psiotrclosure.h -HEADERS += src/HtmlTidy.hpp -HEADERS += src/otrl_extensions.h - -SOURCES += src/PsiOtrPlugin.cpp -SOURCES += src/OtrMessaging.cpp -SOURCES += src/OtrInternal.cpp -SOURCES += src/PsiOtrConfig.cpp -SOURCES += src/psiotrclosure.cpp -SOURCES += src/HtmlTidy.cpp -SOURCES += src/otrl_extensions.c +include(../../psiplugin.pri) + +CONFIG += release +LIBS += -lotr -ltidy -lgcrypt -lgpg-error -L/usr/local/lib +RESOURCES = otrplugin.qrc +unix { + INCLUDEPATH += /usr/include/tidy +} + +HEADERS += src/psiotrplugin.h +HEADERS += src/otrmessaging.h +HEADERS += src/otrinternal.h +HEADERS += src/psiotrconfig.h +HEADERS += src/psiotrclosure.h +HEADERS += src/htmltidy.h +HEADERS += src/otrlextensions.h + +SOURCES += src/psiotrplugin.cpp +SOURCES += src/otrmessaging.cpp +SOURCES += src/otrinternal.cpp +SOURCES += src/psiotrconfig.cpp +SOURCES += src/psiotrclosure.cpp +SOURCES += src/htmltidy.cpp +SOURCES += src/otrlextensions.c diff --git a/src/plugins/generic/otrplugin/otrplugin.qrc b/src/plugins/generic/otrplugin/otrplugin.qrc new file mode 100644 index 000000000..4b67387a7 --- /dev/null +++ b/src/plugins/generic/otrplugin/otrplugin.qrc @@ -0,0 +1,8 @@ + + + + resources/otr_yes.png + resources/otr_no.png + resources/otr_unverified.png + + diff --git a/src/plugins/generic/otrplugin/psi-otr.qrc b/src/plugins/generic/otrplugin/psi-otr.qrc deleted file mode 100644 index ba33f3111..000000000 --- a/src/plugins/generic/otrplugin/psi-otr.qrc +++ /dev/null @@ -1,8 +0,0 @@ - - - - otr_yes.png - otr_no.png - otr_unverified.png - - \ No newline at end of file diff --git a/src/plugins/generic/otrplugin/otr_no.png b/src/plugins/generic/otrplugin/resources/otr_no.png similarity index 100% rename from src/plugins/generic/otrplugin/otr_no.png rename to src/plugins/generic/otrplugin/resources/otr_no.png diff --git a/src/plugins/generic/otrplugin/otr_unverified.png b/src/plugins/generic/otrplugin/resources/otr_unverified.png similarity index 100% rename from src/plugins/generic/otrplugin/otr_unverified.png rename to src/plugins/generic/otrplugin/resources/otr_unverified.png diff --git a/src/plugins/generic/otrplugin/otr_yes.png b/src/plugins/generic/otrplugin/resources/otr_yes.png similarity index 100% rename from src/plugins/generic/otrplugin/otr_yes.png rename to src/plugins/generic/otrplugin/resources/otr_yes.png diff --git a/src/plugins/generic/otrplugin/src/OtrInternal.hpp b/src/plugins/generic/otrplugin/src/OtrInternal.hpp deleted file mode 100644 index eb440cb2a..000000000 --- a/src/plugins/generic/otrplugin/src/OtrInternal.hpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * OtrInternal.h - manages the otr-connection. - * - * Copyright (C) Timo Engel (timo-e@freenet.de), Berlin 2007. - * This program was written as part of a diplom thesis advised by - * Prof. Dr. Ruediger Weis (PST Labor) - * at the Technical University of Applied Sciences Berlin. - * - * 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; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef OTRINTERNAL_HPP_ -#define OTRINTERNAL_HPP_ - -#include "OtrMessaging.hpp" - -#include -#include - -extern "C" -{ -#include -#include -#include -#include "otrl_extensions.h" -} - -class QString; -namespace psiotr { class PsiOtrPlugin; } - -// --------------------------------------------------------------------------- - -/** -* Handles all libotr calls and callbacks. -*/ -class OtrInternal -{ -public: - - OtrInternal(psiotr::OtrCallback* callback, psiotr::OtrPolicy& policy); - - ~OtrInternal(); - - QString encryptMessage(const QString& from, const QString& to, - const QString& message); - - bool decryptMessage(const QString& from, const QString& to, - const QString& message, QString& decrypted); - - QList getFingerprints(); - - void verifyFingerprint(const psiotr::Fingerprint& fingerprint, bool verified); - - void deleteFingerprint(const psiotr::Fingerprint& fingerprint); - - QHash getPrivateKeys(); - - void deleteKey(const QString& account); - - - void startSession(const QString& account, const QString& jid); - - void endSession(const QString& account, const QString& jid); - - void expireSession(const QString& account, const QString& jid); - - - void startSMP(const QString& account, const QString& jid, - const QString& question, const QString& secret); - void startSMP(ConnContext *context, - const QString& question, const QString& secret); - - void continueSMP(const QString& account, const QString& jid, const QString& secret); - void continueSMP(ConnContext *context, const QString& secret); - - void abortSMP(const QString& account, const QString& jid); - void abortSMP(ConnContext *context); - - - psiotr::OtrMessageState getMessageState(const QString& account, - const QString& contact); - - QString getMessageStateString(const QString& account, - const QString& contact); - - QString getSessionId(const QString& account, const QString& contact); - - psiotr::Fingerprint getActiveFingerprint(const QString& account, - const QString& contact); - - bool isVerified(const QString& account, const QString& contact); - - bool smpSucceeded(const QString& account, const QString& contact); - - void generateKey(const QString& account); - - static QString humanFingerprint(const unsigned char *fingerprint); - - /*** otr callback functions ***/ - OtrlPolicy policy(ConnContext *context); - void create_privkey(const char *accountname, const char *protocol); - int is_logged_in(const char *accountname, const char *protocol, - const char *recipient); - void inject_message(const char *accountname, const char *protocol, - const char *recipient, const char *message); - void notify(OtrlNotifyLevel level, const char *accountname, - const char *protocol, const char *username, const char *title, - const char *primary, const char *secondary); - int display_otr_message(const char *accountname, const char *protocol, - const char *username, const char *msg); - void update_context_list(); - const char* protocol_name(const char *protocol); - void protocol_name_free(const char *protocol_name); - void new_fingerprint(OtrlUserState us, const char *accountname, - const char *protocol, const char *username, - unsigned char fingerprint[20]); - void write_fingerprints(); - void gone_secure(ConnContext *context); - void gone_insecure(ConnContext *context); - void still_secure(ConnContext *context, int is_reply); - void log_message(const char *message); - - const char* account_name(const char *account, - const char *protocol); - void account_name_free(const char *account_name); - - - /*** static otr callback wrapper-functions ***/ - static OtrlPolicy cb_policy(void *opdata, ConnContext *context); - static void cb_create_privkey(void *opdata, const char *accountname, - const char *protocol); - static int cb_is_logged_in(void *opdata, const char *accountname, - const char *protocol, const char *recipient); - static void cb_inject_message(void *opdata, const char *accountname, - const char *protocol, const char *recipient, - const char *message); - static void cb_notify(void *opdata, OtrlNotifyLevel level, - const char *accountname, const char *protocol, - const char *username, const char *title, - const char *primary, const char *secondary); - static int cb_display_otr_message(void *opdata, const char *accountname, - const char *protocol, const char *username, - const char *msg); - static void cb_update_context_list(void *opdata); - static const char* cb_protocol_name(void *opdata, const char *protocol); - static void cb_protocol_name_free(void *opdata, const char *protocol_name); - static void cb_new_fingerprint(void *opdata, OtrlUserState us, - const char *accountname, const char *protocol, - const char *username, unsigned char fingerprint[20]); - static void cb_write_fingerprints(void *opdata); - static void cb_gone_secure(void *opdata, ConnContext *context); - static void cb_gone_insecure(void *opdata, ConnContext *context); - static void cb_still_secure(void *opdata, ConnContext *context, int is_reply); - static void cb_log_message(void *opdata, const char *message); - - static const char* cb_account_name(void *opdata, const char *account, const char *protocol); - static void cb_account_name_free(void *opdata, const char *account_name); -private: - - /** - * The userstate contains keys and known fingerprints. - */ - OtrlUserState m_userstate; - - /** - * Pointers to callback functions. - */ - OtrlMessageAppOps m_uiOps; - - /** - * Pointer to a class for callbacks from OTR to application. - */ - psiotr::OtrCallback* m_callback; - - /** - * Name of the file storing dsa-keys. - */ - QString m_keysFile; - - /** - * Name of the file storing known fingerprints. - */ - QString m_fingerprintFile; - - /** - * Reference to the default OTR policy - */ - psiotr::OtrPolicy& m_otrPolicy; -}; - -// --------------------------------------------------------------------------- - -#endif diff --git a/src/plugins/generic/otrplugin/src/HtmlTidy.cpp b/src/plugins/generic/otrplugin/src/htmltidy.cpp similarity index 63% rename from src/plugins/generic/otrplugin/src/HtmlTidy.cpp rename to src/plugins/generic/otrplugin/src/htmltidy.cpp index bed03d355..bfb15c606 100644 --- a/src/plugins/generic/otrplugin/src/HtmlTidy.cpp +++ b/src/plugins/generic/otrplugin/src/htmltidy.cpp @@ -1,5 +1,32 @@ -#include "HtmlTidy.hpp" -#include +/* + * htmltidy.cpp - Tidy html with libtidy + * + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011 Florian Fieber + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "htmltidy.h" + +#include +#include +#include +#include + //----------------------------------------------------------------------------- @@ -9,14 +36,14 @@ HtmlTidy::HtmlTidy(const QString& html) m_output(), m_input(html) { - tidyOptSetBool(m_tidyDoc, TidyXmlOut, yes); + tidyOptSetBool (m_tidyDoc, TidyXmlOut, yes); tidyOptSetValue(m_tidyDoc, TidyCharEncoding, "utf8"); - tidyOptSetInt(m_tidyDoc, TidyNewline, TidyLF); - tidyOptSetBool(m_tidyDoc, TidyQuoteNbsp, no); - tidyOptSetBool(m_tidyDoc, TidyForceOutput, yes); + tidyOptSetInt (m_tidyDoc, TidyNewline, TidyLF); + tidyOptSetBool (m_tidyDoc, TidyQuoteNbsp, no); + tidyOptSetBool (m_tidyDoc, TidyForceOutput, yes); + + tidySetErrorBuffer(m_tidyDoc, &m_errorOutput); - tidySetErrorBuffer(m_tidyDoc, &m_errorOutput); - tidyParseString(m_tidyDoc, m_input.toUtf8().data()); tidyCleanAndRepair(m_tidyDoc); } @@ -43,7 +70,7 @@ QString HtmlTidy::writeOutput() #endif sink.sinkData = this; tidySaveSink(m_tidyDoc, &sink); - + return QString::fromUtf8(m_output); } @@ -66,7 +93,7 @@ QDomElement HtmlTidy::output(QDomDocument& document) int errorLine = 0; int errorColumn = 0; QString errorText; - + QString html = writeOutput(); if (!document.setContent(html, true, &errorText, &errorLine, &errorColumn)) @@ -90,7 +117,7 @@ void HtmlTidy::putByte(void* sinkData, byte bt) } //----------------------------------------------------------------------------- - + void HtmlTidy::putByte(byte bt) { m_output.append(bt); diff --git a/src/plugins/generic/otrplugin/src/HtmlTidy.hpp b/src/plugins/generic/otrplugin/src/htmltidy.h similarity index 69% rename from src/plugins/generic/otrplugin/src/HtmlTidy.hpp rename to src/plugins/generic/otrplugin/src/htmltidy.h index 65e907eba..724638dc0 100644 --- a/src/plugins/generic/otrplugin/src/HtmlTidy.hpp +++ b/src/plugins/generic/otrplugin/src/htmltidy.h @@ -1,6 +1,9 @@ /* - * HtmlTidy.hpp - tidy html with libtidy - * Copyright (C) 2010 Timo Engel (timo-e@freenet.de) + * htmltidy.h - Tidy html with libtidy + * + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011 Florian Fieber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -13,18 +16,27 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program. If not, see . * */ -#ifndef HTMLTIDY_HPP_ -#define HTMLTIDY_HPP_ +#ifndef HTMLTIDY_H_ +#define HTMLTIDY_H_ + +#include +#include #include +#ifndef Q_WS_WIN #include #include -#include +#else +#include +#include +#endif + +class QDomDocument; +class QDomElement; class HtmlTidy { @@ -34,13 +46,13 @@ class HtmlTidy QString output(); QDomElement output(QDomDocument& document); static void putByte(void* sinkData, byte bt); - + protected: void putByte(byte bt); QString writeOutput(); - - + + private: TidyDoc m_tidyDoc; TidyBuffer m_errorOutput; diff --git a/src/plugins/generic/otrplugin/src/OtrInternal.cpp b/src/plugins/generic/otrplugin/src/otrinternal.cpp similarity index 73% rename from src/plugins/generic/otrplugin/src/OtrInternal.cpp rename to src/plugins/generic/otrplugin/src/otrinternal.cpp index 62c7fcc3f..4379ee154 100644 --- a/src/plugins/generic/otrplugin/src/OtrInternal.cpp +++ b/src/plugins/generic/otrplugin/src/otrinternal.cpp @@ -1,9 +1,12 @@ /* - * OtrInternal.cpp - manages the otr connection. + * otrinternal.cpp - Manages the OTR connection * - * Copyright (C) Timo Engel (timo-e@freenet.de), Berlin 2007. - * This program was written as part of a diplom thesis advised by - * Prof. Dr. Ruediger Weis (PST Labor) + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011-2012 Florian Fieber + * + * This program was originally written as part of a diplom thesis + * advised by Prof. Dr. Ruediger Weis (PST Labor) * at the Technical University of Applied Sciences Berlin. * * This program is free software; you can redistribute it and/or @@ -17,17 +20,24 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program. If not, see . * */ -#include "OtrInternal.hpp" +#include "otrinternal.h" #include +#include +#include +#include +#include +#include #include #include -#include +#include +#include +#include +#include #include //----------------------------------------------------------------------------- @@ -38,60 +48,6 @@ static const QString OTR_KEYS_FILE = "otr.keys"; //----------------------------------------------------------------------------- -QString otrlMessageTypeToString(const OtrlMessageType& type) -{ - if (type == OTRL_MSGTYPE_NOTOTR) - { - return QObject::tr("No OTR Message"); - } - else if (type == OTRL_MSGTYPE_TAGGEDPLAINTEXT) - { - return QObject::tr("OTR TaggedPlaintextMessage"); - } - else if (type == OTRL_MSGTYPE_QUERY) - { - return QObject::tr("OTR QueryMessage"); - } - else if (type == OTRL_MSGTYPE_DH_COMMIT) - { - return QObject::tr("OTR DH-Commit Message"); - } - else if (type == OTRL_MSGTYPE_DH_KEY) - { - return QObject::tr("OTR DH-Key Message"); - } - else if (type == OTRL_MSGTYPE_REVEALSIG) - { - return QObject::tr("OTR Reveal Signature Message"); - } - else if (type == OTRL_MSGTYPE_SIGNATURE) - { - return QObject::tr("OTR Signature Message"); - } - else if (type == OTRL_MSGTYPE_V1_KEYEXCH) - { - return QObject::tr("OTR Version 1 Key Exchange Message"); - } - else if (type == OTRL_MSGTYPE_DATA) - { - return QObject::tr("OTR Data Message"); - } - else if (type == OTRL_MSGTYPE_ERROR) - { - return QObject::tr("OTR Error Message"); - } - else if (type == OTRL_MSGTYPE_UNKNOWN) - { - return QObject::tr("OTR Unknown Message"); - } - else - { - return QObject::tr("Unknown OTR Message Type"); - } -} - -//----------------------------------------------------------------------------- - class KeyGeneratorThread : public QThread { public: @@ -103,7 +59,7 @@ class KeyGeneratorThread : public QThread m_protocol(protocol) { } - + void run() { otrl_privkey_generate(m_userstate, QFile::encodeName(m_keysFile).constData(), @@ -124,10 +80,13 @@ OtrInternal::OtrInternal(psiotr::OtrCallback* callback, : m_userstate(), m_uiOps(), m_callback(callback), - m_keysFile(callback->dataDir() + "/" + OTR_KEYS_FILE), - m_fingerprintFile(callback->dataDir() + "/" + OTR_FINGERPRINTS_FILE), m_otrPolicy(policy) { + QDir profileDir(callback->dataDir()); + + m_keysFile = profileDir.filePath(OTR_KEYS_FILE); + m_fingerprintFile = profileDir.filePath(OTR_FINGERPRINTS_FILE); + OTRL_INIT; m_userstate = otrl_userstate_create(); m_uiOps.policy = (*OtrInternal::cb_policy); @@ -167,27 +126,27 @@ OtrInternal::~OtrInternal() //----------------------------------------------------------------------------- -QString OtrInternal::encryptMessage(const QString& from, const QString& to, +QString OtrInternal::encryptMessage(const QString& account, const QString& contact, const QString& message) { char* encMessage = NULL; gcry_error_t err; err = otrl_message_sending(m_userstate, &m_uiOps, this, - from.toUtf8().constData(), OTR_PROTOCOL_STRING, - to.toUtf8().constData(), + account.toUtf8().constData(), OTR_PROTOCOL_STRING, + contact.toUtf8().constData(), message.toUtf8().constData(), NULL, &encMessage, NULL, NULL); if (err != 0) { - m_callback->notifyUser(psiotr::OTR_NOTIFY_ERROR, + m_callback->notifyUser(psiotr::OTR_NOTIFY_ERROR, QObject::tr("Encrypting message to %1 " "failed.\nThe message was not sent.") - .arg(to)); + .arg(contact)); return QString(); } - if (encMessage != NULL) + if (encMessage) { QString retMessage(QString::fromUtf8(encMessage)); otrl_message_free(encMessage); @@ -200,15 +159,16 @@ QString OtrInternal::encryptMessage(const QString& from, const QString& to, //----------------------------------------------------------------------------- -bool OtrInternal::decryptMessage(const QString& from, const QString& to, - const QString& cryptedMessage, - QString& decrypted) +psiotr::OtrMessageType OtrInternal::decryptMessage(const QString& account, + const QString& contact, + const QString& cryptedMessage, + QString& decrypted) { - QByteArray accArray = to.toUtf8(); - QByteArray userArray = from.toUtf8(); + QByteArray accArray = account.toUtf8(); + QByteArray userArray = contact.toUtf8(); const char* accountName = accArray.constData(); const char* userName = userArray.constData(); - bool isOTR = false; + int ignoreMessage = 0; char* newMessage = NULL; OtrlTLV* tlvs = NULL; @@ -223,6 +183,13 @@ bool OtrInternal::decryptMessage(const QString& from, const QString& to, &newMessage, &tlvs, NULL, NULL); + + tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED); + if (tlv) { + m_callback->stateChange(accountName, userName, + psiotr::OTR_STATECHANGE_REMOTECLOSE); + } + // Check for SMP data context = otrl_context_find(m_userstate, userName, accountName, @@ -249,8 +216,8 @@ bool OtrInternal::decryptMessage(const QString& from, const QString& to, } else { - char *question = (char *)tlv->data; - char *eoq = static_cast(memchr(question, '\0', tlv->len)); + char* question = (char *)tlv->data; + char* eoq = static_cast(memchr(question, '\0', tlv->len)); if (eoq) { m_callback->receivedSMP(accountName, userName, QString::fromUtf8(question)); @@ -326,30 +293,17 @@ bool OtrInternal::decryptMessage(const QString& from, const QString& to, { // Internal protocol message - OtrlMessageType type = otrl_proto_message_type( - cryptedMessage.toUtf8().constData()); - - decrypted = QObject::tr("Received %1 [%2]") - .arg(otrlMessageTypeToString(type)) - .arg(getMessageStateString(to, from)); - - if (getMessageState(to, from) == psiotr::OTR_MESSAGESTATE_ENCRYPTED) - { - decrypted.append("
" + QObject::tr("Session ID: ") - + getSessionId(to, from)); - } - - isOTR = true; + return psiotr::OTR_MESSAGETYPE_IGNORE; } - else if (ignoreMessage == 0 && newMessage != NULL) + else if ((ignoreMessage == 0) && newMessage) { // Message has been decrypted, replace it decrypted = QString::fromUtf8(newMessage); otrl_message_free(newMessage); - isOTR = true; + return psiotr::OTR_MESSAGETYPE_OTR; } - return isOTR; + return psiotr::OTR_MESSAGETYPE_NONE; } //----------------------------------------------------------------------------- @@ -369,12 +323,7 @@ QList OtrInternal::getFingerprints() psiotr::Fingerprint fp (fingerprint->fingerprint, QString::fromUtf8(context->accountname), QString::fromUtf8(context->username), - QString::fromUtf8(fingerprint->trust), - (fingerprint == context->active_fingerprint)? - getMessageStateString( - QString::fromUtf8(context->accountname), - QString::fromUtf8(context->username)) : - QString()); + QString::fromUtf8(fingerprint->trust)); fpList.append(fp); fingerprint = fingerprint->next; @@ -393,25 +342,24 @@ void OtrInternal::verifyFingerprint(const psiotr::Fingerprint& fingerprint, fingerprint.account.toUtf8().constData(), OTR_PROTOCOL_STRING, false, NULL, NULL, NULL); - if (context != NULL) + if (context) { ::Fingerprint* fp = otrl_context_find_fingerprint(context, fingerprint.fingerprint, 0, NULL); - if (fp != NULL) + if (fp) { - if (verified) - { - otrl_context_set_trust(fp, "verified"); - } - else + otrl_context_set_trust(fp, verified? "verified" : ""); + write_fingerprints(); + + if (context->active_fingerprint == fp) { - otrl_context_set_trust(fp, ""); + m_callback->stateChange(QString::fromUtf8(context->accountname), + QString::fromUtf8(context->username), + psiotr::OTR_STATECHANGE_TRUST); } } } - - write_fingerprints(); } //----------------------------------------------------------------------------- @@ -423,12 +371,12 @@ void OtrInternal::deleteFingerprint(const psiotr::Fingerprint& fingerprint) fingerprint.account.toUtf8().constData(), OTR_PROTOCOL_STRING, false, NULL, NULL, NULL); - if (context != NULL) + if (context) { ::Fingerprint* fp = otrl_context_find_fingerprint(context, fingerprint.fingerprint, 0, NULL); - if (fp != NULL) + if (fp) { if (context->active_fingerprint == fp) { @@ -469,7 +417,7 @@ QHash OtrInternal::getPrivateKeys() void OtrInternal::deleteKey(const QString& account) { - OtrlPrivKey *privKey = otrl_privkey_find(m_userstate, + OtrlPrivKey* privKey = otrl_privkey_find(m_userstate, account.toUtf8().constData(), OTR_PROTOCOL_STRING); @@ -480,12 +428,12 @@ void OtrInternal::deleteKey(const QString& account) //----------------------------------------------------------------------------- -void OtrInternal::startSession(const QString& account, const QString& jid) +void OtrInternal::startSession(const QString& account, const QString& contact) { - char fingerprint[45]; - if (!otrl_privkey_fingerprint(m_userstate, fingerprint, - account.toUtf8().constData(), - OTR_PROTOCOL_STRING)) + m_callback->stateChange(account, contact, psiotr::OTR_STATECHANGE_GOINGSECURE); + + if (!otrl_privkey_find(m_userstate, account.toUtf8().constData(), + OTR_PROTOCOL_STRING)) { create_privkey(account.toUtf8().constData(), OTR_PROTOCOL_STRING); } @@ -494,91 +442,102 @@ void OtrInternal::startSession(const QString& account, const QString& jid) char* msg = otrl_proto_default_query_msg(m_callback->humanAccountPublic(account).toUtf8().constData(), OTRL_POLICY_DEFAULT); - m_callback->sendMessage(account, jid, QString::fromUtf8(msg)); + m_callback->sendMessage(account, contact, QString::fromUtf8(msg)); + + free(msg); } //----------------------------------------------------------------------------- -void OtrInternal::endSession(const QString& account, const QString& jid) +void OtrInternal::endSession(const QString& account, const QString& contact) { - otrl_message_disconnect(m_userstate, &m_uiOps, this, + ConnContext* context = otrl_context_find(m_userstate, + contact.toUtf8().constData(), + account.toUtf8().constData(), + OTR_PROTOCOL_STRING, false, + NULL, NULL, NULL); + if (context && (context->msgstate != OTRL_MSGSTATE_PLAINTEXT)) + { + m_callback->stateChange(account, contact, psiotr::OTR_STATECHANGE_CLOSE); + } + otrl_message_disconnect(m_userstate, &m_uiOps, this, account.toUtf8().constData(), OTR_PROTOCOL_STRING, - jid.toUtf8().constData()); + contact.toUtf8().constData()); } //----------------------------------------------------------------------------- -void OtrInternal::expireSession(const QString& account, const QString& jid) +void OtrInternal::expireSession(const QString& account, const QString& contact) { ConnContext* context = otrl_context_find(m_userstate, - jid.toUtf8().constData(), + contact.toUtf8().constData(), account.toUtf8().constData(), OTR_PROTOCOL_STRING, false, NULL, NULL, NULL); - if ((context != NULL) && - (context->msgstate == OTRL_MSGSTATE_ENCRYPTED)) + if (context && (context->msgstate == OTRL_MSGSTATE_ENCRYPTED)) { otrl_context_force_finished(context); + m_callback->stateChange(account, contact, + psiotr::OTR_STATECHANGE_GONEINSECURE); } } //----------------------------------------------------------------------------- -void OtrInternal::startSMP(const QString& account, const QString& jid, +void OtrInternal::startSMP(const QString& account, const QString& contact, const QString& question, const QString& secret) { ConnContext* context = otrl_context_find(m_userstate, - jid.toUtf8().constData(), + contact.toUtf8().constData(), account.toUtf8().constData(), OTR_PROTOCOL_STRING, false, NULL, NULL, NULL); if (context) { - startSMP(context, question, secret); - } -} + QByteArray secretArray = secret.toUtf8(); + const char* secretPointer = secretArray.constData(); + size_t secretLength = qstrlen(secretPointer); -void OtrInternal::startSMP(ConnContext *context, - const QString& question, const QString& secret) -{ - QByteArray secretArray = secret.toUtf8(); - const char* secretPointer = secretArray.constData(); - size_t secretLength = qstrlen(secretPointer); - - otrl_message_initiate_smp_q(m_userstate, &m_uiOps, this, context, - question.toUtf8().constData(), - reinterpret_cast(const_cast(secretPointer)), - secretLength); + if (question.isEmpty()) + { + otrl_message_initiate_smp(m_userstate, &m_uiOps, this, context, + reinterpret_cast(const_cast(secretPointer)), + secretLength); + } + else + { + otrl_message_initiate_smp_q(m_userstate, &m_uiOps, this, context, + question.toUtf8().constData(), + reinterpret_cast(const_cast(secretPointer)), + secretLength); + } + } } -void OtrInternal::continueSMP(const QString& account, const QString& jid, +void OtrInternal::continueSMP(const QString& account, const QString& contact, const QString& secret) { ConnContext* context = otrl_context_find(m_userstate, - jid.toUtf8().constData(), + contact.toUtf8().constData(), account.toUtf8().constData(), OTR_PROTOCOL_STRING, false, NULL, NULL, NULL); if (context) { - continueSMP(context, secret); - } -} - -void OtrInternal::continueSMP(ConnContext *context, const QString& secret) -{ - QByteArray secretArray = secret.toUtf8(); - const char* secretPointer = secretArray.constData(); - size_t secretLength = qstrlen(secretPointer); + QByteArray secretArray = secret.toUtf8(); + const char* secretPointer = secretArray.constData(); + size_t secretLength = qstrlen(secretPointer); - otrl_message_respond_smp(m_userstate, &m_uiOps, this, context, - reinterpret_cast(secretPointer), secretLength); + otrl_message_respond_smp(m_userstate, &m_uiOps, this, context, + reinterpret_cast(secretPointer), + secretLength); + } } -void OtrInternal::abortSMP(const QString& account, const QString& jid) +void OtrInternal::abortSMP(const QString& account, const QString& contact) { ConnContext* context = otrl_context_find(m_userstate, - jid.toUtf8().constData(), + contact.toUtf8().constData(), account.toUtf8().constData(), OTR_PROTOCOL_STRING, false, NULL, NULL, NULL); @@ -588,7 +547,7 @@ void OtrInternal::abortSMP(const QString& account, const QString& jid) } } -void OtrInternal::abortSMP(ConnContext *context) +void OtrInternal::abortSMP(ConnContext* context) { otrl_message_abort_smp(m_userstate, &m_uiOps, this, context); } @@ -603,7 +562,7 @@ psiotr::OtrMessageState OtrInternal::getMessageState(const QString& account, account.toUtf8().constData(), OTR_PROTOCOL_STRING, false, NULL, NULL, NULL); - if (context != NULL) + if (context) { if (context->msgstate == OTRL_MSGSTATE_PLAINTEXT) { @@ -641,7 +600,7 @@ QString OtrInternal::getMessageStateString(const QString& account, { return QObject::tr("finished"); } - + return QObject::tr("unknown"); } @@ -654,8 +613,7 @@ QString OtrInternal::getSessionId(const QString& account, context = otrl_context_find(m_userstate, contact.toUtf8().constData(), account.toUtf8().constData(), OTR_PROTOCOL_STRING, false, NULL, NULL, NULL); - if ((context != NULL) && - (context->sessionid_len > 0)) + if (context && (context->sessionid_len > 0)) { QString firstHalf; QString secondHalf; @@ -701,16 +659,12 @@ psiotr::Fingerprint OtrInternal::getActiveFingerprint(const QString& account, account.toUtf8().constData(), OTR_PROTOCOL_STRING, false, NULL, NULL, NULL); - if ((context != NULL) && - (context->active_fingerprint != NULL)) + if (context && context->active_fingerprint) { return psiotr::Fingerprint(context->active_fingerprint->fingerprint, QString::fromUtf8(context->accountname), QString::fromUtf8(context->username), - QString::fromUtf8(context->active_fingerprint->trust), - getMessageStateString( - QString::fromUtf8(context->accountname), - QString::fromUtf8(context->username))); + QString::fromUtf8(context->active_fingerprint->trust)); } return psiotr::Fingerprint(); @@ -723,11 +677,19 @@ bool OtrInternal::isVerified(const QString& account, { ConnContext* context; context = otrl_context_find(m_userstate, contact.toUtf8().constData(), - account.toUtf8().constData(), OTR_PROTOCOL_STRING, + account.toUtf8().constData(), + OTR_PROTOCOL_STRING, false, NULL, NULL, NULL); - if ((context != NULL) && - (context->active_fingerprint != NULL)) + return isVerified(context); +} + +//----------------------------------------------------------------------------- + +bool OtrInternal::isVerified(ConnContext* context) +{ + + if (context && context->active_fingerprint) { return (context->active_fingerprint->trust && context->active_fingerprint->trust[0]); @@ -746,7 +708,7 @@ bool OtrInternal::smpSucceeded(const QString& account, account.toUtf8().constData(), OTR_PROTOCOL_STRING, false, NULL, NULL, NULL); - if (context != NULL) + if (context) { return context->smstate->sm_prog_state == OTRL_SMP_PROG_SUCCEEDED; } @@ -763,7 +725,7 @@ void OtrInternal::generateKey(const QString& account) //----------------------------------------------------------------------------- -QString OtrInternal::humanFingerprint(const unsigned char *fingerprint) +QString OtrInternal::humanFingerprint(const unsigned char* fingerprint) { char fpHash[45]; otrl_privkey_hash_to_human(fpHash, fingerprint); @@ -789,23 +751,23 @@ OtrlPolicy OtrInternal::policy(ConnContext*) } else if (m_otrPolicy == psiotr::OTR_POLICY_REQUIRE) { - return OTRL_POLICY_ALWAYS; // require private messaging + return OTRL_POLICY_ALWAYS; // require private messaging } - + return OTRL_POLICY_NEVER; } // --------------------------------------------------------------------------- -void OtrInternal::create_privkey(const char *accountname, - const char *protocol) +void OtrInternal::create_privkey(const char* accountname, + const char* protocol) { m_callback->stopMessages(); - + KeyGeneratorThread keyGenerator(m_userstate, m_keysFile, accountname, protocol); keyGenerator.start(); - + QMessageBox infoMb(QMessageBox::Information, QObject::tr("Psi OTR"), QObject::tr("Generating keys for account \"%1\"." "\nThis may take a while.") @@ -828,7 +790,15 @@ void OtrInternal::create_privkey(const char *accountname, char fingerprint[45]; if (otrl_privkey_fingerprint(m_userstate, fingerprint, accountname, - protocol) == NULL) + protocol)) + { + infoMb.setText(QObject::tr("Fingerprint for account \"%1\":\n%2") + .arg(m_callback->humanAccount( + QString::fromUtf8(accountname))) + .arg(QString(fingerprint))); + infoMb.exec(); + } + else { QMessageBox failMb(QMessageBox::Critical, QObject::tr("Psi OTR"), QObject::tr("Failed to generate keys for account \"%1\"." @@ -839,22 +809,14 @@ void OtrInternal::create_privkey(const char *accountname, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint); failMb.exec(); } - else - { - infoMb.setText(QObject::tr("Fingerprint for account \"%1\":\n%2") - .arg(m_callback->humanAccount( - QString::fromUtf8(accountname))) - .arg(QString(fingerprint))); - } - infoMb.exec(); m_callback->startMessages(); } // --------------------------------------------------------------------------- -int OtrInternal::is_logged_in(const char *accountname, const char *protocol, - const char *recipient) +int OtrInternal::is_logged_in(const char* accountname, const char* protocol, + const char* recipient) { Q_UNUSED(protocol); @@ -870,10 +832,10 @@ int OtrInternal::is_logged_in(const char *accountname, const char *protocol, } // --------------------------------------------------------------------------- - -void OtrInternal::inject_message(const char *accountname, - const char *protocol, const char *recipient, - const char *message) + +void OtrInternal::inject_message(const char* accountname, + const char* protocol, const char* recipient, + const char* message) { Q_UNUSED(protocol); @@ -881,12 +843,12 @@ void OtrInternal::inject_message(const char *accountname, QString::fromUtf8(recipient), QString::fromUtf8(message)); } - + // --------------------------------------------------------------------------- -void OtrInternal::notify(OtrlNotifyLevel level, const char *accountname, - const char *protocol, const char *username, - const char *title, const char *primary, const char *secondary) +void OtrInternal::notify(OtrlNotifyLevel level, const char* accountname, + const char* protocol, const char* username, + const char* title, const char* primary, const char* secondary) { Q_UNUSED(accountname); Q_UNUSED(protocol); @@ -910,22 +872,32 @@ void OtrInternal::notify(OtrlNotifyLevel level, const char *accountname, m_callback->notifyUser(type, QString(primary) + "\n" + QString(secondary)); } - + // --------------------------------------------------------------------------- - -int OtrInternal::display_otr_message(const char *accountname, - const char *protocol, - const char *username, - const char *msg) + +int OtrInternal::display_otr_message(const char* accountname, + const char* protocol, + const char* username, + const char* msg) { - Q_UNUSED(accountname); Q_UNUSED(protocol); - Q_UNUSED(username); - Q_UNUSED(msg); - - return -1; // use notify() or inline message + + QString message = QString::fromUtf8(msg); + + if (QRegExp("^The following message received " + "from .+ was not encrypted: " + "\\[.+\\]$").exactMatch(message)) + { + return -1; + } + else + { + return m_callback->displayOtrMessage(QString::fromUtf8(accountname), + QString::fromUtf8(username), + message)? 0 : -1; + } } - + // --------------------------------------------------------------------------- void OtrInternal::update_context_list() @@ -942,28 +914,33 @@ const char* OtrInternal::protocol_name(const char* protocol) // --------------------------------------------------------------------------- -void OtrInternal::protocol_name_free(const char *protocol_name) +void OtrInternal::protocol_name_free(const char* protocol_name) { Q_UNUSED(protocol_name); } // --------------------------------------------------------------------------- -void OtrInternal::new_fingerprint(OtrlUserState us, const char *accountname, - const char *protocol, const char *username, +void OtrInternal::new_fingerprint(OtrlUserState us, const char* accountname, + const char* protocol, const char* username, unsigned char fingerprint[20]) { Q_UNUSED(us); - Q_UNUSED(accountname); Q_UNUSED(protocol); - m_callback->notifyUser(psiotr::OTR_NOTIFY_INFO, - QObject::tr("You have received a new " - "fingerprint from %1:\n%2") - .arg(QString::fromUtf8(username)) - .arg(humanFingerprint(fingerprint))); + QString account = QString::fromUtf8(accountname); + QString contact = QString::fromUtf8(username); + QString message = QObject::tr("You have received a new " + "fingerprint from %1:\n%2") + .arg(m_callback->humanContact(account, contact)) + .arg(humanFingerprint(fingerprint)); + + if (!m_callback->displayOtrMessage(account, contact, message)) + { + m_callback->notifyUser(psiotr::OTR_NOTIFY_INFO, message); + } } - + // --------------------------------------------------------------------------- void OtrInternal::write_fingerprints() @@ -974,37 +951,43 @@ void OtrInternal::write_fingerprints() // --------------------------------------------------------------------------- -void OtrInternal::gone_secure(ConnContext *context) +void OtrInternal::gone_secure(ConnContext* context) { - Q_UNUSED(context); + m_callback->stateChange(QString::fromUtf8(context->accountname), + QString::fromUtf8(context->username), + psiotr::OTR_STATECHANGE_GONESECURE); } // --------------------------------------------------------------------------- -void OtrInternal::gone_insecure(ConnContext *context) +void OtrInternal::gone_insecure(ConnContext* context) { - Q_UNUSED(context); + m_callback->stateChange(QString::fromUtf8(context->accountname), + QString::fromUtf8(context->username), + psiotr::OTR_STATECHANGE_GONEINSECURE); } - + // --------------------------------------------------------------------------- -void OtrInternal::still_secure(ConnContext *context, int is_reply) +void OtrInternal::still_secure(ConnContext* context, int is_reply) { - Q_UNUSED(context); Q_UNUSED(is_reply); + m_callback->stateChange(QString::fromUtf8(context->accountname), + QString::fromUtf8(context->username), + psiotr::OTR_STATECHANGE_STILLSECURE); } // --------------------------------------------------------------------------- -void OtrInternal::log_message(const char *message) +void OtrInternal::log_message(const char* message) { Q_UNUSED(message); } // --------------------------------------------------------------------------- -const char* OtrInternal::account_name(const char *account, - const char *protocol) +const char* OtrInternal::account_name(const char* account, + const char* protocol) { Q_UNUSED(protocol); return qstrdup(m_callback->humanAccountPublic(QString::fromUtf8(account)) @@ -1013,80 +996,80 @@ const char* OtrInternal::account_name(const char *account, // --------------------------------------------------------------------------- -void OtrInternal::account_name_free(const char *account_name) +void OtrInternal::account_name_free(const char* account_name) { delete [] account_name; } // --------------------------------------------------------------------------- /*** static wrapper functions ***/ - -OtrlPolicy OtrInternal::cb_policy(void *opdata, ConnContext *context) { + +OtrlPolicy OtrInternal::cb_policy(void* opdata, ConnContext* context) { return static_cast(opdata)->policy(context); } - -void OtrInternal::cb_create_privkey(void *opdata, const char *accountname, const char *protocol) { + +void OtrInternal::cb_create_privkey(void* opdata, const char* accountname, const char* protocol) { static_cast(opdata)->create_privkey(accountname, protocol); -} - -int OtrInternal::cb_is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient) { +} + +int OtrInternal::cb_is_logged_in(void* opdata, const char* accountname, const char* protocol, const char* recipient) { return static_cast(opdata)->is_logged_in(accountname, protocol, recipient); } -void OtrInternal::cb_inject_message(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message) { +void OtrInternal::cb_inject_message(void* opdata, const char* accountname, const char* protocol, const char* recipient, const char* message) { static_cast(opdata)->inject_message(accountname, protocol, recipient, message); } -void OtrInternal::cb_notify(void *opdata, OtrlNotifyLevel level, const char *accountname, const char *protocol, const char *username, const char *title, const char *primary, const char *secondary) { +void OtrInternal::cb_notify(void* opdata, OtrlNotifyLevel level, const char* accountname, const char* protocol, const char* username, const char* title, const char* primary, const char* secondary) { static_cast(opdata)->notify(level, accountname, protocol, username, title, primary, secondary); } - -int OtrInternal::cb_display_otr_message(void *opdata, const char *accountname, const char *protocol, const char *username, const char *msg) { + +int OtrInternal::cb_display_otr_message(void* opdata, const char* accountname, const char* protocol, const char* username, const char* msg) { return static_cast(opdata)->display_otr_message(accountname, protocol, username, msg); } - -void OtrInternal::cb_update_context_list(void *opdata) { + +void OtrInternal::cb_update_context_list(void* opdata) { static_cast(opdata)->update_context_list(); } - -const char* OtrInternal::cb_protocol_name(void *opdata, const char *protocol) { + +const char* OtrInternal::cb_protocol_name(void* opdata, const char* protocol) { return static_cast(opdata)->protocol_name(protocol); } - -void OtrInternal::cb_protocol_name_free(void *opdata, const char *protocol_name) { + +void OtrInternal::cb_protocol_name_free(void* opdata, const char* protocol_name) { static_cast(opdata)->protocol_name(protocol_name); } -void OtrInternal::cb_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, const char *protocol, const char *username, unsigned char fingerprint[20]) { +void OtrInternal::cb_new_fingerprint(void* opdata, OtrlUserState us, const char* accountname, const char* protocol, const char* username, unsigned char fingerprint[20]) { static_cast(opdata)->new_fingerprint(us, accountname, protocol, username, fingerprint); } - -void OtrInternal::cb_write_fingerprints(void *opdata) { + +void OtrInternal::cb_write_fingerprints(void* opdata) { static_cast(opdata)->write_fingerprints(); } - -void OtrInternal::cb_gone_secure(void *opdata, ConnContext *context) { + +void OtrInternal::cb_gone_secure(void* opdata, ConnContext* context) { static_cast(opdata)->gone_secure(context); } -void OtrInternal::cb_gone_insecure(void *opdata, ConnContext *context) { +void OtrInternal::cb_gone_insecure(void* opdata, ConnContext* context) { static_cast(opdata)->gone_insecure(context); } - -void OtrInternal::cb_still_secure(void *opdata, ConnContext *context, int is_reply) { + +void OtrInternal::cb_still_secure(void* opdata, ConnContext* context, int is_reply) { static_cast(opdata)->still_secure(context, is_reply); } - -void OtrInternal::cb_log_message(void *opdata, const char *message) { + +void OtrInternal::cb_log_message(void* opdata, const char* message) { static_cast(opdata)->log_message(message); } -const char* OtrInternal::cb_account_name(void *opdata, const char *account, - const char *protocol) { +const char* OtrInternal::cb_account_name(void* opdata, const char* account, + const char* protocol) { return static_cast(opdata)->account_name(account, protocol); } -void OtrInternal::cb_account_name_free(void *opdata, const char *account_name) { +void OtrInternal::cb_account_name_free(void* opdata, const char* account_name) { static_cast(opdata)->account_name_free(account_name); } diff --git a/src/plugins/generic/otrplugin/src/otrinternal.h b/src/plugins/generic/otrplugin/src/otrinternal.h new file mode 100644 index 000000000..304b402e8 --- /dev/null +++ b/src/plugins/generic/otrplugin/src/otrinternal.h @@ -0,0 +1,208 @@ +/* + * otrinternal.h - Manages the OTR connection + * + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011 Florian Fieber + * + * This program was originally written as part of a diplom thesis + * advised by Prof. Dr. Ruediger Weis (PST Labor) + * at the Technical University of Applied Sciences Berlin. + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef OTRINTERNAL_H_ +#define OTRINTERNAL_H_ + +#include "otrmessaging.h" + +#include +#include + +extern "C" +{ +#include +#include +#include +#include "otrlextensions.h" +} + +class QString; + +// --------------------------------------------------------------------------- + +/** + * Handles all libotr calls and callbacks. + */ +class OtrInternal +{ +public: + + OtrInternal(psiotr::OtrCallback* callback, psiotr::OtrPolicy& policy); + + ~OtrInternal(); + + QString encryptMessage(const QString& account, const QString& contact, + const QString& message); + + psiotr::OtrMessageType decryptMessage(const QString& account, + const QString& contact, + const QString& message, + QString& decrypted); + + QList getFingerprints(); + + void verifyFingerprint(const psiotr::Fingerprint& fingerprint, bool verified); + + void deleteFingerprint(const psiotr::Fingerprint& fingerprint); + + QHash getPrivateKeys(); + + void deleteKey(const QString& account); + + + void startSession(const QString& account, const QString& contact); + + void endSession(const QString& account, const QString& contact); + + void expireSession(const QString& account, const QString& contact); + + + void startSMP(const QString& account, const QString& contact, + const QString& question, const QString& secret); + + void continueSMP(const QString& account, const QString& contact, + const QString& secret); + + void abortSMP(const QString& account, const QString& contact); + void abortSMP(ConnContext* context); + + + psiotr::OtrMessageState getMessageState(const QString& account, + const QString& contact); + + QString getMessageStateString(const QString& account, + const QString& contact); + + QString getSessionId(const QString& account, const QString& contact); + + psiotr::Fingerprint getActiveFingerprint(const QString& account, + const QString& contact); + + bool isVerified(const QString& account, const QString& contact); + bool isVerified(ConnContext* context); + + bool smpSucceeded(const QString& account, const QString& contact); + + void generateKey(const QString& account); + + static QString humanFingerprint(const unsigned char* fingerprint); + + /*** otr callback functions ***/ + OtrlPolicy policy(ConnContext* context); + void create_privkey(const char* accountname, const char* protocol); + int is_logged_in(const char* accountname, const char* protocol, + const char* recipient); + void inject_message(const char* accountname, const char* protocol, + const char* recipient, const char* message); + void notify(OtrlNotifyLevel level, const char* accountname, + const char* protocol, const char* username, const char* title, + const char* primary, const char* secondary); + int display_otr_message(const char* accountname, const char* protocol, + const char* username, const char* msg); + void update_context_list(); + const char* protocol_name(const char* protocol); + void protocol_name_free(const char* protocol_name); + void new_fingerprint(OtrlUserState us, const char* accountname, + const char* protocol, const char* username, + unsigned char fingerprint[20]); + void write_fingerprints(); + void gone_secure(ConnContext* context); + void gone_insecure(ConnContext* context); + void still_secure(ConnContext* context, int is_reply); + void log_message(const char* message); + + const char* account_name(const char* account, + const char* protocol); + void account_name_free(const char* account_name); + + + /*** static otr callback wrapper-functions ***/ + static OtrlPolicy cb_policy(void* opdata, ConnContext* context); + static void cb_create_privkey(void* opdata, const char* accountname, + const char* protocol); + static int cb_is_logged_in(void* opdata, const char* accountname, + const char* protocol, const char* recipient); + static void cb_inject_message(void* opdata, const char* accountname, + const char* protocol, const char* recipient, + const char* message); + static void cb_notify(void* opdata, OtrlNotifyLevel level, + const char* accountname, const char* protocol, + const char* username, const char* title, + const char* primary, const char* secondary); + static int cb_display_otr_message(void* opdata, const char* accountname, + const char* protocol, const char* username, + const char* msg); + static void cb_update_context_list(void* opdata); + static const char* cb_protocol_name(void* opdata, const char* protocol); + static void cb_protocol_name_free(void* opdata, const char* protocol_name); + static void cb_new_fingerprint(void* opdata, OtrlUserState us, + const char* accountname, const char* protocol, + const char* username, unsigned char fingerprint[20]); + static void cb_write_fingerprints(void* opdata); + static void cb_gone_secure(void* opdata, ConnContext* context); + static void cb_gone_insecure(void* opdata, ConnContext* context); + static void cb_still_secure(void* opdata, ConnContext* context, int is_reply); + static void cb_log_message(void* opdata, const char* message); + + static const char* cb_account_name(void* opdata, const char* account, const char* protocol); + static void cb_account_name_free(void* opdata, const char* account_name); +private: + + /** + * The userstate contains keys and known fingerprints. + */ + OtrlUserState m_userstate; + + /** + * Pointers to callback functions. + */ + OtrlMessageAppOps m_uiOps; + + /** + * Pointer to a class for callbacks from OTR to application. + */ + psiotr::OtrCallback* m_callback; + + /** + * Name of the file storing dsa-keys. + */ + QString m_keysFile; + + /** + * Name of the file storing known fingerprints. + */ + QString m_fingerprintFile; + + /** + * Reference to the default OTR policy + */ + psiotr::OtrPolicy& m_otrPolicy; +}; + +// --------------------------------------------------------------------------- + +#endif diff --git a/src/plugins/generic/otrplugin/src/otrl_extensions.h b/src/plugins/generic/otrplugin/src/otrl_extensions.h deleted file mode 100644 index 70f1edf19..000000000 --- a/src/plugins/generic/otrplugin/src/otrl_extensions.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Off-the-Record Messaging library extensions - * - * strongly based on parts of the Off-the-Record Messaging library, - * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of version 2.1 of the GNU Lesser General - * Public License as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __OTRLEXT_H__ -#define __OTRLEXT_H__ - -#include -#include - -/* Store all keys of an OtrlUserState. */ -gcry_error_t otrl_privkey_write(OtrlUserState us, const char *filename); - -/* Store all keys of an OtrlUserState. - * The FILE* must be open for reading and writing. */ -gcry_error_t otrl_privkey_write_FILEp(OtrlUserState us, FILE *privf); - -#endif diff --git a/src/plugins/generic/otrplugin/src/otrl_extensions.c b/src/plugins/generic/otrplugin/src/otrlextensions.c similarity index 60% rename from src/plugins/generic/otrplugin/src/otrl_extensions.c rename to src/plugins/generic/otrplugin/src/otrlextensions.c index 5dacb9375..3765b2451 100644 --- a/src/plugins/generic/otrplugin/src/otrl_extensions.c +++ b/src/plugins/generic/otrplugin/src/otrlextensions.c @@ -1,22 +1,22 @@ /* - * Off-the-Record Messaging library extensions + * otrlextensions.c - Off-the-Record Messaging library extensions * - * strongly based on parts of the Off-the-Record Messaging library, - * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov - * + * Strongly based on parts of the Off-the-Record Messaging library, + * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov + * * - * This library is free software; you can redistribute it and/or - * modify it under the terms of version 2.1 of the GNU Lesser General - * Public License as published by the Free Software Foundation. + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2.1 of the GNU Lesser General + * Public License as published by the Free Software Foundation. * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library. If not, see . * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* system headers */ @@ -31,12 +31,12 @@ /* libotr headers */ #include -#include "otrl_extensions.h" +#include "otrlextensions.h" -static gcry_error_t sexp_write(FILE *privf, gcry_sexp_t sexp) +static gcry_error_t sexp_write(FILE* privf, gcry_sexp_t sexp) { size_t buflen; - char *buf; + char* buf; buflen = gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); buf = malloc(buflen); @@ -44,15 +44,15 @@ static gcry_error_t sexp_write(FILE *privf, gcry_sexp_t sexp) return gcry_error(GPG_ERR_ENOMEM); } gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, buf, buflen); - + fprintf(privf, "%s", buf); free(buf); return gcry_error(GPG_ERR_NO_ERROR); } -static gcry_error_t account_write(FILE *privf, const char *accountname, - const char *protocol, gcry_sexp_t privkey) +static gcry_error_t account_write(FILE* privf, const char* accountname, + const char* protocol, gcry_sexp_t privkey) { gcry_error_t err; gcry_sexp_t names, protos; @@ -78,9 +78,9 @@ static gcry_error_t account_write(FILE *privf, const char *accountname, /* Store all keys of an OtrlUserState. * The FILE* must be open for reading and writing. */ -gcry_error_t otrl_privkey_write_FILEp(OtrlUserState us, FILE *privf) +gcry_error_t otrl_privkey_write_FILEp(OtrlUserState us, FILE* privf) { - OtrlPrivKey *p; + OtrlPrivKey* p; /* Output the other keys we know */ fprintf(privf, "(privkeys\n"); @@ -97,10 +97,10 @@ gcry_error_t otrl_privkey_write_FILEp(OtrlUserState us, FILE *privf) } /* Store all keys of an OtrlUserState. */ -gcry_error_t otrl_privkey_write(OtrlUserState us, const char *filename) +gcry_error_t otrl_privkey_write(OtrlUserState us, const char* filename) { gcry_error_t err; - FILE *privf; + FILE* privf; #ifndef WIN32 mode_t oldmask; #endif diff --git a/src/plugins/generic/otrplugin/src/otrlextensions.h b/src/plugins/generic/otrplugin/src/otrlextensions.h new file mode 100644 index 000000000..777819518 --- /dev/null +++ b/src/plugins/generic/otrplugin/src/otrlextensions.h @@ -0,0 +1,35 @@ +/* + * otrlextensions.h - Off-the-Record Messaging library extensions + * + * Strongly based on parts of the Off-the-Record Messaging library, + * Copyright (C) 2004-2008 Ian Goldberg, Chris Alexander, Nikita Borisov + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of version 2.1 of the GNU Lesser General + * Public License as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library. If not, see . + * + */ + +#ifndef __OTRLEXT_H__ +#define __OTRLEXT_H__ + +#include +#include + +/* Store all keys of an OtrlUserState. */ +gcry_error_t otrl_privkey_write(OtrlUserState us, const char* filename); + +/* Store all keys of an OtrlUserState. + * The FILE* must be open for reading and writing. */ +gcry_error_t otrl_privkey_write_FILEp(OtrlUserState us, FILE* privf); + +#endif diff --git a/src/plugins/generic/otrplugin/src/OtrMessaging.cpp b/src/plugins/generic/otrplugin/src/otrmessaging.cpp similarity index 76% rename from src/plugins/generic/otrplugin/src/OtrMessaging.cpp rename to src/plugins/generic/otrplugin/src/otrmessaging.cpp index e07e63afc..695fc01d6 100644 --- a/src/plugins/generic/otrplugin/src/OtrMessaging.cpp +++ b/src/plugins/generic/otrplugin/src/otrmessaging.cpp @@ -1,6 +1,9 @@ /* - * OtrMessaging.cpp - interface to libotr - * Copyright (C) 2007 Timo Engel (timo-e@freenet.de) + * otrmessaging.cpp - Interface to libotr + * + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011 Florian Fieber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -13,13 +16,16 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program. If not, see . * */ -#include "OtrMessaging.hpp" -#include "OtrInternal.hpp" +#include "otrmessaging.h" +#include "otrinternal.h" + +#include +#include +#include namespace psiotr { @@ -35,20 +41,18 @@ Fingerprint::Fingerprint(const Fingerprint &fp) account(fp.account), username(fp.username), fingerprintHuman(fp.fingerprintHuman), - trust(fp.trust), - messageState(fp.messageState) + trust(fp.trust) { } Fingerprint::Fingerprint(unsigned char* fingerprint, QString account, QString username, - QString trust, QString messageState) + QString trust) : fingerprint(fingerprint), account(account), username(username), - trust(trust), - messageState(messageState) + trust(trust) { fingerprintHuman = OtrInternal::humanFingerprint(fingerprint); } @@ -71,18 +75,20 @@ OtrMessaging::~OtrMessaging() //----------------------------------------------------------------------------- -QString OtrMessaging::encryptMessage(const QString& from, const QString& to, +QString OtrMessaging::encryptMessage(const QString& account, const QString& contact, const QString& message) { - return m_impl->encryptMessage(from, to, message); + return m_impl->encryptMessage(account, contact, message); } //----------------------------------------------------------------------------- -bool OtrMessaging::decryptMessage(const QString& from, const QString& to, - const QString& message, QString& decrypted) +OtrMessageType OtrMessaging::decryptMessage(const QString& account, + const QString& contact, + const QString& message, + QString& decrypted) { - return m_impl->decryptMessage(from, to, message, decrypted); + return m_impl->decryptMessage(account, contact, message, decrypted); } //----------------------------------------------------------------------------- @@ -123,46 +129,46 @@ void OtrMessaging::deleteKey(const QString& account) //----------------------------------------------------------------------------- -void OtrMessaging::startSession(const QString& account, const QString& jid) +void OtrMessaging::startSession(const QString& account, const QString& contact) { - m_impl->startSession(account, jid); + m_impl->startSession(account, contact); } //----------------------------------------------------------------------------- -void OtrMessaging::endSession(const QString& account, const QString& jid) +void OtrMessaging::endSession(const QString& account, const QString& contact) { - m_impl->endSession(account, jid); + m_impl->endSession(account, contact); } //----------------------------------------------------------------------------- -void OtrMessaging::expireSession(const QString& account, const QString& jid) +void OtrMessaging::expireSession(const QString& account, const QString& contact) { - m_impl->expireSession(account, jid); + m_impl->expireSession(account, contact); } //----------------------------------------------------------------------------- -void OtrMessaging::startSMP(const QString& account, const QString& jid, +void OtrMessaging::startSMP(const QString& account, const QString& contact, const QString& question, const QString& secret) { - m_impl->startSMP(account, jid, question, secret); + m_impl->startSMP(account, contact, question, secret); } //----------------------------------------------------------------------------- -void OtrMessaging::continueSMP(const QString& account, const QString& jid, +void OtrMessaging::continueSMP(const QString& account, const QString& contact, const QString& secret) { - m_impl->continueSMP(account, jid, secret); + m_impl->continueSMP(account, contact, secret); } //----------------------------------------------------------------------------- -void OtrMessaging::abortSMP(const QString& account, const QString& jid) +void OtrMessaging::abortSMP(const QString& account, const QString& contact) { - m_impl->abortSMP(account, jid); + m_impl->abortSMP(account, contact); } //----------------------------------------------------------------------------- @@ -234,11 +240,27 @@ void OtrMessaging::generateKey(const QString& account) //----------------------------------------------------------------------------- -QString OtrMessaging::humanAccount(const QString accountId) +void OtrMessaging::stateChange(const QString& account, const QString& contact, + OtrStateChange change) +{ + return m_callback->stateChange(account, contact, change); +} + +//----------------------------------------------------------------------------- + +QString OtrMessaging::humanAccount(const QString& accountId) { return m_callback->humanAccount(accountId); } //----------------------------------------------------------------------------- +QString OtrMessaging::humanContact(const QString& accountId, + const QString& contact) +{ + return m_callback->humanContact(accountId, contact); +} + +//----------------------------------------------------------------------------- + } // namespace psiotr diff --git a/src/plugins/generic/otrplugin/src/OtrMessaging.hpp b/src/plugins/generic/otrplugin/src/otrmessaging.h similarity index 50% rename from src/plugins/generic/otrplugin/src/OtrMessaging.hpp rename to src/plugins/generic/otrplugin/src/otrmessaging.h index ad0ca7faf..155c96341 100644 --- a/src/plugins/generic/otrplugin/src/OtrMessaging.hpp +++ b/src/plugins/generic/otrplugin/src/otrmessaging.h @@ -1,6 +1,9 @@ /* - * OtrMessaging.hpp - interface to libotr - * Copyright (C) 2007 Timo Engel (timo-e@freenet.de) + * otrmessaging.h - Interface to libotr + * + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011 Florian Fieber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -13,18 +16,18 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program. If not, see . * */ -#ifndef OTRMESSAGING_HPP_ -#define OTRMESSAGING_HPP_ +#ifndef OTRMESSAGING_H_ +#define OTRMESSAGING_H_ -#include #include #include +class QString; + class OtrInternal; // --------------------------------------------------------------------------- @@ -32,8 +35,6 @@ class OtrInternal; namespace psiotr { -class PsiOtrPlugin; - // --------------------------------------------------------------------------- enum OtrPolicy @@ -46,6 +47,15 @@ enum OtrPolicy // --------------------------------------------------------------------------- +enum OtrMessageType +{ + OTR_MESSAGETYPE_NONE, + OTR_MESSAGETYPE_IGNORE, + OTR_MESSAGETYPE_OTR +}; + +// --------------------------------------------------------------------------- + enum OtrMessageState { OTR_MESSAGESTATE_UNKNOWN, @@ -56,6 +66,19 @@ enum OtrMessageState // --------------------------------------------------------------------------- +enum OtrStateChange +{ + OTR_STATECHANGE_GOINGSECURE, + OTR_STATECHANGE_GONESECURE, + OTR_STATECHANGE_GONEINSECURE, + OTR_STATECHANGE_STILLSECURE, + OTR_STATECHANGE_CLOSE, + OTR_STATECHANGE_REMOTECLOSE, + OTR_STATECHANGE_TRUST +}; + +// --------------------------------------------------------------------------- + enum OtrNotifyType { OTR_NOTIFY_INFO, @@ -66,27 +89,32 @@ enum OtrNotifyType // --------------------------------------------------------------------------- /** -* Interface for callbacks from libotr to application -* -*/ + * Interface for callbacks from libotr to application + */ class OtrCallback { public: virtual QString dataDir() = 0; /** - * Sends a message from the Account account to the user toJid. + * Sends a message from the Account account to the user contact. * The method is called from the OtrConnection to send messages * during key-exchange. */ - virtual void sendMessage(const QString& account, const QString& recipient, + virtual void sendMessage(const QString& account, const QString& contact, const QString& message) = 0; - virtual bool isLoggedIn(const QString& account, const QString& recipient) = 0; + virtual bool isLoggedIn(const QString& account, const QString& contact) = 0; virtual void notifyUser(const OtrNotifyType& type, const QString& message) = 0; + virtual bool displayOtrMessage(const QString& account, const QString& contact, + const QString& message) = 0; + + virtual void stateChange(const QString& account, const QString& contact, + OtrStateChange change) = 0; + virtual void receivedSMP(const QString& account, const QString& contact, const QString& question) = 0; @@ -98,214 +126,221 @@ class OtrCallback virtual QString humanAccount(const QString& accountId) = 0; virtual QString humanAccountPublic(const QString& accountId) = 0; + virtual QString humanContact(const QString& accountId, + const QString& contact) = 0; }; // --------------------------------------------------------------------------- -/** -* This struct contains all data shown in the table of 'Known Fingerprints'. -*/ +/** + * This struct contains all data shown in the table of 'Known Fingerprints'. + */ struct Fingerprint { - /** - * Pointer to fingerprint in libotr struct. Binary format. - */ - unsigned char* fingerprint; - - /** - * own account - */ + /** + * Pointer to fingerprint in libotr struct. Binary format. + */ + unsigned char* fingerprint; + + /** + * own account + */ QString account; - /** - * owner of the fingerprint - */ + /** + * owner of the fingerprint + */ QString username; - /** - * The fingerprint in a human-readable format - */ + /** + * The fingerprint in a human-readable format + */ QString fingerprintHuman; - /** - * the level of trust - */ + /** + * the level of trust + */ QString trust; - /** - * The messageState of the context (i.e. plaintext, encrypted, finished) - */ - QString messageState; - Fingerprint(); Fingerprint(const Fingerprint &fp); Fingerprint(unsigned char* fingerprint, QString account, QString username, - QString trust, QString messageState); + QString trust); }; // --------------------------------------------------------------------------- -/** -* This class it the interface to the off the record messaging library. -* See the libotr documentation for more information. -* -*/ +/** + * This class is the interface to the Off the Record Messaging library. + * See the libotr documentation for more information. + */ class OtrMessaging { public: - /** - * Constructor - * - * @param plugin Pointer to the plugin, used for sending messages. - * @param policy The default OTR policy - */ + /** + * Constructor + * + * @param plugin Pointer to the plugin, used for sending messages. + * @param policy The default OTR policy + */ OtrMessaging(OtrCallback* callback, OtrPolicy policy); - /** - * Deconstructor - */ + /** + * Deconstructor + */ ~OtrMessaging(); - /** - * Process an outgoing message. - * - * @param from Account the message is send from - * @param to Recipient of message - * @param message The message itself. - * - * @return The encrypted message. - */ - QString encryptMessage(const QString& from, const QString& to, + /** + * Process an outgoing message. + * + * @param account Account the message is send from + * @param contact Recipient of the message + * @param message The message itself + * + * @return The encrypted message + */ + QString encryptMessage(const QString& account, const QString& contact, const QString& message); - /** - * Decrypt an incoming message - * - * @param from Sender of the message - * @param to Account the message is send to. - * @param message the mesasge itself. - * @param decrypted The decrypted message if the original message was - * encrypted. - * @return true, if decrypted was set. - */ - bool decryptMessage(const QString& from, const QString& to, - const QString& message, QString& decrypted); - - /** - * Returns a list of known fingerprints. - */ + /** + * Decrypt an incoming message. + * + * @param account Account the message is send to + * @param contact Sender of the message + * @param message The message itself + * @param decrypted The decrypted message if the original message was + * encrypted + * @return Type of incoming message + */ + OtrMessageType decryptMessage(const QString& account, const QString& contact, + const QString& message, QString& decrypted); + + /** + * Returns a list of known fingerprints. + */ QList getFingerprints(); - /** - * Set fingerprint verified/not verified. - */ + /** + * Set fingerprint verified/not verified. + */ void verifyFingerprint(const Fingerprint& fingerprint, bool verified); - /** - * Delete a known fingerprint. - */ + /** + * Delete a known fingerprint. + */ void deleteFingerprint(const Fingerprint& fingerprint); - /** - * Get hash of fingerprints of own private keys. - * Account -> KeyFingerprint - */ + /** + * Get hash of fingerprints of own private keys. + * Account -> KeyFingerprint + */ QHash getPrivateKeys(); - /** - * Delete a private key. - */ + /** + * Delete a private key. + */ void deleteKey(const QString& account); - /** - * Send an OTR query message from account to contact. - */ + /** + * Send an OTR query message from account to contact. + */ void startSession(const QString& account, const QString& contact); - /** - * Send otr-finished message to user. - */ + /** + * Send otr-finished message to user. + */ void endSession(const QString& account, const QString& contact); - /** - * Force a session to expire. - */ + /** + * Force a session to expire. + */ void expireSession(const QString& account, const QString& contact); /** - * Start the SMP with an optional question - */ + * Start the SMP with an optional question. + */ void startSMP(const QString& account, const QString& contact, const QString& question, const QString& secret); /** - * Continue the SMP - */ + * Continue the SMP. + */ void continueSMP(const QString& account, const QString& contact, const QString& secret); /** - * Abort the SMP - */ + * Abort the SMP. + */ void abortSMP(const QString& account, const QString& contact); /** - * Return the messageState of a context. - * i.e. plaintext, encrypted, finished - */ + * Return the messageState of a context, + * i.e. plaintext, encrypted, finished. + */ OtrMessageState getMessageState(const QString& account, const QString& contact); - /** - * returns the messageState in human-readable string. - */ + /** + * Return the messageState as human-readable string. + */ QString getMessageStateString(const QString& account, const QString& contact); - /** - * Return the secure session id (ssid) for a context - */ + /** + * Return the secure session id (ssid) for a context. + */ QString getSessionId(const QString& account, const QString& contact); - /** - * Return the active fingerprint for a context - */ + /** + * Return the active fingerprint for a context. + */ psiotr::Fingerprint getActiveFingerprint(const QString& account, const QString& contact); - /** - * Return true if the active fingerprint has been verified - */ + /** + * Return true if the active fingerprint has been verified. + */ bool isVerified(const QString& account, const QString& contact); - /** - * Return true if Socialist Millionaires' Protocol succeeded - */ + /** + * Return true if Socialist Millionaires' Protocol succeeded. + */ bool smpSucceeded(const QString& account, const QString& contact); - /** - * Set the default OTR policy. - */ + /** + * Set the default OTR policy. + */ void setPolicy(OtrPolicy policy); - /** - * @return The default OTR policy - */ + /** + * Return the default OTR policy. + */ OtrPolicy getPolicy(); /** - * Generate own keys. - * This function blocks until keys are available. - */ + * Generate own keys. + * This function blocks until keys are available. + */ void generateKey(const QString& account); + /** + * Report a change of state. + */ + void stateChange(const QString& account, const QString& contact, + OtrStateChange change); + /** * Return a human-readable representation - * of an account identified by accountId + * of an account identified by accountId. + */ + QString humanAccount(const QString& accountId); + + /** + * Return the name of a contact. */ - QString humanAccount(const QString accountId); + QString humanContact(const QString& accountId, const QString& contact); private: OtrPolicy m_otrPolicy; diff --git a/src/plugins/generic/otrplugin/src/psiotrclosure.cpp b/src/plugins/generic/otrplugin/src/psiotrclosure.cpp index 81d34f0b0..5613a6e85 100644 --- a/src/plugins/generic/otrplugin/src/psiotrclosure.cpp +++ b/src/plugins/generic/otrplugin/src/psiotrclosure.cpp @@ -1,9 +1,12 @@ /* * psiotrclosure.cpp * - * Copyright (C) Timo Engel (timo-e@freenet.de), Berlin 2007. - * This program was written as part of a diplom thesis advised by - * Prof. Dr. Ruediger Weis (PST Labor) + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011-2012 Florian Fieber + * + * This program was originally written as part of a diplom thesis + * advised by Prof. Dr. Ruediger Weis (PST Labor) * at the Technical University of Applied Sciences Berlin. * * This program is free software; you can redistribute it and/or @@ -17,81 +20,106 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program. If not, see . * */ #include "psiotrclosure.h" -#include -#include +#include +#include #include #include +#include +#include +#include #include +#include +#include +#include +#include #include namespace psiotr { AuthenticationDialog::AuthenticationDialog(OtrMessaging* otrc, - const QString& account, const QString& contact, - const QString& question, bool sender, QWidget *parent) + const QString& account, + const QString& contact, + const QString& question, + bool sender, QWidget* parent) : QDialog(parent), m_otr(otrc), - m_method(0), + m_method(METHOD_QUESTION), m_account(account), m_contact(contact), m_isSender(sender) { setAttribute(Qt::WA_DeleteOnClose); + m_contactName = m_otr->humanContact(m_account, m_contact); + QString qaExplanation; + QString ssExplanation; QString fprExplanation; if (m_isSender) { - setWindowTitle(tr("Authenticate %1").arg(contact)); + setWindowTitle(tr("Authenticate %1").arg(m_contactName)); qaExplanation = tr("To authenticate via question and answer, " "ask a question whose answer is only known " - "to you and %1."); + "to you and %1.").arg(m_contactName); + ssExplanation = tr("To authenticate via shared secret, " + "enter a secret only known " + "to you and %1.").arg(m_contactName); fprExplanation = tr("To authenticate manually, exchange your " - "fingerprints over an authenticated channel " - "and compare each other's fingerprint with the one " + "fingerprints over an authenticated channel and " + "compare each other's fingerprint with the one " "listed beneath."); } else { - setWindowTitle(tr("Authenticate to %1").arg(contact)); + setWindowTitle(tr("Authenticate to %1").arg(m_contactName)); qaExplanation = tr("%1 wants to authenticate you. To authenticate, " - "answer the question asked below."); + "answer the question asked below.") + .arg(m_contactName); + ssExplanation = tr("%1 wants to authenticate you. To authenticate, " + "enter your shared secret below.") + .arg(m_contactName); } - + m_methodBox = new QComboBox(this); m_methodBox->setMinimumWidth(300); - + m_methodBox->addItem(tr("Question and answer")); + m_methodBox->addItem(tr("Shared secret")); m_methodBox->addItem(tr("Fingerprint verification")); - - QLabel* qaExplanationLabel = new QLabel(qaExplanation.arg(m_contact), this); + + QLabel* qaExplanationLabel = new QLabel(qaExplanation, this); qaExplanationLabel->setWordWrap(true); + QLabel* ssExplanationLabel = new QLabel(ssExplanation, this); + ssExplanationLabel->setWordWrap(true); + + m_questionEdit = new QLineEdit(this); + m_answerEdit = new QLineEdit(this); + m_sharedSecretEdit = new QLineEdit(this); - m_questionEdit = new QLineEdit(this); - m_answerEdit = new QLineEdit(this); - QLabel* questionLabel = new QLabel(tr("&Question:"), this); questionLabel->setBuddy(m_questionEdit); - + QLabel* answerLabel = new QLabel(tr("A&nswer:"), this); answerLabel->setBuddy(m_answerEdit); - + + QLabel* sharedSecretLabel = new QLabel(tr("&Shared Secret:"), this); + sharedSecretLabel->setBuddy(m_sharedSecretEdit); + m_progressBar = new QProgressBar(this); - + m_cancelButton = new QPushButton(tr("&Cancel"), this); m_startButton = new QPushButton(tr("&Authenticate"), this); - + m_startButton->setDefault(true); - - m_methodWidget[0] = new QWidget(this); + + m_methodWidget[METHOD_QUESTION] = new QWidget(this); QVBoxLayout* qaLayout = new QVBoxLayout; qaLayout->setContentsMargins(0, 0, 0, 0); qaLayout->addWidget(qaExplanationLabel); @@ -102,10 +130,19 @@ AuthenticationDialog::AuthenticationDialog(OtrMessaging* otrc, qaLayout->addWidget(answerLabel); qaLayout->addWidget(m_answerEdit); qaLayout->addSpacing(20); - qaLayout->addWidget(m_progressBar); - m_methodWidget[0]->setLayout(qaLayout); - - m_methodWidget[1] = NULL; + m_methodWidget[METHOD_QUESTION]->setLayout(qaLayout); + + m_methodWidget[METHOD_SHARED_SECRET] = new QWidget(this); + QVBoxLayout* ssLayout = new QVBoxLayout; + ssLayout->setContentsMargins(0, 0, 0, 0); + ssLayout->addWidget(ssExplanationLabel); + ssLayout->addSpacing(20); + ssLayout->addWidget(sharedSecretLabel); + ssLayout->addWidget(m_sharedSecretEdit); + ssLayout->addSpacing(20); + m_methodWidget[METHOD_SHARED_SECRET]->setLayout(ssLayout); + + m_methodWidget[METHOD_FINGERPRINT] = NULL; QLabel* authenticatedLabel = NULL; if (m_isSender) { @@ -115,12 +152,12 @@ AuthenticationDialog::AuthenticationDialog(OtrMessaging* otrc, .arg(tr("This contact is already " "authenticated.")), this); } - - QString ownFpr = m_otr->getPrivateKeys() - .value(m_account, - tr("No private key for account \"%1\"") - .arg(m_otr->humanAccount(m_account))); - + + QString ownFpr = m_otr->getPrivateKeys().value( + m_account, + tr("No private key for account \"%1\"") + .arg(m_otr->humanAccount(m_account))); + m_fpr = m_otr->getActiveFingerprint(m_account, m_contact); QLabel* fprExplanationLabel = new QLabel(fprExplanation, this); @@ -128,12 +165,13 @@ AuthenticationDialog::AuthenticationDialog(OtrMessaging* otrc, QLabel* ownFprDescLabel = new QLabel(tr("Your fingerprint:"), this); QLabel* ownFprLabel = new QLabel(ownFpr, this); - QLabel* fprDescLabel = new QLabel(tr("Fingerprint for %1:").arg(m_contact), this); + QLabel* fprDescLabel = new QLabel(tr("%1's fingerprint:") + .arg(m_contactName), this); QLabel* fprLabel = new QLabel(m_fpr.fingerprintHuman, this); ownFprLabel->setFont(QFont("monospace")); fprLabel->setFont(QFont("monospace")); - m_methodWidget[1] = new QWidget(this); + m_methodWidget[METHOD_FINGERPRINT] = new QWidget(this); QVBoxLayout* fprLayout = new QVBoxLayout; fprLayout->setContentsMargins(0, 0, 0, 0); fprLayout->addWidget(fprExplanationLabel); @@ -143,8 +181,7 @@ AuthenticationDialog::AuthenticationDialog(OtrMessaging* otrc, fprLayout->addSpacing(10); fprLayout->addWidget(fprDescLabel); fprLayout->addWidget(fprLabel); - m_methodWidget[1]->setLayout(fprLayout); - m_methodWidget[1]->setVisible(false); + m_methodWidget[METHOD_FINGERPRINT]->setLayout(fprLayout); } QHBoxLayout* buttonLayout = new QHBoxLayout; @@ -161,34 +198,54 @@ AuthenticationDialog::AuthenticationDialog(OtrMessaging* otrc, } mainLayout->addWidget(m_methodBox); mainLayout->addSpacing(20); - mainLayout->addWidget(m_methodWidget[0]); - if (m_methodWidget[1]) + mainLayout->addWidget(m_methodWidget[METHOD_QUESTION]); + mainLayout->addWidget(m_methodWidget[METHOD_SHARED_SECRET]); + if (m_methodWidget[METHOD_FINGERPRINT]) { - mainLayout->addWidget(m_methodWidget[1]); + mainLayout->addWidget(m_methodWidget[METHOD_FINGERPRINT]); } + mainLayout->addWidget(m_progressBar); mainLayout->addSpacing(20); mainLayout->addLayout(buttonLayout); setLayout(mainLayout); - - + + connect(m_methodBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changeMethod(int))); + connect(m_methodBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(checkRequirements())); + connect(m_questionEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(checkRequirements())); + connect(m_answerEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(checkRequirements())); + connect(m_sharedSecretEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(checkRequirements())); connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(reject())); connect(m_startButton, SIGNAL(clicked()), this, SLOT(startAuthentication())); - if (!m_isSender && !question.isEmpty()) + if (!m_isSender) { - m_questionEdit->setText(question); + if (question.isEmpty()) + { + m_method = METHOD_SHARED_SECRET; + } + else + { + m_questionEdit->setText(question); + } } + changeMethod(m_method); + m_methodBox->setCurrentIndex(m_method); + reset(); } AuthenticationDialog::~AuthenticationDialog() { - + } void AuthenticationDialog::reject() @@ -197,7 +254,7 @@ void AuthenticationDialog::reject() { m_otr->abortSMP(m_account, m_contact); } - + QDialog::reject(); } @@ -208,10 +265,12 @@ void AuthenticationDialog::reset() m_methodBox->setEnabled(m_isSender); m_questionEdit->setEnabled(m_isSender); m_answerEdit->setEnabled(true); + m_sharedSecretEdit->setEnabled(true); m_progressBar->setEnabled(false); - m_startButton->setEnabled(true); - + m_progressBar->setValue(0); + + checkRequirements(); } bool AuthenticationDialog::finished() @@ -219,63 +278,109 @@ bool AuthenticationDialog::finished() return m_state == AUTH_FINISHED; } +void AuthenticationDialog::checkRequirements() +{ + m_startButton->setEnabled((m_method == METHOD_QUESTION && + !m_questionEdit->text().isEmpty() && + !m_answerEdit->text().isEmpty()) || + (m_method == METHOD_SHARED_SECRET && + !m_sharedSecretEdit->text().isEmpty()) || + (m_method == METHOD_FINGERPRINT)); +} + void AuthenticationDialog::changeMethod(int index) { - m_method = index; - for (int i=0; i<2; i++) + m_method = static_cast(index); + for (int i=0; i<3; i++) { - m_methodWidget[i]->setVisible(i == index); + if (m_methodWidget[i]) + { + m_methodWidget[i]->setVisible(i == index); + } } + m_progressBar->setVisible(m_method != METHOD_FINGERPRINT); adjustSize(); } void AuthenticationDialog::startAuthentication() { - if (m_method == 0) + switch (m_method) { - if (m_answerEdit->text().isEmpty()) - { - return; - } + case METHOD_QUESTION: + if (m_questionEdit->text().isEmpty() || + m_answerEdit->text().isEmpty()) + { + return; + } - m_state = AUTH_IN_PROGRESS; - - m_methodBox->setEnabled(false); - m_questionEdit->setEnabled(false); - m_answerEdit->setEnabled(false); - m_progressBar->setEnabled(true); - m_startButton->setEnabled(false); - - if (m_isSender) - { - m_otr->startSMP(m_account, m_contact, - m_questionEdit->text(), m_answerEdit->text()); - } - else - { - m_otr->continueSMP(m_account, m_contact, m_answerEdit->text()); - } + m_state = AUTH_IN_PROGRESS; - updateSMP(33); - } - else - { - if (m_fpr.fingerprint != NULL) - { - QString msg(tr("Account: ") + m_otr->humanAccount(m_account) + "\n" + - tr("User: ") + m_contact + "\n" + - tr("Fingerprint: ") + m_fpr.fingerprintHuman + "\n\n" + - tr("Have you verified that this is in fact the correct fingerprint?")); - - QMessageBox mb(QMessageBox::Information, tr("Psi OTR"), - msg, QMessageBox::Yes | QMessageBox::No, this, - Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint); - - m_otr->verifyFingerprint(m_fpr, - mb.exec() == QMessageBox::Yes); - - close(); - } + m_methodBox->setEnabled(false); + m_questionEdit->setEnabled(false); + m_answerEdit->setEnabled(false); + m_progressBar->setEnabled(true); + m_startButton->setEnabled(false); + + if (m_isSender) + { + m_otr->startSMP(m_account, m_contact, + m_questionEdit->text(), m_answerEdit->text()); + } + else + { + m_otr->continueSMP(m_account, m_contact, m_answerEdit->text()); + } + + updateSMP(33); + + break; + + case METHOD_SHARED_SECRET: + if (m_sharedSecretEdit->text().isEmpty()) + { + return; + } + + m_state = AUTH_IN_PROGRESS; + + m_methodBox->setEnabled(false); + m_sharedSecretEdit->setEnabled(false); + m_progressBar->setEnabled(true); + m_startButton->setEnabled(false); + + if (m_isSender) + { + m_otr->startSMP(m_account, m_contact, + QString(), m_sharedSecretEdit->text()); + } + else + { + m_otr->continueSMP(m_account, m_contact, m_sharedSecretEdit->text()); + } + + updateSMP(33); + + break; + + case METHOD_FINGERPRINT: + if (m_fpr.fingerprint) + { + QString msg(tr("Account: ") + m_otr->humanAccount(m_account) + "\n" + + tr("User: ") + m_contact + "\n" + + tr("Fingerprint: ") + m_fpr.fingerprintHuman + "\n\n" + + tr("Have you verified that this is in fact the correct fingerprint?")); + + QMessageBox mb(QMessageBox::Information, tr("Psi OTR"), + msg, QMessageBox::Yes | QMessageBox::No, this, + Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint); + + m_otr->verifyFingerprint(m_fpr, + mb.exec() == QMessageBox::Yes); + + close(); + } + + break; } } @@ -287,7 +392,7 @@ void AuthenticationDialog::updateSMP(int progress) { notify(QMessageBox::Warning, tr("%1 has canceled the authentication process.") - .arg(m_contact)); + .arg(m_contactName)); } else { @@ -306,23 +411,30 @@ void AuthenticationDialog::updateSMP(int progress) return; } - + m_progressBar->setValue(progress); if (progress == 100) { + if (m_isSender || m_method == METHOD_SHARED_SECRET) + { + m_otr->stateChange(m_account, m_contact, + psiotr::OTR_STATECHANGE_TRUST); + } if (m_otr->smpSucceeded(m_account, m_contact)) { m_state = AUTH_FINISHED; if (m_otr->isVerified(m_account, m_contact)) { - notify(QMessageBox::Information, tr("Authentication successful.")); + notify(QMessageBox::Information, + tr("Authentication successful.")); } else { notify(QMessageBox::Information, tr("You have been successfully authenticated.\n\n" "You should authenticate %1 as " - "well by asking your own question.").arg(m_contact)); + "well by asking your own question.") + .arg(m_contactName)); } close(); } else { @@ -352,9 +464,9 @@ void AuthenticationDialog::notify(const QMessageBox::Icon icon, //----------------------------------------------------------------------------- -PsiOtrClosure::PsiOtrClosure(const QString& account, const QString& contact, +PsiOtrClosure::PsiOtrClosure(const QString& account, const QString& contact, OtrMessaging* otrc) - : m_otr(otrc), + : m_otr(otrc), m_account(account), m_contact(contact), m_chatDlgMenu(0), @@ -402,7 +514,7 @@ void PsiOtrClosure::authenticateContact(bool) QString(), true); connect(m_authDialog, SIGNAL(destroyed()), - this, SLOT(finishSMP())); + this, SLOT(finishAuth())); m_authDialog->show(); } @@ -419,13 +531,14 @@ void PsiOtrClosure::receivedSMP(const QString& question) if (m_authDialog) { disconnect(m_authDialog, SIGNAL(destroyed()), - this, SLOT(finishSMP())); + this, SLOT(finishAuth())); + finishAuth(); } m_authDialog = new AuthenticationDialog(m_otr, m_account, m_contact, question, false); connect(m_authDialog, SIGNAL(destroyed()), - this, SLOT(finishSMP())); + this, SLOT(finishAuth())); m_authDialog->show(); } @@ -443,7 +556,7 @@ void PsiOtrClosure::updateSMP(int progress) //----------------------------------------------------------------------------- -void PsiOtrClosure::finishSMP() +void PsiOtrClosure::finishAuth() { m_authDialog = 0; @@ -508,7 +621,7 @@ void PsiOtrClosure::fingerprint(bool) void PsiOtrClosure::updateMessageState() { - if (m_chatDlgAction != 0) + if (m_chatDlgAction) { OtrMessageState state = m_otr->getMessageState(m_account, m_contact); @@ -519,17 +632,17 @@ void PsiOtrClosure::updateMessageState() { if (m_otr->isVerified(m_account, m_contact)) { - m_chatDlgAction->setIcon(QIcon(":/psi-otr/otr_yes.png")); + m_chatDlgAction->setIcon(QIcon(":/otrplugin/otr_yes.png")); } else { - m_chatDlgAction->setIcon(QIcon(":/psi-otr/otr_unverified.png")); + m_chatDlgAction->setIcon(QIcon(":/otrplugin/otr_unverified.png")); stateString += ", " + tr("unverified"); } } else { - m_chatDlgAction->setIcon(QIcon(":/psi-otr/otr_no.png")); + m_chatDlgAction->setIcon(QIcon(":/otrplugin/otr_no.png")); } m_chatDlgAction->setText(tr("OTR Messaging [%1]").arg(stateString)); @@ -611,7 +724,7 @@ void PsiOtrClosure::showMenu() { m_chatDlgMenu->popup(QCursor::pos(), m_chatDlgAction); } - + //----------------------------------------------------------------------------- void PsiOtrClosure::setIsLoggedIn(bool isLoggedIn) @@ -630,12 +743,12 @@ bool PsiOtrClosure::isLoggedIn() const void PsiOtrClosure::disable() { - if (m_chatDlgAction != 0) + if (m_chatDlgAction) { m_chatDlgAction->setEnabled(false); } } - + //----------------------------------------------------------------------------- bool PsiOtrClosure::encrypted() const @@ -643,7 +756,7 @@ bool PsiOtrClosure::encrypted() const return m_otr->getMessageState(m_account, m_contact) == OTR_MESSAGESTATE_ENCRYPTED; } - + //----------------------------------------------------------------------------- } // namespace diff --git a/src/plugins/generic/otrplugin/src/psiotrclosure.h b/src/plugins/generic/otrplugin/src/psiotrclosure.h index d08f80154..05d09f041 100644 --- a/src/plugins/generic/otrplugin/src/psiotrclosure.h +++ b/src/plugins/generic/otrplugin/src/psiotrclosure.h @@ -1,9 +1,12 @@ /* - * psiotrclosure.hpp + * psiotrclosure.h * - * Copyright (C) Timo Engel (timo-e@freenet.de), Berlin 2007. - * This program was written as part of a diplom thesis advised by - * Prof. Dr. Ruediger Weis (PST Labor) + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011-2012 Florian Fieber + * + * This program was originally written as part of a diplom thesis + * advised by Prof. Dr. Ruediger Weis (PST Labor) * at the Technical University of Applied Sciences Berlin. * * This program is free software; you can redistribute it and/or @@ -17,31 +20,28 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program. If not, see . * */ #ifndef PSIOTRCLOSURE_H_ #define PSIOTRCLOSURE_H_ -#include "OtrMessaging.hpp" +#include "otrmessaging.h" #include #include -#include -#include -#include -#include #include class QAction; class QMenu; +class QComboBox; +class QLineEdit; +class QProgressBar; +class QPushButton; namespace psiotr { -class PsiOtrPlugin; -class OtrMessaging; //----------------------------------------------------------------------------- @@ -52,7 +52,7 @@ class AuthenticationDialog : public QDialog AuthenticationDialog(OtrMessaging* otrc, const QString& account, const QString& contact, const QString& question, bool sender, - QWidget *parent = 0); + QWidget* parent = 0); ~AuthenticationDialog(); void reset(); @@ -65,25 +65,29 @@ public slots: private: enum AuthState {AUTH_READY, AUTH_IN_PROGRESS, AUTH_FINISHED}; + enum Method {METHOD_QUESTION, METHOD_SHARED_SECRET, METHOD_FINGERPRINT}; OtrMessaging* m_otr; - int m_method; + Method m_method; QString m_account; QString m_contact; + QString m_contactName; bool m_isSender; AuthState m_state; Fingerprint m_fpr; - QWidget* m_methodWidget[2]; + QWidget* m_methodWidget[3]; QComboBox* m_methodBox; QLineEdit* m_questionEdit; QLineEdit* m_answerEdit; + QLineEdit* m_sharedSecretEdit; QProgressBar* m_progressBar; QPushButton* m_cancelButton; QPushButton* m_startButton; - + private slots: void changeMethod(int index); + void checkRequirements(); void startAuthentication(); }; @@ -128,7 +132,7 @@ public slots: void sessionID(bool b); void fingerprint(bool b); void showMenu(); - void finishSMP(); + void finishAuth(); }; //----------------------------------------------------------------------------- diff --git a/src/plugins/generic/otrplugin/src/PsiOtrConfig.cpp b/src/plugins/generic/otrplugin/src/psiotrconfig.cpp similarity index 94% rename from src/plugins/generic/otrplugin/src/PsiOtrConfig.cpp rename to src/plugins/generic/otrplugin/src/psiotrconfig.cpp index f55d0ca06..0c0b5e1f7 100644 --- a/src/plugins/generic/otrplugin/src/PsiOtrConfig.cpp +++ b/src/plugins/generic/otrplugin/src/psiotrconfig.cpp @@ -1,6 +1,9 @@ /* - * PsiOtrConfig.cpp - configuration dialogs for Psi OTR plugin - * Copyright (C) 2007 Timo Engel (timo-e@freenet.de) + * psiotrconfig.cpp - Configuration dialogs + * + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011 Florian Fieber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -13,26 +16,34 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program. If not, see . * */ -#include "PsiOtrConfig.hpp" +#include "psiotrconfig.h" #include "optionaccessinghost.h" #include "accountinfoaccessinghost.h" + +#include +#include #include #include #include #include #include #include +#include #include +#include #include +#include +#include +#include #include #include #include +#include //----------------------------------------------------------------------------- @@ -49,7 +60,7 @@ ConfigDialog::ConfigDialog(OtrMessaging* otr, OptionAccessingHost* optionHost, m_optionHost(optionHost), m_accountInfo(accountInfo) { - QVBoxLayout *mainLayout = new QVBoxLayout(this); + QVBoxLayout* mainLayout = new QVBoxLayout(this); QTabWidget* tabWidget = new QTabWidget(this); tabWidget->addTab(new FingerprintWidget(m_otr, tabWidget), @@ -135,7 +146,7 @@ ConfigOtrWidget::ConfigOtrWidget(OptionAccessingHost* optionHost, void ConfigOtrWidget::updateOptions() { OtrPolicy policy = static_cast(m_policy->checkedId()); - + m_optionHost->setPluginOption(OPTION_POLICY, policy); m_optionHost->setPluginOption(OPTION_END_WHEN_OFFLINE, m_endWhenOffline->checkState() == Qt::Checked); @@ -206,7 +217,8 @@ void FingerprintWidget::updateData() row.append(new QStandardItem(fp.username)); row.append(new QStandardItem(fp.fingerprintHuman)); row.append(new QStandardItem(fp.trust)); - row.append(new QStandardItem(fp.messageState)); + row.append(new QStandardItem(m_otr->getMessageStateString(fp.account, + fp.username))); m_tableModel->appendRow(row); @@ -295,7 +307,7 @@ void FingerprintWidget::copyFingerprint() } text += m_fingerprints[fpIndex].fingerprintHuman; } - QClipboard *clipboard = QApplication::clipboard(); + QClipboard* clipboard = QApplication::clipboard(); clipboard->setText(text); } @@ -309,10 +321,10 @@ void FingerprintWidget::contextMenu(const QPoint& pos) return; } - QMenu *menu = new QMenu(this); + QMenu* menu = new QMenu(this); menu->addAction(QIcon::fromTheme("edit-delete"), tr("Delete"), this, SLOT(deleteFingerprint())); - menu->addAction(QIcon(":/psi-otr/otr_unverified.png"), tr("Verify fingerprint"), this, SLOT(verifyFingerprint())); + menu->addAction(QIcon(":/otrplugin/otr_unverified.png"), tr("Verify fingerprint"), this, SLOT(verifyFingerprint())); menu->addAction(QIcon::fromTheme("edit-copy"), tr("Copy fingerprint"), this, SLOT(copyFingerprint())); menu->exec(QCursor::pos()); @@ -486,7 +498,7 @@ void PrivKeyWidget::copyFingerprint() } text += m_tableModel->item(selectIndex.row(), 1)->text(); } - QClipboard *clipboard = QApplication::clipboard(); + QClipboard* clipboard = QApplication::clipboard(); clipboard->setText(text); } @@ -500,7 +512,7 @@ void PrivKeyWidget::contextMenu(const QPoint& pos) return; } - QMenu *menu = new QMenu(this); + QMenu* menu = new QMenu(this); menu->addAction(QIcon::fromTheme("edit-delete"), tr("Delete"), this, SLOT(deleteKey())); menu->addAction(QIcon::fromTheme("edit-copy"), tr("Copy fingerprint"), this, SLOT(copyFingerprint())); diff --git a/src/plugins/generic/otrplugin/src/PsiOtrConfig.hpp b/src/plugins/generic/otrplugin/src/psiotrconfig.h similarity index 82% rename from src/plugins/generic/otrplugin/src/PsiOtrConfig.hpp rename to src/plugins/generic/otrplugin/src/psiotrconfig.h index d53f32daa..fc7afbce8 100644 --- a/src/plugins/generic/otrplugin/src/PsiOtrConfig.hpp +++ b/src/plugins/generic/otrplugin/src/psiotrconfig.h @@ -1,6 +1,9 @@ /* - * PsiOtrConfig.hpp - configuration dialogs for Psi OTR plugin - * Copyright (C) 2007 Timo Engel (timo-e@freenet.de) + * psiotrconfig.h - Configuration dialogs + * + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011 Florian Fieber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -13,29 +16,26 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program. If not, see . * */ -#ifndef PSIOTRCONFIG_HPP_ -#define PSIOTRCONFIG_HPP_ +#ifndef PSIOTRCONFIG_H_ +#define PSIOTRCONFIG_H_ -#include "OtrMessaging.hpp" +#include "otrmessaging.h" #include -#include -#include -#include -#include -#include -#include +#include class OptionAccessingHost; class AccountInfoAccessingHost; +class QButtonGroup; +class QComboBox; class QCheckBox; class QStandardItemModel; class QTableView; +class QPoint; // --------------------------------------------------------------------------- @@ -51,9 +51,9 @@ const QVariant DEFAULT_END_WHEN_OFFLINE = QVariant(false); // --------------------------------------------------------------------------- -/** -* This dialog appears in the 'Plugins' section of the Psi configuration. -*/ +/** + * This dialog appears in the 'Plugins' section of the Psi configuration. + */ class ConfigDialog : public QWidget { Q_OBJECT @@ -71,9 +71,9 @@ Q_OBJECT // --------------------------------------------------------------------------- -/** -* Configure OTR policy. -*/ +/** + * Configure OTR policy. + */ class ConfigOtrWidget : public QWidget { Q_OBJECT @@ -97,9 +97,9 @@ private slots: // --------------------------------------------------------------------------- -/** -* Show fingerprint of your contacts. -*/ +/** + * Show fingerprint of your contacts. + */ class FingerprintWidget : public QWidget { Q_OBJECT @@ -125,9 +125,9 @@ private slots: // --------------------------------------------------------------------------- -/** -* Display a table with account and fingerprint of private key. -*/ +/** + * Display a table with account and fingerprint of private key. + */ class PrivKeyWidget : public QWidget { Q_OBJECT diff --git a/src/plugins/generic/otrplugin/src/PsiOtrPlugin.cpp b/src/plugins/generic/otrplugin/src/psiotrplugin.cpp similarity index 61% rename from src/plugins/generic/otrplugin/src/PsiOtrPlugin.cpp rename to src/plugins/generic/otrplugin/src/psiotrplugin.cpp index 6e5647297..736c9610a 100644 --- a/src/plugins/generic/otrplugin/src/PsiOtrPlugin.cpp +++ b/src/plugins/generic/otrplugin/src/psiotrplugin.cpp @@ -1,9 +1,12 @@ /* - * psi-otr.cpp - off-the-record messaging plugin for psi + * psiotrplugin.cpp * - * Copyright (C) Timo Engel (timo-e@freenet.de), Berlin 2007. - * This program was written as part of a diplom thesis advised by - * Prof. Dr. Ruediger Weis (PST Labor) + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011 Florian Fieber + * + * This program was originally written as part of a diplom thesis + * advised by Prof. Dr. Ruediger Weis (PST Labor) * at the Technical University of Applied Sciences Berlin. * * This program is free software; you can redistribute it and/or @@ -17,16 +20,23 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program. If not, see . * */ -#include "PsiOtrPlugin.hpp" +#include "psiotrplugin.h" #include "psiotrclosure.h" -#include "PsiOtrConfig.hpp" +#include "psiotrconfig.h" +#include "htmltidy.h" #include "applicationinfoaccessinghost.h" -#include "HtmlTidy.hpp" +#include "psiaccountcontrollinghost.h" +#include "accountinfoaccessinghost.h" +#include "contactinfoaccessinghost.h" +#include "iconfactoryaccessinghost.h" + +#include +#include +#include namespace psiotr { @@ -39,7 +49,7 @@ namespace // --------------------------------------------------------------------------- /** - * Removes the resource from a given JID. + * Removes the resource from a given JID. * Example: * removeResource("user@jabber.org/Home") * returns "user@jabber.org" @@ -51,7 +61,6 @@ QString removeResource(const QString& aJid) if (pos > -1) { addr.truncate(pos); - return addr; } return addr; } @@ -101,8 +110,10 @@ PsiOtrPlugin::PsiOtrPlugin() m_optionHost(NULL), m_senderHost(NULL), m_applicationInfo(NULL), + m_accountHost(NULL), m_accountInfo(NULL), - m_contactInfo(NULL) + m_contactInfo(NULL), + m_iconHost(NULL) { } @@ -130,7 +141,7 @@ QString PsiOtrPlugin::shortName() const QString PsiOtrPlugin::version() const { - return "0.9.4"; + return "0.9.5pre"; } // --------------------------------------------------------------------------- @@ -145,7 +156,7 @@ QWidget* PsiOtrPlugin::options() { return new ConfigDialog(m_otrConnection, m_optionHost, m_accountInfo); } -} +} // --------------------------------------------------------------------------- @@ -155,6 +166,22 @@ bool PsiOtrPlugin::enable() m_otrConnection = new OtrMessaging(this, static_cast(policyOption.toInt())); m_enabled = true; + + QFile f(":/otrplugin/otr_yes.png"); + f.open(QIODevice::ReadOnly); + m_iconHost->addIcon("otrplugin/otr_yes", f.readAll()); + f.close(); + + f.setFileName(":/otrplugin/otr_no.png"); + f.open(QIODevice::ReadOnly); + m_iconHost->addIcon("otrplugin/otr_no", f.readAll()); + f.close(); + + f.setFileName(":/otrplugin/otr_unverified.png"); + f.open(QIODevice::ReadOnly); + m_iconHost->addIcon("otrplugin/otr_unverified", f.readAll()); + f.close(); + return true; } @@ -162,12 +189,14 @@ bool PsiOtrPlugin::enable() bool PsiOtrPlugin::disable() { - foreach (QString account, m_onlineUsers.keys()) + foreach(QString account, m_onlineUsers.keys()) { foreach(QString contact, m_onlineUsers.value(account).keys()) { - m_otrConnection->endSession(account, contact); - m_onlineUsers[account][contact]->updateMessageState(); + if (m_onlineUsers[account][contact]->encrypted()) + { + m_otrConnection->endSession(account, contact); + } m_onlineUsers[account][contact]->disable(); delete m_onlineUsers[account][contact]; } @@ -194,8 +223,45 @@ void PsiOtrPlugin::restoreOptions() //----------------------------------------------------------------------------- +QString PsiOtrPlugin::pluginInfo() { + return QString("%1
" + "%2

" + "%3" + "
" + "
%4
%5
" + "
%6
%7
" + "
%8
%9
" + "
%10
%11
" + "
" + "%12") + .arg(tr("Off-the-Record Messaging plugin for Psi+")) + .arg(tr("Authors: %1") + .arg("Timo Engel, Florian Fieber")) + .arg(tr("Off-the-Record (OTR) Messaging allows you to have private " + "conversations over instant messaging by providing:")) + .arg(tr("Encryption")) + .arg(tr("No one else can read your instant messages.")) + .arg(tr("Authentication")) + .arg(tr("You are assured the correspondent is who you think it is.")) + .arg(tr("Deniability")) + .arg(tr("The messages you send do not have digital signatures that " + "are checkable by a third party. Anyone can forge messages " + "after a conversation to make them look like they came from " + "you. However, during a conversation, your correspondent is " + "assured the messages he sees are authentic and unmodified.")) + .arg(tr("Perfect forward secrecy")) + .arg(tr("If you lose control of your private keys, no previous " + "conversation is compromised.")) + .arg(tr("For further information, see " + "<http://www.cypherpunks.ca/otr/>.")); +} + +//----------------------------------------------------------------------------- + bool PsiOtrPlugin::processEvent(int accountIndex, QDomElement& e) { + bool ignore = false; + QDomElement messageElement = e.firstChildElement("message"); if (m_enabled && e.attribute("type") == "MessageEvent" && @@ -224,68 +290,71 @@ bool PsiOtrPlugin::processEvent(int accountIndex, QDomElement& e) } QString decrypted; - if (m_otrConnection->decryptMessage(contact, account, cyphertext, - decrypted)) + OtrMessageType messageType = m_otrConnection->decryptMessage( + account, contact, + cyphertext, decrypted); + switch (messageType) { - if (m_onlineUsers.contains(account) && - m_onlineUsers.value(account).contains(contact)) - { - m_onlineUsers[account][contact]->updateMessageState(); - } - - QString bodyText; - - bool isHTML = !htmlElement.isNull() || - Qt::mightBeRichText(decrypted); - - if (!isHTML) - { - bodyText = decrypted; - } - else - { - HtmlTidy htmlTidy("" + - decrypted + ""); - decrypted = htmlTidy.output(); - - bodyText = htmlToPlain(decrypted); - - // replace html body - if (htmlElement.isNull()) + case OTR_MESSAGETYPE_NONE: + break; + case OTR_MESSAGETYPE_IGNORE: + ignore = true; + break; + case OTR_MESSAGETYPE_OTR: + QString bodyText; + + bool isHTML = !htmlElement.isNull() || + Qt::mightBeRichText(decrypted); + + if (!isHTML) { - htmlElement = e.ownerDocument().createElement("html"); - htmlElement.setAttribute("xmlns", - "http://jabber.org/protocol/xhtml-im"); - messageElement.appendChild(htmlElement); + bodyText = decrypted; } else { - htmlElement.removeChild(htmlElement.firstChildElement("body")); + HtmlTidy htmlTidy("" + + decrypted + ""); + decrypted = htmlTidy.output(); + + bodyText = htmlToPlain(decrypted); + + // replace html body + if (htmlElement.isNull()) + { + htmlElement = e.ownerDocument().createElement("html"); + htmlElement.setAttribute("xmlns", + "http://jabber.org/protocol/xhtml-im"); + messageElement.appendChild(htmlElement); + } + else + { + htmlElement.removeChild(htmlElement.firstChildElement("body")); + } + + QDomDocument document; + int errorLine = 0, errorColumn = 0; + QString errorText; + if (document.setContent(decrypted, true, &errorText, &errorLine, + &errorColumn)) + { + htmlElement.appendChild(document.documentElement()); + } + else + { + qWarning() << "---- parsing error:\n" << decrypted << + "\n----\n" << errorText << " line:" << + errorLine << " column:" << errorColumn; + messageElement.removeChild(htmlElement); + } } - QDomDocument document; - int errorLine = 0, errorColumn = 0; - QString errorText; - if (document.setContent(decrypted, true, &errorText, &errorLine, - &errorColumn)) - { - htmlElement.appendChild(document.documentElement()); - } - else - { - qWarning() << "---- parsing error:\n" << decrypted << - "\n----\n" << errorText << " line:" << - errorLine << " column:" << errorColumn; - messageElement.removeChild(htmlElement); - } - } - - // replace plaintext body - plainBody.removeChild(plainBody.firstChild()); - plainBody.appendChild(e.ownerDocument().createTextNode(unescape(bodyText))); + // replace plaintext body + plainBody.removeChild(plainBody.firstChild()); + plainBody.appendChild(e.ownerDocument().createTextNode(unescape(bodyText))); + break; } } - return false; + return ignore; } //----------------------------------------------------------------------------- @@ -333,7 +402,7 @@ void PsiOtrPlugin::logout(int accountIndex) { return; } - + QString account = m_accountInfo->getId(accountIndex); if (m_onlineUsers.contains(account)) @@ -364,7 +433,7 @@ void PsiOtrPlugin::optionChanged(const QString&) //----------------------------------------------------------------------------- -void PsiOtrPlugin::setStanzaSendingHost(StanzaSendingHost *host) +void PsiOtrPlugin::setStanzaSendingHost(StanzaSendingHost* host) { m_senderHost = host; } @@ -378,31 +447,44 @@ void PsiOtrPlugin::setApplicationInfoAccessingHost(ApplicationInfoAccessingHost* //----------------------------------------------------------------------------- -void PsiOtrPlugin::setAccountInfoAccessingHost(AccountInfoAccessingHost *host) +void PsiOtrPlugin::setPsiAccountControllingHost(PsiAccountControllingHost* host) { + m_accountHost = host; +} + +//----------------------------------------------------------------------------- + +void PsiOtrPlugin::setAccountInfoAccessingHost(AccountInfoAccessingHost* host) { m_accountInfo = host; } //----------------------------------------------------------------------------- -void PsiOtrPlugin::setContactInfoAccessingHost(ContactInfoAccessingHost *host) +void PsiOtrPlugin::setContactInfoAccessingHost(ContactInfoAccessingHost* host) { m_contactInfo = host; } //----------------------------------------------------------------------------- +void PsiOtrPlugin::setIconFactoryAccessingHost(IconFactoryAccessingHost* host) +{ + m_iconHost = host; +} + +//----------------------------------------------------------------------------- + bool PsiOtrPlugin::incomingStanza(int accountIndex, const QDomElement& xml) { if (!m_enabled || xml.nodeName() != "presence") { return false; } - + QString account = m_accountInfo->getId(accountIndex); QString contact = getCorrectJid(accountIndex, xml.attribute("from")); QString type = xml.attribute("type", "available"); - + if (type == "available") { if (!m_onlineUsers.value(account).contains(contact)) @@ -411,12 +493,12 @@ bool PsiOtrPlugin::incomingStanza(int accountIndex, const QDomElement& xml) contact, m_otrConnection); } - + m_onlineUsers[account][contact]->setIsLoggedIn(true); } else if (type == "unavailable") { - if (m_onlineUsers.contains(account) && + if (m_onlineUsers.contains(account) && m_onlineUsers.value(account).contains(contact)) { if (m_optionHost->getPluginOption(OPTION_END_WHEN_OFFLINE, @@ -478,7 +560,7 @@ QAction* PsiOtrPlugin::getAction(QObject* parent, int accountIndex, QString contact = getCorrectJid(accountIndex, contactJid); QString account = m_accountInfo->getId(accountIndex); - + if (!m_onlineUsers.value(account).contains(contact)) { m_onlineUsers[account][contact] = new PsiOtrClosure(account, @@ -496,7 +578,7 @@ QString PsiOtrPlugin::dataDir() return m_applicationInfo->appCurrentProfileDir( ApplicationInfoAccessingHost::DataLocation); } - + //----------------------------------------------------------------------------- void PsiOtrPlugin::sendMessage(const QString& account, const QString& contact, @@ -530,7 +612,7 @@ void PsiOtrPlugin::notifyUser(const OtrNotifyType& type, const QString& message) QMessageBox::Icon messageBoxIcon; if (type == OTR_NOTIFY_ERROR) { - messageBoxIcon = QMessageBox::Critical; + messageBoxIcon = QMessageBox::Critical; } else if (type == OTR_NOTIFY_WARNING) { @@ -548,10 +630,87 @@ void PsiOtrPlugin::notifyUser(const OtrNotifyType& type, const QString& message) //----------------------------------------------------------------------------- +bool PsiOtrPlugin::displayOtrMessage(const QString& account, + const QString& contact, + const QString& message) +{ + return appendSysMsg(account, contact, message); +} + +//----------------------------------------------------------------------------- + +void PsiOtrPlugin::stateChange(const QString& account, const QString& contact, + OtrStateChange change) +{ + if (!m_onlineUsers.value(account).contains(contact)) + { + m_onlineUsers[account][contact] = new PsiOtrClosure(account, contact, + m_otrConnection); + } + + m_onlineUsers[account][contact]->updateMessageState(); + + bool verified = m_otrConnection->isVerified(account, contact); + bool encrypted = m_onlineUsers[account][contact]->encrypted(); + QString msg; + QString icon; + + switch (change) + { + case OTR_STATECHANGE_GOINGSECURE: + msg = encrypted? + tr("Attempting to refresh the private conversation") + : tr("Attempting to start a private conversation"); + break; + + case OTR_STATECHANGE_GONESECURE: + msg = verified? tr("Private conversation started") + : tr("Unverified conversation started"); + icon = verified? "otrplugin/otr_yes" + : "otrplugin/otr_unverified"; + break; + + case OTR_STATECHANGE_GONEINSECURE: + msg = tr("Private conversation lost"); + icon = "otrplugin/otr_no"; + break; + + case OTR_STATECHANGE_CLOSE: + msg = tr("Private conversation closed"); + icon = "otrplugin/otr_no"; + break; + + case OTR_STATECHANGE_REMOTECLOSE: + msg = tr("%1 has ended the private conversation with you; " + "you should do the same.") + .arg(humanContact(account, contact)); + icon = "otrplugin/otr_no"; + break; + + case OTR_STATECHANGE_STILLSECURE: + msg = verified? tr("Private conversation refreshed") + : tr("Unverified conversation refreshed"); + icon = verified? "otrplugin/otr_yes" + : "otrplugin/otr_unverified"; + break; + + case OTR_STATECHANGE_TRUST: + msg = verified? tr("Contact authenticated") + : tr("Contact not authenticated"); + icon = verified? "otrplugin/otr_yes" + : "otrplugin/otr_unverified"; + break; + } + + appendSysMsg(account, contact, msg, icon); +} + +//----------------------------------------------------------------------------- + void PsiOtrPlugin::receivedSMP(const QString& account, const QString& contact, const QString& question) { - if (m_onlineUsers.contains(account) && + if (m_onlineUsers.contains(account) && m_onlineUsers.value(account).contains(contact)) { m_onlineUsers[account][contact]->receivedSMP(question); @@ -563,8 +722,8 @@ void PsiOtrPlugin::receivedSMP(const QString& account, const QString& contact, void PsiOtrPlugin::updateSMP(const QString& account, const QString& contact, int progress) { - - if (m_onlineUsers.contains(account) && + + if (m_onlineUsers.contains(account) && m_onlineUsers.value(account).contains(contact)) { m_onlineUsers[account][contact]->updateSMP(progress); @@ -601,6 +760,30 @@ QString PsiOtrPlugin::humanAccountPublic(const QString& accountId) return getAccountJidById(accountId); } +//----------------------------------------------------------------------------- + +QString PsiOtrPlugin::humanContact(const QString& accountId, + const QString& contact) +{ + return m_contactInfo->name(getAccountIndexById(accountId), contact); +} + +//----------------------------------------------------------------------------- + +bool PsiOtrPlugin::appendSysMsg(const QString& account, + const QString& contact, + const QString& message, + const QString& icon) +{ + QString iconTag; + if (!icon.isEmpty()) + { + iconTag = QString(" ").arg(icon); + } + return m_accountHost->appendSysMsg(getAccountIndexById(account), + contact, iconTag + message); +} + // --------------------------------------------------------------------------- int PsiOtrPlugin::getAccountIndexById(const QString& accountId) diff --git a/src/plugins/generic/otrplugin/src/PsiOtrPlugin.hpp b/src/plugins/generic/otrplugin/src/psiotrplugin.h similarity index 71% rename from src/plugins/generic/otrplugin/src/PsiOtrPlugin.hpp rename to src/plugins/generic/otrplugin/src/psiotrplugin.h index 09ab7695f..066a1d5b2 100644 --- a/src/plugins/generic/otrplugin/src/PsiOtrPlugin.hpp +++ b/src/plugins/generic/otrplugin/src/psiotrplugin.h @@ -1,9 +1,12 @@ /* - * psi-otr.h - off-the-record messaging plugin for psi + * psiotrplugin.h * - * Copyright (C) Timo Engel (timo-e@freenet.de), Berlin 2007. - * This program was written as part of a diplom thesis advised by - * Prof. Dr. Ruediger Weis (PST Labor) + * Off-the-Record Messaging plugin for Psi+ + * Copyright (C) 2007-2011 Timo Engel (timo-e@freenet.de) + * 2011 Florian Fieber + * + * This program was originally written as part of a diplom thesis + * advised by Prof. Dr. Ruediger Weis (PST Labor) * at the Technical University of Applied Sciences Berlin. * * This program is free software; you can redistribute it and/or @@ -17,65 +20,76 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * along with this program. If not, see . * */ -#ifndef PSIOTRPLUGIN_HPP_ -#define PSIOTRPLUGIN_HPP_ +#ifndef PSIOTRPLUGIN_H_ +#define PSIOTRPLUGIN_H_ #include -#include -#include -#include "OtrMessaging.hpp" +#include "otrmessaging.h" #include "psiplugin.h" +#include "plugininfoprovider.h" #include "eventfilter.h" #include "optionaccessinghost.h" #include "optionaccessor.h" #include "stanzasender.h" #include "stanzasendinghost.h" #include "applicationinfoaccessor.h" +#include "psiaccountcontroller.h" #include "stanzafilter.h" #include "toolbariconaccessor.h" -#include "accountinfoaccessinghost.h" #include "accountinfoaccessor.h" -#include "contactinfoaccessinghost.h" #include "contactinfoaccessor.h" +#include "iconfactoryaccessor.h" class ApplicationInfoAccessingHost; +class PsiAccountControllingHost; +class AccountInfoAccessingHost; +class ContactInfoAccessingHost; +class IconFactoryAccessingHost; + +class QDomElement; +class QString; +class QAction; namespace psiotr { -class ConfigDlg; class PsiOtrClosure; - + //----------------------------------------------------------------------------- class PsiOtrPlugin : public QObject, public PsiPlugin, + public PluginInfoProvider, public EventFilter, public OptionAccessor, public StanzaSender, public ApplicationInfoAccessor, + public PsiAccountController, public StanzaFilter, public ToolbarIconAccessor, public AccountInfoAccessor, public ContactInfoAccessor, + public IconFactoryAccessor, public OtrCallback { Q_OBJECT Q_INTERFACES(PsiPlugin + PluginInfoProvider EventFilter OptionAccessor StanzaSender ApplicationInfoAccessor + PsiAccountController StanzaFilter ToolbarIconAccessor AccountInfoAccessor - ContactInfoAccessor) + ContactInfoAccessor + IconFactoryAccessor) public: PsiOtrPlugin(); @@ -85,12 +99,15 @@ Q_INTERFACES(PsiPlugin virtual QString name() const; virtual QString shortName() const; virtual QString version() const; - virtual QWidget* options(); + virtual QWidget* options(); virtual bool enable(); virtual bool disable(); virtual void applyOptions(); virtual void restoreOptions(); + // PluginInfoProvider + virtual QString pluginInfo(); + // EventFilter virtual bool processEvent(int accountIndex, QDomElement& e); virtual bool processMessage(int accountIndex, const QString& contact, @@ -105,14 +122,17 @@ Q_INTERFACES(PsiPlugin virtual void optionChanged(const QString& option); // StanzaSender - virtual void setStanzaSendingHost(StanzaSendingHost *host); + virtual void setStanzaSendingHost(StanzaSendingHost* host); // ApplicationInfoAccessor virtual void setApplicationInfoAccessingHost(ApplicationInfoAccessingHost* host); + // PsiAccountController + virtual void setPsiAccountControllingHost(PsiAccountControllingHost* host); + // StanzaFilter virtual bool incomingStanza(int accountIndex, const QDomElement& xml); - virtual bool outgoingStanza(int accountIndex, QDomElement &xml); + virtual bool outgoingStanza(int accountIndex, QDomElement& xml); // ToolbarIconAccessor virtual QList getButtonParam(); @@ -125,20 +145,39 @@ Q_INTERFACES(PsiPlugin // ContactInfoAccessor virtual void setContactInfoAccessingHost(ContactInfoAccessingHost* host); + // IconFactoryAccessingHost + virtual void setIconFactoryAccessingHost(IconFactoryAccessingHost* host); + // OtrCallback virtual QString dataDir(); virtual void sendMessage(const QString& account, const QString& contact, const QString& message); virtual bool isLoggedIn(const QString& account, const QString& contact); virtual void notifyUser(const OtrNotifyType& type, const QString& message); + + virtual bool displayOtrMessage(const QString& account, const QString& contact, + const QString& message); + virtual void stateChange(const QString& account, const QString& contact, + OtrStateChange change); + virtual void receivedSMP(const QString& account, const QString& contact, const QString& question); virtual void updateSMP(const QString& account, const QString& contact, int progress); + virtual void stopMessages(); virtual void startMessages(); + virtual QString humanAccount(const QString& accountId); virtual QString humanAccountPublic(const QString& accountId); + virtual QString humanContact(const QString& accountId, + const QString& contact); + + /** + * Displays a rich text system message for (account, contact) + */ + bool appendSysMsg(const QString& account, const QString& contact, + const QString& message, const QString& icon = ""); // Helper methods /** @@ -169,8 +208,10 @@ Q_INTERFACES(PsiPlugin OptionAccessingHost* m_optionHost; StanzaSendingHost* m_senderHost; ApplicationInfoAccessingHost* m_applicationInfo; + PsiAccountControllingHost* m_accountHost; AccountInfoAccessingHost* m_accountInfo; ContactInfoAccessingHost* m_contactInfo; + IconFactoryAccessingHost* m_iconHost; }; //-----------------------------------------------------------------------------