From 0894931fa1fb9751993538983e8af2f3e5b99689 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 9 Apr 2016 09:57:55 +0400 Subject: [PATCH] Encapsulated DocumentData location and serialization. DocumentData now can point to mtproto or http file. --- Telegram/SourceFiles/app.cpp | 40 ++-- Telegram/SourceFiles/history.cpp | 2 +- Telegram/SourceFiles/historywidget.cpp | 16 +- .../inline_bot_layout_internal.cpp | 2 +- .../inline_bots/inline_bot_result.cpp | 2 +- Telegram/SourceFiles/layout.cpp | 6 +- Telegram/SourceFiles/localstorage.cpp | 211 +++++------------- Telegram/SourceFiles/localstorage.h | 1 + .../serialize/serialize_common.cpp | 47 ++++ .../SourceFiles/serialize/serialize_common.h | 33 +++ .../serialize/serialize_document.cpp | 163 ++++++++++++++ .../serialize/serialize_document.h | 51 +++++ Telegram/SourceFiles/structs.cpp | 89 +++++--- Telegram/SourceFiles/structs.h | 68 ++++-- Telegram/Telegram.vcxproj | 4 + Telegram/Telegram.vcxproj.filters | 15 ++ 16 files changed, 514 insertions(+), 236 deletions(-) create mode 100644 Telegram/SourceFiles/serialize/serialize_common.cpp create mode 100644 Telegram/SourceFiles/serialize/serialize_common.h create mode 100644 Telegram/SourceFiles/serialize/serialize_document.cpp create mode 100644 Telegram/SourceFiles/serialize/serialize_document.h diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index f13e34b58..5a1343229 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1521,7 +1521,7 @@ namespace { DocumentData *document(const DocumentId &document) { DocumentsData::const_iterator i = ::documentsData.constFind(document); if (i == ::documentsData.cend()) { - i = ::documentsData.insert(document, new DocumentData(document)); + i = ::documentsData.insert(document, DocumentData::create(document)); } return i.value(); } @@ -1529,45 +1529,44 @@ namespace { DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 date, const QVector &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation) { bool sentSticker = false; if (convert) { + MediaKey oldKey = convert->mediaKey(); if (convert->id != document) { DocumentsData::iterator i = ::documentsData.find(convert->id); if (i != ::documentsData.cend() && i.value() == convert) { ::documentsData.erase(i); } - // inline bot sent gifs caching - if (!convert->voice() && !convert->isVideo()) { - Local::copyStickerImage(mediaKey(DocumentFileLocation, convert->dc, convert->id), mediaKey(DocumentFileLocation, dc, document)); - } - convert->id = document; convert->status = FileReady; sentSticker = (convert->sticker() != 0); } if (date) { - convert->access = access; - convert->date = date; convert->setattributes(attributes); + convert->setRemoteLocation(dc, access); + convert->date = date; convert->mime = mime; if (!thumb->isNull() && (convert->thumb->isNull() || convert->thumb->width() < thumb->width() || convert->thumb->height() < thumb->height())) { updateImage(convert->thumb, thumb); } - convert->dc = dc; convert->size = size; convert->recountIsImage(); if (convert->sticker() && convert->sticker()->loc.isNull() && !thumbLocation.isNull()) { convert->sticker()->loc = thumbLocation; } + + MediaKey newKey = convert->mediaKey(); + if (newKey != oldKey) { + if (convert->voice()) { + Local::copyAudio(oldKey, newKey); + } else if (convert->sticker() || convert->isAnimation()) { + Local::copyStickerImage(oldKey, newKey); + } + } } if (cSavedGifs().indexOf(convert) >= 0) { // id changed Local::writeSavedGifs(); } - - const FileLocation &loc(convert->location(true)); - if (!loc.isEmpty()) { - Local::writeFileLocation(convert->mediaKey(), loc); - } } DocumentsData::const_iterator i = ::documentsData.constFind(document); DocumentData *result; @@ -1575,7 +1574,11 @@ namespace { if (convert) { result = convert; } else { - result = new DocumentData(document, access, date, attributes, mime, thumb, dc, size); + result = DocumentData::create(document, dc, access, attributes); + result->date = date; + result->mime = mime; + result->thumb = thumb; + result->size = size; result->recountIsImage(); if (result->sticker()) { result->sticker()->loc = thumbLocation; @@ -1585,14 +1588,15 @@ namespace { } else { result = i.value(); if (result != convert && date) { - result->access = access; - result->date = date; result->setattributes(attributes); + if (!result->isValid()) { + result->setRemoteLocation(dc, access); + } + result->date = date; result->mime = mime; if (!thumb->isNull() && (result->thumb->isNull() || result->thumb->width() < thumb->width() || result->thumb->height() < thumb->height())) { result->thumb = thumb; } - result->dc = dc; result->size = size; result->recountIsImage(); if (result->sticker() && result->sticker()->loc.isNull() && !thumbLocation.isNull()) { diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index c72b18438..bfaa9caf5 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -4606,7 +4606,7 @@ void HistoryDocument::getState(ClickHandlerPtr &lnk, HistoryCursorState &state, } height -= captioned->_caption.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom(); } - if (x >= 0 && y >= 0 && x < _width && y < height && !_data->loading() && !_data->uploading() && _data->access) { + if (x >= 0 && y >= 0 && x < _width && y < height && !_data->loading() && !_data->uploading() && _data->isValid()) { lnk = _openl; return; } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index b334bf19f..a25461361 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3422,7 +3422,10 @@ void HistoryWidget::savedGifsGot(const MTPmessages_SavedGifs &gifs) { void HistoryWidget::saveGif(DocumentData *doc) { if (doc->isGifv() && cSavedGifs().indexOf(doc) != 0) { - MTP::send(MTPmessages_SaveGif(MTP_inputDocument(MTP_long(doc->id), MTP_long(doc->access)), MTP_bool(false)), rpcDone(&HistoryWidget::saveGifDone, doc)); + MTPInputDocument mtpInput = doc->mtpInput(); + if (mtpInput.type() != mtpc_inputDocumentEmpty) { + MTP::send(MTPmessages_SaveGif(mtpInput, MTP_bool(false)), rpcDone(&HistoryWidget::saveGifDone, doc)); + } } } @@ -7027,7 +7030,14 @@ void HistoryWidget::ReplyEditMessageDataCallback::call(ChannelData *channel, Msg } void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &caption) { - if (!_history || !doc || !canSendMessages(_peer)) return; + if (!_history || !doc || !canSendMessages(_peer)) { + return; + } + + MTPInputDocument mtpInput = doc->mtpInput(); + if (mtpInput.type() == mtpc_inputDocumentEmpty) { + return; + } App::main()->readServerHistory(_history, false); fastShowAtEnd(_history); @@ -7060,7 +7070,7 @@ void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &capti } _history->addNewDocument(newId.msg, flags, 0, replyToId(), date(MTP_int(unixtime())), showFromName ? MTP::authedId() : 0, doc, caption, MTPnullMarkup); - _history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaDocument(MTP_inputDocument(MTP_long(doc->id), MTP_long(doc->access)), MTP_string(caption)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId); + _history->sendRequestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_inputMediaDocument(mtpInput, MTP_string(caption)), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId); App::main()->finishForwarding(_history, _broadcast.checked(), _silent.checked()); cancelReply(lastKeyboardUsed); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 0174a4dfe..d0ed5b1a4 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -179,7 +179,7 @@ void DeleteSavedGifClickHandler::onClickImpl() const { cRefSavedGifs().remove(index); Local::writeSavedGifs(); - MTP::send(MTPmessages_SaveGif(MTP_inputDocument(MTP_long(_data->id), MTP_long(_data->access)), MTP_bool(true))); + MTP::send(MTPmessages_SaveGif(_data->mtpInput(), MTP_bool(true))); } if (App::main()) emit App::main()->savedGifsUpdated(); } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index dac0f4237..2fa4e65aa 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -124,7 +124,7 @@ UniquePointer Result::create(uint64 queryId, const MTPBotInlineResult &m message = &r.vsend_message; } break; } - bool badAttachment = (result->_photo && !result->_photo->access) || (result->_document && !result->_document->access); + bool badAttachment = (result->_photo && !result->_photo->access) || (result->_document && !result->_document->isValid()); if (!message) { return UniquePointer(); diff --git a/Telegram/SourceFiles/layout.cpp b/Telegram/SourceFiles/layout.cpp index f7ea92855..04eee040f 100644 --- a/Telegram/SourceFiles/layout.cpp +++ b/Telegram/SourceFiles/layout.cpp @@ -801,7 +801,7 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti bool selected = (selection == FullSelection); _data->automaticLoad(_parent); - bool loaded = _data->loaded() || Local::willStickerImageLoad(mediaKey(DocumentFileLocation, _data->dc, _data->id)), displayLoading = _data->displayLoading(); + bool loaded = _data->loaded() || Local::willStickerImageLoad(_data->mediaKey()), displayLoading = _data->displayLoading(); if (displayLoading) { ensureRadial(); @@ -973,7 +973,7 @@ void LayoutOverviewDocument::paint(Painter &p, const QRect &clip, uint32 selecti } void LayoutOverviewDocument::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const { - bool loaded = _data->loaded() || Local::willStickerImageLoad(mediaKey(DocumentFileLocation, _data->dc, _data->id)); + bool loaded = _data->loaded() || Local::willStickerImageLoad(_data->mediaKey()); bool showPause = updateStatusText(); @@ -1014,7 +1014,7 @@ void LayoutOverviewDocument::getState(ClickHandlerPtr &link, HistoryCursorState return; } } - if (!_data->loading() && _data->access) { + if (!_data->loading() && _data->isValid()) { if (loaded && rtlrect(0, st::linksBorder, nameleft, _height - st::linksBorder, _width).contains(x, y)) { link = _namel; return; diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 84c871665..79d5e1857 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -19,22 +19,18 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" - #include "localstorage.h" #include +#include "serialize/serialize_document.h" +#include "serialize/serialize_common.h" + #include "mainwidget.h" #include "window.h" #include "lang.h" namespace { - enum StickerSetType { - StickerSetTypeEmpty = 0, - StickerSetTypeID = 1, - StickerSetTypeShortName = 2, - }; - typedef quint64 FileKey; static const char tdfMagic[] = { 'T', 'D', 'F', '$' }; @@ -150,10 +146,6 @@ namespace { return (sizeof(qint64) + sizeof(quint32) + sizeof(qint8)); } - uint32 _stringSize(const QString &str) { - return sizeof(quint32) + str.size() * sizeof(ushort); - } - uint32 _bytearraySize(const QByteArray &arr) { return sizeof(quint32) + arr.size(); } @@ -691,7 +683,7 @@ namespace { quint32 size = 0; for (FileLocations::const_iterator i = _fileLocations.cbegin(), e = _fileLocations.cend(); i != e; ++i) { // location + type + namelen + name - size += sizeof(quint64) * 2 + sizeof(quint32) + _stringSize(i.value().name()); + size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(i.value().name()); if (AppVersion > 9013) { // bookmark size += _bytearraySize(i.value().bookmark()); @@ -701,7 +693,7 @@ namespace { } //end mark - size += sizeof(quint64) * 2 + sizeof(quint32) + _stringSize(QString()); + size += sizeof(quint64) * 2 + sizeof(quint32) + Serialize::stringSize(QString()); if (AppVersion > 9013) { size += _bytearraySize(QByteArray()); } @@ -716,7 +708,7 @@ namespace { size += sizeof(quint32); // web files count for (WebFilesMap::const_iterator i = _webFilesMap.cbegin(), e = _webFilesMap.cend(); i != e; ++i) { // url + filekey + size - size += _stringSize(i.key()) + sizeof(quint64) + sizeof(qint32); + size += Serialize::stringSize(i.key()) + sizeof(quint64) + sizeof(qint32); } EncryptedDescriptor data(size); @@ -1580,11 +1572,11 @@ namespace { } uint32 size = 16 * (sizeof(quint32) + sizeof(qint32)); - size += sizeof(quint32) + _stringSize(cAskDownloadPath() ? QString() : cDownloadPath()) + _bytearraySize(cAskDownloadPath() ? QByteArray() : cDownloadPathBookmark()); + size += sizeof(quint32) + Serialize::stringSize(cAskDownloadPath() ? QString() : cDownloadPath()) + _bytearraySize(cAskDownloadPath() ? QByteArray() : cDownloadPathBookmark()); size += sizeof(quint32) + sizeof(qint32) + (cRecentEmojisPreload().isEmpty() ? cGetRecentEmojis().size() : cRecentEmojisPreload().size()) * (sizeof(uint64) + sizeof(ushort)); size += sizeof(quint32) + sizeof(qint32) + cEmojiVariants().size() * (sizeof(uint32) + sizeof(uint64)); size += sizeof(quint32) + sizeof(qint32) + (cRecentStickersPreload().isEmpty() ? cGetRecentStickers().size() : cRecentStickersPreload().size()) * (sizeof(uint64) + sizeof(ushort)); - size += sizeof(quint32) + _stringSize(cDialogLastPath()); + size += sizeof(quint32) + Serialize::stringSize(cDialogLastPath()); size += sizeof(quint32) + 3 * sizeof(qint32); if (!Global::HiddenPinnedMessages().isEmpty()) { size += sizeof(quint32) + sizeof(qint32) + Global::HiddenPinnedMessages().size() * (sizeof(PeerId) + sizeof(MsgId)); @@ -2209,14 +2201,14 @@ namespace Local { quint32 size = 12 * (sizeof(quint32) + sizeof(qint32)); for (auto i = dcOpts.cbegin(), e = dcOpts.cend(); i != e; ++i) { size += sizeof(quint32) + sizeof(quint32) + sizeof(quint32); - size += sizeof(quint32) + _stringSize(QString::fromUtf8(i->ip.data(), i->ip.size())); + size += sizeof(quint32) + Serialize::stringSize(QString::fromUtf8(i->ip.data(), i->ip.size())); } - size += sizeof(quint32) + _stringSize(cLangFile()); + size += sizeof(quint32) + Serialize::stringSize(cLangFile()); size += sizeof(quint32) + sizeof(qint32); if (cConnectionType() == dbictHttpProxy || cConnectionType() == dbictTcpProxy) { const ConnectionProxy &proxy(cConnectionProxy()); - size += _stringSize(proxy.host) + sizeof(qint32) + _stringSize(proxy.user) + _stringSize(proxy.password); + size += Serialize::stringSize(proxy.host) + sizeof(qint32) + Serialize::stringSize(proxy.user) + Serialize::stringSize(proxy.password); } size += sizeof(quint32) + sizeof(qint32) * 6; @@ -2347,7 +2339,7 @@ namespace Local { _writeMap(WriteMapFast); } - EncryptedDescriptor data(sizeof(quint64) + _stringSize(msgDraft.text) + 2 * sizeof(qint32) + _stringSize(editDraft.text) + 2 * sizeof(qint32)); + EncryptedDescriptor data(sizeof(quint64) + Serialize::stringSize(msgDraft.text) + 2 * sizeof(qint32) + Serialize::stringSize(editDraft.text) + 2 * sizeof(qint32)); data.stream << quint64(peer); data.stream << msgDraft.text << qint32(msgDraft.msgId) << qint32(msgDraft.previewCancelled ? 1 : 0); data.stream << editDraft.text << qint32(editDraft.msgId) << qint32(editDraft.previewCancelled ? 1 : 0); @@ -2734,7 +2726,7 @@ namespace Local { type = StorageFilePartial; } void clearInMap() { - StorageMap::iterator j = _stickerImagesMap.find(_location); + auto j = _stickerImagesMap.find(_location); if (j != _stickerImagesMap.cend() && j->first == _key) { clearKey(j.value().first, UserPath); _storageStickersSize -= j.value().second; @@ -2744,7 +2736,7 @@ namespace Local { }; TaskId startStickerImageLoad(const StorageKey &location, mtpFileLoader *loader) { - StorageMap::const_iterator j = _stickerImagesMap.constFind(location); + auto j = _stickerImagesMap.constFind(location); if (j == _stickerImagesMap.cend() || !_localLoader) { return 0; } @@ -2756,7 +2748,7 @@ namespace Local { } void copyStickerImage(const StorageKey &oldLocation, const StorageKey &newLocation) { - StorageMap::const_iterator i = _stickerImagesMap.constFind(oldLocation); + auto i = _stickerImagesMap.constFind(oldLocation); if (i != _stickerImagesMap.cend()) { _stickerImagesMap.insert(newLocation, i.value()); _mapChanged = true; @@ -2806,7 +2798,7 @@ namespace Local { type = StorageFilePartial; } void clearInMap() { - StorageMap::iterator j = _audiosMap.find(_location); + auto j = _audiosMap.find(_location); if (j != _audiosMap.cend() && j->first == _key) { clearKey(j.value().first, UserPath); _storageAudiosSize -= j.value().second; @@ -2816,13 +2808,22 @@ namespace Local { }; TaskId startAudioLoad(const StorageKey &location, mtpFileLoader *loader) { - StorageMap::const_iterator j = _audiosMap.constFind(location); + auto j = _audiosMap.constFind(location); if (j == _audiosMap.cend() || !_localLoader) { return 0; } return _localLoader->addTask(new AudioLoadTask(j->first, location, loader)); } + void copyAudio(const StorageKey &oldLocation, const StorageKey &newLocation) { + auto i = _audiosMap.constFind(oldLocation); + if (i != _audiosMap.cend()) { + _audiosMap.insert(newLocation, i.value()); + _mapChanged = true; + _writeMap(); + } + } + int32 hasAudios() { return _audiosMap.size(); } @@ -2833,7 +2834,7 @@ namespace Local { qint32 _storageWebFileSize(const QString &url, qint32 rawlen) { // fulllen + url + len + data - qint32 result = sizeof(uint32) + _stringSize(url) + sizeof(quint32) + rawlen; + qint32 result = sizeof(uint32) + Serialize::stringSize(url) + sizeof(quint32) + rawlen; if (result & 0x0F) result += 0x10 - (result & 0x0F); result += tdfMagicLen + sizeof(qint32) + sizeof(quint32) + 0x10 + 0x10; // magic + version + len of encrypted + part of sha1 + md5 return result; @@ -2851,7 +2852,7 @@ namespace Local { } else if (!overwrite) { return; } - EncryptedDescriptor data(_stringSize(url) + sizeof(quint32) + sizeof(quint32) + content.size()); + EncryptedDescriptor data(Serialize::stringSize(url) + sizeof(quint32) + sizeof(quint32) + content.size()); data.stream << url << content; FileWriteDescriptor file(i.value().first, UserPath); file.writeEncrypted(data); @@ -3012,23 +3013,6 @@ namespace Local { } } - void _writeStorageImageLocation(QDataStream &stream, const StorageImageLocation &loc) { - stream << qint32(loc.width()) << qint32(loc.height()); - stream << qint32(loc.dc()) << quint64(loc.volume()) << qint32(loc.local()) << quint64(loc.secret()); - } - - uint32 _storageImageLocationSize() { - // width + height + dc + volume + local + secret - return sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(quint64) + sizeof(qint32) + sizeof(quint64); - } - - StorageImageLocation _readStorageImageLocation(FileReadDescriptor &from) { - qint32 thumbWidth, thumbHeight, thumbDc, thumbLocal; - quint64 thumbVolume, thumbSecret; - from.stream >> thumbWidth >> thumbHeight >> thumbDc >> thumbVolume >> thumbLocal >> thumbSecret; - return StorageImageLocation(thumbWidth, thumbHeight, thumbDc, thumbVolume, thumbLocal, thumbSecret); - } - void _writeStickerSet(QDataStream &stream, uint64 setId) { auto it = Global::StickerSets().constFind(setId); if (it == Global::StickerSets().cend()) return; @@ -3043,21 +3027,7 @@ namespace Local { stream << quint64(it->id) << quint64(it->access) << it->title << it->shortName << qint32(it->stickers.size()) << qint32(it->hash) << qint32(it->flags); for (StickerPack::const_iterator j = it->stickers.cbegin(), e = it->stickers.cend(); j != e; ++j) { - DocumentData *doc = *j; - stream << quint64(doc->id) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << doc->sticker()->alt; - switch (doc->sticker()->set.type()) { - case mtpc_inputStickerSetID: { - stream << qint32(StickerSetTypeID); - } break; - case mtpc_inputStickerSetShortName: { - stream << qint32(StickerSetTypeShortName); - } break; - case mtpc_inputStickerSetEmpty: - default: { - stream << qint32(StickerSetTypeEmpty); - } break; - } - _writeStorageImageLocation(stream, doc->sticker()->loc); + Serialize::Document::writeToStream(stream, *j); } if (AppVersion > 9018) { @@ -3097,21 +3067,15 @@ namespace Local { } // id + access + title + shortName + stickersCount + hash + flags - size += sizeof(quint64) * 2 + _stringSize(i->title) + _stringSize(i->shortName) + sizeof(quint32) + sizeof(qint32) * 2; + size += sizeof(quint64) * 2 + Serialize::stringSize(i->title) + Serialize::stringSize(i->shortName) + sizeof(quint32) + sizeof(qint32) * 2; for (StickerPack::const_iterator j = i->stickers.cbegin(), e = i->stickers.cend(); j != e; ++j) { - DocumentData *doc = *j; - - // id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt + type-of-set - size += sizeof(quint64) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(doc->sticker()->alt) + sizeof(qint32); - - // loc - size += _storageImageLocationSize(); + size += Serialize::Document::sizeInStream(*j); } if (AppVersion > 9018) { size += sizeof(qint32); // emojiCount for (StickersByEmojiMap::const_iterator j = i->emoji.cbegin(), e = i->emoji.cend(); j != e; ++j) { - size += _stringSize(emojiString(j.key())) + sizeof(qint32) + (j->size() * sizeof(quint64)); + size += Serialize::stringSize(emojiString(j.key())) + sizeof(qint32) + (j->size() * sizeof(quint64)); } } @@ -3267,48 +3231,16 @@ namespace Local { set.stickers.reserve(scnt); - QMap read; + Serialize::Document::StickerSetInfo info(setId, setAccess, setShortName); + OrderedSet read; for (int32 j = 0; j < scnt; ++j) { - quint64 id, access; - QString name, mime, alt; - qint32 date, dc, size, width, height, type, typeOfSet; - stickers.stream >> id >> access >> date >> name >> mime >> dc >> size >> width >> height >> type >> alt >> typeOfSet; + auto document = Serialize::Document::readStickerFromStream(stickers.stream, info); + if (!document || !document->sticker()) continue; - StorageImageLocation thumb(_readStorageImageLocation(stickers)); + if (read.contains(document->id)) continue; + read.insert(document->id); - if (read.contains(id)) continue; - read.insert(id, true); - - if (setId == Stickers::DefaultSetId || setId == Stickers::CustomSetId) { - typeOfSet = StickerSetTypeEmpty; - } - - QVector attributes; - if (!name.isEmpty()) attributes.push_back(MTP_documentAttributeFilename(MTP_string(name))); - if (type == AnimatedDocument) { - attributes.push_back(MTP_documentAttributeAnimated()); - } else if (type == StickerDocument) { - switch (typeOfSet) { - case StickerSetTypeID: { - attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetID(MTP_long(setId), MTP_long(setAccess)))); - } break; - case StickerSetTypeShortName: { - attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetShortName(MTP_string(setShortName)))); - } break; - case StickerSetTypeEmpty: - default: { - attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetEmpty())); - } break; - } - } - if (width > 0 && height > 0) { - attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height))); - } - - DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.isNull() ? ImagePtr() : ImagePtr(thumb), dc, size, thumb); - if (!doc->sticker()) continue; - - set.stickers.push_back(doc); + set.stickers.push_back(document); ++set.count; } @@ -3383,14 +3315,8 @@ namespace Local { _writeMap(); } else { quint32 size = sizeof(quint32); // count - for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) { - DocumentData *doc = *i; - - // id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + duration - size += sizeof(quint64) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32); - - // thumb - size += _storageImageLocationSize(); + for_const (auto gif, saved) { + size += Serialize::Document::sizeInStream(gif); } if (!_savedGifsKey) { @@ -3400,11 +3326,8 @@ namespace Local { } EncryptedDescriptor data(size); data.stream << quint32(saved.size()); - for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) { - DocumentData *doc = *i; - - data.stream << quint64(doc->id) << quint64(doc->access) << qint32(doc->date) << doc->name << doc->mime << qint32(doc->dc) << qint32(doc->size) << qint32(doc->dimensions.width()) << qint32(doc->dimensions.height()) << qint32(doc->type) << qint32(doc->duration()); - _writeStorageImageLocation(data.stream, doc->thumb->location()); + for_const (auto gif, saved) { + Serialize::Document::writeToStream(data.stream, gif); } FileWriteDescriptor file(_savedGifsKey); file.writeEncrypted(data); @@ -3428,35 +3351,15 @@ namespace Local { quint32 cnt; gifs.stream >> cnt; saved.reserve(cnt); - QMap read; + OrderedSet read; for (uint32 i = 0; i < cnt; ++i) { - quint64 id, access; - QString name, mime; - qint32 date, dc, size, width, height, type, duration; - gifs.stream >> id >> access >> date >> name >> mime >> dc >> size >> width >> height >> type >> duration; + DocumentData *document = Serialize::Document::readFromStream(gifs.stream); + if (!document || !document->isAnimation()) continue; - StorageImageLocation thumb(_readStorageImageLocation(gifs)); + if (read.contains(document->id)) continue; + read.insert(document->id); - if (read.contains(id)) continue; - read.insert(id, NullType()); - - QVector attributes; - if (!name.isEmpty()) attributes.push_back(MTP_documentAttributeFilename(MTP_string(name))); - if (type == AnimatedDocument) { - attributes.push_back(MTP_documentAttributeAnimated()); - } - if (width > 0 && height > 0) { - if (duration >= 0) { - attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(width), MTP_int(height))); - } else { - attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height))); - } - } - - DocumentData *doc = App::documentSet(id, 0, access, date, attributes, mime, thumb.isNull() ? ImagePtr() : ImagePtr(thumb), dc, size, thumb); - if (!doc->isAnimation()) continue; - - saved.push_back(doc); + saved.push_back(document); } } @@ -3522,12 +3425,12 @@ namespace Local { } uint32 _peerSize(PeerData *peer) { - uint32 result = sizeof(quint64) + sizeof(quint64) + _storageImageLocationSize(); + uint32 result = sizeof(quint64) + sizeof(quint64) + Serialize::storageImageLocationSize(); if (peer->isUser()) { UserData *user = peer->asUser(); // first + last + phone + username + access - result += _stringSize(user->firstName) + _stringSize(user->lastName) + _stringSize(user->phone) + _stringSize(user->username) + sizeof(quint64); + result += Serialize::stringSize(user->firstName) + Serialize::stringSize(user->lastName) + Serialize::stringSize(user->phone) + Serialize::stringSize(user->username) + sizeof(quint64); // flags if (AppVersion >= 9012) { @@ -3540,19 +3443,19 @@ namespace Local { ChatData *chat = peer->asChat(); // name + count + date + version + admin + forbidden + left + invitationUrl - result += _stringSize(chat->name) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(chat->invitationUrl); + result += Serialize::stringSize(chat->name) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + Serialize::stringSize(chat->invitationUrl); } else if (peer->isChannel()) { ChannelData *channel = peer->asChannel(); // name + access + date + version + forbidden + flags + invitationUrl - result += _stringSize(channel->name) + sizeof(quint64) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + _stringSize(channel->invitationUrl); + result += Serialize::stringSize(channel->name) + sizeof(quint64) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + Serialize::stringSize(channel->invitationUrl); } return result; } void _writePeer(QDataStream &stream, PeerData *peer) { stream << quint64(peer->id) << quint64(peer->photoId); - _writeStorageImageLocation(stream, peer->photoLoc); + Serialize::writeStorageImageLocation(stream, peer->photoLoc); if (peer->isUser()) { UserData *user = peer->asUser(); @@ -3583,7 +3486,7 @@ namespace Local { quint64 peerId = 0, photoId = 0; from.stream >> peerId >> photoId; - StorageImageLocation photoLoc(_readStorageImageLocation(from)); + StorageImageLocation photoLoc(Serialize::readStorageImageLocation(from.stream)); PeerData *result = App::peerLoaded(peerId); bool wasLoaded = (result != nullptr); @@ -3711,13 +3614,13 @@ namespace Local { quint32 size = sizeof(quint32) * 3, writeCnt = 0, searchCnt = 0, botsCnt = cRecentInlineBots().size(); for (RecentHashtagPack::const_iterator i = write.cbegin(), e = write.cend(); i != e; ++i) { if (!i->first.isEmpty()) { - size += _stringSize(i->first) + sizeof(quint16); + size += Serialize::stringSize(i->first) + sizeof(quint16); ++writeCnt; } } for (RecentHashtagPack::const_iterator i = search.cbegin(), e = search.cend(); i != e; ++i) { if (!i->first.isEmpty()) { - size += _stringSize(i->first) + sizeof(quint16); + size += Serialize::stringSize(i->first) + sizeof(quint16); ++searchCnt; } } diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index 314108ba6..f62dc045c 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -135,6 +135,7 @@ namespace Local { void writeAudio(const StorageKey &location, const QByteArray &data, bool overwrite = true); TaskId startAudioLoad(const StorageKey &location, mtpFileLoader *loader); + void copyAudio(const StorageKey &oldLocation, const StorageKey &newLocation); int32 hasAudios(); qint64 storageAudiosSize(); diff --git a/Telegram/SourceFiles/serialize/serialize_common.cpp b/Telegram/SourceFiles/serialize/serialize_common.cpp new file mode 100644 index 000000000..0d7ee09f6 --- /dev/null +++ b/Telegram/SourceFiles/serialize/serialize_common.cpp @@ -0,0 +1,47 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop 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. + +It 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "serialize/serialize_common.h" + +namespace Serialize { + +int stringSize(const QString &str) { + return sizeof(quint32) + str.size() * sizeof(ushort); +} + +void writeStorageImageLocation(QDataStream &stream, const StorageImageLocation &loc) { + stream << qint32(loc.width()) << qint32(loc.height()); + stream << qint32(loc.dc()) << quint64(loc.volume()) << qint32(loc.local()) << quint64(loc.secret()); +} + +StorageImageLocation readStorageImageLocation(QDataStream &stream) { + qint32 width, height, dc, local; + quint64 volume, secret; + stream >> width >> height >> dc >> volume >> local >> secret; + return StorageImageLocation(width, height, dc, volume, local, secret); +} + +int storageImageLocationSize() { + // width + height + dc + volume + local + secret + return sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(quint64) + sizeof(qint32) + sizeof(quint64); +} + +} // namespace Serialize diff --git a/Telegram/SourceFiles/serialize/serialize_common.h b/Telegram/SourceFiles/serialize/serialize_common.h new file mode 100644 index 000000000..7412104fe --- /dev/null +++ b/Telegram/SourceFiles/serialize/serialize_common.h @@ -0,0 +1,33 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop 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. + +It 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "ui/images.h" + +namespace Serialize { + +int stringSize(const QString &str); + +void writeStorageImageLocation(QDataStream &stream, const StorageImageLocation &loc); +StorageImageLocation readStorageImageLocation(QDataStream &stream); +int storageImageLocationSize(); + +} // namespace Serialize diff --git a/Telegram/SourceFiles/serialize/serialize_document.cpp b/Telegram/SourceFiles/serialize/serialize_document.cpp new file mode 100644 index 000000000..73823edf5 --- /dev/null +++ b/Telegram/SourceFiles/serialize/serialize_document.cpp @@ -0,0 +1,163 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop 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. + +It 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "serialize/serialize_document.h" + +#include "serialize/serialize_common.h" + +namespace { + +enum StickerSetType { + StickerSetTypeEmpty = 0, + StickerSetTypeID = 1, + StickerSetTypeShortName = 2, +}; + +} // namespace + +namespace Serialize { + +void Document::writeToStream(QDataStream &stream, DocumentData *document) { + stream << quint64(document->id) << quint64(document->_access) << qint32(document->date); + stream << document->name << document->mime << qint32(document->_dc) << qint32(document->size); + stream << qint32(document->dimensions.width()) << qint32(document->dimensions.height()); + stream << qint32(document->type); + if (auto sticker = document->sticker()) { + stream << document->sticker()->alt; + switch (document->sticker()->set.type()) { + case mtpc_inputStickerSetID: { + stream << qint32(StickerSetTypeID); + } break; + case mtpc_inputStickerSetShortName: { + stream << qint32(StickerSetTypeShortName); + } break; + case mtpc_inputStickerSetEmpty: + default: { + stream << qint32(StickerSetTypeEmpty); + } break; + } + writeStorageImageLocation(stream, document->sticker()->loc); + } else { + stream << qint32(document->duration()); + writeStorageImageLocation(stream, document->thumb->location()); + } +} + +DocumentData *Document::readFromStreamHelper(QDataStream &stream, const StickerSetInfo *info) { + quint64 id, access; + QString name, mime; + qint32 date, dc, size, width, height, type; + stream >> id >> access >> date; + stream >> name >> mime >> dc >> size; + stream >> width >> height; + stream >> type; + + QVector attributes; + if (!name.isEmpty()) { + attributes.push_back(MTP_documentAttributeFilename(MTP_string(name))); + } + + qint32 duration = -1; + StorageImageLocation thumb; + if (type == StickerDocument) { + QString alt; + qint32 typeOfSet; + stream >> alt >> typeOfSet; + + thumb = readStorageImageLocation(stream); + + if (typeOfSet == StickerSetTypeEmpty) { + attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetEmpty())); + } else if (info) { + if (info->setId == Stickers::DefaultSetId || info->setId == Stickers::CustomSetId) { + typeOfSet = StickerSetTypeEmpty; + } + + switch (typeOfSet) { + case StickerSetTypeID: { + attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetID(MTP_long(info->setId), MTP_long(info->accessHash)))); + } break; + case StickerSetTypeShortName: { + attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetShortName(MTP_string(info->shortName)))); + } break; + case StickerSetTypeEmpty: + default: { + attributes.push_back(MTP_documentAttributeSticker(MTP_string(alt), MTP_inputStickerSetEmpty())); + } break; + } + } + } else { + stream >> duration; + if (type == AnimatedDocument) { + attributes.push_back(MTP_documentAttributeAnimated()); + } + thumb = readStorageImageLocation(stream); + } + if (width > 0 && height > 0) { + if (duration >= 0) { + attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(width), MTP_int(height))); + } else { + attributes.push_back(MTP_documentAttributeImageSize(MTP_int(width), MTP_int(height))); + } + } + + if (!dc && !access) { + return nullptr; + } + return App::documentSet(id, nullptr, access, date, attributes, mime, thumb.isNull() ? ImagePtr() : ImagePtr(thumb), dc, size, thumb); +} + +DocumentData *Document::readStickerFromStream(QDataStream &stream, const StickerSetInfo &info) { + return readFromStreamHelper(stream, &info); +} + +DocumentData *Document::readFromStream(QDataStream &stream) { + return readFromStreamHelper(stream, nullptr); +} + +int Document::sizeInStream(DocumentData *document) { + int result = 0; + + // id + access + date + result += sizeof(quint64) + sizeof(quint64) + sizeof(qint32); + // + namelen + name + mimelen + mime + dc + size + result += stringSize(document->name) + stringSize(document->mime) + sizeof(qint32) + sizeof(qint32); + // + width + height + result += sizeof(qint32) + sizeof(qint32); + // + type + result += sizeof(qint32); + + if (auto sticker = document->sticker()) { // type == StickerDocument + // + altlen + alt + type-of-set + result += stringSize(sticker->alt) + sizeof(qint32); + // + thumb loc + result += Serialize::storageImageLocationSize(); + } else { + // + duration + result += sizeof(qint32); + // + thumb loc + result += Serialize::storageImageLocationSize(); + } + + return result; +} + +} // namespace Serialize diff --git a/Telegram/SourceFiles/serialize/serialize_document.h b/Telegram/SourceFiles/serialize/serialize_document.h new file mode 100644 index 000000000..b0ed77282 --- /dev/null +++ b/Telegram/SourceFiles/serialize/serialize_document.h @@ -0,0 +1,51 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop 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. + +It 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. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "structs.h" + +namespace Serialize { + +class Document { +public: + + struct StickerSetInfo { + StickerSetInfo(uint64 setId, uint64 accessHash, QString shortName) + : setId(setId) + , accessHash(accessHash) + , shortName(shortName) { + } + uint64 setId; + uint64 accessHash; + QString shortName; + }; + + static void writeToStream(QDataStream &stream, DocumentData *document); + static DocumentData *readStickerFromStream(QDataStream &stream, const StickerSetInfo &info); + static DocumentData *readFromStream(QDataStream &stream); + static int sizeInStream(DocumentData *document); + +private: + static DocumentData *readFromStreamHelper(QDataStream &stream, const StickerSetInfo *info); + +}; + +} // namespace Serialize diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 95aa896c5..95153ca86 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -986,22 +986,30 @@ VoiceData::~VoiceData() { } } -DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 date, const QVector &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) : id(id) -, type(FileDocument) -, access(access) -, date(date) -, mime(mime) -, thumb(thumb) -, dc(dc) -, size(size) -, status(FileReady) -, uploadOffset(0) -, _additional(0) -, _duration(-1) -, _actionOnLoad(ActionOnLoadNone) -, _loader(0) { +DocumentAdditionalData::~DocumentAdditionalData() { +} + +DocumentData::DocumentData(DocumentId id, int32 dc, uint64 accessHash, const QString &url, const QVector &attributes) +: id(id) +, _dc(dc) +, _access(accessHash) +, _url(url) { setattributes(attributes); - _location = Local::readFileLocation(mediaKey()); + if (_dc && _access) { + _location = Local::readFileLocation(mediaKey()); + } +} + +DocumentData *DocumentData::create(DocumentId id) { + return new DocumentData(id, 0, 0, QString(), QVector()); +} + +DocumentData *DocumentData::create(DocumentId id, int32 dc, uint64 accessHash, const QVector &attributes) { + return new DocumentData(id, dc, accessHash, QString(), attributes); +} + +DocumentData *DocumentData::create(DocumentId id, const QString &url, const QVector &attributes) { + return new DocumentData(id, 0, 0, url, attributes); } void DocumentData::setattributes(const QVector &attributes) { @@ -1013,15 +1021,13 @@ void DocumentData::setattributes(const QVector &attributes } break; case mtpc_documentAttributeAnimated: if (type == FileDocument || type == StickerDocument || type == VideoDocument) { type = AnimatedDocument; - delete _additional; - _additional = 0; + _additional.clear(); } break; case mtpc_documentAttributeSticker: { const auto &d(attributes[i].c_documentAttributeSticker()); if (type == FileDocument) { type = StickerDocument; - StickerData *sticker = new StickerData(); - _additional = sticker; + _additional = MakeUnique(); } if (sticker()) { sticker()->alt = qs(d.valt); @@ -1041,12 +1047,10 @@ void DocumentData::setattributes(const QVector &attributes if (type == FileDocument) { if (d.is_voice()) { type = VoiceDocument; - VoiceData *voice = new VoiceData(); - _additional = voice; + _additional = MakeUnique(); } else { type = SongDocument; - SongData *song = new SongData(); - _additional = song; + _additional = MakeUnique(); } } if (voice()) { @@ -1071,8 +1075,7 @@ void DocumentData::setattributes(const QVector &attributes if (type == StickerDocument) { if (dimensions.width() <= 0 || dimensions.height() <= 0 || dimensions.width() > StickerMaxSize || dimensions.height() > StickerMaxSize || size > StickerInMemory) { type = FileDocument; - delete _additional; - _additional = 0; + _additional.clear(); } } } @@ -1203,7 +1206,7 @@ bool DocumentData::loaded(FilePathResolveType type) const { if (loading() && _loader->done()) { if (_loader->fileType() == mtpc_storage_fileUnknown) { _loader->deleteLater(); - _loader->rpcClear(); + _loader->stop(); _loader = CancelledMtpFileLoader; } else { DocumentData *that = const_cast(this); @@ -1214,8 +1217,8 @@ bool DocumentData::loaded(FilePathResolveType type) const { } _loader->deleteLater(); - _loader->rpcClear(); - _loader = 0; + _loader->stop(); + _loader = nullptr; } notifyLayoutChanged(); } @@ -1284,7 +1287,11 @@ void DocumentData::save(const QString &toFile, ActionOnLoad action, const FullMs if (fromCloud == LoadFromCloudOrLocal) _loader->permitLoadFromCloud(); } else { status = FileReady; - _loader = new mtpFileLoader(dc, id, access, locationType(), toFile, size, (saveToCache() ? LoadToCacheAsWell : LoadToFileOnly), fromCloud, autoLoading); + if (!_access && !_url.isEmpty()) { + _loader = new webFileLoader(_url, toFile, fromCloud, autoLoading); + } else { + _loader = new mtpFileLoader(_dc, id, _access, locationType(), toFile, size, (saveToCache() ? LoadToCacheAsWell : LoadToFileOnly), fromCloud, autoLoading); + } _loader->connect(_loader, SIGNAL(progress(FileLoader*)), App::main(), SLOT(documentLoadProgress(FileLoader*))); _loader->connect(_loader, SIGNAL(failed(FileLoader*,bool)), App::main(), SLOT(documentLoadFailed(FileLoader*,bool))); _loader->start(); @@ -1295,12 +1302,12 @@ void DocumentData::save(const QString &toFile, ActionOnLoad action, const FullMs void DocumentData::cancel() { if (!loading()) return; - mtpFileLoader *l = _loader; + FileLoader *l = _loader; _loader = CancelledMtpFileLoader; if (l) { l->cancel(); l->deleteLater(); - l->rpcClear(); + l->stop(); notifyLayoutChanged(); } @@ -1418,13 +1425,27 @@ void DocumentData::recountIsImage() { _duration = fileIsImage(name, mime) ? 1 : -1; // hack } -DocumentData::~DocumentData() { - delete _additional; +void DocumentData::setRemoteLocation(int32 dc, uint64 access) { + _dc = dc; + _access = access; + if (isValid()) { + if (_location.check()) { + Local::writeFileLocation(mediaKey(), _location); + } else { + _location = Local::readFileLocation(mediaKey()); + } + } +} +void DocumentData::setContentUrl(const QString &url) { + _url = url; +} + +DocumentData::~DocumentData() { if (loading()) { _loader->deleteLater(); _loader->stop(); - _loader = 0; + _loader = nullptr; } } diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 740cd6316..c5dc969b1 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -954,6 +954,7 @@ enum DocumentType { }; struct DocumentAdditionalData { + virtual ~DocumentAdditionalData(); }; struct StickerData : public DocumentAdditionalData { @@ -989,9 +990,16 @@ struct VoiceData : public DocumentAdditionalData { bool fileIsImage(const QString &name, const QString &mime); +namespace Serialize { +class Document; +} // namespace Serialize; + class DocumentData { public: - DocumentData(const DocumentId &id, const uint64 &access = 0, int32 date = 0, const QVector &attributes = QVector(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0); + static DocumentData *create(DocumentId id); + static DocumentData *create(DocumentId id, int32 dc, uint64 accessHash, const QVector &attributes); + static DocumentData *create(DocumentId id, const QString &url, const QVector &attributes); + void setattributes(const QVector &attributes); void automaticLoad(const HistoryItem *item); // auto load sticker or video @@ -1026,7 +1034,7 @@ public: ImagePtr makeReplyPreview(); StickerData *sticker() { - return (type == StickerDocument) ? static_cast(_additional) : nullptr; + return (type == StickerDocument) ? static_cast(_additional.data()) : nullptr; } void checkSticker() { StickerData *s = sticker(); @@ -1046,16 +1054,16 @@ public: } } SongData *song() { - return (type == SongDocument) ? static_cast(_additional) : nullptr; + return (type == SongDocument) ? static_cast(_additional.data()) : nullptr; } const SongData *song() const { - return (type == SongDocument) ? static_cast(_additional) : nullptr; + return (type == SongDocument) ? static_cast(_additional.data()) : nullptr; } VoiceData *voice() { - return (type == VoiceDocument) ? static_cast(_additional) : nullptr; + return (type == VoiceDocument) ? static_cast(_additional.data()) : nullptr; } const VoiceData *voice() const { - return (type == VoiceDocument) ? static_cast(_additional) : nullptr; + return (type == VoiceDocument) ? static_cast(_additional.data()) : nullptr; } bool isAnimation() const { return (type == AnimatedDocument) || !mime.compare(qstr("image/gif"), Qt::CaseInsensitive); @@ -1064,7 +1072,7 @@ public: return (type == AnimatedDocument) && !mime.compare(qstr("video/mp4"), Qt::CaseInsensitive); } bool isMusic() const { - return (type == SongDocument) ? !static_cast(_additional)->title.isEmpty() : false; + return (type == SongDocument) ? !static_cast(_additional.data())->title.isEmpty() : false; } bool isVideo() const { return (type == VideoDocument); @@ -1080,41 +1088,59 @@ public: _data = data; } + void setRemoteLocation(int32 dc, uint64 access); + void setContentUrl(const QString &url); + bool isValid() const { + return (_dc != 0 && _access != 0) || !_url.isEmpty(); + } + MTPInputDocument mtpInput() const { + if (_access) { + return MTP_inputDocument(MTP_long(id), MTP_long(_access)); + } + return MTP_inputDocumentEmpty(); + } + ~DocumentData(); DocumentId id; - DocumentType type; + DocumentType type = FileDocument; QSize dimensions; - uint64 access; - int32 date; + int32 date = 0; QString name, mime; ImagePtr thumb, replyPreview; - int32 dc; - int32 size; + int32 size = 0; - FileStatus status; - int32 uploadOffset; + FileStatus status = FileReady; + int32 uploadOffset = 0; int32 md5[8]; MediaKey mediaKey() const { - return ::mediaKey(locationType(), dc, id); + return ::mediaKey(locationType(), _dc, id); } private: + DocumentData(DocumentId id, int32 dc, uint64 accessHash, const QString &url, const QVector &attributes); - FileLocation _location; - QByteArray _data; - DocumentAdditionalData *_additional; - int32 _duration; + friend class Serialize::Document; LocationType locationType() const { return voice() ? AudioFileLocation : (isVideo() ? VideoFileLocation : DocumentFileLocation); } - ActionOnLoad _actionOnLoad; + // Two types of location: from MTProto by dc+access or from web by url + int32 _dc = 0; + uint64 _access = 0; + QString _url; + + FileLocation _location; + QByteArray _data; + UniquePointer _additional; + int32 _duration = -1; + + ActionOnLoad _actionOnLoad = ActionOnLoadNone; FullMsgId _actionOnLoadMsgId; - mutable mtpFileLoader *_loader; + mutable FileLoader *_loader = nullptr; void notifyLayoutChanged() const; diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index a5e72354e..466fa7794 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -1124,6 +1124,8 @@ true true + + @@ -1329,6 +1331,8 @@ .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/mtproto/session.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl\Release\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\..\..\Libraries\breakpad\src" "-I.\ThirdParty\minizip" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui" + + $(QTDIR)\bin\moc.exe;%(FullPath) diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters index 7576a456f..a732d11e2 100644 --- a/Telegram/Telegram.vcxproj.filters +++ b/Telegram/Telegram.vcxproj.filters @@ -58,6 +58,9 @@ {815de139-ef13-45d6-a131-a3556eefae55} + + {e9244e0a-a3ae-43dc-8a72-fd7d14cee20b} + @@ -1008,6 +1011,12 @@ ui\toast + + serialize + + + serialize + @@ -1124,6 +1133,12 @@ ui\toast + + serialize + + + serialize +