/* 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-2017 John Preston, https://desktop.telegram.org */ #pragma once #include "core/observer.h" namespace MTP { void clearLoaderPriorities(); } enum LocationType { UnknownFileLocation = 0, // 1, 2, etc are used as "version" value in mediaKey() method. DocumentFileLocation = 0x4e45abe9, // mtpc_inputDocumentFileLocation AudioFileLocation = 0x74dc404d, // mtpc_inputAudioFileLocation VideoFileLocation = 0x3d0364ec, // mtpc_inputVideoFileLocation }; enum StorageFileType { StorageFileUnknown = 0xaa963b05, // mtpc_storage_fileUnknown StorageFileJpeg = 0x7efe0e, // mtpc_storage_fileJpeg StorageFileGif = 0xcae1aadf, // mtpc_storage_fileGif StorageFilePng = 0xa4f63c0, // mtpc_storage_filePng StorageFilePdf = 0xae1e508d, // mtpc_storage_filePdf StorageFileMp3 = 0x528a0677, // mtpc_storage_fileMp3 StorageFileMov = 0x4b09ebbc, // mtpc_storage_fileMov StorageFilePartial = 0x40bc6f52, // mtpc_storage_filePartial StorageFileMp4 = 0xb3cea0e4, // mtpc_storage_fileMp4 StorageFileWebp = 0x1081464c, // mtpc_storage_fileWebp }; inline StorageFileType mtpToStorageType(mtpTypeId type) { switch (type) { case mtpc_storage_fileJpeg: return StorageFileJpeg; case mtpc_storage_fileGif: return StorageFileGif; case mtpc_storage_filePng: return StorageFilePng; case mtpc_storage_filePdf: return StorageFilePdf; case mtpc_storage_fileMp3: return StorageFileMp3; case mtpc_storage_fileMov: return StorageFileMov; case mtpc_storage_filePartial: return StorageFilePartial; case mtpc_storage_fileMp4: return StorageFileMp4; case mtpc_storage_fileWebp: return StorageFileWebp; case mtpc_storage_fileUnknown: default: return StorageFileUnknown; } } inline mtpTypeId mtpFromStorageType(StorageFileType type) { switch (type) { case StorageFileJpeg: return mtpc_storage_fileJpeg; case StorageFileGif: return mtpc_storage_fileGif; case StorageFilePng: return mtpc_storage_filePng; case StorageFilePdf: return mtpc_storage_filePdf; case StorageFileMp3: return mtpc_storage_fileMp3; case StorageFileMov: return mtpc_storage_fileMov; case StorageFilePartial: return mtpc_storage_filePartial; case StorageFileMp4: return mtpc_storage_fileMp4; case StorageFileWebp: return mtpc_storage_fileWebp; case StorageFileUnknown: default: return mtpc_storage_fileUnknown; } } struct StorageImageSaved { StorageImageSaved() : type(StorageFileUnknown) { } StorageImageSaved(StorageFileType type, const QByteArray &data) : type(type), data(data) { } StorageFileType type; QByteArray data; }; enum LocalLoadStatus { LocalNotTried, LocalNotFound, LocalLoading, LocalLoaded, LocalFailed, }; using TaskId = void*; // no interface, just id enum LoadFromCloudSetting { LoadFromCloudOrLocal, LoadFromLocalOnly, }; enum LoadToCacheSetting { LoadToFileOnly, LoadToCacheAsWell, }; class mtpFileLoader; class webFileLoader; struct FileLoaderQueue; class FileLoader : public QObject { Q_OBJECT public: FileLoader(const QString &toFile, int32 size, LocationType locationType, LoadToCacheSetting, LoadFromCloudSetting fromCloud, bool autoLoading); bool done() const { return _complete; } mtpTypeId fileType() const { return _type; } const QByteArray &bytes() const { return _data; } virtual uint64 objId() const { return 0; } QByteArray imageFormat(const QSize &shrinkBox = QSize()) const; QPixmap imagePixmap(const QSize &shrinkBox = QSize()) const; QString fileName() const { return _fname; } float64 currentProgress() const; virtual int32 currentOffset(bool includeSkipped = false) const = 0; int32 fullSize() const; bool setFileName(const QString &filename); // set filename for loaders to cache void permitLoadFromCloud(); void pause(); void start(bool loadFirst = false, bool prior = true); void cancel(); bool loading() const { return _inQueue; } bool paused() const { return _paused; } bool started() const { return _inQueue || _paused; } bool loadingLocal() const { return (_localStatus == LocalLoading); } bool autoLoading() const { return _autoLoading; } virtual void stop() { } virtual ~FileLoader(); void localLoaded(const StorageImageSaved &result, const QByteArray &imageFormat = QByteArray(), const QPixmap &imagePixmap = QPixmap()); signals: void progress(FileLoader *loader); void failed(FileLoader *loader, bool started); protected: void readImage(const QSize &shrinkBox) const; FileLoader *_prev = nullptr; FileLoader *_next = nullptr; int _priority = 0; FileLoaderQueue *_queue; bool _paused = false; bool _autoLoading = false; bool _inQueue = false; bool _complete = false; mutable LocalLoadStatus _localStatus = LocalNotTried; virtual bool tryLoadLocal() = 0; virtual void cancelRequests() = 0; void startLoading(bool loadFirst, bool prior); void removeFromQueue(); void cancel(bool failed); void loadNext(); virtual bool loadPart() = 0; QFile _file; QString _fname; bool _fileIsOpen = false; LoadToCacheSetting _toCache; LoadFromCloudSetting _fromCloud; QByteArray _data; int32 _size; mtpTypeId _type; LocationType _locationType; TaskId _localTaskId = 0; mutable QByteArray _imageFormat; mutable QPixmap _imagePixmap; }; class StorageImageLocation; class mtpFileLoader : public FileLoader, public RPCSender { Q_OBJECT public: mtpFileLoader(const StorageImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading); mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, int32 version, LocationType type, const QString &toFile, int32 size, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading); int32 currentOffset(bool includeSkipped = false) const override; uint64 objId() const override { return _id; } void stop() override { rpcClear(); } ~mtpFileLoader(); protected: bool tryLoadLocal() override; void cancelRequests() override; typedef QMap Requests; Requests _requests; bool loadPart() override; void partLoaded(int32 offset, const MTPupload_File &result, mtpRequestId req); bool partFailed(const RPCError &error); bool _lastComplete = false; int32 _skippedBytes = 0; int32 _nextRequestOffset = 0; int32 _dc; const StorageImageLocation *_location = nullptr; uint64 _id = 0; // for other locations uint64 _access = 0; int32 _version = 0; }; class webFileLoaderPrivate; class webFileLoader : public FileLoader { Q_OBJECT public: webFileLoader(const QString &url, const QString &to, LoadFromCloudSetting fromCloud, bool autoLoading); virtual int32 currentOffset(bool includeSkipped = false) const; virtual webFileLoader *webLoader() { return this; } virtual const webFileLoader *webLoader() const { return this; } void onProgress(qint64 already, qint64 size); void onFinished(const QByteArray &data); void onError(); virtual void stop() { cancelRequests(); } ~webFileLoader(); protected: virtual void cancelRequests(); virtual bool tryLoadLocal(); virtual bool loadPart(); QString _url; bool _requestSent; int32 _already; friend class WebLoadManager; webFileLoaderPrivate *_private; }; enum WebReplyProcessResult { WebReplyProcessError, WebReplyProcessProgress, WebReplyProcessFinished, }; class WebLoadManager : public QObject { Q_OBJECT public: WebLoadManager(QThread *thread); #ifndef TDESKTOP_DISABLE_NETWORK_PROXY void setProxySettings(const QNetworkProxy &proxy); #endif // !TDESKTOP_DISABLE_NETWORK_PROXY void append(webFileLoader *loader, const QString &url); void stop(webFileLoader *reader); bool carries(webFileLoader *reader) const; ~WebLoadManager(); signals: void processDelayed(); void proxyApplyDelayed(); void progress(webFileLoader *loader, qint64 already, qint64 size); void finished(webFileLoader *loader, QByteArray data); void error(webFileLoader *loader); public slots: void onFailed(QNetworkReply *reply); void onFailed(QNetworkReply::NetworkError error); void onProgress(qint64 already, qint64 size); void onMeta(); void process(); void proxyApply(); void finish(); private: void clear(); void sendRequest(webFileLoaderPrivate *loader, const QString &redirect = QString()); bool handleReplyResult(webFileLoaderPrivate *loader, WebReplyProcessResult result); #ifndef TDESKTOP_DISABLE_NETWORK_PROXY QNetworkProxy _proxySettings; #endif // !TDESKTOP_DISABLE_NETWORK_PROXY QNetworkAccessManager _manager; typedef QMap LoaderPointers; LoaderPointers _loaderPointers; mutable QMutex _loaderPointersMutex; typedef OrderedSet Loaders; Loaders _loaders; typedef QMap Replies; Replies _replies; }; class WebLoadMainManager : public QObject { Q_OBJECT public: public slots: void progress(webFileLoader *loader, qint64 already, qint64 size); void finished(webFileLoader *loader, QByteArray data); void error(webFileLoader *loader); }; static FileLoader * const CancelledFileLoader = SharedMemoryLocation(); static mtpFileLoader * const CancelledMtpFileLoader = static_cast(CancelledFileLoader); static webFileLoader * const CancelledWebFileLoader = static_cast(CancelledFileLoader); static WebLoadManager * const FinishedWebLoadManager = SharedMemoryLocation(); void reinitWebLoadManager(); void stopWebLoadManager(); namespace FileDownload { base::Observable &ImageLoaded(); } // namespace FileDownload