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 <QBuffer>
27 #include <QDebug>
28 
29 #include "SignOn/signonplugincommon.h"
30 
31 #define SIGNON_IPC_BUFFER_PAGE_SIZE 16384
32 
33 using namespace SignOn;
34 
35 BlobIOHandler::BlobIOHandler(QIODevice *readChannel,
36  QIODevice *writeChannel,
37  QObject *parent):
38  QObject(parent),
39  m_readChannel(readChannel),
40  m_writeChannel(writeChannel),
41  m_readNotifier(0),
42  m_blobSize(-1)
43 {
44 }
45 
46 void BlobIOHandler::setReadChannelSocketNotifier(QSocketNotifier *notifier)
47 {
48  if (notifier == 0)
49  return;
50 
51  m_readNotifier = notifier;
52 }
53 
54 bool BlobIOHandler::sendData(const QVariantMap &map)
55 {
56  if (m_writeChannel == 0) {
57  TRACE() << "NULL write channel.";
58  return false;
59  }
60 
61  QDataStream stream(m_writeChannel);
62  QByteArray ba = variantMapToByteArray(map);
63  stream << ba.size();
64 
65  QVector<QByteArray> pages = pageByteArray(ba);
66  for (int i = 0; i < pages.count(); ++i)
67  stream << pages[i];
68 
69  return true;
70 }
71 
72 void BlobIOHandler::setReadNotificationEnabled(bool enabled)
73 {
74  if (enabled) {
75  if (m_readNotifier != 0) {
76  m_readNotifier->setEnabled(true);
77  connect(m_readNotifier, SIGNAL(activated(int)),
78  this, SLOT(readBlob()));
79  } else {
80  connect(m_readChannel, SIGNAL(readyRead()),
81  this, SLOT(readBlob()));
82  }
83  } else {
84  if (m_readNotifier != 0) {
85  disconnect(m_readNotifier, SIGNAL(activated(int)),
86  this, SLOT(readBlob()));
87  m_readNotifier->setEnabled(false);
88  } else {
89  disconnect(m_readChannel, SIGNAL(readyRead()),
90  this, SLOT(readBlob()));
91  }
92  }
93 }
94 
95 void BlobIOHandler::receiveData(int expectedDataSize)
96 {
97  m_blobBuffer.clear();
98  m_blobSize = expectedDataSize;
99 
100  //Enable read notification only if more than 1 BLOB page is to be received
101  //This does not allow duplicate read attempts if only 1 page is available
102  if (m_blobSize > SIGNON_IPC_BUFFER_PAGE_SIZE)
103  setReadNotificationEnabled(true);
104 
105  readBlob();
106 }
107 
108 void BlobIOHandler::readBlob()
109 {
110  QDataStream in(m_readChannel);
111 
112  QByteArray fractionBa;
113  in >> fractionBa;
114  m_blobBuffer.append(fractionBa);
115 
116  //Avoid infinite loops if the other party behaves badly
117  if ((fractionBa.size() == 0) && (m_blobBuffer.size() < m_blobSize)) {
118  setReadNotificationEnabled(false);
119  emit error();
120  return;
121  }
122 
123  if (m_blobBuffer.size() == m_blobSize) {
124  QVariantMap sessionDataMap;
125  sessionDataMap = byteArrayToVariantMap(m_blobBuffer);
126 
127  if (m_blobSize > SIGNON_IPC_BUFFER_PAGE_SIZE)
128  setReadNotificationEnabled(false);
129 
130  emit dataReceived(sessionDataMap);
131  }
132 }
133 
134 static QVariantMap filterOutComplexTypes(const QVariantMap &map)
135 {
136  QVariantMap filteredMap;
137  QVariantMap::const_iterator i;
138  for (i = map.constBegin(); i != map.constEnd(); i++) {
139  /* QDBusArgument are complex types; there is no QDataStream
140  * serialization for them, so keeping them in the map would make the
141  * serialization fail for the whole map.
142  * Therefore, skip them. */
143  if (qstrcmp(i.value().typeName(), "QDBusArgument") == 0) {
144  BLAME() << "Found QDBusArgument in map; skipping.";
145  continue;
146  }
147  filteredMap.insert(i.key(), i.value());
148  }
149  return filteredMap;
150 }
151 
152 QByteArray BlobIOHandler::variantMapToByteArray(const QVariantMap &map)
153 {
154  QBuffer buffer;
155  if (!buffer.open(QIODevice::WriteOnly))
156  BLAME() << "Buffer opening failed.";
157 
158  QDataStream stream(&buffer);
159  stream << filterOutComplexTypes(map);
160  buffer.close();
161 
162  return buffer.data();
163 }
164 
165 QVariantMap BlobIOHandler::byteArrayToVariantMap(const QByteArray &array)
166 {
167  QByteArray nonConst = array;
168  QBuffer buffer(&nonConst);
169  if (!buffer.open(QIODevice::ReadOnly))
170  BLAME() << "Buffer opening failed.";
171 
172  buffer.reset();
173  QDataStream stream(&buffer);
174  QVariantMap map;
175  stream >> map;
176  buffer.close();
177 
178  return map;
179 }
180 
181 QVector<QByteArray> BlobIOHandler::pageByteArray(const QByteArray &array)
182 {
183  QVector<QByteArray> dataPages;
184  QByteArray ba = array;
185  QBuffer pagingBuffer(&ba);
186 
187  if (!pagingBuffer.open(QIODevice::ReadOnly))
188  BLAME() << "Error while paging BLOB. Buffer opening failed.";
189 
190  while (!pagingBuffer.atEnd()) {
191  QByteArray page = pagingBuffer.read(SIGNON_IPC_BUFFER_PAGE_SIZE);
192  dataPages.append(page);
193  }
194  pagingBuffer.close();
195 
196  return dataPages;
197 }