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