Chris@208: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@208: Chris@208: /* Chris@208: Sonic Visualiser Chris@208: An audio file viewer and annotation editor. Chris@208: Centre for Digital Music, Queen Mary, University of London. Chris@208: This file copyright 2007 QMUL. Chris@208: Chris@208: This program is free software; you can redistribute it and/or Chris@208: modify it under the terms of the GNU General Public License as Chris@208: published by the Free Software Foundation; either version 2 of the Chris@208: License, or (at your option) any later version. See the file Chris@208: COPYING included with this distribution for more information. Chris@208: */ Chris@208: Chris@208: #ifndef _REMOTE_FILE_H_ Chris@208: #define _REMOTE_FILE_H_ Chris@208: Chris@208: #include Chris@208: #include Chris@208: #include Chris@210: #include Chris@208: Chris@304: #include Chris@304: Chris@208: class QFtp; Chris@208: class QHttp; Chris@208: class QFile; Chris@210: class QHttpResponseHeader; Chris@392: class ProgressReporter; Chris@208: Chris@325: /** Chris@325: * FileSource is a class used to refer to the contents of a file that Chris@325: * may be either local or at a remote location such as a HTTP URL. Chris@325: * Chris@325: * When a FileSource object is constructed, the file or URL passed to Chris@325: * its constructor is tested for validity, and if it refers to a Chris@325: * remote HTTP or FTP location it is retrieved asynchronously (the Qt Chris@325: * event loop must be running) and cached locally. You can then query Chris@325: * a local path for the file and refer to that as a normal filename. Chris@325: * Chris@325: * Use isAvailable() to test whether the file or remote URL exists, Chris@325: * and isOK() to test for internal validity or transmission errors. Chris@325: * Call waitForStatus() to block until the availability and validity Chris@325: * of the URL have been established so that isAvailable may be called, Chris@325: * and waitForData() to block until the entire file has been cached. Chris@325: * Chris@325: * FileSource handles reference counting for cache files. You can Chris@325: * construct many FileSource objects with the same URL and you will Chris@325: * receive the same cached file for each; it is also reasonable to Chris@325: * pass FileSource objects by value. FileSource only makes sense for Chris@325: * stateless URLs that result in the same data on each request. Chris@325: * Chris@460: * Cached files (in their default temporary mode) share a lifetime Chris@460: * with their "owning" FileSource objects. When the last FileSource Chris@460: * referring to a given URL is deleted or goes out of scope, its Chris@460: * cached file (if any) is also removed. You can change this Chris@460: * behaviour by using persistent cache mode; \see LocalCacheMode for Chris@460: * details and caveats. Chris@325: */ Chris@317: class FileSource : public QObject Chris@208: { Chris@208: Q_OBJECT Chris@208: Chris@208: public: Chris@325: /** Chris@460: * Type of local cache to be used when retrieving remote files. Chris@460: * Chris@460: * Temporary cache files are created when a FileSource object is Chris@460: * first created for a given URL, and removed when the last extant Chris@460: * temporary cache mode FileSource object referring to a given URL Chris@460: * is deleted (i.e. when its reference count is lowered to zero). Chris@460: * They are also stored in a temporary directory that will be Chris@460: * deleted when the program exits. Chris@460: * Chris@460: * Persistent cache files are created only when first retrieving a Chris@460: * URL for which no persistent cache already exists, and are never Chris@460: * deleted (by FileSource anyway). They are stored in a directory Chris@460: * that is not deleted when the program exits. FileSource creates Chris@460: * a unique local file name for each source URL, so as long as the Chris@460: * local cache file remains on disc, the remote URL will not be Chris@460: * retrieved again during any further run of the program. You can Chris@460: * find out what local file name will be used for the persistent Chris@460: * cache of a given URL by calling getPersistentCacheFilePath, if Chris@460: * you want to do something such as delete it by hand. Chris@460: * Chris@460: * Note that FileSource does not cache local files (i.e. does not Chris@460: * make a copy of files that already appear to be stored on the Chris@460: * local filesystem) in either mode. Chris@460: */ Chris@460: enum LocalCacheMode { Chris@460: TemporaryCache, Chris@460: PersistentCache Chris@460: }; Chris@460: Chris@460: /** Chris@325: * Construct a FileSource using the given local file path or URL. Chris@357: * The URL may be raw or encoded. Chris@357: * Chris@392: * If a ProgressReporter is provided, it will be updated with Chris@392: * progress status. Note that the progress() signal will also be Chris@392: * emitted regularly during retrieval, even if no reporter is Chris@392: * supplied here. Caller retains ownership of the reporter object. Chris@460: * Chris@460: * If LocalCacheMode is PersistentCache, a persistent cache file Chris@460: * will be used. See LocalCacheMode documentation for details. Chris@325: */ Chris@460: FileSource(QString fileOrUrl, Chris@460: ProgressReporter *reporter = 0, Chris@460: LocalCacheMode mode = TemporaryCache); Chris@325: Chris@325: /** Chris@357: * Construct a FileSource using the given remote URL. Chris@357: * Chris@392: * If a ProgressReporter is provided, it will be updated with Chris@392: * progress status. Note that the progress() signal will also be Chris@392: * emitted regularly during retrieval, even if no reporter is Chris@392: * supplied here. Caller retains ownership of the reporter object. Chris@460: * Chris@460: * If LocalCacheMode is PersistentCache, a persistent cache file Chris@460: * will be used. See LocalCacheMode documentation for details. Chris@325: */ Chris@460: FileSource(QUrl url, Chris@460: ProgressReporter *reporter = 0, Chris@460: LocalCacheMode mode = TemporaryCache); Chris@325: Chris@317: FileSource(const FileSource &); Chris@316: Chris@317: virtual ~FileSource(); Chris@208: Chris@325: /** Chris@325: * Block on a sub-event-loop until the availability of the file or Chris@325: * remote URL is known. Chris@325: */ Chris@325: void waitForStatus(); Chris@325: Chris@325: /** Chris@325: * Block on a sub-event-loop until the whole of the data has been Chris@325: * retrieved (if it is remote). Chris@325: */ Chris@325: void waitForData(); Chris@325: Chris@325: /** Chris@325: * Return true if the FileSource object is valid and no error Chris@325: * occurred in looking up the file or remote URL. Non-existence Chris@325: * of the file or URL is not an error -- call isAvailable() to Chris@325: * test for that. Chris@325: */ Chris@325: bool isOK() const; Chris@325: Chris@325: /** Chris@325: * Return true if the file or remote URL exists. This may block Chris@325: * on a sub-event-loop (calling waitForStatus) if the status is Chris@325: * not yet available. Chris@325: */ Chris@210: bool isAvailable(); Chris@210: Chris@325: /** Chris@325: * Return true if the entire file has been retrieved and is Chris@325: * available. Chris@325: */ Chris@325: bool isDone() const; Chris@316: Chris@325: /** Chris@325: * Return true if this FileSource is referring to a remote URL. Chris@325: */ Chris@325: bool isRemote() const; Chris@325: Chris@325: /** Chris@325: * Return the location filename or URL as passed to the Chris@325: * constructor. Chris@325: */ Chris@325: QString getLocation() const; Chris@325: Chris@325: /** Chris@325: * Return the name of the local file this FileSource refers to. Chris@325: * This is the filename that should be used when reading normally Chris@325: * from the FileSource. If the filename passed to the constructor Chris@325: * was a local file, this will return the same filename; otherwise Chris@325: * this will be the name of the temporary cache file owned by the Chris@325: * FileSource. Chris@325: */ Chris@325: QString getLocalFilename() const; Chris@325: Chris@325: /** Chris@325: * Return the MIME content type of this file, if known. Chris@325: */ Chris@325: QString getContentType() const; Chris@325: Chris@325: /** Chris@325: * Return the file extension for this file, if any. Chris@325: */ Chris@325: QString getExtension() const; Chris@325: Chris@325: /** Chris@325: * Return an error message, if isOK() is false. Chris@325: */ Chris@325: QString getErrorString() const; Chris@325: Chris@325: /** Chris@325: * Specify whether any local, cached file should remain on disc Chris@325: * after this FileSource has been destroyed. The default is false Chris@460: * (cached files share their FileSource owners' lifespans). This Chris@460: * is only meaningful in TemporaryCache mode; even if this setting Chris@460: * is true, the temporary cache will still be deleted when the Chris@460: * program exits. Use PersistentCache mode if you want the cache Chris@460: * to outlast the program. Chris@325: */ Chris@316: void setLeaveLocalFile(bool leave); Chris@208: Chris@325: /** Chris@325: * Return true if the given filename or URL refers to a remote Chris@325: * URL. Chris@325: */ Chris@325: static bool isRemote(QString fileOrUrl); Chris@210: Chris@325: /** Chris@325: * Return true if FileSource can handle the retrieval scheme for Chris@325: * the given URL (or if the URL is for a local file). Chris@325: */ Chris@208: static bool canHandleScheme(QUrl url); Chris@208: Chris@460: /** Chris@460: * Return the path that will be used for the cache file copy of Chris@460: * the given remote URL by a FileSource object constructed in Chris@460: * PersistentCache mode. Chris@460: * Chris@460: * This method also creates the containing directory for such Chris@460: * cache files, if it does not already exist, and so may throw Chris@460: * DirectoryCreationFailed. Chris@460: */ Chris@460: static QString getPersistentCacheFilePath(QUrl url); Chris@460: Chris@208: signals: Chris@325: /** Chris@325: * Emitted during URL retrieval, when the retrieval progress Chris@325: * notches up to a new percentage. Chris@325: */ Chris@208: void progress(int percent); Chris@325: Chris@325: /** Chris@325: * Emitted when the file's existence has been tested and/or Chris@325: * response header received. Calls to isAvailable() after this Chris@325: * has been emitted will not block. Chris@325: */ Chris@325: void statusAvailable(); Chris@325: Chris@325: /** Chris@325: * Emitted when the entire file data has been retrieved and the Chris@325: * local file is complete (if no error has occurred). Chris@325: */ Chris@208: void ready(); Chris@208: Chris@208: protected slots: Chris@208: void dataReadProgress(int done, int total); Chris@214: void httpResponseHeaderReceived(const QHttpResponseHeader &resp); Chris@214: void ftpCommandFinished(int, bool); Chris@208: void dataTransferProgress(qint64 done, qint64 total); Chris@208: void done(bool error); Chris@210: void cancelled(); Chris@208: Chris@208: protected: Chris@317: FileSource &operator=(const FileSource &); // not provided Chris@316: Chris@304: QUrl m_url; Chris@460: LocalCacheMode m_cacheMode; Chris@208: QFtp *m_ftp; Chris@208: QHttp *m_http; Chris@208: QFile *m_localFile; Chris@208: QString m_localFilename; Chris@208: QString m_errorString; Chris@315: QString m_contentType; Chris@208: bool m_ok; Chris@210: int m_lastStatus; Chris@316: bool m_remote; Chris@208: bool m_done; Chris@316: bool m_leaveLocalFile; Chris@392: ProgressReporter *m_reporter; Chris@208: Chris@304: typedef std::map RemoteRefCountMap; Chris@304: typedef std::map RemoteLocalMap; Chris@304: static RemoteRefCountMap m_refCountMap; Chris@304: static RemoteLocalMap m_remoteLocalMap; Chris@304: static QMutex m_mapMutex; Chris@316: bool m_refCounted; Chris@316: Chris@357: void init(); Chris@316: void initHttp(); Chris@316: void initFtp(); Chris@304: Chris@211: void cleanup(); Chris@211: Chris@316: // Create a local file for m_url. If it already existed, return true. Chris@316: // The local filename is stored in m_localFilename. Chris@316: bool createCacheFile(); Chris@316: void deleteCacheFile(); Chris@208: Chris@460: static QString getPersistentCacheDirectory(); Chris@460: Chris@208: static QMutex m_fileCreationMutex; Chris@208: static int m_count; Chris@208: }; Chris@208: Chris@208: #endif