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 
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  m_isReading(false)
46 {
47 }
48 
49 void BlobIOHandler::setReadChannelSocketNotifier(QSocketNotifier *notifier)
50 {
51  if (notifier == 0)
52  return;
53 
54  m_readNotifier = notifier;
55 }
56 
57 bool 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 
76 void 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 
98 void 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 
111 void 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 
137 QVariantMap 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 
171 static 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 
196 QByteArray 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 
209 QVariantMap 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 
225 QVector<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.
Definition: uisessiondata.h:36