1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 
41 #include "qppsobject_p.h"
42 
43 #include "qppsobjectprivate_p.h"
44 #include "qppsattribute_p.h"
45 #include "qppsattributeprivate_p.h"
46 #include "qcore_unix_p.h"
47 
48 #include <QObject>
49 #include <QSocketNotifier>
50 
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <errno.h>
54 #include <string.h>
55 #include <confname.h>
56 
57 #include <sys/pps.h>
58 
59 QT_BEGIN_NAMESPACE
60 
61 ///////////////////////////////////////////////////////////////////////////////
safeAssign(bool * pointer,bool value)62 static inline void safeAssign(bool *pointer, bool value)
63 {
64     if (pointer)
65         *pointer = value;
66 }
67 
68 class QPpsMaxSize
69 {
70 public:
QPpsMaxSize()71     QPpsMaxSize()
72     {
73         int fd = qt_safe_open("/pps/.all", O_RDONLY);
74         if (fd == -1) {
75             qWarning("qppsobject.cpp: qt_safe_open failed");
76             value = -1;
77         }
78 
79         // This tells us the maximum transfer size across PPS
80         value = ::fpathconf(fd, _PC_REC_MAX_XFER_SIZE);
81 
82         qt_safe_close(fd);
83     }
84 
85     int value;
86 };
87 
Q_GLOBAL_STATIC(QPpsMaxSize,ppsMaxSize)88 Q_GLOBAL_STATIC(QPpsMaxSize, ppsMaxSize)
89 
90 
91 ///////////////////////////////////////////////////////////////////////////////
92 //
93 // QPpsObjectPrivate
94 //
95 ///////////////////////////////////////////////////////////////////////////////
96 
97 QPpsObjectPrivate::QPpsObjectPrivate(const QString &path)
98     : notifier(0),
99       path(path),
100       error(EOK),
101       fd(-1),
102       readyReadEnabled(true)
103 {
104 }
105 
decode(const QByteArray & rawData,bool * ok)106 QPpsAttributeMap QPpsObjectPrivate::decode(const QByteArray &rawData, bool *ok)
107 {
108     QPpsAttributeMap attributeMap;
109     pps_decoder_t decoder;
110 
111     QByteArray mutableData(rawData);
112     pps_decoder_error_t error = pps_decoder_initialize(&decoder, mutableData.data());
113     if (error == PPS_DECODER_OK) {
114         // no need to check ok in this case
115         attributeMap = decodeObject(&decoder, ok);
116     } else {
117         qWarning("QPpsObjectPrivate::decode: pps_decoder_initialize failed");
118         *ok = false;
119     }
120 
121     pps_decoder_cleanup(&decoder);
122     return attributeMap;
123 }
124 
variantMapFromPpsAttributeMap(const QPpsAttributeMap & data)125 QVariantMap QPpsObjectPrivate::variantMapFromPpsAttributeMap(const QPpsAttributeMap &data)
126 {
127     QVariantMap variantMap;
128 
129     for (QPpsAttributeMap::const_iterator it = data.constBegin(); it != data.constEnd(); ++it) {
130         QVariant variant = variantFromPpsAttribute(it.value());
131         if (!variant.isValid())
132             return QVariantMap();
133         variantMap[it.key()] = variant;
134     }
135 
136     return variantMap;
137 }
138 
readFlags(pps_decoder_t * decoder)139 QPpsAttribute::Flags QPpsObjectPrivate::readFlags(pps_decoder_t *decoder)
140 {
141     int rawFlags = pps_decoder_flags(decoder, 0);
142 
143     QPpsAttribute::Flags attributeFlags;
144 
145     if (rawFlags & PPS_INCOMPLETE)
146         attributeFlags |= QPpsAttribute::Incomplete;
147     if (rawFlags & PPS_DELETED)
148         attributeFlags |= QPpsAttribute::Deleted;
149     if (rawFlags & PPS_CREATED)
150         attributeFlags |= QPpsAttribute::Created;
151     if (rawFlags & PPS_TRUNCATED)
152         attributeFlags |= QPpsAttribute::Truncated;
153     if (rawFlags & PPS_PURGED)
154         attributeFlags |= QPpsAttribute::Purged;
155 
156     return attributeFlags;
157 }
158 
decodeString(pps_decoder_t * decoder)159 QPpsAttribute QPpsObjectPrivate::decodeString(pps_decoder_t *decoder)
160 {
161     const char *value = 0;
162     pps_decoder_error_t error = pps_decoder_get_string(decoder, 0, &value);
163 
164     if (error != PPS_DECODER_OK) {
165         qWarning("QPpsObjectPrivate::decodeString: PPS_DECODER_GET_STRING failed");
166         return QPpsAttribute();
167     }
168 
169     QPpsAttribute::Flags flags = readFlags(decoder);
170     return QPpsAttributePrivate::createPpsAttribute(QString::fromUtf8(value), flags);
171 }
172 
decodeNumber(pps_decoder_t * decoder)173 QPpsAttribute QPpsObjectPrivate::decodeNumber(pps_decoder_t *decoder)
174 {
175     // In order to support more number types, we have to do something stupid because the PPS
176     // library won't let us work any other way. Basically, we have to probe the encoded type in
177     // order to try to get exactly what we want.
178     int64_t llValue;
179     double dValue;
180     int iValue;
181     QPpsAttribute::Flags flags;
182 
183     if (pps_decoder_is_integer(decoder, 0)) {
184         pps_decoder_error_t error = pps_decoder_get_int(decoder, 0, &iValue);
185         switch (error) {
186         case PPS_DECODER_OK:
187             flags = readFlags(decoder);
188             return QPpsAttributePrivate::createPpsAttribute(iValue, flags);
189         case PPS_DECODER_CONVERSION_FAILED:
190             error = pps_decoder_get_int64(decoder, 0, &llValue);
191             if (error != PPS_DECODER_OK) {
192                 qWarning("QPpsObjectPrivate::decodeNumber: failed to decode integer");
193                 return QPpsAttribute();
194             }
195             flags = readFlags(decoder);
196             return QPpsAttributePrivate::createPpsAttribute(static_cast<long long>(llValue), flags);
197         default:
198             qWarning("QPpsObjectPrivate::decodeNumber: pps_decoder_get_int failed");
199             return QPpsAttribute();
200         }
201     } else {
202         pps_decoder_error_t error = pps_decoder_get_double(decoder, 0, &dValue);
203         if (error != PPS_DECODER_OK) {
204             qWarning("QPpsObjectPrivate::decodeNumber: pps_decoder_get_double failed");
205             return QPpsAttribute();
206         }
207         flags = readFlags(decoder);
208         return QPpsAttributePrivate::createPpsAttribute(dValue, flags);
209     }
210 }
211 
decodeBool(pps_decoder_t * decoder)212 QPpsAttribute QPpsObjectPrivate::decodeBool(pps_decoder_t *decoder)
213 {
214     bool value;
215     pps_decoder_error_t error = pps_decoder_get_bool(decoder, 0, &value);
216 
217     if (error != PPS_DECODER_OK) {
218         qWarning("QPpsObjectPrivate::decodeBool: pps_decoder_get_bool failed");
219         return QPpsAttribute();
220     }
221 
222     QPpsAttribute::Flags flags = readFlags(decoder);
223     return QPpsAttributePrivate::createPpsAttribute(value, flags);
224 }
225 
226 template<typename T>
decodeNestedData(T (* decodeFunction)(pps_decoder_t *,bool *),pps_decoder_t * decoder)227 QPpsAttribute QPpsObjectPrivate::decodeNestedData(T (*decodeFunction)(pps_decoder_t *, bool *),
228                                                   pps_decoder_t *decoder)
229 {
230     // We must read the flags before we push into the object,
231     // otherwise we'll get the flags for the first element in the object.
232     QPpsAttribute::Flags flags = readFlags(decoder);
233 
234     if (!decoderPush(decoder))
235         return QPpsAttribute();
236 
237     bool ok = false;
238 
239     T attributeContainer = decodeFunction(decoder, &ok);
240 
241     if (!ok)
242         return QPpsAttribute();
243 
244     QPpsAttribute returnVal = QPpsAttributePrivate::createPpsAttribute(attributeContainer, flags);
245 
246     if (!decoderPop(decoder))
247         return QPpsAttribute();
248 
249     return returnVal;
250 }
251 
decodeData(pps_decoder_t * decoder)252 QPpsAttribute QPpsObjectPrivate::decodeData(pps_decoder_t *decoder)
253 {
254     pps_node_type_t nodeType = pps_decoder_type(decoder, 0);
255     switch (nodeType) {
256     case PPS_TYPE_BOOL:
257         return decodeBool(decoder);
258     case PPS_TYPE_NUMBER:
259         return decodeNumber(decoder);
260     case PPS_TYPE_STRING:
261         return decodeString(decoder);
262     case PPS_TYPE_ARRAY:
263         return decodeNestedData(&QPpsObjectPrivate::decodeArray, decoder);
264     case PPS_TYPE_OBJECT:
265         return decodeNestedData(&QPpsObjectPrivate::decodeObject, decoder);
266     case PPS_TYPE_DELETED: {
267         // This should create an attribute with the flags set to PpsAttribute::Deleted.
268         // However, we need to create a valid QPpsAttribute while doing so. To do this,
269         // I'll create an empty map as a sentinel. Note that the readFlags() call with produce
270         // the correct set of flags. While I suspect that there will never be any other flags
271         // set in conjunction with this one, I'd rather not be surprised later.
272         QPpsAttributeMap emptyMap;
273         QPpsAttribute::Flags flags = readFlags(decoder);
274         QPpsAttribute returnVal = QPpsAttributePrivate::createPpsAttribute(emptyMap, flags);
275         return returnVal;
276     }
277     case PPS_TYPE_NULL:
278     case PPS_TYPE_NONE:
279     case PPS_TYPE_UNKNOWN:
280     default:
281         qWarning("QPpsObjectPrivate::decodeData: invalid pps_node_type");
282         return QPpsAttribute();
283     }
284 }
285 
decodeArray(pps_decoder_t * decoder,bool * ok)286 QPpsAttributeList QPpsObjectPrivate::decodeArray(pps_decoder_t *decoder, bool *ok)
287 {
288     QPpsAttributeList list;
289 
290     int length = pps_decoder_length(decoder);
291     for (int i = 0; i < length; ++i) {
292         // Force movement to a specific index.
293         pps_decoder_error_t error = pps_decoder_goto_index(decoder, i);
294         if (error != PPS_DECODER_OK) {
295             qWarning("QPpsObjectPrivate::decodeArray: pps_decoder_goto_index failed");
296             *ok = false;
297             return QPpsAttributeList();
298         }
299 
300         QPpsAttribute ppsAttribute = decodeData(decoder);
301         if (!ppsAttribute.isValid()) {
302             *ok = false;
303             return QPpsAttributeList();
304         }
305 
306         list << ppsAttribute;
307     }
308 
309     *ok = true;
310     return list;
311 }
312 
decodeObject(pps_decoder_t * decoder,bool * ok)313 QPpsAttributeMap QPpsObjectPrivate::decodeObject(pps_decoder_t *decoder, bool *ok)
314 {
315     QPpsAttributeMap map;
316 
317     int length = pps_decoder_length(decoder);
318     for (int i = 0; i < length; ++i) {
319         // Force movement to a specific index.
320         pps_decoder_error_t error = pps_decoder_goto_index(decoder, i);
321         if (error != PPS_DECODER_OK) {
322             qWarning("QPpsObjectPrivate::decodeObject: pps_decoder_goto_index failed");
323             *ok = false;
324             return QPpsAttributeMap();
325         }
326         QString name = QString::fromUtf8(pps_decoder_name(decoder));
327         QPpsAttribute ppsAttribute = decodeData(decoder);
328         if (!ppsAttribute.isValid()) {
329             *ok = false;
330             return QPpsAttributeMap();
331         }
332         map[name] = ppsAttribute;
333     }
334 
335     *ok = true;
336     return map;
337 }
338 
variantFromPpsAttribute(const QPpsAttribute & attribute)339 QVariant QPpsObjectPrivate::variantFromPpsAttribute(const QPpsAttribute &attribute)
340 {
341     switch (attribute.type()) {
342     case QPpsAttribute::Number:
343         switch (attribute.toVariant().type()) {
344         case QVariant::Int:
345             return attribute.toInt();
346         case QVariant::LongLong:
347             return attribute.toLongLong();
348         default:
349             return attribute.toDouble();
350         }
351         break;
352     case QPpsAttribute::Bool:
353         return attribute.toBool();
354     case QPpsAttribute::String:
355         return attribute.toString();
356     case QPpsAttribute::Array: {
357         QVariantList variantList;
358         const auto attrs = attribute.toList();
359         for (const QPpsAttribute &attr : attrs) {
360             QVariant variant = variantFromPpsAttribute(attr);
361             if (!variant.isValid())
362                 return QVariantList();
363             variantList << variant;
364         }
365         return variantList;
366     }
367     case QPpsAttribute::Object:
368         return variantMapFromPpsAttributeMap(attribute.toMap());
369     case QPpsAttribute::None:
370     default:
371         qWarning("QPpsObjectPrivate::variantFromPpsAttribute: invalid attribute parameter");
372         return QVariant();
373     }
374 }
375 
encode(const QVariantMap & ppsData,bool * ok)376 QByteArray QPpsObjectPrivate::encode(const QVariantMap &ppsData, bool *ok)
377 {
378     pps_encoder_t encoder;
379     pps_encoder_initialize(&encoder, false);
380 
381     encodeObject(&encoder, ppsData, ok);
382     const char *rawData = 0;
383     if (*ok) {
384         // rawData points to a memory owned by encoder.
385         // The memory will be freed when pps_encoder_cleanup is called.
386         rawData = pps_encoder_buffer(&encoder);
387         if (!rawData) {
388             qWarning("QPpsObjectPrivate::encode: pps_encoder_buffer failed");
389             *ok = false;
390         }
391     }
392 
393     pps_encoder_cleanup(&encoder);
394     return QByteArray(rawData);
395 }
396 
encodeData(pps_encoder_t * encoder,const char * name,const QVariant & data,bool * ok)397 void QPpsObjectPrivate::encodeData(pps_encoder_t *encoder, const char *name, const QVariant &data,
398                                    bool *ok)
399 {
400     const char *errorFunction;
401     pps_encoder_error_t error = PPS_ENCODER_OK;
402     switch (data.type()) {
403     case QVariant::Bool:
404         error = pps_encoder_add_bool(encoder, name, data.toBool());
405         errorFunction = "pps_encoder_add_bool";
406         break;
407     // We want to support encoding uint even though libpps doesn't support it directly.
408     // We can't encode uint as an int since that will lose precision (e.g. 2^31+1 can't be
409     // encoded that way). However, we can convert uint to double without losing precision.
410     // QVariant.toDouble() conveniently takes care of the conversion for us.
411     case QVariant::UInt:
412     case QVariant::Double:
413         error = pps_encoder_add_double(encoder, name, data.toDouble());
414         errorFunction = "pps_encoder_add_double";
415         break;
416     case QVariant::Int:
417         error = pps_encoder_add_int(encoder, name, data.toInt());
418         errorFunction = "pps_encoder_add_int";
419         break;
420     case QVariant::LongLong:
421         error = pps_encoder_add_int64(encoder, name, data.toLongLong());
422         errorFunction = "pps_encoder_add_int64";
423         break;
424     case QVariant::String:
425         error = pps_encoder_add_string(encoder, name, data.toString().toUtf8().constData());
426         errorFunction = "pps_encoder_add_string";
427         break;
428     case QVariant::List:
429         error = pps_encoder_start_array(encoder, name);
430         errorFunction = "pps_encoder_start_array";
431         if (error == PPS_ENCODER_OK) {
432             encodeArray(encoder, data.toList(), ok);
433             error = pps_encoder_end_array(encoder);
434             errorFunction = "pps_encoder_end_array";
435         }
436         break;
437     case QVariant::Map:
438         error = pps_encoder_start_object(encoder, name);
439         errorFunction = "pps_encoder_start_object";
440         if (error == PPS_ENCODER_OK) {
441             encodeObject(encoder, data.toMap(), ok);
442             error = pps_encoder_end_object(encoder);
443             errorFunction = "pps_encoder_end_object";
444         }
445         break;
446     case QVariant::Invalid:
447         error = pps_encoder_add_null(encoder, name);
448         errorFunction = "pps_encoder_add_null";
449         break;
450     default:
451         qWarning("QPpsObjectPrivate::encodeData: the type of the parameter data is invalid");
452         *ok = false;
453         return;
454     }
455 
456     if (error != PPS_ENCODER_OK) {
457         qWarning("QPpsObjectPrivate::encodeData: %s failed", errorFunction);
458         *ok = false;
459     } else {
460         *ok = true;
461     }
462 }
463 
encodeArray(pps_encoder_t * encoder,const QVariantList & data,bool * ok)464 void QPpsObjectPrivate::encodeArray(pps_encoder_t *encoder, const QVariantList &data, bool *ok)
465 {
466     for (QVariantList::const_iterator it = data.constBegin(); it != data.constEnd(); ++it) {
467         encodeData(encoder, 0, *it, ok);
468         if (!(*ok))
469             return;
470     }
471     // if the passed data is empty, nothing went wrong and ok is set to true
472     *ok = true;
473 }
474 
encodeObject(pps_encoder_t * encoder,const QVariantMap & data,bool * ok)475 void QPpsObjectPrivate::encodeObject(pps_encoder_t *encoder, const QVariantMap &data, bool *ok)
476 {
477     for (QVariantMap::const_iterator it = data.constBegin(); it != data.constEnd(); ++it) {
478         encodeData(encoder, it.key().toUtf8().constData(), it.value(), ok);
479         if (!(*ok))
480             return;
481     }
482     // if the passed data is empty, nothing went wrong and ok is set to true
483     *ok = true;
484 }
485 
486 
487 
488 ///////////////////////////////////////////////////////////////////////////////
489 //
490 // QPpsObjectPrivate
491 //
492 ///////////////////////////////////////////////////////////////////////////////
493 
QPpsObject(const QString & path,QObject * parent)494 QPpsObject::QPpsObject(const QString &path, QObject *parent)
495     : QObject(*new QPpsObjectPrivate(path), parent)
496 {
497 }
498 
~QPpsObject()499 QPpsObject::~QPpsObject()
500 {
501     // RAII - ensure file gets closed
502     if (isOpen())
503         close();
504 }
505 
error() const506 int QPpsObject::error() const
507 {
508     Q_D(const QPpsObject);
509     return d->error;
510 }
511 
errorString() const512 QString QPpsObject::errorString() const
513 {
514     Q_D(const QPpsObject);
515     return qt_error_string(d->error);
516 }
517 
isReadyReadEnabled() const518 bool QPpsObject::isReadyReadEnabled() const
519 {
520     Q_D(const QPpsObject);
521 
522     // query state of read ready signal
523     return d->readyReadEnabled;
524 }
525 
setReadyReadEnabled(bool enable)526 void QPpsObject::setReadyReadEnabled(bool enable)
527 {
528     Q_D(QPpsObject);
529 
530     // toggle whether socket notifier will emit a signal on read ready
531     d->readyReadEnabled = enable;
532     if (isOpen())
533         d->notifier->setEnabled(enable);
534 }
535 
isBlocking() const536 bool QPpsObject::isBlocking() const
537 {
538     Q_D(const QPpsObject);
539 
540     // reset last error
541     d->error = EOK;
542 
543     // abort if file not open
544     if (!isOpen()) {
545         d->error = EBADF;
546         return false;
547     }
548 
549     // query file status flags
550     int flags = fcntl(d->fd, F_GETFL);
551     if (flags == -1) {
552         d->error = errno;
553         return false;
554     }
555     // check if non-blocking flag is unset
556     return ((flags & O_NONBLOCK) != O_NONBLOCK);
557 }
558 
setBlocking(bool enable)559 bool QPpsObject::setBlocking(bool enable)
560 {
561     Q_D(QPpsObject);
562 
563     // reset last error
564     d->error = EOK;
565 
566     // abort if file not open
567     if (!isOpen()) {
568         d->error = EBADF;
569         return false;
570     }
571 
572     // query file status flags
573     int flags = fcntl(d->fd, F_GETFL);
574     if (flags == -1) {
575         d->error = errno;
576         return false;
577     }
578 
579     // configure non-blocking flag
580     if (enable)
581         flags &= ~O_NONBLOCK;
582     else
583         flags |= O_NONBLOCK;
584 
585     // update file status flags
586     flags = fcntl(d->fd, F_SETFL, flags);
587     if (flags == -1) {
588         d->error = errno;
589         return false;
590     }
591 
592     return true;
593 }
594 
isOpen() const595 bool QPpsObject::isOpen() const
596 {
597     Q_D(const QPpsObject);
598     return (d->fd != -1);
599 }
600 
open(QPpsObject::OpenModes mode)601 bool QPpsObject::open(QPpsObject::OpenModes mode)
602 {
603     Q_D(QPpsObject);
604 
605     // reset last error
606     d->error = EOK;
607 
608     // abort if file already open
609     if (isOpen()) {
610         d->error = EBUSY;
611         return false;
612     }
613 
614     // convert pps flags to open flags
615     int oflags = 0;
616     if ((mode & QPpsObject::Publish) && (mode & QPpsObject::Subscribe))
617         oflags |= O_RDWR;
618     else if (mode & QPpsObject::Publish)
619         oflags |= O_WRONLY;
620     else if (mode & QPpsObject::Subscribe)
621         oflags |= O_RDONLY;
622 
623     if (mode & QPpsObject::Create)
624         oflags |= O_CREAT | O_EXCL;
625 
626     if (mode & QPpsObject::DeleteContents)
627         oflags |= O_TRUNC;
628 
629     // open pps file
630     d->fd = qt_safe_open(d->path.toUtf8().data(), oflags, 0666);
631     if (d->fd == -1) {
632         d->error = errno;
633         return false;
634     }
635     // wire up socket notifier to know when reads are ready
636     d->notifier = new QSocketNotifier(d->fd, QSocketNotifier::Read, this);
637     d->notifier->setEnabled(d->readyReadEnabled);
638     QObject::connect(d->notifier, &QSocketNotifier::activated, this, &QPpsObject::readyRead);
639     return true;
640 }
641 
close()642 bool QPpsObject::close()
643 {
644     Q_D(QPpsObject);
645 
646     // reset last error
647     d->error = EOK;
648 
649     // abort if file not open
650     if (!isOpen()) {
651         d->error = EBADF;
652         return false;
653     }
654 
655     // shutdown socket notifier
656     delete d->notifier;
657     d->notifier = 0;
658 
659     // close pps file
660     const int result = qt_safe_close(d->fd);
661     d->fd = -1;
662 
663     // check success of operation
664     if (result != 0) {
665         d->error = errno;
666         return false;
667     }
668     return true;
669 }
670 
read(bool * ok)671 QByteArray QPpsObject::read(bool *ok)
672 {
673     Q_D(QPpsObject);
674 
675     // reset last error
676     d->error = EOK;
677 
678     // abort if file not open
679     if (!isOpen()) {
680         d->error = EBADF;
681         safeAssign(ok, false);
682         return QByteArray();
683     }
684 
685     const int maxSize = ppsMaxSize->value;
686     if (maxSize == -1) {
687         qWarning("QPpsObject::read: maxSize is equal to -1");
688         safeAssign(ok, false);
689         return QByteArray();
690     }
691 
692     QByteArray byteArray;
693     byteArray.resize(maxSize); // resize doesn't initialize the data
694     const int result = qt_safe_read(d->fd, byteArray.data(), byteArray.size());
695 
696     if (result == -1) {
697         d->error = errno;
698         qWarning() << "QPpsObject::read failed to read pps data, error " << errorString();
699         safeAssign(ok, false);
700         return QByteArray(); // Specifically return a default-constructed QByteArray.
701     }
702     if (result == 0) {
703         // normalize the behavior of read() when no data is ready so a pps object
704         // put in non-blocking mode via opening w/o wait (read returns 0) looks
705         // the same as a pps object put in non-blocking mode by setting O_NONBLOCK
706         // (read returns EAGAIN)
707         d->error = EAGAIN;
708         safeAssign(ok, false);
709         return QByteArray(); // Specifically return a default-constructed QByteArray.
710     }
711     // resize byte array to amount actually read
712     byteArray.resize(result);
713     safeAssign(ok, true);
714     return byteArray;
715 }
716 
write(const QByteArray & byteArray)717 bool QPpsObject::write(const QByteArray &byteArray)
718 {
719     Q_D(QPpsObject);
720 
721     // reset last error
722     d->error = EOK;
723 
724     // abort if file not open
725     if (!isOpen()) {
726         d->error = EBADF;
727         return false;
728     }
729 
730     // write entire byte array to pps file
731     const int result = qt_safe_write(d->fd, byteArray.data(), byteArray.size());
732     if (result == -1)
733         d->error = errno;
734 
735     return (result == byteArray.size());
736 }
737 
writeMessage(const QString & msg,const QVariantMap & dat)738 int QPpsObject::writeMessage(const QString &msg, const QVariantMap &dat)
739 {
740     // Treat empty msg as an encoding error
741     if (msg.isEmpty())
742         return -1;
743 
744     bool ok;
745     QByteArray byteArray = encodeMessage(msg, dat, &ok);
746 
747     if (!ok)
748         return -1;
749 
750     ok = write(byteArray);
751     if (!ok)
752         return error();
753 
754     return EOK;
755 }
756 
writeMessage(const QString & msg,const QString & id,const QVariantMap & dat)757 int QPpsObject::writeMessage(const QString &msg, const QString &id, const QVariantMap &dat)
758 {
759     // Treat empty msg or id as an encoding error
760     if (msg.isEmpty() || id.isEmpty())
761         return -1;
762 
763     bool ok;
764     QByteArray byteArray = encodeMessage(msg, id, dat, &ok);
765 
766     if (!ok)
767         return -1;
768 
769     ok = write(byteArray);
770     if (!ok)
771         return error();
772 
773     return EOK;
774 }
775 
remove()776 bool QPpsObject::remove()
777 {
778     Q_D(QPpsObject);
779 
780     // reset last error
781     d->error = EOK;
782 
783     // delete pps file
784     const int result = unlink(d->path.toUtf8().data());
785 
786     // check success of operation
787     if (result != 0) {
788         d->error = errno;
789         return false;
790     }
791     return true;
792 }
793 
794 // static
decode(const QByteArray & rawData,bool * ok)795 QVariantMap QPpsObject::decode(const QByteArray &rawData, bool *ok)
796 {
797     QPpsAttributeMap mapData = decodeWithFlags(rawData, 0, ok);
798 
799     // If *ok is false, then mapData is empty, so the resulting QVariantMap
800     // will also be empty, as desired.
801     return QPpsObjectPrivate::variantMapFromPpsAttributeMap(mapData);
802 }
803 
804 // static
decodeWithFlags(const QByteArray & rawData,bool * ok)805 QPpsAttributeMap QPpsObject::decodeWithFlags(const QByteArray &rawData, bool *ok)
806 {
807     return QPpsObject::decodeWithFlags(rawData, 0, ok);
808 }
809 
810 // static
decodeWithFlags(const QByteArray & rawData,QPpsAttribute * objectAttribute,bool * ok)811 QPpsAttributeMap QPpsObject::decodeWithFlags(const QByteArray &rawData,
812     QPpsAttribute *objectAttribute, bool *ok)
813 {
814     safeAssign(ok, true);
815 
816     bool success = false;
817     QPpsAttributeMap mapData =  QPpsObjectPrivate::decode(rawData, &success);
818     if (!success) {
819         safeAssign(ok, false);
820         return QPpsAttributeMap();
821     }
822 
823     // The object name is the key of the first element, and the flags of that attribute
824     // give the status for the object as a whole.
825     if (!mapData.isEmpty() && objectAttribute) {
826         QString extractedName = mapData.begin().key();
827         QPpsAttribute topmostAttribute = mapData.begin().value();
828         QPpsAttribute::Flags topmostFlags = topmostAttribute.flags();
829         QPpsAttribute toplevelAttribute =
830             QPpsAttributePrivate::createPpsAttribute(extractedName, topmostFlags);
831         *objectAttribute = toplevelAttribute;
832     }
833 
834     return mapData;
835 }
836 
837 
838 // static
encode(const QVariantMap & ppsData,bool * ok)839 QByteArray QPpsObject::encode(const QVariantMap &ppsData, bool *ok)
840 {
841     safeAssign(ok, true);
842 
843     bool success = false;
844     QByteArray byteArray = QPpsObjectPrivate::encode(ppsData, &success);
845     if (!success) {
846         safeAssign(ok, false);
847         return QByteArray();
848     }
849     return byteArray;
850 }
851 
852 // static
encodeMessage(const QString & msg,const QVariantMap & dat,bool * ok)853 QByteArray QPpsObject::encodeMessage(const QString &msg, const QVariantMap &dat, bool *ok)
854 {
855     safeAssign(ok, true);
856 
857     // Treat empty msg as an encoding error
858     if (msg.isEmpty()) {
859         safeAssign(ok, false);
860         return QByteArray();
861     }
862 
863     QVariantMap ppsData;
864     ppsData[QStringLiteral("msg")] = msg;
865     ppsData[QStringLiteral("dat")] = dat;
866 
867     return QPpsObject::encode(ppsData, ok);
868 }
869 
870 // static
encodeMessage(const QString & msg,const QString & id,const QVariantMap & dat,bool * ok)871 QByteArray QPpsObject::encodeMessage(const QString &msg, const QString &id, const QVariantMap &dat,
872                                      bool *ok)
873 {
874     safeAssign(ok, true);
875 
876     // Treat empty msg or id as an encoding error
877     if (msg.isEmpty() || id.isEmpty()) {
878         safeAssign(ok, false);
879         return QByteArray();
880     }
881 
882     QVariantMap ppsData;
883     ppsData[QStringLiteral("msg")] = msg;
884     ppsData[QStringLiteral("id")] = id;
885     ppsData[QStringLiteral("dat")] = dat;
886 
887     return QPpsObject::encode(ppsData, ok);
888 }
889 
890 // static
sendMessage(const QString & path,const QString & message)891 int QPpsObject::sendMessage(const QString &path, const QString &message)
892 {
893     QPpsObject pps(path);
894 
895     bool ok = pps.open(QPpsObject::Publish);
896     if (!ok)
897         return pps.error();
898 
899     ok = pps.write(message.toLocal8Bit());
900     if (!ok)
901         return pps.error();
902 
903     return EOK;
904 }
905 
906 // static
sendMessage(const QString & path,const QVariantMap & message)907 int QPpsObject::sendMessage(const QString &path, const QVariantMap &message)
908 {
909     QPpsObject pps(path);
910 
911     bool ok = pps.open(QPpsObject::Publish);
912     if (!ok)
913         return pps.error();
914 
915     QByteArray payload = QPpsObject::encode(message, &ok);
916     if (!ok)
917         return -1;
918 
919     ok = pps.write(payload);
920     if (!ok)
921         return pps.error();
922 
923     return EOK;
924 }
925 
926 // static
sendMessage(const QString & path,const QString & msg,const QVariantMap & dat)927 int QPpsObject::sendMessage(const QString &path, const QString &msg, const QVariantMap &dat)
928 {
929     // Treat empty msg as an encoding error
930     if (msg.isEmpty())
931         return -1;
932 
933     QPpsObject pps(path);
934 
935     bool ok = pps.open(QPpsObject::Publish);
936     if (!ok)
937         return pps.error();
938 
939     QByteArray payload = QPpsObject::encodeMessage(msg, dat, &ok);
940     if (!ok)
941         return -1;
942 
943     ok = pps.write(payload);
944     if (!ok)
945         return pps.error();
946 
947     return EOK;
948 }
949 
950 // static
sendMessage(const QString & path,const QByteArray & ppsData)951 int QPpsObject::sendMessage(const QString &path, const QByteArray &ppsData)
952 {
953     QPpsObject pps(path);
954 
955     bool ok = pps.open(QPpsObject::Publish);
956     if (!ok)
957         return pps.error();
958 
959     ok = pps.write(ppsData);
960     if (!ok)
961         return pps.error();
962 
963     return EOK;
964 }
965 
966 QT_END_NAMESPACE
967