| /**************************************************************************** |
| ** |
| ** Copyright (C) 2013 BlackBerry Limited. All rights reserved. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtCore module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| |
| #include "qppsobject_p.h" |
| |
| #include "qppsobjectprivate_p.h" |
| #include "qppsattribute_p.h" |
| #include "qppsattributeprivate_p.h" |
| #include "qcore_unix_p.h" |
| |
| #include <QObject> |
| #include <QSocketNotifier> |
| |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <confname.h> |
| |
| #include <sys/pps.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| static inline void safeAssign(bool *pointer, bool value) |
| { |
| if (pointer) |
| *pointer = value; |
| } |
| |
| class QPpsMaxSize |
| { |
| public: |
| QPpsMaxSize() |
| { |
| int fd = qt_safe_open("/pps/.all", O_RDONLY); |
| if (fd == -1) { |
| qWarning("qppsobject.cpp: qt_safe_open failed"); |
| value = -1; |
| } |
| |
| // This tells us the maximum transfer size across PPS |
| value = ::fpathconf(fd, _PC_REC_MAX_XFER_SIZE); |
| |
| qt_safe_close(fd); |
| } |
| |
| int value; |
| }; |
| |
| Q_GLOBAL_STATIC(QPpsMaxSize, ppsMaxSize) |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // QPpsObjectPrivate |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| QPpsObjectPrivate::QPpsObjectPrivate(const QString &path) |
| : notifier(0), |
| path(path), |
| error(EOK), |
| fd(-1), |
| readyReadEnabled(true) |
| { |
| } |
| |
| QPpsAttributeMap QPpsObjectPrivate::decode(const QByteArray &rawData, bool *ok) |
| { |
| QPpsAttributeMap attributeMap; |
| pps_decoder_t decoder; |
| |
| QByteArray mutableData(rawData); |
| pps_decoder_error_t error = pps_decoder_initialize(&decoder, mutableData.data()); |
| if (error == PPS_DECODER_OK) { |
| // no need to check ok in this case |
| attributeMap = decodeObject(&decoder, ok); |
| } else { |
| qWarning("QPpsObjectPrivate::decode: pps_decoder_initialize failed"); |
| *ok = false; |
| } |
| |
| pps_decoder_cleanup(&decoder); |
| return attributeMap; |
| } |
| |
| QVariantMap QPpsObjectPrivate::variantMapFromPpsAttributeMap(const QPpsAttributeMap &data) |
| { |
| QVariantMap variantMap; |
| |
| for (QPpsAttributeMap::const_iterator it = data.constBegin(); it != data.constEnd(); ++it) { |
| QVariant variant = variantFromPpsAttribute(it.value()); |
| if (!variant.isValid()) |
| return QVariantMap(); |
| variantMap[it.key()] = variant; |
| } |
| |
| return variantMap; |
| } |
| |
| QPpsAttribute::Flags QPpsObjectPrivate::readFlags(pps_decoder_t *decoder) |
| { |
| int rawFlags = pps_decoder_flags(decoder, 0); |
| |
| QPpsAttribute::Flags attributeFlags; |
| |
| if (rawFlags & PPS_INCOMPLETE) |
| attributeFlags |= QPpsAttribute::Incomplete; |
| if (rawFlags & PPS_DELETED) |
| attributeFlags |= QPpsAttribute::Deleted; |
| if (rawFlags & PPS_CREATED) |
| attributeFlags |= QPpsAttribute::Created; |
| if (rawFlags & PPS_TRUNCATED) |
| attributeFlags |= QPpsAttribute::Truncated; |
| if (rawFlags & PPS_PURGED) |
| attributeFlags |= QPpsAttribute::Purged; |
| |
| return attributeFlags; |
| } |
| |
| QPpsAttribute QPpsObjectPrivate::decodeString(pps_decoder_t *decoder) |
| { |
| const char *value = 0; |
| pps_decoder_error_t error = pps_decoder_get_string(decoder, 0, &value); |
| |
| if (error != PPS_DECODER_OK) { |
| qWarning("QPpsObjectPrivate::decodeString: PPS_DECODER_GET_STRING failed"); |
| return QPpsAttribute(); |
| } |
| |
| QPpsAttribute::Flags flags = readFlags(decoder); |
| return QPpsAttributePrivate::createPpsAttribute(QString::fromUtf8(value), flags); |
| } |
| |
| QPpsAttribute QPpsObjectPrivate::decodeNumber(pps_decoder_t *decoder) |
| { |
| // In order to support more number types, we have to do something stupid because the PPS |
| // library won't let us work any other way. Basically, we have to probe the encoded type in |
| // order to try to get exactly what we want. |
| int64_t llValue; |
| double dValue; |
| int iValue; |
| QPpsAttribute::Flags flags; |
| |
| if (pps_decoder_is_integer(decoder, 0)) { |
| pps_decoder_error_t error = pps_decoder_get_int(decoder, 0, &iValue); |
| switch (error) { |
| case PPS_DECODER_OK: |
| flags = readFlags(decoder); |
| return QPpsAttributePrivate::createPpsAttribute(iValue, flags); |
| case PPS_DECODER_CONVERSION_FAILED: |
| error = pps_decoder_get_int64(decoder, 0, &llValue); |
| if (error != PPS_DECODER_OK) { |
| qWarning("QPpsObjectPrivate::decodeNumber: failed to decode integer"); |
| return QPpsAttribute(); |
| } |
| flags = readFlags(decoder); |
| return QPpsAttributePrivate::createPpsAttribute(static_cast<long long>(llValue), flags); |
| default: |
| qWarning("QPpsObjectPrivate::decodeNumber: pps_decoder_get_int failed"); |
| return QPpsAttribute(); |
| } |
| } else { |
| pps_decoder_error_t error = pps_decoder_get_double(decoder, 0, &dValue); |
| if (error != PPS_DECODER_OK) { |
| qWarning("QPpsObjectPrivate::decodeNumber: pps_decoder_get_double failed"); |
| return QPpsAttribute(); |
| } |
| flags = readFlags(decoder); |
| return QPpsAttributePrivate::createPpsAttribute(dValue, flags); |
| } |
| } |
| |
| QPpsAttribute QPpsObjectPrivate::decodeBool(pps_decoder_t *decoder) |
| { |
| bool value; |
| pps_decoder_error_t error = pps_decoder_get_bool(decoder, 0, &value); |
| |
| if (error != PPS_DECODER_OK) { |
| qWarning("QPpsObjectPrivate::decodeBool: pps_decoder_get_bool failed"); |
| return QPpsAttribute(); |
| } |
| |
| QPpsAttribute::Flags flags = readFlags(decoder); |
| return QPpsAttributePrivate::createPpsAttribute(value, flags); |
| } |
| |
| template<typename T> |
| QPpsAttribute QPpsObjectPrivate::decodeNestedData(T (*decodeFunction)(pps_decoder_t *, bool *), |
| pps_decoder_t *decoder) |
| { |
| // We must read the flags before we push into the object, |
| // otherwise we'll get the flags for the first element in the object. |
| QPpsAttribute::Flags flags = readFlags(decoder); |
| |
| if (!decoderPush(decoder)) |
| return QPpsAttribute(); |
| |
| bool ok = false; |
| |
| T attributeContainer = decodeFunction(decoder, &ok); |
| |
| if (!ok) |
| return QPpsAttribute(); |
| |
| QPpsAttribute returnVal = QPpsAttributePrivate::createPpsAttribute(attributeContainer, flags); |
| |
| if (!decoderPop(decoder)) |
| return QPpsAttribute(); |
| |
| return returnVal; |
| } |
| |
| QPpsAttribute QPpsObjectPrivate::decodeData(pps_decoder_t *decoder) |
| { |
| pps_node_type_t nodeType = pps_decoder_type(decoder, 0); |
| switch (nodeType) { |
| case PPS_TYPE_BOOL: |
| return decodeBool(decoder); |
| case PPS_TYPE_NUMBER: |
| return decodeNumber(decoder); |
| case PPS_TYPE_STRING: |
| return decodeString(decoder); |
| case PPS_TYPE_ARRAY: |
| return decodeNestedData(&QPpsObjectPrivate::decodeArray, decoder); |
| case PPS_TYPE_OBJECT: |
| return decodeNestedData(&QPpsObjectPrivate::decodeObject, decoder); |
| case PPS_TYPE_DELETED: { |
| // This should create an attribute with the flags set to PpsAttribute::Deleted. |
| // However, we need to create a valid QPpsAttribute while doing so. To do this, |
| // I'll create an empty map as a sentinel. Note that the readFlags() call with produce |
| // the correct set of flags. While I suspect that there will never be any other flags |
| // set in conjunction with this one, I'd rather not be surprised later. |
| QPpsAttributeMap emptyMap; |
| QPpsAttribute::Flags flags = readFlags(decoder); |
| QPpsAttribute returnVal = QPpsAttributePrivate::createPpsAttribute(emptyMap, flags); |
| return returnVal; |
| } |
| case PPS_TYPE_NULL: |
| case PPS_TYPE_NONE: |
| case PPS_TYPE_UNKNOWN: |
| default: |
| qWarning("QPpsObjectPrivate::decodeData: invalid pps_node_type"); |
| return QPpsAttribute(); |
| } |
| } |
| |
| QPpsAttributeList QPpsObjectPrivate::decodeArray(pps_decoder_t *decoder, bool *ok) |
| { |
| QPpsAttributeList list; |
| |
| int length = pps_decoder_length(decoder); |
| for (int i = 0; i < length; ++i) { |
| // Force movement to a specific index. |
| pps_decoder_error_t error = pps_decoder_goto_index(decoder, i); |
| if (error != PPS_DECODER_OK) { |
| qWarning("QPpsObjectPrivate::decodeArray: pps_decoder_goto_index failed"); |
| *ok = false; |
| return QPpsAttributeList(); |
| } |
| |
| QPpsAttribute ppsAttribute = decodeData(decoder); |
| if (!ppsAttribute.isValid()) { |
| *ok = false; |
| return QPpsAttributeList(); |
| } |
| |
| list << ppsAttribute; |
| } |
| |
| *ok = true; |
| return list; |
| } |
| |
| QPpsAttributeMap QPpsObjectPrivate::decodeObject(pps_decoder_t *decoder, bool *ok) |
| { |
| QPpsAttributeMap map; |
| |
| int length = pps_decoder_length(decoder); |
| for (int i = 0; i < length; ++i) { |
| // Force movement to a specific index. |
| pps_decoder_error_t error = pps_decoder_goto_index(decoder, i); |
| if (error != PPS_DECODER_OK) { |
| qWarning("QPpsObjectPrivate::decodeObject: pps_decoder_goto_index failed"); |
| *ok = false; |
| return QPpsAttributeMap(); |
| } |
| QString name = QString::fromUtf8(pps_decoder_name(decoder)); |
| QPpsAttribute ppsAttribute = decodeData(decoder); |
| if (!ppsAttribute.isValid()) { |
| *ok = false; |
| return QPpsAttributeMap(); |
| } |
| map[name] = ppsAttribute; |
| } |
| |
| *ok = true; |
| return map; |
| } |
| |
| QVariant QPpsObjectPrivate::variantFromPpsAttribute(const QPpsAttribute &attribute) |
| { |
| switch (attribute.type()) { |
| case QPpsAttribute::Number: |
| switch (attribute.toVariant().type()) { |
| case QVariant::Int: |
| return attribute.toInt(); |
| case QVariant::LongLong: |
| return attribute.toLongLong(); |
| default: |
| return attribute.toDouble(); |
| } |
| break; |
| case QPpsAttribute::Bool: |
| return attribute.toBool(); |
| case QPpsAttribute::String: |
| return attribute.toString(); |
| case QPpsAttribute::Array: { |
| QVariantList variantList; |
| const auto attrs = attribute.toList(); |
| for (const QPpsAttribute &attr : attrs) { |
| QVariant variant = variantFromPpsAttribute(attr); |
| if (!variant.isValid()) |
| return QVariantList(); |
| variantList << variant; |
| } |
| return variantList; |
| } |
| case QPpsAttribute::Object: |
| return variantMapFromPpsAttributeMap(attribute.toMap()); |
| case QPpsAttribute::None: |
| default: |
| qWarning("QPpsObjectPrivate::variantFromPpsAttribute: invalid attribute parameter"); |
| return QVariant(); |
| } |
| } |
| |
| QByteArray QPpsObjectPrivate::encode(const QVariantMap &ppsData, bool *ok) |
| { |
| pps_encoder_t encoder; |
| pps_encoder_initialize(&encoder, false); |
| |
| encodeObject(&encoder, ppsData, ok); |
| const char *rawData = 0; |
| if (*ok) { |
| // rawData points to a memory owned by encoder. |
| // The memory will be freed when pps_encoder_cleanup is called. |
| rawData = pps_encoder_buffer(&encoder); |
| if (!rawData) { |
| qWarning("QPpsObjectPrivate::encode: pps_encoder_buffer failed"); |
| *ok = false; |
| } |
| } |
| |
| pps_encoder_cleanup(&encoder); |
| return QByteArray(rawData); |
| } |
| |
| void QPpsObjectPrivate::encodeData(pps_encoder_t *encoder, const char *name, const QVariant &data, |
| bool *ok) |
| { |
| const char *errorFunction; |
| pps_encoder_error_t error = PPS_ENCODER_OK; |
| switch (data.type()) { |
| case QVariant::Bool: |
| error = pps_encoder_add_bool(encoder, name, data.toBool()); |
| errorFunction = "pps_encoder_add_bool"; |
| break; |
| // We want to support encoding uint even though libpps doesn't support it directly. |
| // We can't encode uint as an int since that will lose precision (e.g. 2^31+1 can't be |
| // encoded that way). However, we can convert uint to double without losing precision. |
| // QVariant.toDouble() conveniently takes care of the conversion for us. |
| case QVariant::UInt: |
| case QVariant::Double: |
| error = pps_encoder_add_double(encoder, name, data.toDouble()); |
| errorFunction = "pps_encoder_add_double"; |
| break; |
| case QVariant::Int: |
| error = pps_encoder_add_int(encoder, name, data.toInt()); |
| errorFunction = "pps_encoder_add_int"; |
| break; |
| case QVariant::LongLong: |
| error = pps_encoder_add_int64(encoder, name, data.toLongLong()); |
| errorFunction = "pps_encoder_add_int64"; |
| break; |
| case QVariant::String: |
| error = pps_encoder_add_string(encoder, name, data.toString().toUtf8().constData()); |
| errorFunction = "pps_encoder_add_string"; |
| break; |
| case QVariant::List: |
| error = pps_encoder_start_array(encoder, name); |
| errorFunction = "pps_encoder_start_array"; |
| if (error == PPS_ENCODER_OK) { |
| encodeArray(encoder, data.toList(), ok); |
| error = pps_encoder_end_array(encoder); |
| errorFunction = "pps_encoder_end_array"; |
| } |
| break; |
| case QVariant::Map: |
| error = pps_encoder_start_object(encoder, name); |
| errorFunction = "pps_encoder_start_object"; |
| if (error == PPS_ENCODER_OK) { |
| encodeObject(encoder, data.toMap(), ok); |
| error = pps_encoder_end_object(encoder); |
| errorFunction = "pps_encoder_end_object"; |
| } |
| break; |
| case QVariant::Invalid: |
| error = pps_encoder_add_null(encoder, name); |
| errorFunction = "pps_encoder_add_null"; |
| break; |
| default: |
| qWarning("QPpsObjectPrivate::encodeData: the type of the parameter data is invalid"); |
| *ok = false; |
| return; |
| } |
| |
| if (error != PPS_ENCODER_OK) { |
| qWarning("QPpsObjectPrivate::encodeData: %s failed", errorFunction); |
| *ok = false; |
| } else { |
| *ok = true; |
| } |
| } |
| |
| void QPpsObjectPrivate::encodeArray(pps_encoder_t *encoder, const QVariantList &data, bool *ok) |
| { |
| for (QVariantList::const_iterator it = data.constBegin(); it != data.constEnd(); ++it) { |
| encodeData(encoder, 0, *it, ok); |
| if (!(*ok)) |
| return; |
| } |
| // if the passed data is empty, nothing went wrong and ok is set to true |
| *ok = true; |
| } |
| |
| void QPpsObjectPrivate::encodeObject(pps_encoder_t *encoder, const QVariantMap &data, bool *ok) |
| { |
| for (QVariantMap::const_iterator it = data.constBegin(); it != data.constEnd(); ++it) { |
| encodeData(encoder, it.key().toUtf8().constData(), it.value(), ok); |
| if (!(*ok)) |
| return; |
| } |
| // if the passed data is empty, nothing went wrong and ok is set to true |
| *ok = true; |
| } |
| |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // QPpsObjectPrivate |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| QPpsObject::QPpsObject(const QString &path, QObject *parent) |
| : QObject(*new QPpsObjectPrivate(path), parent) |
| { |
| } |
| |
| QPpsObject::~QPpsObject() |
| { |
| // RAII - ensure file gets closed |
| if (isOpen()) |
| close(); |
| } |
| |
| int QPpsObject::error() const |
| { |
| Q_D(const QPpsObject); |
| return d->error; |
| } |
| |
| QString QPpsObject::errorString() const |
| { |
| Q_D(const QPpsObject); |
| return qt_error_string(d->error); |
| } |
| |
| bool QPpsObject::isReadyReadEnabled() const |
| { |
| Q_D(const QPpsObject); |
| |
| // query state of read ready signal |
| return d->readyReadEnabled; |
| } |
| |
| void QPpsObject::setReadyReadEnabled(bool enable) |
| { |
| Q_D(QPpsObject); |
| |
| // toggle whether socket notifier will emit a signal on read ready |
| d->readyReadEnabled = enable; |
| if (isOpen()) |
| d->notifier->setEnabled(enable); |
| } |
| |
| bool QPpsObject::isBlocking() const |
| { |
| Q_D(const QPpsObject); |
| |
| // reset last error |
| d->error = EOK; |
| |
| // abort if file not open |
| if (!isOpen()) { |
| d->error = EBADF; |
| return false; |
| } |
| |
| // query file status flags |
| int flags = fcntl(d->fd, F_GETFL); |
| if (flags == -1) { |
| d->error = errno; |
| return false; |
| } |
| // check if non-blocking flag is unset |
| return ((flags & O_NONBLOCK) != O_NONBLOCK); |
| } |
| |
| bool QPpsObject::setBlocking(bool enable) |
| { |
| Q_D(QPpsObject); |
| |
| // reset last error |
| d->error = EOK; |
| |
| // abort if file not open |
| if (!isOpen()) { |
| d->error = EBADF; |
| return false; |
| } |
| |
| // query file status flags |
| int flags = fcntl(d->fd, F_GETFL); |
| if (flags == -1) { |
| d->error = errno; |
| return false; |
| } |
| |
| // configure non-blocking flag |
| if (enable) |
| flags &= ~O_NONBLOCK; |
| else |
| flags |= O_NONBLOCK; |
| |
| // update file status flags |
| flags = fcntl(d->fd, F_SETFL, flags); |
| if (flags == -1) { |
| d->error = errno; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool QPpsObject::isOpen() const |
| { |
| Q_D(const QPpsObject); |
| return (d->fd != -1); |
| } |
| |
| bool QPpsObject::open(QPpsObject::OpenModes mode) |
| { |
| Q_D(QPpsObject); |
| |
| // reset last error |
| d->error = EOK; |
| |
| // abort if file already open |
| if (isOpen()) { |
| d->error = EBUSY; |
| return false; |
| } |
| |
| // convert pps flags to open flags |
| int oflags = 0; |
| if ((mode & QPpsObject::Publish) && (mode & QPpsObject::Subscribe)) |
| oflags |= O_RDWR; |
| else if (mode & QPpsObject::Publish) |
| oflags |= O_WRONLY; |
| else if (mode & QPpsObject::Subscribe) |
| oflags |= O_RDONLY; |
| |
| if (mode & QPpsObject::Create) |
| oflags |= O_CREAT | O_EXCL; |
| |
| if (mode & QPpsObject::DeleteContents) |
| oflags |= O_TRUNC; |
| |
| // open pps file |
| d->fd = qt_safe_open(d->path.toUtf8().data(), oflags, 0666); |
| if (d->fd == -1) { |
| d->error = errno; |
| return false; |
| } |
| // wire up socket notifier to know when reads are ready |
| d->notifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, this); |
| d->notifier->setEnabled(d->readyReadEnabled); |
| QObject::connect(d->notifier, &QSocketNotifier::activated, this, &QPpsObject::readyRead); |
| return true; |
| } |
| |
| bool QPpsObject::close() |
| { |
| Q_D(QPpsObject); |
| |
| // reset last error |
| d->error = EOK; |
| |
| // abort if file not open |
| if (!isOpen()) { |
| d->error = EBADF; |
| return false; |
| } |
| |
| // shutdown socket notifier |
| delete d->notifier; |
| d->notifier = 0; |
| |
| // close pps file |
| const int result = qt_safe_close(d->fd); |
| d->fd = -1; |
| |
| // check success of operation |
| if (result != 0) { |
| d->error = errno; |
| return false; |
| } |
| return true; |
| } |
| |
| QByteArray QPpsObject::read(bool *ok) |
| { |
| Q_D(QPpsObject); |
| |
| // reset last error |
| d->error = EOK; |
| |
| // abort if file not open |
| if (!isOpen()) { |
| d->error = EBADF; |
| safeAssign(ok, false); |
| return QByteArray(); |
| } |
| |
| const int maxSize = ppsMaxSize->value; |
| if (maxSize == -1) { |
| qWarning("QPpsObject::read: maxSize is equal to -1"); |
| safeAssign(ok, false); |
| return QByteArray(); |
| } |
| |
| QByteArray byteArray; |
| byteArray.resize(maxSize); // resize doesn't initialize the data |
| const int result = qt_safe_read(d->fd, byteArray.data(), byteArray.size()); |
| |
| if (result == -1) { |
| d->error = errno; |
| qWarning() << "QPpsObject::read failed to read pps data, error " << errorString(); |
| safeAssign(ok, false); |
| return QByteArray(); // Specifically return a default-constructed QByteArray. |
| } |
| if (result == 0) { |
| // normalize the behavior of read() when no data is ready so a pps object |
| // put in non-blocking mode via opening w/o wait (read returns 0) looks |
| // the same as a pps object put in non-blocking mode by setting O_NONBLOCK |
| // (read returns EAGAIN) |
| d->error = EAGAIN; |
| safeAssign(ok, false); |
| return QByteArray(); // Specifically return a default-constructed QByteArray. |
| } |
| // resize byte array to amount actually read |
| byteArray.resize(result); |
| safeAssign(ok, true); |
| return byteArray; |
| } |
| |
| bool QPpsObject::write(const QByteArray &byteArray) |
| { |
| Q_D(QPpsObject); |
| |
| // reset last error |
| d->error = EOK; |
| |
| // abort if file not open |
| if (!isOpen()) { |
| d->error = EBADF; |
| return false; |
| } |
| |
| // write entire byte array to pps file |
| const int result = qt_safe_write(d->fd, byteArray.data(), byteArray.size()); |
| if (result == -1) |
| d->error = errno; |
| |
| return (result == byteArray.size()); |
| } |
| |
| int QPpsObject::writeMessage(const QString &msg, const QVariantMap &dat) |
| { |
| // Treat empty msg as an encoding error |
| if (msg.isEmpty()) |
| return -1; |
| |
| bool ok; |
| QByteArray byteArray = encodeMessage(msg, dat, &ok); |
| |
| if (!ok) |
| return -1; |
| |
| ok = write(byteArray); |
| if (!ok) |
| return error(); |
| |
| return EOK; |
| } |
| |
| int QPpsObject::writeMessage(const QString &msg, const QString &id, const QVariantMap &dat) |
| { |
| // Treat empty msg or id as an encoding error |
| if (msg.isEmpty() || id.isEmpty()) |
| return -1; |
| |
| bool ok; |
| QByteArray byteArray = encodeMessage(msg, id, dat, &ok); |
| |
| if (!ok) |
| return -1; |
| |
| ok = write(byteArray); |
| if (!ok) |
| return error(); |
| |
| return EOK; |
| } |
| |
| bool QPpsObject::remove() |
| { |
| Q_D(QPpsObject); |
| |
| // reset last error |
| d->error = EOK; |
| |
| // delete pps file |
| const int result = unlink(d->path.toUtf8().data()); |
| |
| // check success of operation |
| if (result != 0) { |
| d->error = errno; |
| return false; |
| } |
| return true; |
| } |
| |
| // static |
| QVariantMap QPpsObject::decode(const QByteArray &rawData, bool *ok) |
| { |
| QPpsAttributeMap mapData = decodeWithFlags(rawData, 0, ok); |
| |
| // If *ok is false, then mapData is empty, so the resulting QVariantMap |
| // will also be empty, as desired. |
| return QPpsObjectPrivate::variantMapFromPpsAttributeMap(mapData); |
| } |
| |
| // static |
| QPpsAttributeMap QPpsObject::decodeWithFlags(const QByteArray &rawData, bool *ok) |
| { |
| return QPpsObject::decodeWithFlags(rawData, 0, ok); |
| } |
| |
| // static |
| QPpsAttributeMap QPpsObject::decodeWithFlags(const QByteArray &rawData, |
| QPpsAttribute *objectAttribute, bool *ok) |
| { |
| safeAssign(ok, true); |
| |
| bool success = false; |
| QPpsAttributeMap mapData = QPpsObjectPrivate::decode(rawData, &success); |
| if (!success) { |
| safeAssign(ok, false); |
| return QPpsAttributeMap(); |
| } |
| |
| // The object name is the key of the first element, and the flags of that attribute |
| // give the status for the object as a whole. |
| if (!mapData.isEmpty() && objectAttribute) { |
| QString extractedName = mapData.begin().key(); |
| QPpsAttribute topmostAttribute = mapData.begin().value(); |
| QPpsAttribute::Flags topmostFlags = topmostAttribute.flags(); |
| QPpsAttribute toplevelAttribute = |
| QPpsAttributePrivate::createPpsAttribute(extractedName, topmostFlags); |
| *objectAttribute = toplevelAttribute; |
| } |
| |
| return mapData; |
| } |
| |
| |
| // static |
| QByteArray QPpsObject::encode(const QVariantMap &ppsData, bool *ok) |
| { |
| safeAssign(ok, true); |
| |
| bool success = false; |
| QByteArray byteArray = QPpsObjectPrivate::encode(ppsData, &success); |
| if (!success) { |
| safeAssign(ok, false); |
| return QByteArray(); |
| } |
| return byteArray; |
| } |
| |
| // static |
| QByteArray QPpsObject::encodeMessage(const QString &msg, const QVariantMap &dat, bool *ok) |
| { |
| safeAssign(ok, true); |
| |
| // Treat empty msg as an encoding error |
| if (msg.isEmpty()) { |
| safeAssign(ok, false); |
| return QByteArray(); |
| } |
| |
| QVariantMap ppsData; |
| ppsData[QStringLiteral("msg")] = msg; |
| ppsData[QStringLiteral("dat")] = dat; |
| |
| return QPpsObject::encode(ppsData, ok); |
| } |
| |
| // static |
| QByteArray QPpsObject::encodeMessage(const QString &msg, const QString &id, const QVariantMap &dat, |
| bool *ok) |
| { |
| safeAssign(ok, true); |
| |
| // Treat empty msg or id as an encoding error |
| if (msg.isEmpty() || id.isEmpty()) { |
| safeAssign(ok, false); |
| return QByteArray(); |
| } |
| |
| QVariantMap ppsData; |
| ppsData[QStringLiteral("msg")] = msg; |
| ppsData[QStringLiteral("id")] = id; |
| ppsData[QStringLiteral("dat")] = dat; |
| |
| return QPpsObject::encode(ppsData, ok); |
| } |
| |
| // static |
| int QPpsObject::sendMessage(const QString &path, const QString &message) |
| { |
| QPpsObject pps(path); |
| |
| bool ok = pps.open(QPpsObject::Publish); |
| if (!ok) |
| return pps.error(); |
| |
| ok = pps.write(message.toLocal8Bit()); |
| if (!ok) |
| return pps.error(); |
| |
| return EOK; |
| } |
| |
| // static |
| int QPpsObject::sendMessage(const QString &path, const QVariantMap &message) |
| { |
| QPpsObject pps(path); |
| |
| bool ok = pps.open(QPpsObject::Publish); |
| if (!ok) |
| return pps.error(); |
| |
| QByteArray payload = QPpsObject::encode(message, &ok); |
| if (!ok) |
| return -1; |
| |
| ok = pps.write(payload); |
| if (!ok) |
| return pps.error(); |
| |
| return EOK; |
| } |
| |
| // static |
| int QPpsObject::sendMessage(const QString &path, const QString &msg, const QVariantMap &dat) |
| { |
| // Treat empty msg as an encoding error |
| if (msg.isEmpty()) |
| return -1; |
| |
| QPpsObject pps(path); |
| |
| bool ok = pps.open(QPpsObject::Publish); |
| if (!ok) |
| return pps.error(); |
| |
| QByteArray payload = QPpsObject::encodeMessage(msg, dat, &ok); |
| if (!ok) |
| return -1; |
| |
| ok = pps.write(payload); |
| if (!ok) |
| return pps.error(); |
| |
| return EOK; |
| } |
| |
| // static |
| int QPpsObject::sendMessage(const QString &path, const QByteArray &ppsData) |
| { |
| QPpsObject pps(path); |
| |
| bool ok = pps.open(QPpsObject::Publish); |
| if (!ok) |
| return pps.error(); |
| |
| ok = pps.write(ppsData); |
| if (!ok) |
| return pps.error(); |
| |
| return EOK; |
| } |
| |
| QT_END_NAMESPACE |