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 <QDataStream>
29 #include <QDebug>
30 
31 #include "SignOn/signonplugincommon.h"
32 
33 #define SIGNON_IPC_BUFFER_PAGE_SIZE 16384
34 
35 using namespace SignOn;
36 
37 BlobIOHandler::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 {
46 }
47 
48 void BlobIOHandler::setReadChannelSocketNotifier(QSocketNotifier *notifier)
49 {
50  if (notifier == 0)
51  return;
52 
53  m_readNotifier = notifier;
54 }
55 
56 bool BlobIOHandler::sendData(const QVariantMap &map)
57 {
58  if (m_writeChannel == 0) {
59  TRACE() << "NULL write channel.";
60  return false;
61  }
62 
63  QDataStream stream(m_writeChannel);
64  QByteArray ba = variantMapToByteArray(map);
65  stream << ba.size();
66 
67  QVector<QByteArray> pages = pageByteArray(ba);
68  for (int i = 0; i < pages.count(); ++i)
69  stream << pages[i];
70 
71  return true;
72 }
73 
74 void BlobIOHandler::setReadNotificationEnabled(bool enabled)
75 {
76  if (enabled) {
77  if (m_readNotifier != 0) {
78  m_readNotifier->setEnabled(true);
79  connect(m_readNotifier, SIGNAL(activated(int)),
80  this, SLOT(readBlob()));
81  } else {
82  connect(m_readChannel, SIGNAL(readyRead()),
83  this, SLOT(readBlob()));
84  }
85  } else {
86  if (m_readNotifier != 0) {
87  disconnect(m_readNotifier, SIGNAL(activated(int)),
88  this, SLOT(readBlob()));
89  m_readNotifier->setEnabled(false);
90  } else {
91  disconnect(m_readChannel, SIGNAL(readyRead()),
92  this, SLOT(readBlob()));
93  }
94  }
95 }
96 
97 void 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 
110 void 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 
136 QVariantMap 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 
170 static 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 
195 QByteArray 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 
208 QVariantMap 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 
224 QVector<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.
Definition: uisessiondata.h:35