- Main Page
- Classes
- Files

- File List

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