My Project
blobiohandler.cpp
1/*
2 * This file is part of signon
3 *
4 * Copyright (C) 2009-2011 Nokia Corporation.
5 * Copyright (C) 2012-2016 Canonical Ltd.
6 *
7 * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * version 2.1 as published by the Free Software Foundation.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 */
23
24#include "blobiohandler.h"
25
26#include <QDBusArgument>
27#include <QBuffer>
28#include <QDataStream>
29#include <QDebug>
30
31#include "SignOn/signonplugincommon.h"
32
33#define SIGNON_IPC_BUFFER_PAGE_SIZE 16384
34
35using namespace SignOn;
36
37BlobIOHandler::BlobIOHandler(QIODevice *readChannel,
38 QIODevice *writeChannel,
39 QObject *parent):
40 QObject(parent),
41 m_readChannel(readChannel),
42 m_writeChannel(writeChannel),
43 m_readNotifier(0),
44 m_blobSize(-1),
45 m_isReading(false)
46{
47}
48
49void BlobIOHandler::setReadChannelSocketNotifier(QSocketNotifier *notifier)
50{
51 if (notifier == 0)
52 return;
53
54 m_readNotifier = notifier;
55}
56
57bool BlobIOHandler::sendData(const QVariantMap &map)
58{
59 if (m_writeChannel == 0) {
60 TRACE() << "NULL write channel.";
61 return false;
62 }
63
64 QDataStream stream(m_writeChannel);
65 QByteArray ba = variantMapToByteArray(map);
66 // in Qt6 QByteArray::size() is 64 bit, but the receiving side expects int
67 stream << static_cast<int>(ba.size());
68
69 QVector<QByteArray> pages = pageByteArray(ba);
70 for (int i = 0; i < pages.count(); ++i)
71 stream << pages[i];
72
73 return true;
74}
75
76void BlobIOHandler::setReadNotificationEnabled(bool enabled)
77{
78 m_isReading = enabled;
79 if (enabled) {
80 if (m_readNotifier != 0) {
81 connect(m_readNotifier, SIGNAL(activated(int)),
82 this, SLOT(readBlob()));
83 } else {
84 connect(m_readChannel, SIGNAL(readyRead()),
85 this, SLOT(readBlob()));
86 }
87 } else {
88 if (m_readNotifier != 0) {
89 disconnect(m_readNotifier, SIGNAL(activated(int)),
90 this, SLOT(readBlob()));
91 } else {
92 disconnect(m_readChannel, SIGNAL(readyRead()),
93 this, SLOT(readBlob()));
94 }
95 }
96}
97
98void BlobIOHandler::receiveData(int expectedDataSize)
99{
100 m_blobBuffer.clear();
101 m_blobSize = expectedDataSize;
102
103 //Enable read notification only if more than 1 BLOB page is to be received
104 //This does not allow duplicate read attempts if only 1 page is available
105 if (m_blobSize > SIGNON_IPC_BUFFER_PAGE_SIZE)
106 setReadNotificationEnabled(true);
107
108 readBlob();
109}
110
111void BlobIOHandler::readBlob()
112{
113 QDataStream in(m_readChannel);
114
115 in.startTransaction();
116 QByteArray fractionBa;
117 in >> fractionBa;
118
119 if (!in.commitTransaction()) {
120 setReadNotificationEnabled(true);
121 // FIXME: with next ABI break, add a timer to prevent infinite waiting
122 // if the other party behaves badly
123 return;
124 }
125
126 m_blobBuffer.append(fractionBa);
127
128 //Avoid infinite loops if the other party behaves badly
129 if ((fractionBa.size() == 0) && (m_blobBuffer.size() < m_blobSize)) {
130 setReadNotificationEnabled(false);
131 emit error();
132 return;
133 }
134
135 if (m_blobBuffer.size() == m_blobSize) {
136 QVariantMap sessionDataMap;
137 sessionDataMap = byteArrayToVariantMap(m_blobBuffer);
138
139 setReadNotificationEnabled(false);
140
141 emit dataReceived(sessionDataMap);
142 }
143}
144
145QVariantMap expandDBusArgumentValue(const QVariant &value, bool *success)
146{
147 // first, convert the QDBusArgument to a map
148 QDBusArgument dbusValue = value.value<QDBusArgument>();
149 QVariantMap converted;
150 if (dbusValue.currentType() == QDBusArgument::MapType &&
151 // We only care about a{sv}
152 dbusValue.currentSignature() == "a{sv}") {
153 converted = qdbus_cast<QVariantMap>(dbusValue);
154 } else {
155 *success = false;
156 return QVariantMap();
157 }
158
159 // Then, check each value of the converted map
160 // and if any QDBusArgument is a value, convert that.
161 QVariantMap returnValue;
162 QVariantMap::const_iterator i;
163 for (i = converted.constBegin(); i != converted.constEnd(); ++i) {
164 if (qstrcmp(i.value().typeName(), "QDBusArgument") == 0) {
165 QVariantMap convertedValue = expandDBusArgumentValue(i.value(), success);
166 if (*success == false) {
167 //bail out to prevent error in serialization
168 return QVariantMap();
169 }
170 returnValue.insert(i.key(), convertedValue);
171 } else {
172 returnValue.insert(i.key(), i.value());
173 }
174 }
175
176 return returnValue;
177}
178
179static QVariantMap filterOutComplexTypes(const QVariantMap &map)
180{
181 QVariantMap filteredMap;
182 QVariantMap::const_iterator i;
183 for (i = map.constBegin(); i != map.constEnd(); i++) {
184 if (qstrcmp(i.value().typeName(), "QDBusArgument") == 0) {
185 bool success = true;
186 QVariantMap convertedMap = expandDBusArgumentValue(i.value(), &success);
187 if (success == false) {
188 /* QDBusArgument are complex types; there is no QDataStream
189 * serialization for them, so keeping them in the map would
190 * make the serialization fail for the whole map, if we are
191 * unable to convert to a QVariantMap.
192 * Therefore, skip them. */
193 BLAME() << "Found non-map QDBusArgument in data; skipping.";
194 continue;
195 }
196 filteredMap.insert(i.key(), convertedMap);
197 } else {
198 filteredMap.insert(i.key(), i.value());
199 }
200 }
201 return filteredMap;
202}
203
204QByteArray BlobIOHandler::variantMapToByteArray(const QVariantMap &map)
205{
206 QBuffer buffer;
207 if (!buffer.open(QIODevice::WriteOnly))
208 BLAME() << "Buffer opening failed.";
209
210 QDataStream stream(&buffer);
211 stream << filterOutComplexTypes(map);
212 buffer.close();
213
214 return buffer.data();
215}
216
217QVariantMap BlobIOHandler::byteArrayToVariantMap(const QByteArray &array)
218{
219 QByteArray nonConst = array;
220 QBuffer buffer(&nonConst);
221 if (!buffer.open(QIODevice::ReadOnly))
222 BLAME() << "Buffer opening failed.";
223
224 buffer.reset();
225 QDataStream stream(&buffer);
226 QVariantMap map;
227 stream >> map;
228 buffer.close();
229
230 return map;
231}
232
233QVector<QByteArray> BlobIOHandler::pageByteArray(const QByteArray &array)
234{
235 QVector<QByteArray> dataPages;
236 QByteArray ba = array;
237 QBuffer pagingBuffer(&ba);
238
239 if (!pagingBuffer.open(QIODevice::ReadOnly))
240 BLAME() << "Error while paging BLOB. Buffer opening failed.";
241
242 while (!pagingBuffer.atEnd()) {
243 QByteArray page = pagingBuffer.read(SIGNON_IPC_BUFFER_PAGE_SIZE);
244 dataPages.append(page);
245 }
246 pagingBuffer.close();
247
248 return dataPages;
249}
Error codes for ui interaction.