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#include <QDataStream>
31
32#include "SignOn/signonplugincommon.h"
33
34#define SIGNON_IPC_BUFFER_PAGE_SIZE 16384
35
36using namespace SignOn;
37
38BlobIOHandler::BlobIOHandler(QIODevice *readChannel,
39 QIODevice *writeChannel,
40 QObject *parent):
41 QObject(parent),
42 m_readChannel(readChannel),
43 m_writeChannel(writeChannel),
44 m_readNotifier(0),
45 m_blobSize(-1),
46 m_isReading(false)
47{
48}
49
50void BlobIOHandler::setReadChannelSocketNotifier(QSocketNotifier *notifier)
51{
52 if (notifier == 0)
53 return;
54
55 m_readNotifier = notifier;
56}
57
58bool BlobIOHandler::sendData(const QVariantMap &map)
59{
60 if (m_writeChannel == 0) {
61 TRACE() << "NULL write channel.";
62 return false;
63 }
64
65 QDataStream stream(m_writeChannel);
66 QByteArray ba = variantMapToByteArray(map);
67 // in Qt6 QByteArray::size() is 64 bit, but the receiving side expects int
68 stream << static_cast<int>(ba.size());
69
70 QVector<QByteArray> pages = pageByteArray(ba);
71 for (int i = 0; i < pages.count(); ++i)
72 stream << pages[i];
73
74 return true;
75}
76
77void BlobIOHandler::setReadNotificationEnabled(bool enabled)
78{
79 m_isReading = enabled;
80 if (enabled) {
81 if (m_readNotifier != 0) {
82 connect(m_readNotifier, SIGNAL(activated(int)),
83 this, SLOT(readBlob()));
84 } else {
85 connect(m_readChannel, SIGNAL(readyRead()),
86 this, SLOT(readBlob()));
87 }
88 } else {
89 if (m_readNotifier != 0) {
90 disconnect(m_readNotifier, SIGNAL(activated(int)),
91 this, SLOT(readBlob()));
92 } else {
93 disconnect(m_readChannel, SIGNAL(readyRead()),
94 this, SLOT(readBlob()));
95 }
96 }
97}
98
99void BlobIOHandler::receiveData(int expectedDataSize)
100{
101 m_blobBuffer.clear();
102 m_blobSize = expectedDataSize;
103
104 //Enable read notification only if more than 1 BLOB page is to be received
105 //This does not allow duplicate read attempts if only 1 page is available
106 if (m_blobSize > SIGNON_IPC_BUFFER_PAGE_SIZE)
107 setReadNotificationEnabled(true);
108
109 readBlob();
110}
111
112void BlobIOHandler::readBlob()
113{
114 QDataStream in(m_readChannel);
115
116 QByteArray fractionBa;
117 in >> fractionBa;
118 m_blobBuffer.append(fractionBa);
119
120 //Avoid infinite loops if the other party behaves badly
121 if ((fractionBa.size() == 0) && (m_blobBuffer.size() < m_blobSize)) {
122 setReadNotificationEnabled(false);
123 emit error();
124 return;
125 }
126
127 if (m_blobBuffer.size() == m_blobSize) {
128 QVariantMap sessionDataMap;
129 sessionDataMap = byteArrayToVariantMap(m_blobBuffer);
130
131 if (m_blobSize > SIGNON_IPC_BUFFER_PAGE_SIZE)
132 setReadNotificationEnabled(false);
133
134 emit dataReceived(sessionDataMap);
135 }
136}
137
138QVariantMap expandDBusArgumentValue(const QVariant &value, bool *success)
139{
140 // first, convert the QDBusArgument to a map
141 QDBusArgument dbusValue = value.value<QDBusArgument>();
142 QVariantMap converted;
143 if (dbusValue.currentType() == QDBusArgument::MapType &&
144 // We only care about a{sv}
145 dbusValue.currentSignature() == "a{sv}") {
146 converted = qdbus_cast<QVariantMap>(dbusValue);
147 } else {
148 *success = false;
149 return QVariantMap();
150 }
151
152 // Then, check each value of the converted map
153 // and if any QDBusArgument is a value, convert that.
154 QVariantMap returnValue;
155 QVariantMap::const_iterator i;
156 for (i = converted.constBegin(); i != converted.constEnd(); ++i) {
157 if (qstrcmp(i.value().typeName(), "QDBusArgument") == 0) {
158 QVariantMap convertedValue = expandDBusArgumentValue(i.value(), success);
159 if (*success == false) {
160 //bail out to prevent error in serialization
161 return QVariantMap();
162 }
163 returnValue.insert(i.key(), convertedValue);
164 } else {
165 returnValue.insert(i.key(), i.value());
166 }
167 }
168
169 return returnValue;
170}
171
172static QVariantMap filterOutComplexTypes(const QVariantMap &map)
173{
174 QVariantMap filteredMap;
175 QVariantMap::const_iterator i;
176 for (i = map.constBegin(); i != map.constEnd(); i++) {
177 if (qstrcmp(i.value().typeName(), "QDBusArgument") == 0) {
178 bool success = true;
179 QVariantMap convertedMap = expandDBusArgumentValue(i.value(), &success);
180 if (success == false) {
181 /* QDBusArgument are complex types; there is no QDataStream
182 * serialization for them, so keeping them in the map would
183 * make the serialization fail for the whole map, if we are
184 * unable to convert to a QVariantMap.
185 * Therefore, skip them. */
186 BLAME() << "Found non-map QDBusArgument in data; skipping.";
187 continue;
188 }
189 filteredMap.insert(i.key(), convertedMap);
190 } else {
191 filteredMap.insert(i.key(), i.value());
192 }
193 }
194 return filteredMap;
195}
196
197QByteArray BlobIOHandler::variantMapToByteArray(const QVariantMap &map)
198{
199 QBuffer buffer;
200 if (!buffer.open(QIODevice::WriteOnly))
201 BLAME() << "Buffer opening failed.";
202
203 QDataStream stream(&buffer);
204 stream << filterOutComplexTypes(map);
205 buffer.close();
206
207 return buffer.data();
208}
209
210QVariantMap BlobIOHandler::byteArrayToVariantMap(const QByteArray &array)
211{
212 QByteArray nonConst = array;
213 QBuffer buffer(&nonConst);
214 if (!buffer.open(QIODevice::ReadOnly))
215 BLAME() << "Buffer opening failed.";
216
217 buffer.reset();
218 QDataStream stream(&buffer);
219 QVariantMap map;
220 stream >> map;
221 buffer.close();
222
223 return map;
224}
225
226QVector<QByteArray> BlobIOHandler::pageByteArray(const QByteArray &array)
227{
228 QVector<QByteArray> dataPages;
229 QByteArray ba = array;
230 QBuffer pagingBuffer(&ba);
231
232 if (!pagingBuffer.open(QIODevice::ReadOnly))
233 BLAME() << "Error while paging BLOB. Buffer opening failed.";
234
235 while (!pagingBuffer.atEnd()) {
236 QByteArray page = pagingBuffer.read(SIGNON_IPC_BUFFER_PAGE_SIZE);
237 dataPages.append(page);
238 }
239 pagingBuffer.close();
240
241 return dataPages;
242}
Error codes for ui interaction.