/*************************************************************************** * Copyright 2010 Artur Duque de Souza * * * * 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 #include #include #include #include #include #include "shareprovider.h" #include "share_package.h" Plasma::PackageStructure::Ptr ShareProvider::m_packageStructure(0); ShareProvider::ShareProvider(QObject *parent) : QObject(parent), m_isBlob(false), m_isPost(true) { // 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; } } KUrl 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 in a post form QByteArray str; QString length = QString("%1").arg(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 QString &content) { // add a file in a post form (gets it using KIO) m_contentKey = contentKey; m_content = content; // we expect either text or an URL of a file. The file can be a text file // that is an exception that we handle in this case. So we have basically // three use cases: // 1 - just text // 2 - a file that is a text // 3 - other kind of files (images, pdf, etc..) // // The applet using this engine must ensure that the provider selected // supports the file format that is added here as a parameter, otherwise // it will return as an error later in the process. KUrl url(m_content); 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(job); if (!job) { return; } if (mjob->error()) { // it's not a file - usually this happens when we are // just sharing plain text, so add the content and publish it addPostItem(m_contentKey, m_content, "text/plain"); addQueryItem(m_contentKey, m_content); emit readyToPublish(); 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.indexOf("text/") != 0) m_isBlob = true; // try to open the file KIO::FileJob *fjob = KIO::open(KUrl(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(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); static_cast(job)->close(); if (data.length() == 0) { error(i18n("It was not possible to read the selected file")); return; } 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("%1").arg(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(KUrl(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. emit handleResultData(QString(m_data)); } 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 == "") { 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", QLatin1String("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(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*,KUrl)), this, SLOT(redirected(KIO::Job*,KUrl))); } void ShareProvider::redirected(KIO::Job *job, const KUrl &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; } emit handleRedirection(toString); } 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::Ptr ShareProvider::packageStructure() { if (!m_packageStructure) { m_packageStructure = new SharePackage(); } return m_packageStructure; } #include "moc_shareprovider.cpp"