common/Log.hpp | 16 ++++++++++++ common/Util.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++-- common/Util.hpp | 10 ++++++++ test/TileCacheTests.cpp | 2 - test/WhiteBoxTests.cpp | 29 +++++++++++++++++++++++ wsd/DocumentBroker.cpp | 20 ++++++++-------- wsd/DocumentBroker.hpp | 4 +-- wsd/Storage.cpp | 40 ++++++-------------------------- wsd/Storage.hpp | 10 ++++---- wsd/TileCache.cpp | 29 +++++++---------------- wsd/TileCache.hpp | 3 -- 11 files changed, 150 insertions(+), 73 deletions(-)
New commits: commit f7079c9644502c372cde88051e21143ad1337b39 Author: Michael Meeks <[email protected]> AuthorDate: Mon Sep 2 04:18:12 2019 -0400 Commit: Michael Meeks <[email protected]> CommitDate: Mon Sep 2 15:50:37 2019 -0400 remove un-necessary includes. Change-Id: I360169b15fc245dfe43befb2934739b101547e26 diff --git a/wsd/TileCache.cpp b/wsd/TileCache.cpp index c0d2705d3..27d0fa9d2 100644 --- a/wsd/TileCache.cpp +++ b/wsd/TileCache.cpp @@ -14,6 +14,7 @@ #include <cassert> #include <climits> #include <cstdio> +#include <cstddef> #include <fstream> #include <iostream> #include <memory> @@ -21,14 +22,6 @@ #include <string> #include <vector> -#include <Poco/DigestEngine.h> -#include <Poco/DirectoryIterator.h> -#include <Poco/Exception.h> -#include <Poco/File.h> -#include <Poco/Path.h> -#include <Poco/StringTokenizer.h> -#include <Poco/URI.h> - #include "ClientSession.hpp" #include <Common.hpp> #include <Protocol.hpp> @@ -38,8 +31,6 @@ using namespace LOOLProtocol; -using Poco::StringTokenizer; - TileCache::TileCache(const std::string& docURL, const std::chrono::system_clock::time_point& modifiedTime, bool dontCache) : @@ -326,15 +317,15 @@ void TileCache::invalidateTiles(const std::string& tiles) std::pair<int, Util::Rectangle> TileCache::parseInvalidateMsg(const std::string& tiles) { - StringTokenizer tokens(tiles, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); + std::vector<std::string> tokens = LOOLProtocol::tokenize(tiles); - assert(tokens[0] == "invalidatetiles:"); + assert(tokens.size() > 0 && tokens[0] == "invalidatetiles:"); - if (tokens.count() == 2 && tokens[1] == "EMPTY") + if (tokens.size() == 2 && tokens[1] == "EMPTY") { return std::pair<int, Util::Rectangle>(-1, Util::Rectangle(0, 0, INT_MAX, INT_MAX)); } - else if (tokens.count() == 3 && tokens[1] == "EMPTY,") + else if (tokens.size() == 3 && tokens[1] == "EMPTY,") { int part = 0; if (stringToInteger(tokens[2], part)) @@ -345,7 +336,7 @@ std::pair<int, Util::Rectangle> TileCache::parseInvalidateMsg(const std::string& else { int part, x, y, width, height; - if (tokens.count() == 6 && + if (tokens.size() == 6 && getTokenInteger(tokens[1], "part", part) && getTokenInteger(tokens[2], "x", x) && getTokenInteger(tokens[3], "y", y) && commit 22f1656e08d1cc873a523bf1f869bdd75066dd2a Author: DarkByt31 <[email protected]> AuthorDate: Fri Aug 30 23:07:55 2019 +0530 Commit: Michael Meeks <[email protected]> CommitDate: Mon Sep 2 15:50:37 2019 -0400 tdf#107038 Poco::Timestamp replacement with std::chrono Added functions to get file timestamp and to convert chrono timestamp in ISO8601 fraction format and some test cases. Change-Id: I58961a31f7262b367cff9f33cffdec7571a2f8f7 diff --git a/common/Log.hpp b/common/Log.hpp index 5b7e77f91..fc0408088 100644 --- a/common/Log.hpp +++ b/common/Log.hpp @@ -38,6 +38,12 @@ inline std::ostream& operator<< (std::ostream& os, const Poco::Timestamp& ts) return os; } +inline std::ostream& operator<< (std::ostream& os, const std::chrono::system_clock::time_point& ts) +{ + os << Util::getIso8601FracformatTime(ts); + return os; +} + namespace Log { /// Initialize the logging system. @@ -201,6 +207,16 @@ namespace Log return lhs; } + inline StreamLogger& operator<<(StreamLogger& lhs, const std::chrono::system_clock::time_point& rhs) + { + if (lhs.enabled()) + { + lhs.getStream() << Util::getIso8601FracformatTime(rhs); + } + + return lhs; + } + inline void operator<<(StreamLogger& lhs, const _end_marker&) { (void)end; diff --git a/common/Util.cpp b/common/Util.cpp index 9cba4a24c..00454f0f8 100644 --- a/common/Util.cpp +++ b/common/Util.cpp @@ -762,11 +762,11 @@ namespace Util std::string getHttpTimeNow() { - char time_now[50]; + char time_now[64]; std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); std::time_t now_c = std::chrono::system_clock::to_time_t(now); std::tm now_tm = *std::gmtime(&now_c); - strftime(time_now, 50, "%a, %d %b %Y %T", &now_tm); + strftime(time_now, sizeof(time_now), "%a, %d %b %Y %T", &now_tm); return time_now; } @@ -784,6 +784,62 @@ namespace Util } return std::string::npos; } + + std::chrono::system_clock::time_point getFileTimestamp(std::string str_path) + { + struct stat file; + stat(str_path.c_str(), &file); + std::chrono::seconds ns{file.st_mtime}; + std::chrono::system_clock::time_point mod_time_point{ns}; + + return mod_time_point; + } + + std::string getIso8601FracformatTime(std::chrono::system_clock::time_point time){ + char time_modified[64]; + std::time_t lastModified_us_t = std::chrono::high_resolution_clock::to_time_t(time); + std::tm lastModified_tm = *std::gmtime(&lastModified_us_t); + strftime(time_modified, sizeof(time_modified), "%FT%T.", &lastModified_tm); + + auto lastModified_s = std::chrono::time_point_cast<std::chrono::seconds>(time); + + std::ostringstream oss; + oss << std::setfill('0') + << time_modified + << std::setw(6) + << (time - lastModified_s).count() / 1000 + << "Z"; + + return oss.str(); + } + + std::chrono::system_clock::time_point iso8601ToTimestamp(const std::string& iso8601Time, const std::string& logName) + { + std::chrono::system_clock::time_point timestamp; + std::tm tm{}; + std::istringstream iss(iso8601Time); + if (!(iss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S"))) + { + LOG_WRN(logName << " [" << iso8601Time << "] is in invalid format." + << "Returning " << timestamp.time_since_epoch().count()); + return timestamp; + } + timestamp += std::chrono::seconds(timegm(&tm)); + if (iss.eof()) + return timestamp; + double us; + if (iss.peek() != '.' || !(iss >> us)) + { + LOG_WRN(logName << " [" << iso8601Time << "] is in invalid format." + << ". Returning " << timestamp.time_since_epoch().count()); + return timestamp; + } + std::size_t seconds_us = us * std::chrono::system_clock::period::den / std::chrono::system_clock::period::num; + + timestamp += std::chrono::system_clock::duration(seconds_us); + + return timestamp; + } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/common/Util.hpp b/common/Util.hpp index e16479f68..52b960c82 100644 --- a/common/Util.hpp +++ b/common/Util.hpp @@ -924,6 +924,16 @@ int main(int argc, char**argv) //// Return current time in HTTP format. std::string getHttpTimeNow(); + + //// Return timestamp of file + std::chrono::system_clock::time_point getFileTimestamp(std::string str_path); + + //// Return time in ISO8061 fraction format + std::string getIso8601FracformatTime(std::chrono::system_clock::time_point time); + + //// Convert time from ISO8061 fraction format + std::chrono::system_clock::time_point iso8601ToTimestamp(const std::string& iso8601Time, const std::string& logName); + } // end namespace Util #endif diff --git a/test/TileCacheTests.cpp b/test/TileCacheTests.cpp index ef8c1e4c5..48f664131 100644 --- a/test/TileCacheTests.cpp +++ b/test/TileCacheTests.cpp @@ -194,7 +194,7 @@ void TileCacheTests::testSimple() // Create TileCache and pretend the file was modified as recently as // now, so it discards the cached data. - TileCache tc("doc.ods", Poco::Timestamp()); + TileCache tc("doc.ods", std::chrono::system_clock::time_point()); int part = 0; int width = 256; diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp index 3f5f028cf..35c6faf5b 100644 --- a/test/WhiteBoxTests.cpp +++ b/test/WhiteBoxTests.cpp @@ -55,6 +55,7 @@ class WhiteBoxTests : public CPPUNIT_NS::TestFixture void testAuthorization(); void testJson(); void testAnonymization(); + void testTime(); }; void WhiteBoxTests::testLOOLProtocolFunctions() @@ -731,6 +732,34 @@ void WhiteBoxTests::testAnonymization() CPPUNIT_ASSERT_EQUAL(urlAnonymized3, Util::anonymizeUrl(fileUrl, nAnonymizationSalt)); } +void WhiteBoxTests::testTime() +{ + std::ostringstream oss; + + std::chrono::system_clock::time_point t(std::chrono::nanoseconds(1567444337874777375)); + CPPUNIT_ASSERT_EQUAL(std::string("2019-09-02T17:12:17.874777Z"), Util::getIso8601FracformatTime(t)); + + t = std::chrono::system_clock::time_point(std::chrono::nanoseconds(0)); + CPPUNIT_ASSERT_EQUAL(std::string("1970-01-01T00:00:00.000000Z"), Util::getIso8601FracformatTime(t)); + + t = Util::iso8601ToTimestamp("2019-09-02T17:12:17.874777Z", "LastModifiedTime"); + oss << t.time_since_epoch().count(); + CPPUNIT_ASSERT_EQUAL(std::string("1567444337874777000"), oss.str()); + + t = Util::iso8601ToTimestamp("1970-01-01T00:00:00.000000Z", "LastModifiedTime"); + oss << t.time_since_epoch().count(); + CPPUNIT_ASSERT_EQUAL(std::string("0"), oss.str()); + + t = std::chrono::system_clock::now(); + uint64_t t_in_micros = (t.time_since_epoch().count() / 1000) * 1000; + oss << t_in_micros; + std::string first = oss.str(); + std::string s = Util::getIso8601FracformatTime(t); + t = Util::iso8601ToTimestamp(s, "LastModifiedTime"); + oss << t.time_since_epoch().count(); + CPPUNIT_ASSERT_EQUAL(first, oss.str()); +} + CPPUNIT_TEST_SUITE_REGISTRATION(WhiteBoxTests); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp index 026bdda08..046f916cb 100644 --- a/wsd/DocumentBroker.cpp +++ b/wsd/DocumentBroker.cpp @@ -69,7 +69,7 @@ namespace void sendLastModificationTime(const std::shared_ptr<Session>& session, DocumentBroker* documentBroker, - const Poco::Timestamp& documentLastModifiedTime) + const std::chrono::system_clock::time_point& documentLastModifiedTime) { if (!session) return; @@ -679,7 +679,7 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s { // Check if document has been modified by some external action LOG_TRC("Document modified time: " << fileInfo.getModifiedTime()); - static const Poco::Timestamp Zero(Poco::Timestamp::fromEpochTime(0)); + static const std::chrono::system_clock::time_point Zero; if (_documentLastModifiedTime != Zero && fileInfo.getModifiedTime() != Zero && _documentLastModifiedTime != fileInfo.getModifiedTime()) @@ -776,8 +776,8 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s _filename = fileInfo.getFilename(); // Use the local temp file's timestamp. - _lastFileModifiedTime = templateSource.empty() ? Poco::File(_storage->getRootFilePath()).getLastModified() : - Poco::Timestamp::fromEpochTime(0); + _lastFileModifiedTime = templateSource.empty() ? Util::getFileTimestamp(_storage->getRootFilePath()) : + std::chrono::system_clock::time_point(); bool dontUseCache = false; #if MOBILEAPP @@ -894,12 +894,14 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId, const std::string uriAnonym = LOOLWSD::anonymizeUrl(uri); // If the file timestamp hasn't changed, skip saving. - const Poco::Timestamp newFileModifiedTime = Poco::File(_storage->getRootFilePath()).getLastModified(); + const std::chrono::system_clock::time_point newFileModifiedTime = Util::getFileTimestamp(_storage->getRootFilePath()); if (!isSaveAs && newFileModifiedTime == _lastFileModifiedTime && !isRename) { // Nothing to do. + auto timeInSec = std::chrono::duration_cast<std::chrono::seconds> + (std::chrono::system_clock::now() - _lastFileModifiedTime); LOG_DBG("Skipping unnecessary saving to URI [" << uriAnonym << "] with docKey [" << _docKey << - "]. File last modified " << _lastFileModifiedTime.elapsed() / 1000000 << " seconds ago."); + "]. File last modified " << timeInSec.count() << " seconds ago."); _poll->wakeup(); return true; } @@ -1108,7 +1110,7 @@ bool DocumentBroker::sendUnoSave(const std::string& sessionId, bool dontTerminat if (_sessions.find(sessionId) != _sessions.end()) { // Invalidate the timestamp to force persisting. - _lastFileModifiedTime = Poco::Timestamp::fromEpochTime(0); + _lastFileModifiedTime = std::chrono::system_clock::time_point(); // We do not want save to terminate editing mode if we are in edit mode now @@ -1578,7 +1580,7 @@ bool DocumentBroker::lookupSendClipboardTag(const std::shared_ptr<StreamSocket> { std::ostringstream oss; oss << "HTTP/1.1 200 OK\r\n" - << "Last-Modified: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n" + << "Last-Modified: " << Util::getHttpTimeNow() << "\r\n" << "User-Agent: " << WOPI_AGENT_STRING << "\r\n" << "Content-Length: " << saved->length() << "\r\n" << "Content-Type: application/octet-stream\r\n" @@ -1601,7 +1603,7 @@ bool DocumentBroker::lookupSendClipboardTag(const std::shared_ptr<StreamSocket> // Bad request. std::ostringstream oss; oss << "HTTP/1.1 400\r\n" - << "Date: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n" + << "Date: " << Util::getHttpTimeNow() << "\r\n" << "User-Agent: LOOLWSD WOPI Agent\r\n" << "Content-Length: 0\r\n" << "\r\n" diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index fe370cdd6..d3bdaa6d9 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -449,10 +449,10 @@ private: std::chrono::steady_clock::time_point _lastSaveResponseTime; /// The document's last-modified time on storage. - Poco::Timestamp _documentLastModifiedTime; + std::chrono::system_clock::time_point _documentLastModifiedTime; /// The jailed file last-modified time. - Poco::Timestamp _lastFileModifiedTime; + std::chrono::system_clock::time_point _lastFileModifiedTime; /// All session of this DocBroker by ID. std::map<std::string, std::shared_ptr<ClientSession> > _sessions; diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp index 0057f8cf8..b7fef179b 100644 --- a/wsd/Storage.cpp +++ b/wsd/Storage.cpp @@ -18,8 +18,6 @@ #include <iconv.h> #include <string> -#include <Poco/DateTime.h> -#include <Poco/DateTimeParser.h> #include <Poco/Exception.h> #include <Poco/JSON/Object.h> #include <Poco/JSON/Parser.h> @@ -41,7 +39,6 @@ #endif #include <Poco/StreamCopier.h> -#include <Poco/Timestamp.h> #include <Poco/URI.h> #include "Auth.hpp" @@ -261,9 +258,10 @@ std::unique_ptr<LocalStorage::LocalFileInfo> LocalStorage::getLocalFileInfo() const Poco::Path path = Poco::Path(getUri().getPath()); LOG_DBG("Getting info for local uri [" << LOOLWSD::anonymizeUrl(getUri().toString()) << "], path [" << LOOLWSD::anonymizeUrl(path.toString()) << "]."); + std::string str_path = path.toString(); const auto& filename = path.getFileName(); const Poco::File file = Poco::File(path); - const Poco::Timestamp lastModified = file.getLastModified(); + std::chrono::high_resolution_clock::time_point lastModified = Util::getFileTimestamp(str_path); const size_t size = file.getSize(); setFileInfo(FileInfo({filename, "localhost", lastModified, size})); @@ -350,7 +348,9 @@ StorageBase::SaveResult LocalStorage::saveLocalFileToStorage(const Authorization // update its fileinfo object. This is used later to check if someone else changed the // document while we are/were editing it - getFileInfo().setModifiedTime(Poco::File(getUri().getPath()).getLastModified()); + const Poco::Path path = Poco::Path(getUri().getPath()); + std::string str_path = path.toString(); + getFileInfo().setModifiedTime(Util::getFileTimestamp(str_path)); LOG_TRC("New FileInfo modified time in storage " << getFileInfo().getModifiedTime()); } catch (const Poco::Exception& exc) @@ -397,28 +397,6 @@ void addStorageDebugCookie(Poco::Net::HTTPRequest& request) #endif } -Poco::Timestamp iso8601ToTimestamp(const std::string& iso8601Time, const std::string& name) -{ - Poco::Timestamp timestamp = Poco::Timestamp::fromEpochTime(0); - try - { - if (!iso8601Time.empty()) - { - int timeZoneDifferential; - Poco::DateTime dateTime; - Poco::DateTimeParser::parse(Poco::DateTimeFormat::ISO8601_FRAC_FORMAT, iso8601Time, dateTime, timeZoneDifferential); - timestamp = dateTime.timestamp(); - } - } - catch (const Poco::SyntaxException& exc) - { - LOG_WRN(name << " [" << iso8601Time << "] is in invalid format: " << exc.displayText() << - (exc.nested() ? " (" + exc.nested()->displayText() + ")" : "") << ". Returning " << timestamp); - } - - return timestamp; -} - } // anonymous namespace std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Authorization& auth) @@ -610,7 +588,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Au throw UnauthorizedRequestException("Access denied. WOPI::CheckFileInfo failed on: " + uriAnonym); } - const Poco::Timestamp modifiedTime = iso8601ToTimestamp(lastModifiedTime, "LastModifiedTime"); + const std::chrono::system_clock::time_point modifiedTime = Util::iso8601ToTimestamp(lastModifiedTime, "LastModifiedTime"); setFileInfo(FileInfo({filename, ownerId, modifiedTime, size})); return std::unique_ptr<WopiStorage::WOPIFileInfo>(new WOPIFileInfo( @@ -745,9 +723,7 @@ StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const Authorization& if (!getForceSave()) { // Request WOPI host to not overwrite if timestamps mismatch - request.set("X-LOOL-WOPI-Timestamp", - Poco::DateTimeFormatter::format(Poco::DateTime(getFileInfo().getModifiedTime()), - Poco::DateTimeFormat::ISO8601_FRAC_FORMAT)); + request.set("X-LOOL-WOPI-Timestamp", Util::getIso8601FracformatTime(getFileInfo().getModifiedTime())); } } else @@ -855,7 +831,7 @@ StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const Authorization& { const std::string lastModifiedTime = JsonUtil::getJSONValue<std::string>(object, "LastModifiedTime"); LOG_TRC(wopiLog << " returns LastModifiedTime [" << lastModifiedTime << "]."); - getFileInfo().setModifiedTime(iso8601ToTimestamp(lastModifiedTime, "LastModifiedTime")); + getFileInfo().setModifiedTime(Util::iso8601ToTimestamp(lastModifiedTime, "LastModifiedTime")); if (isSaveAs || isRename) { diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp index db37087ca..06887c657 100644 --- a/wsd/Storage.hpp +++ b/wsd/Storage.hpp @@ -34,7 +34,7 @@ public: public: FileInfo(const std::string& filename, const std::string& ownerId, - const Poco::Timestamp& modifiedTime, + const std::chrono::system_clock::time_point& modifiedTime, size_t /*size*/) : _filename(filename), _ownerId(ownerId), @@ -52,14 +52,14 @@ public: const std::string& getOwnerId() const { return _ownerId; } - void setModifiedTime(const Poco::Timestamp& modifiedTime) { _modifiedTime = modifiedTime; } + void setModifiedTime(const std::chrono::system_clock::time_point& modifiedTime) { _modifiedTime = modifiedTime; } - const Poco::Timestamp& getModifiedTime() const { return _modifiedTime; } + const std::chrono::system_clock::time_point& getModifiedTime() const { return _modifiedTime; } private: std::string _filename; std::string _ownerId; - Poco::Timestamp _modifiedTime; + std::chrono::system_clock::time_point _modifiedTime; }; class SaveResult @@ -124,7 +124,7 @@ public: _uri(uri), _localStorePath(localStorePath), _jailPath(jailPath), - _fileInfo("", "lool", Poco::Timestamp::fromEpochTime(0), 0), + _fileInfo("", "lool", std::chrono::system_clock::time_point(), 0), _isLoaded(false), _forceSave(false), _isUserModified(false), diff --git a/wsd/TileCache.cpp b/wsd/TileCache.cpp index a4f16a5f0..c0d2705d3 100644 --- a/wsd/TileCache.cpp +++ b/wsd/TileCache.cpp @@ -27,7 +27,6 @@ #include <Poco/File.h> #include <Poco/Path.h> #include <Poco/StringTokenizer.h> -#include <Poco/Timestamp.h> #include <Poco/URI.h> #include "ClientSession.hpp" @@ -40,18 +39,17 @@ using namespace LOOLProtocol; using Poco::StringTokenizer; -using Poco::Timestamp; TileCache::TileCache(const std::string& docURL, - const Timestamp& modifiedTime, + const std::chrono::system_clock::time_point& modifiedTime, bool dontCache) : _docURL(docURL), _dontCache(dontCache) { #ifndef BUILDING_TESTS LOG_INF("TileCache ctor for uri [" << LOOLWSD::anonymizeUrl(_docURL) << - "], modifiedTime=" << (modifiedTime.raw()/1000000) << - "], dontCache=" << _dontCache); + "], modifiedTime=" << std::chrono::duration_cast<std::chrono::seconds> + (modifiedTime.time_since_epoch()).count() << "], dontCache=" << _dontCache); #endif (void)modifiedTime; } diff --git a/wsd/TileCache.hpp b/wsd/TileCache.hpp index d922b941f..88d837e10 100644 --- a/wsd/TileCache.hpp +++ b/wsd/TileCache.hpp @@ -16,7 +16,6 @@ #include <string> #include <unordered_map> -#include <Poco/Timestamp.h> #include <Rectangle.hpp> #include "TileDesc.hpp" @@ -83,7 +82,7 @@ public: /// When the docURL is a non-file:// url, the timestamp has to be provided by the caller. /// For file:// url's, it's ignored. /// When it is missing for non-file:// url, it is assumed the document must be read, and no cached value used. - TileCache(const std::string& docURL, const Poco::Timestamp& modifiedTime, bool dontCache = false); + TileCache(const std::string& docURL, const std::chrono::system_clock::time_point& modifiedTime, bool dontCache = false); ~TileCache(); /// Completely clear the cache contents. _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
