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 QByteArray fractionBa;
116 in >> fractionBa;
117 m_blobBuffer.append(fractionBa);
118
119 //Avoid infinite loops if the other party behaves badly
120 if ((fractionBa.size() == 0) && (m_blobBuffer.size() < m_blobSize)) {
121 setReadNotificationEnabled(false);
122 emit error();
123 return;
124 }
125
126 if (m_blobBuffer.size() == m_blobSize) {
127 QVariantMap sessionDataMap;
128 sessionDataMap = byteArrayToVariantMap(m_blobBuffer);
129
130 if (m_blobSize > SIGNON_IPC_BUFFER_PAGE_SIZE)
131 setReadNotificationEnabled(false);
132
133 emit dataReceived(sessionDataMap);
134 }
135}
136
137QVariantMap expandDBusArgumentValue(const QVariant &value, bool *success)
138{
139 // first, convert the QDBusArgument to a map
140 QDBusArgument dbusValue = value.value<QDBusArgument>();
141 QVariantMap converted;
142 if (dbusValue.currentType() == QDBusArgument::MapType &&
143 // We only care about a{sv}
144 dbusValue.currentSignature() == "a{sv}") {
145 converted = qdbus_cast<QVariantMap>(dbusValue);
146 } else {
147 *success = false;
148 return QVariantMap();
149 }
150
151 // Then, check each value of the converted map
152 // and if any QDBusArgument is a value, convert that.
153 QVariantMap returnValue;
154 QVariantMap::const_iterator i;
155 for (i = converted.constBegin(); i != converted.constEnd(); ++i) {
156 if (qstrcmp(i.value().typeName(), "QDBusArgument") == 0) {
157 QVariantMap convertedValue = expandDBusArgumentValue(i.value(), success);
158 if (*success == false) {
159 //bail out to prevent error in serialization
160 return QVariantMap();
161 }
162 returnValue.insert(i.key(), convertedValue);
163 } else {
164 returnValue.insert(i.key(), i.value());
165 }
166 }
167
168 return returnValue;
169}
170
171static QVariantMap filterOutComplexTypes(const QVariantMap &map)
172{
173 QVariantMap filteredMap;
174 QVariantMap::const_iterator i;
175 for (i = map.constBegin(); i != map.constEnd(); i++) {
176 if (qstrcmp(i.value().typeName(), "QDBusArgument") == 0) {
177 bool success = true;
178 QVariantMap convertedMap = expandDBusArgumentValue(i.value(), &success);
179 if (success == false) {
180 /* QDBusArgument are complex types; there is no QDataStream
181 * serialization for them, so keeping them in the map would
182 * make the serialization fail for the whole map, if we are
183 * unable to convert to a QVariantMap.
184 * Therefore, skip them. */
185 BLAME() << "Found non-map QDBusArgument in data; skipping.";
186 continue;
187 }
188 filteredMap.insert(i.key(), convertedMap);
189 } else {
190 filteredMap.insert(i.key(), i.value());
191 }
192 }
193 return filteredMap;
194}
195
196QByteArray BlobIOHandler::variantMapToByteArray(const QVariantMap &map)
197{
198 QBuffer buffer;
199 if (!buffer.open(QIODevice::WriteOnly))
200 BLAME() << "Buffer opening failed.";
201
202 QDataStream stream(&buffer);
203 stream << filterOutComplexTypes(map);
204 buffer.close();
205
206 return buffer.data();
207}
208
209QVariantMap BlobIOHandler::byteArrayToVariantMap(const QByteArray &array)
210{
211 QByteArray nonConst = array;
212 QBuffer buffer(&nonConst);
213 if (!buffer.open(QIODevice::ReadOnly))
214 BLAME() << "Buffer opening failed.";
215
216 buffer.reset();
217 QDataStream stream(&buffer);
218 QVariantMap map;
219 stream >> map;
220 buffer.close();
221
222 return map;
223}
224
225QVector<QByteArray> BlobIOHandler::pageByteArray(const QByteArray &array)
226{
227 QVector<QByteArray> dataPages;
228 QByteArray ba = array;
229 QBuffer pagingBuffer(&ba);
230
231 if (!pagingBuffer.open(QIODevice::ReadOnly))
232 BLAME() << "Error while paging BLOB. Buffer opening failed.";
233
234 while (!pagingBuffer.atEnd()) {
235 QByteArray page = pagingBuffer.read(SIGNON_IPC_BUFFER_PAGE_SIZE);
236 dataPages.append(page);
237 }
238 pagingBuffer.close();
239
240 return dataPages;
241}
Error codes for ui interaction.