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