#include "customuploader.hpp" #include #include #include #include #include #include #include #include #include using std::runtime_error; void error(QString absFilePath, QString err) { throw runtime_error((QString("Invalid file: ").append(absFilePath) + ": " + err).toStdString()); } CustomUploader::CustomUploader(QString absFilePath) { types.insert("PNG", "image/png"); // Is a list of supported formats, too types.insert("GIF", "image/gif"); types.insert("JPG", "image/jpeg"); types.insert("JPEG", "image/jpeg"); types.insert("WEBM", "video/webm"); types.insert("WEBM", "video/mp4"); // Let's go QFile file(absFilePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) error(absFilePath, file.errorString()); QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); if (!doc.isObject()) { error(absFilePath, "Root not an object"); } QJsonObject obj = doc.object(); if (!obj["name"].isString()) error(absFilePath, "name is not a string"); else uName = obj["name"].toString(); if (!obj.contains("desc")) { if (!obj["desc"].isString()) /*t*/ error(absFilePath, "desc not a string"); else desc = obj["desc"].toString(); } else desc = absFilePath; QJsonValue m = obj["method"]; if (!m.isUndefined() && !m.isNull()) { if (!m.isString()) error(absFilePath, "method not a string"); QString toCheck = m.toString().toLower(); if (toCheck == "post") method = HttpMethod::POST; else error(absFilePath, "method invalid"); } QJsonValue url = obj["target"]; if (!url.isString()) { error(absFilePath, "target missing"); } QUrl target(url.toString()); if (!target.isValid()) error(absFilePath, "target not URL"); this->target = target; QJsonValue formatValue = obj["format"]; if (!formatValue.isUndefined() && !formatValue.isNull()) { if (formatValue.isString()) { QString formatString = formatValue.toString().toLower(); if (formatString == "x-www-form-urlencoded") format = RequestFormat::X_WWW_FORM_URLENCODED; else if (formatString == "json") format = RequestFormat::JSON; else if (formatString == "plain") format = RequestFormat::PLAIN; else error(absFilePath, "format invalid"); } } else error(absFilePath, "format provided but not string"); QJsonValue imageValue = obj["imageformat"]; if (!imageValue.isString()) { error(absFilePath, "imageformat not string"); } QString imageFormat = imageValue.toString(); if (imageFormat == "base64" || QRegExp("base64\\([^+]+\\+[^+]+)").exactMatch(imageFormat) || QRegExp("[^+]+\\+[^+]+").exactMatch(imageFormat)) { this->iFormat = imageFormat; } else error(absFilePath, "imageformat invalid"); QJsonValue bodyValue = obj["body"]; if (format != RequestFormat::PLAIN) { if (bodyValue.isUndefined()) error(absFilePath, "body not set"); if (bodyValue.isObject()) body = bodyValue; else error(absFilePath, "body not object"); } else { if (bodyValue.isString()) { body = bodyValue; } else error(absFilePath, "body not string (reason: format: PLAIN)"); } QJsonValue headerVal = obj["headers"]; if (!(headerVal.isUndefined() || headerVal.isNull())) { if (!headerVal.isObject()) error(absFilePath, "headers must be object"); headers = headerVal.toObject(); } else headers = QJsonObject(); QJsonValue returnPsVal = obj["return"]; if (returnPsVal.isString()) { returnPathspec = returnPsVal.toString(); } else error(absFilePath, "return invalid"); } QString CustomUploader::name() { return uName; } QString CustomUploader::description() { return desc; } QString getCType(RequestFormat format, QString plainType) { switch (format) { case RequestFormat::X_WWW_FORM_URLENCODED: return "application/x-www-form-urlencoded"; case RequestFormat::JSON: return "application/json"; case RequestFormat::PLAIN: return plainType; } return plainType; } QList> getHeaders(QJsonObject h, QString imageFormat, QMap types, RequestFormat format) { QList> headers; for (QString s : h.keys()) { if (s.toLower() == "content-type") continue; QJsonValue v = h[s]; if (!v.isString()) headers << QPair(s, QJsonDocument::fromVariant(v.toVariant()).toJson()); else headers << QPair(s, v.toString()); } headers << QPair("Content-Type", getCType(format, types.value(imageFormat))); return headers; } QByteArray imageBytes(QPixmap *pixmap, QString format) { QByteArray returnVal; QBuffer buff(&returnVal); buff.open(QIODevice::WriteOnly); pixmap->save(&buff, format.toUpper().toLocal8Bit().constData()); return returnVal; } QString CustomUploader::getFormatString(bool animated) { if (iFormat == "base64") return animated ? "GIF" : "PNG"; else if (QRegExp("[^+]+\\+[^+]+").exactMatch(iFormat)) return iFormat.split('+')[(int)animated]; else if (QRegExp("base64\\([^+]+\\+[^+]+)").exactMatch(iFormat)) return iFormat.mid(7, iFormat.length() - 8).split('+')[(int)animated]; return ""; } QJsonObject recurseAndReplace(QJsonObject &body, QByteArray &data, QString contentType) { QJsonObject o; for (QString s : body.keys()) { QJsonValue v = body[s]; if (v.isObject()) { QJsonObject vo = v.toObject(); o.insert(s, recurseAndReplace(vo, data, contentType)); } else if (v.isString()) { QString str = v.toString(); if (str.startsWith("/") && str.endsWith("/")) { o.insert(s, str.replace("%image", data).replace("%contenttype", contentType)); } } } return o; } QString parsePathspec(QJsonDocument &response, QString &pathspec) { if (!pathspec.startsWith(".")) { // Does not point to anything return ""; } else { if (!response.isObject()) return ""; QStringList fields = pathspec.right(pathspec.length() - 1).split('.', QString::SkipEmptyParts); QJsonObject o = response.object(); if (pathspec == ".") { return QString::fromUtf8(response.toJson()); } QJsonValue val = o[fields.at(0)]; if (val.isUndefined() || val.isNull()) return ""; else if (val.isString()) return val.toString(); else if (!val.isObject()) return QString::fromUtf8(QJsonDocument::fromVariant(val.toVariant()).toJson()); for (int i = 1; i < fields.size(); i++) { if (val.isUndefined() || val.isNull()) return ""; else if (val.isString()) return val.toString(); else if (!val.isObject()) return QString::fromUtf8(QJsonDocument::fromVariant(val.toVariant()).toJson()); else val = val.toObject()[fields.at(i)]; } if (val.isUndefined() || val.isNull()) return ""; else if (val.isString()) return val.toString(); else if (!val.isObject()) return QString::fromUtf8(QJsonDocument::fromVariant(val.toVariant()).toJson()); } return ""; } void parseResult(QJsonDocument result, QString returnPathspec, QString name) { if (result.isObject()) { QString url = parsePathspec(result, returnPathspec); if (!url.isEmpty()) { QApplication::clipboard()->setText(url); notifications::notify("KShare Custom Uploader " + name, "Copied upload link to clipboard!"); } else notifications::notify("KShare Custom Uploader " + name, "Upload done, but result empty!"); } } void CustomUploader::doUpload(QPixmap *pixmap) { auto h = getHeaders(headers, getFormatString(false), types, this->format); QString format = getFormatString(false); // Soon:tm: QByteArray data; QByteArray imgData = imageBytes(pixmap, format); if (iFormat == "base64" || QRegExp("base64\\([^+]\\+[^+]\\)").exactMatch(iFormat)) imgData = imgData.toBase64(); switch (this->format) { case RequestFormat::PLAIN: { data = imgData; } break; case RequestFormat::JSON: { if (body.isString()) { QStringList split = body.toString().replace("%contenttype", types.value(format)).split("%imagedata"); for (int i = 0; i < split.size(); i++) { data.append(split[i]); if (i < split.size() - 1) data.append(imgData); } } else { QJsonObject vo = body.toObject(); data = QJsonDocument::fromVariant(recurseAndReplace(vo, imgData, types.value(format)).toVariantMap()).toJson(); } } break; case RequestFormat::X_WWW_FORM_URLENCODED: { QJsonObject body = this->body.toObject(); for (QString key : body.keys()) { QJsonValue val = body[key]; if (val.isString()) { QString str = val.toString(); QByteArray strB; if (str.startsWith("/") && str.endsWith("/")) { str = str.mid(1, str.length() - 2); QStringList split = str.replace("%contenttype", types.value(format)).split("%imagedata"); for (int i = 0; i < split.size(); i++) { strB.append(split[i]); if (i < split.size() - 1) strB.append(imgData); } } data.append(QUrl::toPercentEncoding(key)).append('=').append(strB); } else { if (!data.isEmpty()) data.append('&'); data.append(QUrl::toPercentEncoding(key)) .append('=') .append(QUrl::toPercentEncoding(QJsonDocument::fromVariant(body[key].toVariant()).toJson())); } } } break; } switch (method) { case HttpMethod::POST: if (returnPathspec == "|") { ioutils::postData(target, h, data, [&](QByteArray result, QNetworkReply *) { QApplication::clipboard()->setText(QString::fromUtf8(result)); notifications::notify("KShare Custom Uploader " + name(), "Copied upload result to clipboard!"); }); } else { ioutils::postJson(target, h, data, [&](QJsonDocument result, QNetworkReply *) { parseResult(result, returnPathspec, name()); }); } break; } }