X-Git-Url: https://git.sven.stormbind.net/?a=blobdiff_plain;f=src%2Fconfluence-agent.cpp;fp=src%2Fconfluence-agent.cpp;h=18251988448f158317d652e05b1a68e11dbf7a0e;hb=d483bd8e6523c23c6f1d8908a2e0611c2bc9ff4f;hp=0000000000000000000000000000000000000000;hpb=7dfa3fe589d1722d49681f42cdb0bf1e6efb5223;p=sven%2Fvym.git diff --git a/src/confluence-agent.cpp b/src/confluence-agent.cpp new file mode 100644 index 0000000..1825198 --- /dev/null +++ b/src/confluence-agent.cpp @@ -0,0 +1,1014 @@ +#include "confluence-agent.h" + +#include +#include + +#include // FIXME-2 for debugging... + +#include "branchitem.h" +#include "confluence-user.h" +#include "file.h" +#include "mainwindow.h" +#include "misc.h" +#include "vymmodel.h" +#include "warningdialog.h" + +extern Main *mainWindow; +extern QDir vymBaseDir; +extern QString confluencePassword; +extern Settings settings; +extern bool debug; + +bool ConfluenceAgent::available() +{ + if (!QSslSocket::supportsSsl()) + return false; + if ( settings.value("/atlassian/confluence/username", "").toString().isEmpty()) + return false; + + if ( settings.value("/atlassian/confluence/url", "").toString().isEmpty()) + return false; + + return true; +} + +ConfluenceAgent::ConfluenceAgent() { + //qDebug() << "Constr. ConfluenceAgent jobType="; + init(); +} + +ConfluenceAgent::ConfluenceAgent(BranchItem *bi) +{ + //qDebug() << "Constr. ConfluenceAgent selbi = " << bi; + + if (!bi) { + qWarning("Const ConfluenceAgent: bi == nullptr"); + // This will leave the agent hanging around undeleted... + return; + } + + init(); + + setBranch(bi); +} + +ConfluenceAgent::~ConfluenceAgent() +{ + // qDebug() << "Destr ConfluenceAgent." << jobType; + if (killTimer) + delete killTimer; +} + +void ConfluenceAgent::init() +{ + jobType = Undefined; + jobStep = -1; + abortJob = false; + + killTimer = nullptr; + + networkManager = new QNetworkAccessManager(this); + + modelID = 0; // invalid ID + + killTimer = new QTimer(this); + killTimer->setInterval(15000); + killTimer->setSingleShot(true); + + QObject::connect(killTimer, SIGNAL(timeout()), this, SLOT(timeout())); + + apiURL = baseURL + "/rest/api"; + baseURL = settings.value("/atlassian/confluence/url", "baseURL").toString(); + + // Attachments + attachmentsAgent = nullptr; + currentUploadAttachmentIndex = -1; + + // Read credentials + authUsingPAT = + settings.value("/atlassian/confluence/authUsingPAT", true).toBool(); + if (authUsingPAT) + personalAccessToken = + settings.value("/atlassian/confluence/PAT", "undefined").toString(); + else { + username = + settings.value("/atlassian/confluence/username", "user_johnDoe").toString(); + if (!confluencePassword.isEmpty()) + password = confluencePassword; + else + password = + settings.value("/atlassian/confluence/password", "").toString(); + } + + if (!authUsingPAT && password.isEmpty()) { + // Set global password + if (!mainWindow->settingsConfluence()) + abortJob = true; + } +} + +void ConfluenceAgent::setJobType(JobType jt) +{ + jobType = jt; +} + +void ConfluenceAgent::setBranch(BranchItem *bi) +{ + if (!bi) { + qWarning() << "ConfluenceAgent::setBranch bi == nullptr"; + abortJob = true; + } else { + branchID = bi->getID(); + VymModel *model = bi->getModel(); + modelID = model->getModelID(); + } +} + +void ConfluenceAgent::setModelID(uint id) +{ + modelID = id; +} + +void ConfluenceAgent::setPageURL(const QString &u) +{ + pageURL = u; +} + +void ConfluenceAgent::setNewPageName(const QString &t) +{ + newPageName = t; +} + +void ConfluenceAgent::setUploadPagePath(const QString &fp) +{ + uploadPagePath = fp; +} + +void ConfluenceAgent::addUploadAttachmentPath(const QString &fp) +{ + uploadAttachmentPaths << fp; +} + +void ConfluenceAgent::startJob() +{ + if (jobStep > 0) { + unknownStepWarningFinishJob(); + } else { + jobStep = 0; + continueJob(); + } +} + +void ConfluenceAgent::continueJob(int nextStep) +{ + if (abortJob) { + finishJob(); + return; + } + + if (nextStep < 0) + jobStep++; + else + jobStep = nextStep; + + VymModel *model; + + // qDebug() << "CA::contJob " << jobType << " Step: " << jobStep; + + switch(jobType) { + case CopyPagenameToHeading: + if (jobStep == 1) { + startGetPageSourceRequest(pageURL); + return; + } + if (jobStep == 2) { + startGetPageDetailsRequest(); + return; + } + if (jobStep == 3) { + model = mainWindow->getModel(modelID); + if (model) { + BranchItem *bi = (BranchItem *)(model->findID(branchID)); + + if (bi) { + QString h = spaceKey + ": " + pageObj["title"].toString(); + model->setHeading(h, bi); + } else + qWarning() << "CA::continueJob couldn't find branch " + << branchID; + } else + qWarning() << "CA::continueJob couldn't find model " << modelID; + finishJob(); + return; + } + unknownStepWarningFinishJob(); + return; + + case CreatePage: + if (jobStep == 1) { + if (pageURL.isEmpty()) { + qWarning() << "CA::contJob NewPage: pageURL is empty"; + finishJob(); + return; + } + if (newPageName.isEmpty()) { + qWarning() << "CA::contJob NewPage: newPageName is empty"; + finishJob(); + return; + } + + mainWindow->statusMessage( + QString("Starting to create Confluence page %1").arg(pageURL)); + + // Check if parent page with url already exists and get pageID, spaceKey + startGetPageSourceRequest(pageURL); + return; + } + if (jobStep == 2) { + // Create new page with parent url + startCreatePageRequest(); + return; + } + if (jobStep == 3) { + + pageID = pageObj["id"].toString(); + + // Upload attachments? + if (uploadAttachmentPaths.count() > 0) { + attachmentsAgent = new ConfluenceAgent; + attachmentsAgent->setJobType(ConfluenceAgent::UploadAttachments); + attachmentsAgent->pageID = pageID; + attachmentsAgent->uploadAttachmentPaths = uploadAttachmentPaths; + + connect(attachmentsAgent, &ConfluenceAgent::attachmentsSuccess, + this, &ConfluenceAgent::attachmentsUploadSuccess); + connect(attachmentsAgent, &ConfluenceAgent::attachmentsFailure, + this, &ConfluenceAgent::attachmentsUploadFailure); + attachmentsAgent->startJob(); + return; + } else + // Proceed to next step + jobStep = 4; + } + if (jobStep == 4) { + //qDebug() << "CA::finished Created page with ID: " << pageObj["id"].toString(); + mainWindow->statusMessage( + QString("Created Confluence page %1").arg(pageURL)); + finishJob(); + return; + } + unknownStepWarningFinishJob(); + return; + + case UpdatePage: + if (jobStep == 1) { + if (pageURL.isEmpty()) { + qWarning() << "CA::contJob UpdatePage: pageURL is empty"; + finishJob(); + return; + } + + mainWindow->statusMessage( + QString("Starting to update Confluence page %1").arg(pageURL)); + + // Check if page with url already exists and get pageID, spaceKey + startGetPageSourceRequest(pageURL); + return; + } + if (jobStep == 2) { + // Get title, which is required by Confluence to update a page + startGetPageDetailsRequest(); + return; + } + if (jobStep == 3) { + // Upload attachments? + if (uploadAttachmentPaths.count() > 0) { + attachmentsAgent = new ConfluenceAgent; + attachmentsAgent->setJobType(ConfluenceAgent::UploadAttachments); + attachmentsAgent->pageID = pageID; + attachmentsAgent->uploadAttachmentPaths = uploadAttachmentPaths; + + connect(attachmentsAgent, &ConfluenceAgent::attachmentsSuccess, + this, &ConfluenceAgent::attachmentsUploadSuccess); + connect(attachmentsAgent, &ConfluenceAgent::attachmentsFailure, + this, &ConfluenceAgent::attachmentsUploadFailure); + attachmentsAgent->startJob(); + return; + } + } + if (jobStep == 4) { + // Update page with parent url + if (newPageName.isEmpty()) + newPageName = pageObj["title"].toString(); + startUpdatePageRequest(); + return; + } + if (jobStep == 5) { + //qDebug() << "CA::finished Updated page with ID: " << pageObj["id"].toString(); + mainWindow->statusMessage( + QString("Updated Confluence page %1").arg(pageURL)); + finishJob(); + return; + } + unknownStepWarningFinishJob(); + return; + + case GetUserInfo: + if (jobStep == 1) { + // qDebug() << "CA:: begin getting UserInfo"; + startGetUserInfoRequest(); + return; + } + if (jobStep == 2) { + QJsonArray array = pageObj["results"].toArray(); + QJsonObject userObj; + QJsonObject u; + ConfluenceUser user; + userList.clear(); + for (int i = 0; i < array.size(); ++i) { + userObj = array[i].toObject(); + + u = userObj["user"].toObject(); + user.setTitle( userObj["title"].toString()); + user.setURL( "https://" + baseURL + "/" + + "display/~" + u["username"].toString()); + user.setUserKey( u["userKey"].toString()); + user.setUserName( u["username"].toString()); + user.setDisplayName( u["displayName"].toString()); + userList << user; + } + emit (foundUsers(userList)); + finishJob(); + return; + } + unknownStepWarningFinishJob(); + return; + + case UploadAttachments: + if (jobStep == 1) { + + if (uploadAttachmentPaths.count() <= 0) { + qWarning() << "ConfluenceAgent: No attachments to upload!"; + emit(attachmentsFailure()); + finishJob(); + return; + } + + // Prepare to upload first attachment in list + currentUploadAttachmentIndex = 0; + + // Try to get info for attachments + startGetAttachmentsInfoRequest(); + return; + } + if (jobStep == 2) { + // Entry point for looping over list of attachments to upload + + if (currentUploadAttachmentIndex >= uploadAttachmentPaths.count()) { + // All uploaded, let's finish uploading + emit(attachmentsSuccess()); + finishJob(); + } else { + currentAttachmentPath = uploadAttachmentPaths.at(currentUploadAttachmentIndex); + currentAttachmentTitle = basename(currentAttachmentPath); + + // Create attachment with image of map, if required + if (attachmentsTitles.count() == 0 || + !attachmentsTitles.contains(currentAttachmentTitle)) { + // Create new attachment + startCreateAttachmentRequest(); + } else { + // Update existing attachment + startUpdateAttachmentRequest(); + } + } + return; + } + unknownStepWarningFinishJob(); + return; + + default: + qWarning() << "ConfluenceAgent::continueJob unknown jobType " << jobType; + } +} + +void ConfluenceAgent::finishJob() +{ + deleteLater(); +} + +void ConfluenceAgent::unknownStepWarningFinishJob() +{ + qWarning() << "CA::contJob unknow step in jobType = " + << jobType + << "jobStep = " << jobStep; + finishJob(); +} + +void ConfluenceAgent::getUsers(const QString &usrQuery) +{ + userQuery = usrQuery; + if (usrQuery.contains(QRegExp("\\W+"))) { + qWarning() << "ConfluenceAgent::getUsers Forbidden characters in " << usrQuery; + return; + } + + setJobType(GetUserInfo); + startJob(); +} + +QNetworkRequest ConfluenceAgent::createRequest(const QUrl &url) +{ + QNetworkRequest request = QNetworkRequest(url); + + QString headerData; + if (authUsingPAT) + headerData = QString("Bearer %1").arg(personalAccessToken); + else { + QString concatenated = username + ":" + password; + QByteArray data = concatenated.toLocal8Bit().toBase64(); + headerData = "Basic " + data; + } + request.setRawHeader("Authorization", headerData.toLocal8Bit()); + + return request; +} + +void ConfluenceAgent::startGetPageSourceRequest(QUrl requestedURL) +{ + //qDebug() << "CA::startGetPageSourceRequest " << requestedURL; + if (!requestedURL.toString().startsWith("http")) + requestedURL.setPath("https://" + requestedURL.path()); + + QUrl url = requestedURL; + + QNetworkRequest request = createRequest(url); + + if (debug) + qDebug() << "CA::startGetPageSourceRequest: url = " + request.url().toString(); + + killTimer->start(); + + connect(networkManager, &QNetworkAccessManager::finished, + this, &ConfluenceAgent::pageSourceReceived); + + networkManager->get(request); +} + +void ConfluenceAgent::pageSourceReceived(QNetworkReply *reply) +{ + if (debug) qDebug() << "CA::pageSourceReceived"; + + killTimer->stop(); + networkManager->disconnect(); + reply->deleteLater(); + + QByteArray fullReply = reply->readAll(); + if (!wasRequestSuccessful(reply, "receive page source", fullReply)) + return; + + // Find pageID + QRegExp rx("\\sname=\"ajs-page-id\"\\scontent=\"(\\d*)\""); + rx.setMinimal(true); + + if (rx.indexIn(fullReply, 0) != -1) { + pageID = rx.cap(1); + } + else { + qWarning() + << "ConfluenceAgent::pageSourceReveived Couldn't find page ID"; + //qWarning() << fullReply; + return; + } + + // Find spaceKey + rx.setPattern("meta\\s*id=\"confluence-space-key\"\\s* " + "name=\"confluence-space-key\"\\s*content=\"(.*)\""); + if (rx.indexIn(fullReply, 0) != -1) { + spaceKey = rx.cap(1); + } + else { + qWarning() << "ConfluenceAgent::pageSourceReveived Couldn't find " + "space key in response"; + qWarning() << fullReply; + finishJob(); + return; + } + + const QVariant redirectionTarget = + reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + + continueJob(); +} + +void ConfluenceAgent::startGetPageDetailsRequest() +{ + if (debug) qDebug() << "CA::startGetPageDetailsRequest" << pageID; + + // Authentication in URL (only SSL!) + QString url = "https://" + + baseURL + apiURL + + "/content/" + pageID + "?expand=metadata.labels,version"; + + QNetworkRequest request = createRequest(url); + + connect(networkManager, &QNetworkAccessManager::finished, + this, &ConfluenceAgent::pageDetailsReceived); + + killTimer->start(); + + networkManager->get(request); +} + +void ConfluenceAgent::pageDetailsReceived(QNetworkReply *reply) +{ + if (debug) qDebug() << "CA::pageDetailsReceived"; + + killTimer->stop(); + networkManager->disconnect(); + reply->deleteLater(); + + QByteArray fullReply = reply->readAll(); + if (!wasRequestSuccessful(reply, "receive page details", fullReply)) + return; + + QJsonDocument jsdoc; + jsdoc = QJsonDocument::fromJson(fullReply); + + pageObj = jsdoc.object(); + // cout << jsdoc.toJson(QJsonDocument::Indented).toStdString(); + + continueJob(); +} + +void ConfluenceAgent::startCreatePageRequest() +{ + // qDebug() << "CA::startCreatePageRequest"; + + QString url = "https://" + baseURL + apiURL + "/content"; + + QNetworkRequest request = createRequest(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QJsonObject payload; + payload["type"] = "page"; + payload["title"] = newPageName; + + // Build array with ID of parent page + QJsonObject ancestorsID; + ancestorsID["id"] = pageID; + QJsonArray ancestorsArray; + ancestorsArray.append(ancestorsID); + payload["ancestors"] = ancestorsArray; + + // Build object with space key + QJsonObject skey; + skey["key"] = spaceKey; + payload["space"] = skey; + + // Build body + QString body; + if (!loadStringFromDisk(uploadPagePath, body)) + { + qWarning() << "ConfluenceAgent: Couldn't read file to upload:" << uploadPagePath; + finishJob(); + return; + } + + QJsonObject innerStorageObj + { + {"value", body}, + {"representation", "storage"} + }; + QJsonObject outerStorageObj; + outerStorageObj["storage"] = innerStorageObj; + payload["body"] = outerStorageObj; + + QJsonDocument doc(payload); + QByteArray data = doc.toJson(); + + connect(networkManager, &QNetworkAccessManager::finished, + this, &ConfluenceAgent::pageUploaded); + + killTimer->start(); + + networkManager->post(request, data); +} + +void ConfluenceAgent::startUpdatePageRequest() +{ + if (debug) qDebug() << "CA::startUpdatePageRequest"; + + QString url = "https://" + baseURL + apiURL + "/content" + "/" + pageID; + + QNetworkRequest request = createRequest(url); + + request.setHeader( + QNetworkRequest::ContentTypeHeader, + "application/json; charset=utf-8"); + + QJsonObject payload; + payload["id"] = pageID; + payload["type"] = "page"; + payload["title"] = newPageName; + + // Build version object + QJsonObject newVersionObj; + QJsonObject oldVersionObj = pageObj["version"].toObject(); + + newVersionObj["number"] = oldVersionObj["number"].toInt() + 1; + payload["version"] = newVersionObj; + + // Build object with space key + QJsonObject skey; + skey["key"] = spaceKey; + payload["space"] = skey; + + // Build body + QString body; + if (!loadStringFromDisk(uploadPagePath, body)) + { + qWarning() << "ConfluenceAgent: Couldn't read file to upload:" << uploadPagePath; + finishJob(); + return; + } + + QJsonObject innerStorageObj + { + {"value", body}, + {"representation", "storage"} + }; + QJsonObject outerStorageObj; + outerStorageObj["storage"] = innerStorageObj; + payload["body"] = outerStorageObj; + + QJsonDocument doc(payload); + QByteArray data = doc.toJson(); + + connect(networkManager, &QNetworkAccessManager::finished, + this, &ConfluenceAgent::pageUploaded); + + killTimer->start(); + + networkManager->put(request, data); +} + +void ConfluenceAgent::pageUploaded(QNetworkReply *reply) +{ + if (debug) qDebug() << "CA::pageUploaded"; + + killTimer->stop(); + networkManager->disconnect(); + reply->deleteLater(); + + QByteArray fullReply = reply->readAll(); + if (!wasRequestSuccessful(reply, "upload page", fullReply)) + return; + + QJsonDocument jsdoc; + jsdoc = QJsonDocument::fromJson(fullReply); + pageObj = jsdoc.object(); + //cout << jsdoc.toJson(QJsonDocument::Indented).toStdString(); + continueJob(); +} + +void ConfluenceAgent::startGetUserInfoRequest() +{ + if (debug) qDebug() << "CA::startGetInfoRequest for " << userQuery; + + QString url = "https://" + baseURL + apiURL + + "/search?cql=user.fullname~" + userQuery; + + networkManager->disconnect(); + + QNetworkRequest request = createRequest(url); + + connect(networkManager, &QNetworkAccessManager::finished, + this, &ConfluenceAgent::userInfoReceived); + + killTimer->start(); + + networkManager->get(request); +} + +void ConfluenceAgent::userInfoReceived(QNetworkReply *reply) +{ + if (debug) qDebug() << "CA::UserInfopageReceived"; + + killTimer->stop(); + networkManager->disconnect(); + reply->deleteLater(); + + QByteArray fullReply = reply->readAll(); + if (!wasRequestSuccessful(reply, "receive user info", fullReply)) + return; + + QJsonDocument jsdoc; + jsdoc = QJsonDocument::fromJson(fullReply); + pageObj = jsdoc.object(); + continueJob(); +} + +void ConfluenceAgent::startGetAttachmentsInfoRequest() +{ + if (debug) qDebug() << "CA::startGetAttachmentIdRequest"; + + QString url = "https://" + baseURL + apiURL + "/content" + "/" + pageID + "/child/attachment"; + + QNetworkRequest request = createRequest(url); + request.setRawHeader("X-Atlassian-Token", "no-check"); + + connect(networkManager, &QNetworkAccessManager::finished, + this, &ConfluenceAgent::attachmentsInfoReceived); + + killTimer->start(); + + QNetworkReply *reply = networkManager->get(request); +} + +void ConfluenceAgent::attachmentsInfoReceived(QNetworkReply *reply) +{ + if (debug) qDebug() << "CA::attachmentsInfoReceived"; + + killTimer->stop(); + networkManager->disconnect(); + reply->deleteLater(); + + QByteArray fullReply = reply->readAll(); + if (!wasRequestSuccessful(reply, "get attachment info", fullReply)) + return; + + QJsonDocument jsdoc; + jsdoc = QJsonDocument::fromJson(fullReply); + + attachmentObj = jsdoc.object(); + int attachmentsCount = jsdoc["size"].toInt(); + //cout << jsdoc.toJson(QJsonDocument::Indented).toStdString(); + for (int i = 0; i < attachmentsCount; i++) { + attachmentsTitles << jsdoc["results"][i]["title"].toString(); + attachmentsIds << jsdoc["results"][i]["id"].toString(); + //qDebug() << " Title: " << attachmentsTitles.last() << + // " Id: " << attachmentsIds.last(); + } + + continueJob(); +} + +void ConfluenceAgent::startCreateAttachmentRequest() +{ + if (debug) qDebug() << "CA::startCreateAttachmentRequest"; + + QString url = "https://" + baseURL + apiURL + "/content" + "/" + pageID + "/child/attachment"; + + QNetworkRequest request = createRequest(url); + request.setRawHeader("X-Atlassian-Token", "no-check"); + + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + + QHttpPart imagePart; + imagePart.setHeader( + QNetworkRequest::ContentDispositionHeader, + + // Name must be "file" + QVariant( + QString("form-data; name=\"file\"; filename=\"%1\"") + .arg(currentAttachmentTitle))); + imagePart.setHeader( + QNetworkRequest::ContentTypeHeader, + QVariant("image/jpeg")); + + QFile *file = new QFile(currentAttachmentPath); + if (!file->open(QIODevice::ReadOnly)) { + qWarning() << "Problem opening attachment: " << currentAttachmentPath; + QMessageBox::warning( + nullptr, tr("Warning"), + QString("Could not open attachment file \"%1\" in page with ID: %2").arg(currentAttachmentTitle).arg(pageID)); + finishJob(); + return; + } + imagePart.setBodyDevice(file); + /* + qDebug() << " title=" << currentAttachmentTitle; + qDebug() << " path=" << currentAttachmentPath; + qDebug() << " url=" << url; + qDebug() << " file size=" << file->size(); + */ + multiPart->append(imagePart); + file->setParent(multiPart); // delete later with the multiPart + + connect(networkManager, &QNetworkAccessManager::finished, + this, &ConfluenceAgent::attachmentCreated); + + killTimer->start(); + + QNetworkReply *reply = networkManager->post(request, multiPart); + + multiPart->setParent(reply); +} + +void ConfluenceAgent::attachmentCreated(QNetworkReply *reply) +{ + if (debug) qDebug() << "CA::attachmentCreated"; + + killTimer->stop(); + networkManager->disconnect(); + reply->deleteLater(); + + QByteArray fullReply = reply->readAll(); + if (reply->error() == QNetworkReply::ProtocolInvalidOperationError) { + if (fullReply.contains( + QString("Cannot add a new attachment with same file name as an existing attachment").toLatin1())) { + // Replace existing attachment + qWarning() << "Attachment with name " << currentAttachmentTitle << " already exists."; + qWarning() << "AttachmentID unknown, stopping now"; + + finishJob(); + return; + } + if (!wasRequestSuccessful(reply, "create attachment", fullReply)) + return; + } + + QJsonDocument jsdoc; + jsdoc = QJsonDocument::fromJson(fullReply); + attachmentObj = jsdoc.object(); + + //qDebug() << "CA::attachmentCreated Successful:"; + //cout << jsdoc.toJson(QJsonDocument::Indented).toStdString(); + //cout << attachmentObj["results"].toArray().toStdString(); + + currentUploadAttachmentIndex++; + + continueJob(2); +} + +void ConfluenceAgent::startUpdateAttachmentRequest() +{ + if (debug) qDebug() << "CA::startUpdateAttachmentRequest"; + + for (int i = 0; i < attachmentsTitles.count(); i++) { + // qDebug() << " - " << attachmentsTitles.at(i); + if (attachmentsTitles.at(i) == currentAttachmentTitle) { + currentAttachmentId = attachmentsIds.at(i); + break; + } + } + + if (currentAttachmentId.isEmpty()) { + QMessageBox::warning( + nullptr, tr("Warning"), + QString("Could not find existing attachment \"%1\" in page with ID: %2").arg(currentAttachmentTitle).arg(pageID)); + finishJob(); + return; + } + + QString url = "https://" + baseURL + apiURL + "/content" + "/" + pageID + "/child/attachment/" + currentAttachmentId + "/data"; + + QNetworkRequest request = createRequest(url); + request.setRawHeader("X-Atlassian-Token", "no-check"); + + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + QHttpPart imagePart; + imagePart.setHeader( + QNetworkRequest::ContentDispositionHeader, + + // Name must be "file" + QVariant( + QString("form-data; name=\"file\"; filename=\"%1\"") + .arg(currentAttachmentTitle))); + imagePart.setHeader( + QNetworkRequest::ContentTypeHeader, + QVariant("image/jpeg")); + + QFile *file = new QFile(currentAttachmentPath); + if (!file->open(QIODevice::ReadOnly)) { + qWarning() << "Problem opening attachment: " << currentAttachmentPath; + QMessageBox::warning( + nullptr, tr("Warning"), + QString("Could not open attachment file \"%1\" in page with ID: %2").arg(currentAttachmentTitle).arg(pageID)); + finishJob(); + return; + } + imagePart.setBodyDevice(file); + /* + qDebug() << " title=" << currentAttachmentTitle; + qDebug() << " path=" << currentAttachmentPath; + qDebug() << " url=" << url; + qDebug() << " file size=" << file->size(); + */ + multiPart->append(imagePart); + file->setParent(multiPart); + + connect(networkManager, &QNetworkAccessManager::finished, + this, &ConfluenceAgent::attachmentUpdated); + + killTimer->start(); + + QNetworkReply *reply = networkManager->post(request, multiPart); + + multiPart->setParent(reply); +} + +void ConfluenceAgent::attachmentUpdated(QNetworkReply *reply) +{ + if (debug) qDebug() << "CA::attachmentUpdated"; + + killTimer->stop(); + networkManager->disconnect(); + reply->deleteLater(); + + QByteArray fullReply = reply->readAll(); + if (!wasRequestSuccessful(reply, "update attachment", fullReply)) + return; + + QJsonDocument jsdoc; + jsdoc = QJsonDocument::fromJson(fullReply); + attachmentObj = jsdoc.object(); + + //cout << jsdoc.toJson(QJsonDocument::Indented).toStdString(); + + currentUploadAttachmentIndex++; + + continueJob(2); +} + +void ConfluenceAgent::attachmentsUploadSuccess() // slot called from attachmentsAgent +{ + continueJob(); +} + +void ConfluenceAgent::attachmentsUploadFailure() // slot called from attachmentsAgent +{ + qWarning() << "CA::attachmentsUpload failed"; + finishJob(); +} + +bool ConfluenceAgent::wasRequestSuccessful(QNetworkReply *reply, const QString &requestDesc, const QByteArray &fullReply) +{ + if (reply->error()) { + + // Additionally print full error on console + qWarning() << " Step: " << requestDesc; + qWarning() << " Error: " << reply->error(); + qWarning() << " Errorstring: " << reply->errorString(); + + qDebug() << " Request Url: " << reply->url() ; + qDebug() << " Operation: " << reply->operation() ; + + qDebug() << " readAll: "; + QJsonDocument jsdoc; + jsdoc = QJsonDocument::fromJson(fullReply); + QString fullReplyFormatted = QString(jsdoc.toJson(QJsonDocument::Indented)); + cout << fullReplyFormatted.toStdString(); + + /* + qDebug() << "Request headers: "; + QList reqHeaders = reply->rawHeaderList(); + foreach( QByteArray reqName, reqHeaders ) + { + QByteArray reqValue = reply->rawHeader( reqName ); + qDebug() << " " << reqName << ": " << reqValue; + } + */ + + if (reply->error() == QNetworkReply::AuthenticationRequiredError) + QMessageBox::warning( + nullptr, tr("Warning"), + tr("Authentication problem when contacting Confluence") + "\n\n" + + requestDesc); + else { + QString msg = QString("QNetworkReply error when trying to \"%1\"\n\n").arg(requestDesc); + WarningDialog warn; + warn.setText(msg + "\n\n" + fullReplyFormatted); + warn.showCancelButton(false); + warn.exec(); + } + + finishJob(); + return false; + } else + return true; +} + +void ConfluenceAgent::timeout() +{ + qWarning() << "ConfluenceAgent timeout!! jobType = " << jobType; +} + +#ifndef QT_NO_SSL +void ConfluenceAgent::sslErrors(QNetworkReply *reply, const QList &errors) +{ + QString errorString; + foreach (const QSslError &error, errors) { + if (!errorString.isEmpty()) + errorString += '\n'; + errorString += error.errorString(); + } + + reply->ignoreSslErrors(); + qWarning() << "ConfluenceAgent: One or more SSL errors has occurred: " << errorString; + qWarning() << "Errors ignored."; +} +#endif