/******************************************************************************************** * * * Copyright (C) 2017 Armin Felder, Dennis Beier * * This file is part of RocketChatMobileEngine <https://git.fairkom.net/chat/fairchat>. * * * * RocketChatMobileEngine 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. * * * * RocketChatMobileEngine 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 RocketChatMobileEngine. If not, see <http://www.gnu.org/licenses/>. * * * ********************************************************************************************/ #include "fileuploader.h" FileUploader::FileUploader( RocketChatServerData *pServer, QUrl pUri, QString pChannelId ) { this->mServer = pServer; this->mUri = pUri; this->mChannelId = pChannelId; QSharedPointer<QFile> file( new QFile( pUri.path() ) ); if ( file->isOpen() ) { file->close(); } if ( file->open( QIODevice::ReadOnly ) ) { mDump = file->readAll(); this->mSize = mDump.size(); qDebug() << "read file with bytes:" << file->size(); if ( !this->mSize ) { mErrorStatus = true; } else { file->close(); QMimeDatabase db; mType = db.mimeTypeForFile( pUri.fileName() ); } } else { qWarning() << file->errorString(); } } void FileUploader::upload( std::function<void( void )> pCleanup ) { this->mCleanUp = pCleanup; DdpCallback success = [ & ]( QJsonObject response, MeteorDDP * ddp ) { Q_UNUSED( ddp ); onUfsCreated( response ); }; DdpCallback errorFunc = [ & ]( QJsonObject response, MeteorDDP * ddp ) { Q_UNUSED( ddp ); if ( response.contains( "error" ) ) { QJsonObject errorObject = response["error"].toObject(); if ( errorObject.contains( "message" ) ) { QString message = errorObject["message"].toString(); emit error( message ); } } }; QSharedPointer<DDPUfsCreateRequest> request( new DDPUfsCreateRequest( mChannelId, mUri.fileName(), mDump.size(), mType.name(), success ) ); request->setError( errorFunc ); if ( !mErrorStatus ) { mServer->sendDdprequest( request, true ); } } void FileUploader::cancel() { QMutexLocker locker( &mLock ); mCanceled = 1; if ( mPartialRequests.empty() ) { mCleanUp(); } } void FileUploader::onUfsCreated( QJsonObject pResponse ) { if ( pResponse.contains( "result" ) ) { QJsonObject result = pResponse["result"].toObject(); if ( result.contains( "url" ) && result.contains( "token" ) && result.contains( "fileId" ) ) { QString url = result["url"].toString(); mToken = result["token"].toString(); qDebug() << result.keys(); qDebug() << result["fileId"].toString(); QString fileId = result["fileId"].toString(); this->mFileId = fileId; int pos = 0; int arrsize = mSize; int sizeInArray = 100000; emit ufsCreated( fileId ); int chunkNum = arrsize / sizeInArray; chunkNum += ( arrsize % sizeInArray ) ? 1 : 0; qDebug() << "creating partials: " << chunkNum << "of bytes:" << mSize; if ( mSize == 0 ) { throw new std::logic_error( "Zero byte file!" ); } bool cancel = mCanceled; RestRequestCallback nextOne = [ = ]( QNetworkReply *, QJsonObject data, RestApi * ) { Q_UNUSED( data ); QMutexLocker locker( &mLock ); if ( cancel ) { mPartialRequests.clear(); mCleanUp(); } else { nextRequest(); } }; for ( int i = 0; i < chunkNum; i++ ) { qDebug() << "creating partials nr " << chunkNum; QByteArray chunk = mDump.mid( pos, sizeInArray ); double progress = ( 1.0 / chunkNum ) * ( i + 1 ); QString completePath = url + "&progress=" + QString::number( progress ); QSharedPointer<RestFileUploadRequest> partial = QSharedPointer<RestFileUploadRequest>( new RestFileUploadRequest( completePath, mToken, chunk ) ); partial->setProgress( progress ); qDebug() << "creating partial " << chunkNum << ":" << progress << "size:" << chunk.size();; partial->setSuccess( nextOne ); mPartialRequests.append( partial ); pos += chunk.size(); } auto lastRequest = mPartialRequests.last(); lastRequest->setSuccess( [ = ]( QNetworkReply * reply, QJsonObject data, RestApi * ) { Q_UNUSED( reply ); onUploadCompleted( data ); } ); auto request = mPartialRequests.first(); mServer->sendApiRequest( request, true ); } } } void FileUploader::onUploadCompleted( QJsonObject pResponse ) { Q_UNUSED( pResponse ); qDebug() << "upload completed"; QString store = QStringLiteral("rocketchat_uploads"); int major = std::get<0>( RocketChatServerConfig::serverVersion ); int minor = std::get<1>( RocketChatServerConfig::serverVersion ); int patch = std::get<2>( RocketChatServerConfig::serverVersion ); if ( major != -1 && minor != -1 && patch != -1 ) { if ( major >= 0 && minor > 60 ) { store = QStringLiteral( "Uploads" ); } } QJsonArray params = {mFileId, store, mToken}; DdpCallback success2 = [ = ]( QJsonObject pResponse, MeteorDDP * pDdp ) { Q_UNUSED( pDdp ); qDebug() << "ufs created response" << pResponse; if ( pResponse.contains( "result" ) ) { QJsonObject result = pResponse["result"].toObject(); QJsonArray params2 = {mChannelId, QJsonValue::Null, result}; DdpCallback success3 = [ = ]( QJsonObject pResponse, MeteorDDP * pDdp ) { Q_UNUSED( pResponse ); Q_UNUSED( pDdp ); qDebug() << "send file message response"; mCleanUp(); }; QSharedPointer<DDPMethodRequest> request3( new DDPMethodRequest( "sendFileMessage", params2, success3 ) ); mServer->sendDdprequest( request3, true ); } }; QSharedPointer<DDPMethodRequest> request( new DDPMethodRequest( "ufsComplete", params, success2 ) ); mServer->sendDdprequest( request, true ); qDebug() << "file upload ready"; } void FileUploader::nextRequest() { if ( mPartialRequests.size() > 0 ) { mPartialRequests.pop_front(); auto request = mPartialRequests.first(); emit progressChanged( request->getProgress() ); mServer->sendApiRequest( request, true ); } } QString FileUploader::getFileId() const { return mFileId; }