blobiohandler.cpp
/*
* This file is part of signon
*
* Copyright (C) 2009-2011 Nokia Corporation.
* Copyright (C) 2012-2016 Canonical Ltd.
*
* Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "blobiohandler.h"
#include <QDBusArgument>
#include <QBuffer>
#include <QDataStream>
#include <QDebug>
#include "SignOn/signonplugincommon.h"
#define SIGNON_IPC_BUFFER_PAGE_SIZE 16384
using namespace SignOn;
BlobIOHandler::BlobIOHandler(QIODevice *readChannel,
QIODevice *writeChannel,
QObject *parent):
QObject(parent),
m_readChannel(readChannel),
m_writeChannel(writeChannel),
m_readNotifier(0),
m_blobSize(-1),
m_isReading(false)
{
}
void BlobIOHandler::setReadChannelSocketNotifier(QSocketNotifier *notifier)
{
if (notifier == 0)
return;
m_readNotifier = notifier;
}
bool BlobIOHandler::sendData(const QVariantMap &map)
{
if (m_writeChannel == 0) {
TRACE() << "NULL write channel.";
return false;
}
QDataStream stream(m_writeChannel);
QByteArray ba = variantMapToByteArray(map);
stream << ba.size();
QVector<QByteArray> pages = pageByteArray(ba);
for (int i = 0; i < pages.count(); ++i)
stream << pages[i];
return true;
}
void BlobIOHandler::setReadNotificationEnabled(bool enabled)
{
m_isReading = enabled;
if (enabled) {
if (m_readNotifier != 0) {
connect(m_readNotifier, SIGNAL(activated(int)),
this, SLOT(readBlob()));
} else {
connect(m_readChannel, SIGNAL(readyRead()),
this, SLOT(readBlob()));
}
} else {
if (m_readNotifier != 0) {
disconnect(m_readNotifier, SIGNAL(activated(int)),
this, SLOT(readBlob()));
} else {
disconnect(m_readChannel, SIGNAL(readyRead()),
this, SLOT(readBlob()));
}
}
}
void BlobIOHandler::receiveData(int expectedDataSize)
{
m_blobBuffer.clear();
m_blobSize = expectedDataSize;
//Enable read notification only if more than 1 BLOB page is to be received
//This does not allow duplicate read attempts if only 1 page is available
if (m_blobSize > SIGNON_IPC_BUFFER_PAGE_SIZE)
setReadNotificationEnabled(true);
readBlob();
}
void BlobIOHandler::readBlob()
{
QDataStream in(m_readChannel);
QByteArray fractionBa;
in >> fractionBa;
m_blobBuffer.append(fractionBa);
//Avoid infinite loops if the other party behaves badly
if ((fractionBa.size() == 0) && (m_blobBuffer.size() < m_blobSize)) {
setReadNotificationEnabled(false);
emit error();
return;
}
if (m_blobBuffer.size() == m_blobSize) {
QVariantMap sessionDataMap;
sessionDataMap = byteArrayToVariantMap(m_blobBuffer);
if (m_blobSize > SIGNON_IPC_BUFFER_PAGE_SIZE)
setReadNotificationEnabled(false);
emit dataReceived(sessionDataMap);
}
}
QVariantMap expandDBusArgumentValue(const QVariant &value, bool *success)
{
// first, convert the QDBusArgument to a map
QDBusArgument dbusValue = value.value<QDBusArgument>();
QVariantMap converted;
if (dbusValue.currentType() == QDBusArgument::MapType &&
// We only care about a{sv}
dbusValue.currentSignature() == "a{sv}") {
converted = qdbus_cast<QVariantMap>(dbusValue);
} else {
*success = false;
return QVariantMap();
}
// Then, check each value of the converted map
// and if any QDBusArgument is a value, convert that.
QVariantMap returnValue;
QVariantMap::const_iterator i;
for (i = converted.constBegin(); i != converted.constEnd(); ++i) {
if (qstrcmp(i.value().typeName(), "QDBusArgument") == 0) {
QVariantMap convertedValue = expandDBusArgumentValue(i.value(), success);
if (*success == false) {
//bail out to prevent error in serialization
return QVariantMap();
}
returnValue.insert(i.key(), convertedValue);
} else {
returnValue.insert(i.key(), i.value());
}
}
return returnValue;
}
static QVariantMap filterOutComplexTypes(const QVariantMap &map)
{
QVariantMap filteredMap;
QVariantMap::const_iterator i;
for (i = map.constBegin(); i != map.constEnd(); i++) {
if (qstrcmp(i.value().typeName(), "QDBusArgument") == 0) {
bool success = true;
QVariantMap convertedMap = expandDBusArgumentValue(i.value(), &success);
if (success == false) {
/* QDBusArgument are complex types; there is no QDataStream
* serialization for them, so keeping them in the map would
* make the serialization fail for the whole map, if we are
* unable to convert to a QVariantMap.
* Therefore, skip them. */
BLAME() << "Found non-map QDBusArgument in data; skipping.";
continue;
}
filteredMap.insert(i.key(), convertedMap);
} else {
filteredMap.insert(i.key(), i.value());
}
}
return filteredMap;
}
QByteArray BlobIOHandler::variantMapToByteArray(const QVariantMap &map)
{
QBuffer buffer;
if (!buffer.open(QIODevice::WriteOnly))
BLAME() << "Buffer opening failed.";
QDataStream stream(&buffer);
stream << filterOutComplexTypes(map);
buffer.close();
return buffer.data();
}
QVariantMap BlobIOHandler::byteArrayToVariantMap(const QByteArray &array)
{
QByteArray nonConst = array;
QBuffer buffer(&nonConst);
if (!buffer.open(QIODevice::ReadOnly))
BLAME() << "Buffer opening failed.";
buffer.reset();
QDataStream stream(&buffer);
QVariantMap map;
stream >> map;
buffer.close();
return map;
}
QVector<QByteArray> BlobIOHandler::pageByteArray(const QByteArray &array)
{
QVector<QByteArray> dataPages;
QByteArray ba = array;
QBuffer pagingBuffer(&ba);
if (!pagingBuffer.open(QIODevice::ReadOnly))
BLAME() << "Error while paging BLOB. Buffer opening failed.";
while (!pagingBuffer.atEnd()) {
QByteArray page = pagingBuffer.read(SIGNON_IPC_BUFFER_PAGE_SIZE);
dataPages.append(page);
}
pagingBuffer.close();
return dataPages;
}
SignOn
Error codes for ui interaction.
Definition: uisessiondata.h:36