You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

379 lines
11 KiB

/***************************************************************************
* Copyright 2010 Artur Duque de Souza <asouza@kde.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
#include <QFile>
#include <QXmlStreamReader>
#include <krandom.h>
#include <QDebug>
#include <QTemporaryFile>
#include <KIO/MimetypeJob>
#include <KIO/FileJob>
#include <kjsembed/kjsembed.h>
#include <kjsembed/variant_binding.h>
#include <QImage>
#include <QPixmap>
#include "shareprovider.h"
#include "share_package.h"
Plasma::PackageStructure* ShareProvider::m_packageStructure(0);
ShareProvider::ShareProvider(KJSEmbed::Engine* engine, QObject *parent)
: QObject(parent), m_isBlob(false), m_isPost(true),
m_engine(engine)
{
// Just make the boundary random part long enough to be sure
// it's not inside one of the arguments that we are sending
m_boundary = "----------";
m_boundary += KRandom::randomString(55).toAscii();
}
QString ShareProvider::method() const
{
if (!m_isPost) {
return QString("GET");
}
return QString("POST");
}
void ShareProvider::setMethod(const QString &method)
{
if (method == "GET") {
m_isPost = false;
} else {
m_isPost = true;
}
}
QUrl ShareProvider::url() const
{
// the url that is set in this provider
return m_url;
}
void ShareProvider::setUrl(const QString &url)
{
// set the provider's url
m_url = url;
m_service = url;
}
QString ShareProvider::parseXML(const QString &key, const QString &data)
{
// this method helps plugins to parse results from webpages
QXmlStreamReader xml(data);
if (xml.hasError()) {
return QString();
}
while (!xml.atEnd()) {
xml.readNext();
if (xml.name() == key) {
QString url = xml.readElementText();
return url;
}
}
return QString();
}
void ShareProvider::addPostItem(const QString &key, const QString &value,
const QString &contentType)
{
if (!m_isPost)
return;
// add a pair <item,value> in a post form
QByteArray str;
QString length = QString::number(value.length());
str += "--";
str += m_boundary;
str += "\r\n";
if (!key.isEmpty()) {
str += "Content-Disposition: form-data; name=\"";
str += key.toAscii();
str += "\"\r\n";
}
if (!contentType.isEmpty()) {
str += "Content-Type: " + QByteArray(contentType.toAscii());
str += "\r\n";
str += "Mime-version: 1.0 ";
str += "\r\n";
}
str += "Content-Length: ";
str += length.toAscii();
str += "\r\n\r\n";
str += value.toUtf8();
m_buffer.append(str);
m_buffer.append("\r\n");
}
void ShareProvider::addPostFile(const QString &contentKey, const QVariant &content)
{
// add a file in a post form (gets it using KIO)
m_contentKey = contentKey;
if(content.type() == QVariant::String) {
m_content = content.toString();
addPostItem(m_contentKey, m_content, "text/plain");
addQueryItem(m_contentKey, m_content);
emit readyToPublish();
} else if(content.type() == QVariant::Url) {
publishUrl(content.toUrl());
} else if(content.type() == QVariant::Image) {
QTemporaryFile* file = new QTemporaryFile("shareimage-XXXXXX.png", this);
bool b = file->open();
Q_ASSERT(b);
file->close();
QImage image = content.value<QImage>();
b = image.save(file->fileName());
Q_ASSERT(b);
publishUrl(QUrl::fromLocalFile(file->fileName()));
} else if(content.type() == QVariant::Pixmap) {
QTemporaryFile* file = new QTemporaryFile("sharepixmap-XXXXXX.png", this);
bool b = file->open();
Q_ASSERT(b);
file->close();
QPixmap image = content.value<QPixmap>();
b = image.save(file->fileName());
Q_ASSERT(b);
publishUrl(QUrl::fromLocalFile(file->fileName()));
}
}
void ShareProvider::publishUrl(const QUrl& url)
{
m_content = url.toString();
KIO::MimetypeJob *mjob = KIO::mimetype(url, KIO::HideProgressInfo);
connect(mjob, SIGNAL(finished(KJob*)), this, SLOT(mimetypeJobFinished(KJob*)));
}
void ShareProvider::mimetypeJobFinished(KJob *job)
{
KIO::MimetypeJob *mjob = qobject_cast<KIO::MimetypeJob *>(job);
if (!job) {
return;
}
if (mjob->error()) {
qWarning() << "error when figuring out the file type";
return;
}
// It's a valid file because there were no errors
m_mimetype = mjob->mimetype();
if (m_mimetype.isEmpty()) {
// if we ourselves can't determine the mime of the file,
// very unlikely the remote site will be able to identify it
error(i18n("Could not detect the file's mimetype"));
return;
}
// If it's not text then we should handle it later
if (!m_mimetype.startsWith("text/"))
m_isBlob = true;
// try to open the file
KIO::FileJob *fjob = KIO::open(QUrl(m_content), QIODevice::ReadOnly);
connect(fjob, SIGNAL(open(KIO::Job*)), this, SLOT(openFile(KIO::Job*)));
}
void ShareProvider::openFile(KIO::Job *job)
{
// finished opening the file, now try to read it's content
KIO::FileJob *fjob = static_cast<KIO::FileJob*>(job);
fjob->read(fjob->size());
connect(fjob, SIGNAL(data(KIO::Job*,QByteArray)),
this, SLOT(finishedContentData(KIO::Job*,QByteArray)));
}
void ShareProvider::finishedContentData(KIO::Job *job, const QByteArray &data)
{
// Close the job as we don't need it anymore.
// NOTE: this is essential to ensure the job gets de-scheduled and deleted!
job->disconnect(this);
qobject_cast<KIO::FileJob *>(job)->close();
if (data.length() == 0) {
error(i18n("It was not possible to read the selected file"));
return;
}
uploadData(data);
}
void ShareProvider::uploadData(const QByteArray& data)
{
if (!m_isBlob) {
// it's just text and we can return here using data()
addPostItem(m_contentKey, QString::fromLocal8Bit(data), "text/plain");
addQueryItem(m_contentKey, QString::fromLocal8Bit(data));
emit readyToPublish();
return;
}
// Add the special http post stuff with the content of the file
QByteArray str;
const QString fileSize = QString::number(data.size());
str += "--";
str += m_boundary;
str += "\r\n";
str += "Content-Disposition: form-data; name=\"";
str += m_contentKey.toAscii();
str += "\"; ";
str += "filename=\"";
str += QFile::encodeName(QUrl(m_content).fileName()).replace(".tmp", ".jpg");
str += "\"\r\n";
str += "Content-Length: ";
str += fileSize.toAscii();
str += "\r\n";
str += "Content-Type: ";
str += m_mimetype.toAscii();
str += "\r\n\r\n";
m_buffer.append(str);
m_buffer.append(data);
m_buffer.append("\r\n");
// tell the world that we are ready to publish
emit readyToPublish();
}
void ShareProvider::readPublishData(KIO::Job *job, const QByteArray &data)
{
Q_UNUSED(job);
m_data.append(data);
}
void ShareProvider::finishedPublish(KJob *job)
{
Q_UNUSED(job);
if (m_data.length() == 0) {
error(i18n("Service was not available"));
return;
}
// process data. should be interpreted by the plugin.
// plugin must call the right slots after processing the data.
KJS::List kjsargs;
kjsargs.append( KJSEmbed::convertToValue(m_engine->interpreter()->execState(), m_data) );
m_engine->callMethod("handleResultData", kjsargs);
}
void ShareProvider::finishHeader()
{
QByteArray str;
str += "--";
str += m_boundary;
str += "--";
m_buffer.append(str);
}
void ShareProvider::addQueryItem(const QString &key, const QString &value)
{
// just add the item to the query's URL
m_url.addQueryItem(key, value);
}
void ShareProvider::publish()
{
if (m_url.isEmpty()) {
emit finishedError(i18n("You must specify a URL for this service"));
}
// clear the result data before publishing
m_data.clear();
// finish the http form
if (m_isBlob) {
finishHeader();
}
// Multipart is used to upload files
KIO::TransferJob *tf;
if (m_isBlob) {
tf = KIO::http_post(m_service, m_buffer, KIO::HideProgressInfo);
tf->addMetaData("content-type","Content-Type: multipart/form-data; boundary=" + m_boundary);
} else {
if (m_isPost) {
tf = KIO::http_post(m_service,
m_url.encodedQuery(), KIO::HideProgressInfo);
tf->addMetaData("content-type", "Content-Type: application/x-www-form-urlencoded");
} else {
QString url = QString("%1?%2").arg(m_service.url(), QString(m_url.encodedQuery()));
tf = KIO::get(QUrl(url));
}
}
connect(tf, SIGNAL(data(KIO::Job*,QByteArray)),
this, SLOT(readPublishData(KIO::Job*,QByteArray)));
connect(tf, SIGNAL(result(KJob*)), this, SLOT(finishedPublish(KJob*)));
connect(tf, SIGNAL(redirection(KIO::Job*,QUrl)),
this, SLOT(redirected(KIO::Job*,QUrl)));
}
void ShareProvider::redirected(KIO::Job *job, const QUrl &to)
{
Q_UNUSED(job)
const QUrl toUrl(to);
const QUrl serviceUrl(m_service);
const QString toString(toUrl.toString(QUrl::StripTrailingSlash));
const QString serviceString(serviceUrl.toString(QUrl::StripTrailingSlash));
if (toString == serviceString) {
return;
}
KJS::List kjsargs;
kjsargs.append( KJSEmbed::convertToValue(m_engine->interpreter()->execState(), toString) );
m_engine->callMethod("handleRedirection", kjsargs);
}
void ShareProvider::success(const QString &url)
{
// notify the service that it worked and the result url
emit finished(url);
}
void ShareProvider::error(const QString &msg)
{
// notify the service that it didnt work and the error msg
emit finishedError(msg);
}
Plasma::PackageStructure* ShareProvider::packageStructure()
{
if (!m_packageStructure) {
m_packageStructure = new SharePackage();
}
return m_packageStructure;
}
#include "shareprovider.moc"