diff options
-rw-r--r-- | hfpag.h | 5 | ||||
-rw-r--r-- | hostmanagerconn.cc | 110 | ||||
-rw-r--r-- | hostmanagerconn.h | 7 | ||||
m--------- | libwatchfish | 0 | ||||
-rw-r--r-- | main.cc | 2 | ||||
-rw-r--r-- | notificationconn.h | 1 | ||||
-rw-r--r-- | qtjson/json.cc | 549 | ||||
-rw-r--r-- | qtjson/json.h | 112 | ||||
-rw-r--r-- | rpm/sapd.yaml | 4 | ||||
-rw-r--r-- | sapbtlistener.cc | 98 | ||||
-rw-r--r-- | sapbtlistener.h | 17 | ||||
-rw-r--r-- | sapbtpeer.cc | 4 | ||||
-rw-r--r-- | sapbtpeer.h | 9 | ||||
-rw-r--r-- | sapd.pro | 37 | ||||
-rw-r--r-- | sappeer.cc | 67 | ||||
-rw-r--r-- | saprotocol.h | 9 | ||||
-rw-r--r-- | webproxyagent.cc | 57 | ||||
-rw-r--r-- | webproxyagent.h | 27 | ||||
-rw-r--r-- | webproxyconn.cc | 25 | ||||
-rw-r--r-- | webproxyconn.h | 26 |
20 files changed, 251 insertions, 915 deletions
@@ -1,9 +1,8 @@ #ifndef HFPAG_H #define HFPAG_H -#include <QtConnectivity/QBluetoothSocket> - -QTM_USE_NAMESPACE +#include <QtCore/QObject> +#include <QtBluetooth/QBluetoothSocket> class HfpAg : public QObject { diff --git a/hostmanagerconn.cc b/hostmanagerconn.cc index de1c61a..e4f5edc 100644 --- a/hostmanagerconn.cc +++ b/hostmanagerconn.cc @@ -1,8 +1,13 @@ -#include <QtCore/QDateTime> #include <QtCore/QDebug> +#include <QtCore/QDateTime> +#include <QtCore/QTimeZone> #include <QtCore/QXmlStreamWriter> +#include <QtCore/QJsonDocument> -#include "qtjson/json.h" +#if SAILFISH +#include <libwatchfish/walltimemonitor.h> +static watchfish::WallTimeMonitor *monitor = 0; +#endif #include "sappeer.h" #include "hostmanagerconn.h" @@ -16,31 +21,38 @@ HostManagerConn::HostManagerConn(SAPConnection *conn, QObject *parent) connect(_socket, SIGNAL(messageReceived()), SLOT(handleMessageReceived())); } -void HostManagerConn::sendMessage(const QString &json) -{ - qDebug() << "Send JSON:" << json; - _socket->send(QByteArray(3,0) + json.toUtf8()); -} - -void HostManagerConn::sendMessage(const QVariantMap &msg) +void HostManagerConn::sendMessage(const QJsonObject &msg) { - sendMessage(QtJson::serializeStr(msg)); + QJsonDocument doc(msg); + QByteArray data = doc.toJson(QJsonDocument::Compact); + qDebug() << "Send JSON:" << data; + _socket->send(QByteArray(3,0) + doc.toJson(QJsonDocument::Compact)); } -void HostManagerConn::handleMessage(const QVariantMap &msg) +void HostManagerConn::handleMessage(const QJsonObject &msg) { - QString msgId = msg["msgId"].toString(); + const QString msgId = msg["msgId"].toString(); qDebug() << "Got JSON msg" << msgId; if (msgId == "mgr_watch_info_res") { - sendMessage("{\"timestamp\":\"1407542281196=B:L>:<=LAMO\",\"type\":\"connect\",\"msgId\":\"mgr_wearable_status_req\"}"); + QJsonObject reply; + reply["timestamp"] = QLatin1String("1407542281196=B:L>:<=LAMO"); + reply["type"] = QLatin1String("connect"); + reply["msgId"] = QLatin1String("mgr_wearable_status_req"); + sendMessage(reply); } else if (msgId == "mgr_host_status_req") { - sendMessage(QString("{\"type\":\"connect\",\"data\":%1,\"msgId\":\"mgr_host_status_res\",\"preinstalled\":\"true\"}") - .arg(QtJson::sanitizeString(generateHostXml()))); - + QJsonObject reply; + reply["type"] = QLatin1String("connect"); + reply["msgId"] = QLatin1String("mgr_host_status_res"); + reply["preinstalled"] = QLatin1String("true"); + reply["data"] = generateHostXml(); + sendMessage(reply); } else if (msgId == "mgr_status_exchange_done") { performTimeSync(); - sendMessage(QString("{\"btMac\":\"%1\",\"msgId\":\"mgr_setupwizard_eula_finished_req\",\"isOld\":1}") - .arg(_conn->peer()->localName())); + QJsonObject reply; + reply["btMac"] = _conn->peer()->localName(); + reply["msgId"] = QLatin1String("mgr_setupwizard_eula_finished_req"); + reply["isOld"] = 1; + sendMessage(reply); } } @@ -51,22 +63,27 @@ void HostManagerConn::performTimeSync() // "isfrominitial":true,"msgId":"mgr_sync_init_setting_req","usingCamera":"false","safety_cam":"0", // "datetime":"2014 08 29 22 23 48","incomingCall":"false"} - QVariantMap msg; - msg["msgId"] = "mgr_sync_init_setting_req"; + QJsonObject msg; + msg["msgId"] = QLatin1String("mgr_sync_init_setting_req"); - msg["safety_declared"] = "0"; - msg["safety_voice"] = "0"; - msg["safetyVersion"] = "0"; - msg["safety"] = "false"; - msg["tablet"] = "true"; - msg["incomingCall"] = "false"; - msg["usingCamera"] = "false"; - msg["safety_cam"] = "0"; + msg["safety_declared"] = QLatin1String("0"); + msg["safety_voice"] = QLatin1String("0"); + msg["safetyVersion"] = QLatin1String("0"); + msg["safety"] = QLatin1String("false"); + msg["tablet"] = QLatin1String("true"); + msg["incomingCall"] = QLatin1String("false"); + msg["usingCamera"] = QLatin1String("false"); + msg["safety_cam"] = QLatin1String("0"); msg["locale"] = QLocale::system().name(); // ie es_ES - msg["data1224"] = "24"; // TODO + msg["data1224"] = QLatin1String("24"); // TODO msg["dateformat"] = QLocale::system().dateFormat(QLocale::ShortFormat); - msg["timezone"] = "Europe/Madrid"; + +#if SAILFISH + msg["timezone"] = monitor->timezone(); +#else + msg["timezone"] = QString::fromLatin1(QTimeZone::systemTimeZoneId()); +#endif QDateTime dt = QDateTime::currentDateTime(); msg["datetimeepoch"] = QString::number(dt.currentMSecsSinceEpoch()); @@ -124,10 +141,18 @@ QString HostManagerConn::generateHostXml() void HostManagerConn::handleConnected() { qDebug() << "Manager socket now connected!"; - QString msg = QString("{\"btMac\":\"%1\",\"msgId\":\"mgr_watch_info_req\",\"hmVer\":\"2.0.14041404\"}").arg(_conn->peer()->localName()); - qDebug() << msg; - QByteArray data = QByteArray(3,0) + msg.toUtf8(); - _socket->send(data); + +#if SAILFISH + if (!monitor) { + monitor = new watchfish::WallTimeMonitor; + } +#endif + + QJsonObject obj; + obj["btMac"] = _conn->peer()->localName(); + obj["msgId"] = QLatin1String("mgr_watch_info_req"); + obj["hmVer"] = QLatin1String("2.0.14041404"); + sendMessage(obj); } void HostManagerConn::handleMessageReceived() @@ -139,17 +164,16 @@ void HostManagerConn::handleMessageReceived() return; } - data.remove(0, 3); // First two bytes contain something related to ??? + data.remove(0, 3); // What are those first three bytes??? They seem to be always NUL. + + qDebug() << "Got JSON:" << QString::fromUtf8(data); - QString str = QString::fromUtf8(data); - bool success = false; - QVariant json = QtJson::parse(str, success); + QJsonParseError error; + QJsonDocument json = QJsonDocument::fromJson(data, &error); - if (success) { - qDebug() << "Got JSON:" << str; - handleMessage(json.toMap()); + if (json.isObject()) { + handleMessage(json.object()); } else { - qWarning() << "Cannot parse JSON msg:" << str; - return; + qWarning() << "Cannot parse JSON msg:" << error.errorString(); } } diff --git a/hostmanagerconn.h b/hostmanagerconn.h index ab143a0..1361d21 100644 --- a/hostmanagerconn.h +++ b/hostmanagerconn.h @@ -2,6 +2,7 @@ #define HOSTMANAGERPEER_H #include <QtCore/QVariant> +#include <QtCore/QJsonObject> #include "sapconnection.h" #include "sapsocket.h" @@ -13,9 +14,9 @@ public: HostManagerConn(SAPConnection *conn, QObject *parent = 0); private: - void sendMessage(const QString &json); - void sendMessage(const QVariantMap &msg); - void handleMessage(const QVariantMap &msg); + void sendMessage(const QJsonObject &obj); + + void handleMessage(const QJsonObject &obj); void performTimeSync(); diff --git a/libwatchfish b/libwatchfish -Subproject 7ee859f6a5e6a334a172015fce744baeff53905 +Subproject 3b6a0066ef584a076b1a05dc5524cbfa2e2a0bb @@ -7,6 +7,7 @@ #include "capabilityagent.h" #include "hostmanageragent.h" +#include "webproxyagent.h" #include "notificationagent.h" using std::cerr; @@ -44,6 +45,7 @@ int main(int argc, char *argv[]) CapabilityAgent::registerServices(manager); HostManagerAgent::registerServices(manager); + WebProxyAgent::registerServices(manager); NotificationAgent::registerServices(manager); QScopedPointer<SAPBTListener> sap_listener(new SAPBTListener); diff --git a/notificationconn.h b/notificationconn.h index 69f2244..092804a 100644 --- a/notificationconn.h +++ b/notificationconn.h @@ -3,6 +3,7 @@ #include <QtCore/QObject> #include <QtCore/QDateTime> +#include <QtCore/QHash> #include "sapconnection.h" #include "sapsocket.h" diff --git a/qtjson/json.cc b/qtjson/json.cc deleted file mode 100644 index 759cf0e..0000000 --- a/qtjson/json.cc +++ /dev/null @@ -1,549 +0,0 @@ -/** - * QtJson - A simple class for parsing JSON data into a QVariant hierarchies and vice-versa. - * Copyright (C) 2011 Eeli Reilin - * - * 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 3 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file json.cpp - */ - -#include <QDateTime> -#include "json.h" - -namespace QtJson { - static QString dateFormat, dateTimeFormat; - - static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep); - static QVariant parseValue(const QString &json, int &index, bool &success); - static QVariant parseObject(const QString &json, int &index, bool &success); - static QVariant parseArray(const QString &json, int &index, bool &success); - static QVariant parseString(const QString &json, int &index, bool &success); - static QVariant parseNumber(const QString &json, int &index); - static int lastIndexOfNumber(const QString &json, int index); - static void eatWhitespace(const QString &json, int &index); - static int lookAhead(const QString &json, int index); - static int nextToken(const QString &json, int &index); - - template<typename T> - QByteArray serializeMap(const T &map, bool &success) { - QByteArray str = "{ "; - QList<QByteArray> pairs; - for (typename T::const_iterator it = map.begin(), itend = map.end(); it != itend; ++it) { - QByteArray serializedValue = serialize(it.value()); - if (serializedValue.isNull()) { - success = false; - break; - } - pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; - } - - str += join(pairs, ", "); - str += " }"; - return str; - } - - - /** - * parse - */ - QVariant parse(const QString &json) { - bool success = true; - return parse(json, success); - } - - /** - * parse - */ - QVariant parse(const QString &json, bool &success) { - success = true; - - // Return an empty QVariant if the JSON data is either null or empty - if (!json.isNull() || !json.isEmpty()) { - QString data = json; - // We'll start from index 0 - int index = 0; - - // Parse the first value - QVariant value = parseValue(data, index, success); - - // Return the parsed value - return value; - } else { - // Return the empty QVariant - return QVariant(); - } - } - - QByteArray serialize(const QVariant &data) { - bool success = true; - return serialize(data, success); - } - - QByteArray serialize(const QVariant &data, bool &success) { - QByteArray str; - success = true; - - if (!data.isValid()) { // invalid or null? - str = "null"; - } else if ((data.type() == QVariant::List) || - (data.type() == QVariant::StringList)) { // variant is a list? - QList<QByteArray> values; - const QVariantList list = data.toList(); - Q_FOREACH(const QVariant& v, list) { - QByteArray serializedValue = serialize(v); - if (serializedValue.isNull()) { - success = false; - break; - } - values << serializedValue; - } - - str = "[ " + join( values, ", " ) + " ]"; - } else if (data.type() == QVariant::Hash) { // variant is a hash? - str = serializeMap<>(data.toHash(), success); - } else if (data.type() == QVariant::Map) { // variant is a map? - str = serializeMap<>(data.toMap(), success); - } else if ((data.type() == QVariant::String) || - (data.type() == QVariant::ByteArray)) {// a string or a byte array? - str = sanitizeString(data.toString()).toUtf8(); - } else if (data.type() == QVariant::Double) { // double? - double value = data.toDouble(); - if ((value - value) == 0.0) { - str = QByteArray::number(value, 'g'); - if (!str.contains(".") && ! str.contains("e")) { - str += ".0"; - } - } else { - success = false; - } - } else if (data.type() == QVariant::Bool) { // boolean value? - str = data.toBool() ? "true" : "false"; - } else if (data.type() == QVariant::ULongLong) { // large unsigned number? - str = QByteArray::number(data.value<qulonglong>()); - } else if (data.canConvert<qlonglong>()) { // any signed number? - str = QByteArray::number(data.value<qlonglong>()); - } else if (data.canConvert<long>()) { //TODO: this code is never executed because all smaller types can be converted to qlonglong - str = QString::number(data.value<long>()).toUtf8(); - } else if (data.type() == QVariant::DateTime) { // datetime value? - str = sanitizeString(dateTimeFormat.isEmpty() - ? data.toDateTime().toString() - : data.toDateTime().toString(dateTimeFormat)).toUtf8(); - } else if (data.type() == QVariant::Date) { // date value? - str = sanitizeString(dateTimeFormat.isEmpty() - ? data.toDate().toString() - : data.toDate().toString(dateFormat)).toUtf8(); - } else if (data.canConvert<QString>()) { // can value be converted to string? - // this will catch QUrl, ... (all other types which can be converted to string) - str = sanitizeString(data.toString()).toUtf8(); - } else { - success = false; - } - - if (success) { - return str; - } else { - return QByteArray(); - } - } - - QString serializeStr(const QVariant &data) { - return QString::fromUtf8(serialize(data)); - } - - QString serializeStr(const QVariant &data, bool &success) { - return QString::fromUtf8(serialize(data, success)); - } - - - /** - * \enum JsonToken - */ - enum JsonToken { - JsonTokenNone = 0, - JsonTokenCurlyOpen = 1, - JsonTokenCurlyClose = 2, - JsonTokenSquaredOpen = 3, - JsonTokenSquaredClose = 4, - JsonTokenColon = 5, - JsonTokenComma = 6, - JsonTokenString = 7, - JsonTokenNumber = 8, - JsonTokenTrue = 9, - JsonTokenFalse = 10, - JsonTokenNull = 11 - }; - - QString sanitizeString(QString str) { - str.replace(QLatin1String("\\"), QLatin1String("\\\\")); - str.replace(QLatin1String("\""), QLatin1String("\\\"")); - str.replace(QLatin1String("\b"), QLatin1String("\\b")); - str.replace(QLatin1String("\f"), QLatin1String("\\f")); - str.replace(QLatin1String("\n"), QLatin1String("\\n")); - str.replace(QLatin1String("\r"), QLatin1String("\\r")); - str.replace(QLatin1String("\t"), QLatin1String("\\t")); - return QString(QLatin1String("\"%1\"")).arg(str); - } - - static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep) { - QByteArray res; - Q_FOREACH(const QByteArray &i, list) { - if (!res.isEmpty()) { - res += sep; - } - res += i; - } - return res; - } - - /** - * parseValue - */ - static QVariant parseValue(const QString &json, int &index, bool &success) { - // Determine what kind of data we should parse by - // checking out the upcoming token - switch(lookAhead(json, index)) { - case JsonTokenString: - return parseString(json, index, success); - case JsonTokenNumber: - return parseNumber(json, index); - case JsonTokenCurlyOpen: - return parseObject(json, index, success); - case JsonTokenSquaredOpen: - return parseArray(json, index, success); - case JsonTokenTrue: - nextToken(json, index); - return QVariant(true); - case JsonTokenFalse: - nextToken(json, index); - return QVariant(false); - case JsonTokenNull: - nextToken(json, index); - return QVariant(); - case JsonTokenNone: - break; - } - - // If there were no tokens, flag the failure and return an empty QVariant - success = false; - return QVariant(); - } - - /** - * parseObject - */ - static QVariant parseObject(const QString &json, int &index, bool &success) { - QVariantMap map; - int token; - - // Get rid of the whitespace and increment index - nextToken(json, index); - - // Loop through all of the key/value pairs of the object - bool done = false; - while (!done) { - // Get the upcoming token - token = lookAhead(json, index); - - if (token == JsonTokenNone) { - success = false; - return QVariantMap(); - } else if (token == JsonTokenComma) { - nextToken(json, index); - } else if (token == JsonTokenCurlyClose) { - nextToken(json, index); - return map; - } else { - // Parse the key/value pair's name - QString name = parseString(json, index, success).toString(); - - if (!success) { - return QVariantMap(); - } - - // Get the next token - token = nextToken(json, index); - - // If the next token is not a colon, flag the failure - // return an empty QVariant - if (token != JsonTokenColon) { - success = false; - return QVariant(QVariantMap()); - } - - // Parse the key/value pair's value - QVariant value = parseValue(json, index, success); - - if (!success) { - return QVariantMap(); - } - - // Assign the value to the key in the map - map[name] = value; - } - } - - // Return the map successfully - return QVariant(map); - } - - /** - * parseArray - */ - static QVariant parseArray(const QString &json, int &index, bool &success) { - QVariantList list; - - nextToken(json, index); - - bool done = false; - while(!done) { - int token = lookAhead(json, index); - - if (token == JsonTokenNone) { - success = false; - return QVariantList(); - } else if (token == JsonTokenComma) { - nextToken(json, index); - } else if (token == JsonTokenSquaredClose) { - nextToken(json, index); - break; - } else { - QVariant value = parseValue(json, index, success); - if (!success) { - return QVariantList(); - } - list.push_back(value); - } - } - - return QVariant(list); - } - - /** - * parseString - */ - static QVariant parseString(const QString &json, int &index, bool &success) { - QString s; - QChar c; - - eatWhitespace(json, index); - - c = json[index++]; - - bool complete = false; - while(!complete) { - if (index == json.size()) { - break; - } - - c = json[index++]; - - if (c == '\"') { - complete = true; - break; - } else if (c == '\\') { - if (index == json.size()) { - break; - } - - c = json[index++]; - - if (c == '\"') { - s.append('\"'); - } else if (c == '\\') { - s.append('\\'); - } else if (c == '/') { - s.append('/'); - } else if (c == 'b') { - s.append('\b'); - } else if (c == 'f') { - s.append('\f'); - } else if (c == 'n') { - s.append('\n'); - } else if (c == 'r') { - s.append('\r'); - } else if (c == 't') { - s.append('\t'); - } else if (c == 'u') { - int remainingLength = json.size() - index; - if (remainingLength >= 4) { - QString unicodeStr = json.mid(index, 4); - - int symbol = unicodeStr.toInt(0, 16); - - s.append(QChar(symbol)); - - index += 4; - } else { - break; - } - } - } else { - s.append(c); - } - } - - if (!complete) { - success = false; - return QVariant(); - } - - return QVariant(s); - } - - /** - * parseNumber - */ - static QVariant parseNumber(const QString &json, int &index) { - eatWhitespace(json, index); - - int lastIndex = lastIndexOfNumber(json, index); - int charLength = (lastIndex - index) + 1; - QString numberStr; - - numberStr = json.mid(index, charLength); - - index = lastIndex + 1; - bool ok; - - if (numberStr.contains('.')) { - return QVariant(numberStr.toDouble(NULL)); - } else if (numberStr.startsWith('-')) { - int i = numberStr.toInt(&ok); - if (!ok) { - qlonglong ll = numberStr.toLongLong(&ok); - return ok ? ll : QVariant(numberStr); - } - return i; - } else { - uint u = numberStr.toUInt(&ok); - if (!ok) { - qulonglong ull = numberStr.toULongLong(&ok); - return ok ? ull : QVariant(numberStr); - } - return u; - } - } - - /** - * lastIndexOfNumber - */ - static int lastIndexOfNumber(const QString &json, int index) { - int lastIndex; - - for(lastIndex = index; lastIndex < json.size(); lastIndex++) { - if (QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1) { - break; - } - } - - return lastIndex -1; - } - - /** - * eatWhitespace - */ - static void eatWhitespace(const QString &json, int &index) { - for(; index < json.size(); index++) { - if (QString(" \t\n\r").indexOf(json[index]) == -1) { - break; - } - } - } - - /** - * lookAhead - */ - static int lookAhead(const QString &json, int index) { - int saveIndex = index; - return nextToken(json, saveIndex); - } - - /** - * nextToken - */ - static int nextToken(const QString &json, int &index) { - eatWhitespace(json, index); - - if (index == json.size()) { - return JsonTokenNone; - } - - QChar c = json[index]; - index++; - switch(c.toLatin1()) { - case '{': return JsonTokenCurlyOpen; - case '}': return JsonTokenCurlyClose; - case '[': return JsonTokenSquaredOpen; - case ']': return JsonTokenSquaredClose; - case ',': return JsonTokenComma; - case '"': return JsonTokenString; - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case '-': return JsonTokenNumber; - case ':': return JsonTokenColon; - } - index--; // ^ WTF? - - int remainingLength = json.size() - index; - - // True - if (remainingLength >= 4) { - if (json[index] == 't' && json[index + 1] == 'r' && - json[index + 2] == 'u' && json[index + 3] == 'e') { - index += 4; - return JsonTokenTrue; - } - } - - // False - if (remainingLength >= 5) { - if (json[index] == 'f' && json[index + 1] == 'a' && - json[index + 2] == 'l' && json[index + 3] == 's' && - json[index + 4] == 'e') { - index += 5; - return JsonTokenFalse; - } - } - - // Null - if (remainingLength >= 4) { - if (json[index] == 'n' && json[index + 1] == 'u' && - json[index + 2] == 'l' && json[index + 3] == 'l') { - index += 4; - return JsonTokenNull; - } - } - - return JsonTokenNone; - } - - void setDateTimeFormat(const QString &format) { - dateTimeFormat = format; - } - - void setDateFormat(const QString &format) { - dateFormat = format; - } - - QString getDateTimeFormat() { - return dateTimeFormat; - } - - QString getDateFormat() { - return dateFormat; - } - -} //end namespace diff --git a/qtjson/json.h b/qtjson/json.h deleted file mode 100644 index 5482b6a..0000000 --- a/qtjson/json.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - * QtJson - A simple class for parsing JSON data into a QVariant hierarchies and vice-versa. - * Copyright (C) 2011 Eeli Reilin - * - * 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 3 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file json.h - */ - -#ifndef JSON_H -#define JSON_H - -#include <QVariant> -#include <QString> - - -/** - * \namespace QtJson - * \brief A JSON data parser - * - * Json parses a JSON data into a QVariant hierarchy. - */ -namespace QtJson { - typedef QVariantMap JsonObject; - typedef QVariantList JsonArray; - - /** - * Parse a JSON string - * - * \param json The JSON data - */ - QVariant parse(const QString &json); - - /** - * Parse a JSON string - * - * \param json The JSON data - * \param success The success of the parsing - */ - QVariant parse(const QString &json, bool &success); - - /** - * This method generates a textual JSON representation - * - * \param data The JSON data generated by the parser. - * - * \return QByteArray Textual JSON representation in UTF-8 - */ - QByteArray serialize(const QVariant &data); - - /** - * This method generates a textual JSON representation - * - * \param data The JSON data generated by the parser. - * \param success The success of the serialization - * - * \return QByteArray Textual JSON representation in UTF-8 - */ - QByteArray serialize(const QVariant &data, bool &success); - - /** - * This method generates a textual JSON representation - * - * \param data The JSON data generated by the parser. - * - * \return QString Textual JSON representation - */ - QString serializeStr(const QVariant &data); - - /** - * This method generates a textual JSON representation - * - * \param data The JSON data generated by the parser. - * \param success The success of the serialization - * - * \return QString Textual JSON representation - */ - QString serializeStr(const QVariant &data, bool &success); - - /** - * This method sets date(time) format to be used for QDateTime::toString - * If QString is empty, Qt::TextDate is used. - * - * \param format The JSON data generated by the parser. - */ - void setDateTimeFormat(const QString& format); - void setDateFormat(const QString& format); - - /** - * This method gets date(time) format to be used for QDateTime::toString - * If QString is empty, Qt::TextDate is used. - */ - QString getDateTimeFormat(); - QString getDateFormat(); - - QString sanitizeString(QString str); -} - -#endif //JSON_H diff --git a/rpm/sapd.yaml b/rpm/sapd.yaml index 212f735..59681f6 100644 --- a/rpm/sapd.yaml +++ b/rpm/sapd.yaml @@ -1,6 +1,6 @@ Name: sapd Summary: Accesory protocol daemon -Version: 0.1.0 +Version: 0.1.1 Release: 1 Group: Communications/Bluetooth URL: https://gitorious.org/javispedro-jolla-misc/sapd/ @@ -18,6 +18,8 @@ PkgConfigBR: - Qt5DBus - Qt5Bluetooth - openssl + - dbus-1 + - timed-qt5 PkgBR: # Workaround current sailfish qt5connectivity packaging bug diff --git a/sapbtlistener.cc b/sapbtlistener.cc index 76ecb94..40c7ac6 100644 --- a/sapbtlistener.cc +++ b/sapbtlistener.cc @@ -1,10 +1,5 @@ #include <QtCore/QDebug> -#ifdef MANUAL_SDP -#include <bluetooth/sdp.h> -#include <bluetooth/sdp_lib.h> -#endif - #include "saprotocol.h" #include "sapbtlistener.h" #include "sapbtpeer.h" @@ -13,67 +8,6 @@ #include "hfpag.h" #endif -namespace -{ - -#ifdef MANUAL_SDP -// Basically, QBluetoothServiceInfo is not yet compatible with Bluez5, -// so we hack around it by doing our own SDP connection. -void add_sdp_record(sdp_session_t *session, const QBluetoothUuid &btuuid, quint16 port) -{ - sdp_list_t *svclass_id, *apseq, *root; - uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid; - sdp_list_t *aproto, *proto[2]; - sdp_data_t *chan; - - sdp_record_t *record = sdp_record_alloc(); - - sdp_set_info_attr(record, "SAP", "gearbttest", "Samsung Accessory Protocol"); - - sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(0, &root_uuid); - sdp_set_browse_groups(record, root); - - QByteArray uuid = btuuid.toRfc4122(); - sdp_uuid128_create(&svclass_uuid, uuid.constData()); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_set_service_classes(record, svclass_id); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - chan = sdp_data_alloc(SDP_UINT8, &port); - proto[1] = sdp_list_append(proto[1], chan); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(record, aproto); - - if (sdp_record_register(session, record, 0) < 0) { - qWarning() << "sdp_record_register failed"; - } -} - -void add_sdp_records(quint16 port) -{ - bdaddr_t addr_any = {{0, 0, 0, 0, 0, 0}}; - bdaddr_t addr_local = {{0, 0, 0, 0xFF, 0xFF, 0xFF}}; - - sdp_session_t *sess = sdp_connect(&addr_any, &addr_local, 0); - if (!sess) { - qWarning() << "sdp_connect failed"; - return; - } - - add_sdp_record(sess, SAProtocol::dataServiceUuid, port); -} -#endif - -} - SAPBTListener::SAPBTListener(QObject *parent) : QObject(parent), _server(0) { @@ -87,14 +21,11 @@ SAPBTListener::~SAPBTListener() void SAPBTListener::start() { if (_server) { + qWarning() << "Already started"; return; } -#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) _server = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this); -#else - _server = new QRfcommServer(this); -#endif connect(_server, SIGNAL(newConnection()), this, SLOT(acceptConnection())); if (!_server->listen(QBluetoothAddress(), 0)) { qWarning() << "Failed to start Bluetooth listener socket"; @@ -104,12 +35,6 @@ void SAPBTListener::start() quint8 serverPort = _server->serverPort(); -#if MANUAL_SDP - // Basically, QBluetoothServiceInfo is not yet compatible with Bluez5, - // so we hack around it by doing our own SDP connection. - add_sdp_records(serverPort); -#else - /* Service Name: SAP Service RecHandle: 0x1000b @@ -160,7 +85,6 @@ void SAPBTListener::start() if (!_service.registerService()) { qWarning() << "Failed to register the SAP service"; } -#endif } void SAPBTListener::stop() @@ -179,19 +103,20 @@ void SAPBTListener::stop() void SAPBTListener::nudge(const QBluetoothAddress &address) { -#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) QBluetoothSocket *socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this); -#else - QBluetoothSocket *socket = new QBluetoothSocket(QBluetoothSocket::RfcommSocket, this); -#endif - connect(socket, SIGNAL(connected()), socket, SLOT(deleteLater())); + connect(socket, SIGNAL(connected()), this, SLOT(handleNudgeConnected())); connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(handleNudgeError(QBluetoothSocket::SocketError))); qDebug() << "Nudging" << address.toString(); - socket->connectToService(address, 2); //SAPProtocol::nudgeServiceUuid); +#if SAILFISH + // For some reason, using UUIDs here fails on SailfishOS + socket->connectToService(address, 1); +#else + socket->connectToService(address, SAProtocol::nudgeServiceUuid); +#endif #if DESKTOP // At the same time set up and HFP connection to the watch. @@ -214,6 +139,13 @@ void SAPBTListener::acceptConnection() new SAPBTPeer(SAProtocol::ClientRole, socket, this); } +void SAPBTListener::handleNudgeConnected() +{ + QBluetoothSocket *socket = static_cast<QBluetoothSocket*>(sender()); + qDebug() << "Nudge connected:" << socket->peerAddress().toString(); + new SAPBTPeer(SAProtocol::ClientRole, socket, this); +} + void SAPBTListener::handleNudgeError(QBluetoothSocket::SocketError error) { QBluetoothSocket *socket = static_cast<QBluetoothSocket*>(sender()); diff --git a/sapbtlistener.h b/sapbtlistener.h index 3fe821d..398eb1a 100644 --- a/sapbtlistener.h +++ b/sapbtlistener.h @@ -2,19 +2,8 @@ #define SAPBTLISTENER_H #include <QtCore/QObject> - -#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) #include <QtBluetooth/QBluetoothServer> #include <QtBluetooth/QBluetoothServiceInfo> -#elif QT_VERSION >= QT_VERSION_CHECK(5,0,0) -#include <QtBluetooth/QRfcommServer> -#include <QtBluetooth/QBluetoothServiceInfo> -QT_USE_NAMESPACE_BLUETOOTH -#else -#include <QtConnectivity/QRfcommServer> -#include <QtConnectivity/QBluetoothServiceInfo> -QTM_USE_NAMESPACE -#endif class SAPBTListener : public QObject { @@ -35,15 +24,11 @@ private: private slots: void acceptConnection(); + void handleNudgeConnected(); void handleNudgeError(QBluetoothSocket::SocketError error); - private: -#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) QBluetoothServer *_server; -#else - QRfcommServer *_server; -#endif QBluetoothServiceInfo _service; }; diff --git a/sapbtpeer.cc b/sapbtpeer.cc index 9fc56af..e026a4c 100644 --- a/sapbtpeer.cc +++ b/sapbtpeer.cc @@ -1,11 +1,11 @@ -#include <QtEndian> +#include <QtCore/QtEndian> #include "saprotocol.h" #include "wmspeer.h" #include "crc16.h" #include "sapsocket.h" #include "sapbtpeer.h" -#define PROTO_DEBUG 0 +#define PROTO_DEBUG 1 SAPBTPeer::SAPBTPeer(SAProtocol::Role role, QBluetoothSocket *socket, QObject *parent) : SAPPeer(role, socket->localAddress().toString(), socket->peerAddress().toString(), parent), diff --git a/sapbtpeer.h b/sapbtpeer.h index da77f22..66f8cbb 100644 --- a/sapbtpeer.h +++ b/sapbtpeer.h @@ -2,16 +2,7 @@ #define SAPBTPEER_H #include <QtCore/QObject> - -#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) -#include <QtBluetooth/QBluetoothSocket> -#elif QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include <QtBluetooth/QBluetoothSocket> -QT_USE_NAMESPACE_BLUETOOTH -#else -#include <QtConnectivity/QBluetoothSocket> -QTM_USE_NAMESPACE -#endif #include "sappeer.h" @@ -1,33 +1,24 @@ TARGET = sapd TEMPLATE = app -QT += core gui dbus -CONFIG += console +QT += core gui dbus bluetooth +CONFIG += console c++11 CONFIG += link_pkgconfig PKGCONFIG += openssl exists(/usr/lib/libsailfishapp.so) { - # Building for Jolla Sailfish, Qt5, Bluez4 - QT += bluetooth + # Building for Jolla Sailfish, Qt5, Bluez4(?) DEFINES += SAILFISH - PKGCONFIG += dbus-1 - CONFIG += c++11 - SOURCES += libwatchfish/notificationmonitor.cpp libwatchfish/notification.cpp - HEADERS += libwatchfish/notificationmonitor.h libwatchfish/notification.h -} else:greaterThan(QT_MAJOR_VERSION, 4) { + PKGCONFIG += dbus-1 timed-qt5 + SOURCES += libwatchfish/notificationmonitor.cpp libwatchfish/notification.cpp \ + libwatchfish/walltimemonitor.cpp + HEADERS += libwatchfish/notificationmonitor.h libwatchfish/notificationmonitor_p.h libwatchfish/notification.h \ + libwatchfish/walltimemonitor.h libwatchfish/walltimemonitor_p.h +} else { # Building for desktop, Qt5, Bluez5 - QT += bluetooth DEFINES += DESKTOP SOURCES += hfpag.cc HEADERS += hfpag.h -} else { - # Building for desktop, Qt4, Bluez5 - CONFIG += mobility - MOBILITY += connectivity - DEFINES += DESKTOP MANUAL_SDP - SOURCES += hfpag.cc - HEADERS += hfpag.h - PKGCONFIG += bluez } target.path = /usr/bin @@ -56,7 +47,6 @@ SOURCES += main.cc \ sapsocket.cc \ sappeer.cc \ capabilityagent.cc \ - qtjson/json.cc \ sapmanager.cc \ sapserviceinfo.cc \ sapchannelinfo.cc \ @@ -66,7 +56,9 @@ SOURCES += main.cc \ hostmanageragent.cc \ hostmanagerconn.cc \ notificationagent.cc \ - notificationconn.cc + notificationconn.cc \ + webproxyagent.cc \ + webproxyconn.cc HEADERS += \ sapbtlistener.h \ @@ -80,7 +72,6 @@ HEADERS += \ sapagent.h \ sappeer.h \ capabilityagent.h \ - qtjson/json.h \ sapmanager.h \ sapserviceinfo.h \ sapchannelinfo.h \ @@ -91,7 +82,9 @@ HEADERS += \ hostmanagerconn.h \ notificationagent.h \ notificationconn.h \ - endianhelpers.h + endianhelpers.h \ + webproxyagent.h \ + webproxyconn.h OTHER_FILES += \ rpm/sapd.yaml \ @@ -132,7 +132,7 @@ void SAPPeer::acceptServiceConnection(SAPConnectionRequest *connReq, int statusC resp.sessions.push_back(socket->sessionId()); } - qDebug() << "acceptServiceConection" << statusCode; + qDebug() << "Accepting service conection with status" << statusCode; writeToSession(SAProtocol::defaultSessionId, SAProtocol::packServiceConnectionResponseFrame(resp)); @@ -227,7 +227,7 @@ void SAPPeer::handleDefaultSessionMessage(const QByteArray &message) foreach (const SAProtocol::ServiceConnectionRequestSession &s, req.sessions) { if (_sessions.contains(s.sessionId)) { - qWarning() << "Requested session is already in use"; + qWarning() << "Requested session" << s.sessionId << "is already active"; ok = false; } } @@ -256,73 +256,14 @@ void SAPPeer::handleDefaultSessionMessage(const QByteArray &message) // TODO set socket QoS parameters conn->setSocket(s.channelId, socket); + qDebug() << " opening channel" << s.sessionId << s.channelId << "payload=" << s.payloadType; + _sessions.insert(s.sessionId, socket); } SAPConnectionRequest *connReq = new SAPConnectionRequest(conn, req.initiatorId, req.acceptorId); agent->requestConnection(connReq); -#if 0 - resp.messageType = SAProtocol::ServiceConnectionResponse; - resp.acceptorId = req.acceptorId; - resp.initiatorId = req.initiatorId; - resp.profile = req.profile; - resp.statusCode = 0; - - QList<SAPSocket*> created_sockets; - - SAPAgent *agent = _profiles.value(req.profile, 0); - if (agent) { - foreach (const SAProtocol::ServiceConnectionRequestSession &s, req.sessions) { - if (_openSessions.contains(s.sessionId)) { - // Session ID already used. - resp.statusCode = 1; - break; - } - - SAPSocket *socket = new SAPSocket(this, s.sessionId, this); - - qDebug() << "Requesting connection to" << req.profile << s.channelId; - - if (!agent->acceptConnection(req.profile, s.channelId, socket)) { - resp.statusCode = 2; - break; - } - - // Save for later. We can't emit the connected signal right now - // because data might be written to the socket even before - // we send the response. - created_sockets.append(socket); - - resp.sessions.append(s.sessionId); - } - - if (resp.statusCode != 0) { - resp.sessions.clear(); // TODO Is this correct? Who knows! - } - } - - qDebug() << "Service connection request statusCode=" << resp.statusCode; - - writeToSession(SAProtocol::defaultSessionId, - SAProtocol::packServiceConnectionResponseFrame(resp)); - - if (resp.statusCode == 0) { - foreach (SAPSocket *socket, created_sockets) { - const int session = socket->sessionId(); - _openSessions.insert(session); - qDebug() << "Session" << session << "now live"; - - emit socket->connected(); - } - } else { - foreach (SAPSocket *socket, created_sockets) { - emit socket->disconnected(); - socket->deleteLater(); - } - } -#endif - break; } case SAProtocol::ServiceConnectionResponse: { diff --git a/saprotocol.h b/saprotocol.h index 3f38e05..a9f5ac0 100644 --- a/saprotocol.h +++ b/saprotocol.h @@ -2,16 +2,7 @@ #define SAPROTOCOL_H #include <QtCore/QObject> - -#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) -#include <QtBluetooth/QBluetoothUuid> -#elif QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include <QtBluetooth/QBluetoothUuid> -QT_USE_NAMESPACE_BLUETOOTH -#else -#include <QtConnectivity/QBluetoothUuid> -QTM_USE_NAMESPACE -#endif class SAProtocol { diff --git a/webproxyagent.cc b/webproxyagent.cc new file mode 100644 index 0000000..186912f --- /dev/null +++ b/webproxyagent.cc @@ -0,0 +1,57 @@ +#include "sapsocket.h" +#include "sapconnectionrequest.h" +#include "sapserviceinfo.h" +#include "sapchannelinfo.h" +#include "webproxyconn.h" +#include "webproxyagent.h" + +static WebProxyAgent *agent = 0; +static const QLatin1String webproxy_profile("/system/webproxy"); + +WebProxyAgent::WebProxyAgent(QObject *parent) + : QObject(parent), _peer(0), _socket(0) +{ +} + +WebProxyAgent* WebProxyAgent::instance() +{ + if (!agent) { + agent = new WebProxyAgent; + } + return agent; +} + +void WebProxyAgent::peerFound(SAPPeer *peer) +{ + qDebug() << "WebProxy peer found" << peer->peerName(); +} + +void WebProxyAgent::requestConnection(SAPConnectionRequest *request) +{ + qDebug() << "WebProxy request connection from" << request->peer()->peerName(); + SAPConnection *conn = request->connection(); + new WebProxyConn(conn, this); + + request->accept(); +} + +void WebProxyAgent::registerServices(SAPManager *manager) +{ + SAPServiceInfo service; + SAPChannelInfo channel; + + service.setProfile(webproxy_profile); + service.setFriendlyName("WebProxy"); + service.setRole(SAPServiceInfo::RoleProvider); + service.setVersion(2, 0); + service.setConnectionTimeout(0); + + channel.setChannelId(502); + channel.setPayloadType(SAPChannelInfo::PayloadBinary); + channel.setQoSType(SAPChannelInfo::QoSRestricted); + channel.setQoSDataRate(SAPChannelInfo::QoSDataRateLow); + channel.setQoSPriority(SAPChannelInfo::QoSPriorityHigh); + service.addChannel(channel); + + manager->registerServiceAgent(service, instance()); +} diff --git a/webproxyagent.h b/webproxyagent.h new file mode 100644 index 0000000..e3e7329 --- /dev/null +++ b/webproxyagent.h @@ -0,0 +1,27 @@ +#ifndef WEBPROXYAGENT_H +#define WEBPROXYAGENT_H + +#include <QtCore/QObject> +#include "sappeer.h" +#include "sapmanager.h" +#include "sapagent.h" + +class WebProxyAgent : public QObject, public SAPAgent +{ + Q_OBJECT + + explicit WebProxyAgent(QObject *parent = 0); + +public: + static WebProxyAgent * instance(); + static void registerServices(SAPManager *manager); + + void peerFound(SAPPeer *peer); + void requestConnection(SAPConnectionRequest *request); + +private: + SAPPeer *_peer; + SAPSocket *_socket; +}; + +#endif // WEBPROXYAGENT_H diff --git a/webproxyconn.cc b/webproxyconn.cc new file mode 100644 index 0000000..a679803 --- /dev/null +++ b/webproxyconn.cc @@ -0,0 +1,25 @@ +#include <QtCore/QDebug> + +#include "sappeer.h" +#include "webproxyconn.h" + +WebProxyConn::WebProxyConn(SAPConnection *conn, QObject *parent) + : QObject(parent), _conn(conn), _socket(conn->sockets().first()) +{ + connect(_conn, SIGNAL(disconnected()), SLOT(deleteLater())); + Q_ASSERT(_socket); + connect(_socket, SIGNAL(connected()), SLOT(handleConnected())); + connect(_socket, SIGNAL(messageReceived()), SLOT(handleMessageReceived())); +} + +void WebProxyConn::handleConnected() +{ + qDebug() << "WebProxy socket now connected!"; +} + +void WebProxyConn::handleMessageReceived() +{ + QByteArray data = _socket->receive(); + + qDebug() << data << data.toHex(); +} diff --git a/webproxyconn.h b/webproxyconn.h new file mode 100644 index 0000000..3a05ad3 --- /dev/null +++ b/webproxyconn.h @@ -0,0 +1,26 @@ +#ifndef WEBPROXYCONN_H +#define WEBPROXYCONN_H + +#include <QtCore/QObject> +#include "sapconnection.h" +#include "sapsocket.h" + +class WebProxyConn : public QObject +{ + Q_OBJECT + +public: + WebProxyConn(SAPConnection *conn, QObject *parent = 0); + +private: + +private slots: + void handleConnected(); + void handleMessageReceived(); + +private: + SAPConnection *_conn; + SAPSocket *_socket; +}; + +#endif // WEBPROXYCONN_H |