records_management.cpp 12.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
 * Copyright (C) 2014-2018 CZ.NIC
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#include <QMessageBox>

#include "src/datovka_shared/io/records_management_db.h"
27
#include "src/datovka_shared/log/log.h"
28
#include "src/datovka_shared/records_management/json/service_info.h"
29 30
#include "src/datovka_shared/records_management/json/upload_file.h"
#include "src/datovka_shared/records_management/json/upload_hierarchy.h"
31
#include "src/datovka_shared/settings/records_management.h"
32
#include "src/datovka_shared/worker/pool.h"
33
#include "src/global.h"
34
#include "src/models/accountmodel.h"
35
#include "src/qml_interaction/image_provider.h"
36
#include "src/records_management/models/upload_hierarchy_list_model.h"
37
#include "src/records_management.h"
38
#include "src/settings.h"
39
#include "src/sqlite/zfo_db.h"
40
#include "src/worker/emitter.h"
41
#include "src/worker/task_records_management_stored_messages.h"
42

43 44 45 46 47 48 49 50 51 52 53
/*!
 * @brief Process upload file service response.
 *
 * @note Stores location into database.
 *
 * @param[in] ifRes Response structure.
 * @param[in] dmId Message identifier.
 * @patam[in] parent Parent window for potential error dialogues.
 * @return True if response could be processed and location has been saved.
 */
static
54 55
bool processUploadFileResponse(const RecMgmt::UploadFileResp &ufRes,
    qint64 dmId, QWidget *parent = Q_NULLPTR)
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
{
	if (ufRes.id().isEmpty()) {
		QString errorMessage(
		    QObject::tr("Message '%1' could not be uploaded.")
		        .arg(dmId));
		errorMessage += QLatin1String("\n");
		errorMessage += QObject::tr("Received error") +
		    QLatin1String(": ") + ufRes.error().trVerbose();
		errorMessage += QLatin1String("\n");
		errorMessage += ufRes.error().description();

		QMessageBox::critical(parent, QObject::tr("File Upload Error"),
		    errorMessage);
		return false;
	}

	QMessageBox::information(parent, QObject::tr("Successful File Upload"),
	    QObject::tr("Message '%1' was successfully uploaded into the records management service.").arg(dmId) +
	    QStringLiteral("\n") +
	    QObject::tr("It can be now found in the records management service in these locations:") +
	    QStringLiteral("\n") +
	    ufRes.locations().join(QStringLiteral("\n")));

	if (!ufRes.locations().isEmpty()) {
80
		logErrorNL("%s", "Message has been stored into records management service.");
81 82
		if (Q_NULLPTR != GlobInstcs::recMgmtDbPtr) {
			return GlobInstcs::recMgmtDbPtr->updateStoredMsg(dmId,
83 84 85 86 87 88
			    ufRes.locations());
		} else {
			Q_ASSERT(0);
			return true;
		}
	} else {
89
		logErrorNL("%s",
90 91 92 93 94 95
		    "Received empty location list when uploading message.");
	}

	return false;
}

96
RecordsManagement::RecordsManagement(QObject *parent)
97
    : QObject(parent),
98
    m_rmc(RecMgmt::Connection::ignoreSslErrorsDflt, this)
99
{
100 101
	if (GlobInstcs::msgProcEmitterPtr != Q_NULLPTR) {
		connect(GlobInstcs::msgProcEmitterPtr,
102 103 104 105 106
		    SIGNAL(rmSyncFinishedSignal(QString, int, int)),
		    this, SLOT(rmSyncFinished(QString, int, int)));
	} else {
		logErrorNL("%s", "Cannot connect to status message emitter.");
	}
107 108
}

109 110
bool RecordsManagement::callServiceInfo(const QString &urlStr,
    const QString &tokenStr)
111 112
{
	QByteArray response;
113 114 115 116

	if (urlStr.trimmed().isEmpty() || tokenStr.trimmed().isEmpty()) {
		return false;
	}
117 118

	emit statusBarTextChanged(tr("Get service info"), true, true);
119

120 121
	m_rmc.setConnection(urlStr.trimmed(), tokenStr.trimmed());

122
	if (m_rmc.communicate(RecMgmt::Connection::SRVC_SERVICE_INFO,
123
	        QByteArray(), response)) {
124 125
		if (!response.isEmpty()) {
			bool ok = false;
126 127
			RecMgmt::ServiceInfoResp siRes(
			    RecMgmt::ServiceInfoResp::fromJson(response, &ok));
128
			if (ok && siRes.isValid()) {
129 130 131
				if (GlobInstcs::imgProvPtr != Q_NULLPTR) {
					GlobInstcs::imgProvPtr->setSvg(
					    RM_SVG_LOGO_ID, siRes.logoSvg());
132
				}
133 134 135
				emit serviceInfo(siRes.name(), siRes.tokenName());
				emit statusBarTextChanged(tr("Done"), false, true);
				return true;
136
			}
137 138
		}
	}
139

140 141
	emit statusBarTextChanged(tr("Communication error"), false, true);
	return false;
142 143
}

144 145
void RecordsManagement::callUploadHierarchy(
    const QVariant &hirerachyModelVariant)
146
{
147 148 149 150 151
	if (Q_UNLIKELY(GlobInstcs::recMgmtSetPtr == Q_NULLPTR)) {
		Q_ASSERT(0);
		return;
	}

152
	QByteArray response;
153

154 155
	if (GlobInstcs::recMgmtSetPtr->url().isEmpty() ||
	    GlobInstcs::recMgmtSetPtr->token().isEmpty()) {
156 157
		return;
	}
158

159 160 161 162
	UploadHierarchyListModel *hierarchyModel =
	    UploadHierarchyListModel::fromVariant(hirerachyModelVariant);
	if (hierarchyModel == Q_NULLPTR) {
		Q_ASSERT(0);
163
		logErrorNL("%s", "Cannot access upload hierarchy model.");
164 165 166
		return;
	}

167 168
	emit statusBarTextChanged(tr("Upload hierarchy"), true, true);

169
	/* Clear model. */
170
	hierarchyModel->setHierarchy(RecMgmt::UploadHierarchyResp());
171

172 173
	m_rmc.setConnection(GlobInstcs::recMgmtSetPtr->url(),
	    GlobInstcs::recMgmtSetPtr->token());
174

175
	if (m_rmc.communicate(RecMgmt::Connection::SRVC_UPLOAD_HIERARCHY,
176 177 178
	        QByteArray(), response)) {
		if (!response.isEmpty()) {
			bool ok = false;
179 180
			RecMgmt::UploadHierarchyResp uhRes(
			    RecMgmt::UploadHierarchyResp::fromJson(response, &ok));
181 182 183 184 185
			if (ok && uhRes.isValid()) {
				/* Set model. */
				hierarchyModel->setHierarchy(uhRes);
				emit statusBarTextChanged(tr("Done"), false, true);
				return;
186
			}
187 188
		}
	}
189

190
	emit statusBarTextChanged(tr("Communication error"), false, true);
191 192
}

193 194
void RecordsManagement::getStoredMsgInfoFromRecordsManagement(
    const QString &urlStr, const QString &tokenStr)
195
{
196 197
	if (Q_UNLIKELY((GlobInstcs::workPoolPtr == Q_NULLPTR) ||
	        (GlobInstcs::setPtr == Q_NULLPTR) ||
198 199
	        (GlobInstcs::messageDbsPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
200 201 202 203 204 205
		Q_ASSERT(0);
		return;
	}

	QString url(urlStr.trimmed());
	QString token(tokenStr.trimmed());
206 207 208 209 210

	if (url.isEmpty() || token.isEmpty()) {
		return;
	}

211
	emit statusBarTextChanged(tr("Sync service"), true, true);
212 213 214

	TaskRecordsManagementStoredMessages *task =
	    new (::std::nothrow) TaskRecordsManagementStoredMessages(
215 216 217
	        url, token,
	        TaskRecordsManagementStoredMessages::RM_UPDATE_STORED,
	        Q_NULLPTR, QString(), 0, 0);
218
	if (Q_NULLPTR == task) {
219
		logErrorNL("%s", "Cannot create stored_files update task.");
220
		emit statusBarTextChanged(tr("Sync failed"), false, true);
221 222 223 224
		return;
	}
	task->setAutoDelete(true);
	/* Run in background. */
225
	GlobInstcs::workPoolPtr->assignHi(task);
226

227
	int accTotal = GlobInstcs::acntMapPtr->keys().count();
228 229
	int accNumber = 0;

230
	foreach (const QString &userName, GlobInstcs::acntMapPtr->keys()) {
231 232
		accNumber++;

233 234
		MessageDb *msgDb = GlobInstcs::messageDbsPtr->accessMessageDb(
		    GlobInstcs::setPtr->dbsLocation, userName,
235
		    (*GlobInstcs::acntMapPtr)[userName].storeToDisk());
236
		if (msgDb == Q_NULLPTR) {
237
			logWarningNL("%s", "Cannot access message database.");
238
			continue;
239 240 241 242
		}

		TaskRecordsManagementStoredMessages *task =
		    new (::std::nothrow) TaskRecordsManagementStoredMessages(
243
		        url, token,
244
		        TaskRecordsManagementStoredMessages::RM_DOWNLOAD_ALL,
245
		        msgDb, userName, accNumber, accTotal);
246
		if (Q_NULLPTR == task) {
247
			logWarningNL("Cannot create stored_files task for '%s'.",
248 249 250 251 252
			    userName.toUtf8().constData());
			continue;
		}
		task->setAutoDelete(true);
		/* Run in background. */
253
		GlobInstcs::workPoolPtr->assignHi(task);
254 255 256 257

	}
}

258 259
bool RecordsManagement::isValidRecordsManagement(void)
{
260 261 262 263 264 265
	if (GlobInstcs::recMgmtSetPtr != Q_NULLPTR) {
		return GlobInstcs::recMgmtSetPtr->isValid();
	} else {
		Q_ASSERT(0);
		return false;
	}
266 267
}

268 269
void RecordsManagement::loadStoredServiceInfo(void)
{
270
	if (Q_NULLPTR == GlobInstcs::recMgmtDbPtr) {
271 272 273 274
		return;
	}

	RecordsManagementDb::ServiceInfoEntry entry(
275
	    GlobInstcs::recMgmtDbPtr->serviceInfo());
276 277 278 279
	if (!entry.isValid()) {
		return;
	}

280 281
	if (GlobInstcs::imgProvPtr != Q_NULLPTR) {
		GlobInstcs::imgProvPtr->setSvg(RM_SVG_LOGO_ID, entry.logoSvg);
282
	}
Martin Straka's avatar
Martin Straka committed
283
	emit serviceInfo(entry.name, entry.tokenName);
284
}
285

286 287
bool RecordsManagement::uploadMessage(const QString &userName,
    const QString &dmId, enum Messages::MessageType messageType,
288
    const QVariant &hirerachyModelVariant)
289
{
290 291
	if (Q_UNLIKELY((GlobInstcs::zfoDbPtr == Q_NULLPTR) ||
	        (GlobInstcs::acntMapPtr == Q_NULLPTR))) {
292 293 294 295
		Q_ASSERT(0);
		return false;
	}

296 297 298
	bool ok = false;
	qint64 msgId = dmId.toLongLong(&ok);
	if (!ok || (msgId < 0)) {
299 300 301
		return false;
	}

302 303
	QByteArray msgData = QByteArray::fromBase64(
	    GlobInstcs::zfoDbPtr->getZfoContentFromDb(msgId,
304
	        (*GlobInstcs::acntMapPtr)[userName].isTestAccount()));
305

306
	if (msgData.isEmpty()) {
307 308 309
		return false;
	}

310 311 312 313 314 315 316 317 318 319 320 321
	QString msgType;
	switch (messageType) {
	case Messages::TYPE_RECEIVED:
		msgType = QLatin1String("DDZ");
		break;
	case Messages::TYPE_SENT:
		msgType = QLatin1String("ODZ");
		break;
	default:
		msgType = QLatin1String("DZ");
		break;
	}
322

323
	QString msgFileName = QString("%1_%2.zfo").arg(msgType).arg(dmId);
324

325 326 327 328
	UploadHierarchyListModel *hierarchyModel =
	    UploadHierarchyListModel::fromVariant(hirerachyModelVariant);
	if (hierarchyModel == Q_NULLPTR) {
		Q_ASSERT(0);
329
		logErrorNL("%s", "Cannot access upload hierarchy model.");
330 331 332 333
		return false;
	}

	return uploadFile(msgId, msgFileName, msgData, hierarchyModel->selectedIds());
334 335
}

336 337
bool RecordsManagement::updateServiceInfo(const QString &newUrlStr,
    const QString &oldUrlStr, const QString &srName, const QString &srToken)
Martin Straka's avatar
Martin Straka committed
338
{
339
	if (Q_NULLPTR == GlobInstcs::recMgmtDbPtr) {
Martin Straka's avatar
Martin Straka committed
340 341 342
		return false;
	}

343
	QString cUrlStr = newUrlStr.trimmed();
Martin Straka's avatar
Martin Straka committed
344

345
	if (!cUrlStr.isEmpty()) {
Martin Straka's avatar
Martin Straka committed
346
		RecordsManagementDb::ServiceInfoEntry entry;
347
		entry.url = cUrlStr;
Martin Straka's avatar
Martin Straka committed
348 349
		entry.name = srName;
		entry.tokenName = srToken;
350 351
		entry.logoSvg = (GlobInstcs::imgProvPtr != Q_NULLPTR) ?
		    GlobInstcs::imgProvPtr->svg(RM_SVG_LOGO_ID) : QByteArray();
352
		GlobInstcs::recMgmtDbPtr->updateServiceInfo(entry);
353
		if (oldUrlStr != cUrlStr) {
354
			return GlobInstcs::recMgmtDbPtr->deleteAllStoredMsg();
Martin Straka's avatar
Martin Straka committed
355 356
		}
	} else {
357
		return GlobInstcs::recMgmtDbPtr->deleteAllEntries();
Martin Straka's avatar
Martin Straka committed
358 359 360 361 362
	}

	return true;
}

363 364 365 366 367
void RecordsManagement::rmSyncFinished(const QString &userName, int accNumber,
    int accTotal)
{
	if (accNumber < accTotal) {
		emit statusBarTextChanged(
368
		    tr("Update account '%1' (%2/%3)").arg(userName).arg(accNumber).arg(accTotal),
369 370
		    true, true);
	} else {
371
		emit statusBarTextChanged(tr("Update done"), false, true);
372 373 374
	}
}

375 376 377
bool RecordsManagement::uploadFile(qint64 dmId, const QString &msgFileName,
    const QByteArray &msgData, const QStringList &uploadIds)
{
378 379
	QByteArray response;

380 381 382 383 384
	if (Q_UNLIKELY(GlobInstcs::recMgmtSetPtr == Q_NULLPTR)) {
		Q_ASSERT(0);
		return false;
	}

385 386 387 388 389
	if (msgFileName.isEmpty() || msgData.isEmpty()) {
		Q_ASSERT(0);
		return false;
	}

390 391
	if (GlobInstcs::recMgmtSetPtr->url().isEmpty() ||
	    GlobInstcs::recMgmtSetPtr->token().isEmpty()) {
392 393 394
		return false;
	}

395
	RecMgmt::UploadFileReq ufReq(uploadIds, msgFileName, msgData);
396 397 398 399 400 401 402
	if (!ufReq.isValid()) {
		Q_ASSERT(0);
		return false;
	}

	emit statusBarTextChanged(tr("Upload message"), true, true);

403 404
	m_rmc.setConnection(GlobInstcs::recMgmtSetPtr->url(),
	    GlobInstcs::recMgmtSetPtr->token());
405

406
	if (m_rmc.communicate(RecMgmt::Connection::SRVC_UPLOAD_FILE,
407 408 409
	        ufReq.toJson(), response)) {
		if (!response.isEmpty()) {
			bool ok = false;
410 411
			RecMgmt::UploadFileResp ufRes(
			    RecMgmt::UploadFileResp::fromJson(response, &ok));
412 413 414
			if (ok && ufRes.isValid()) {
				emit statusBarTextChanged(tr("Done"), false, true);
				return processUploadFileResponse(ufRes, dmId);
415 416 417 418
			}
		}
	}

419
	emit statusBarTextChanged(tr("Communication error"), false, true);
420 421
	return false;
}