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