From 1009b843632897fe81ae1e35750525eb24cdce25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sat, 23 Jan 2016 16:06:47 +0100 Subject: [PATCH 001/118] added new web controller for knowing the version of the web api --- YACReaderLibrary/headless/main.cpp | 11 +++++- .../server/controllers/versioncontroller.cpp | 10 ++++++ .../server/controllers/versioncontroller.h | 21 ++++++++++++ YACReaderLibrary/server/requestmapper.cpp | 34 ++++++++++++------- YACReaderLibrary/server/server.pri | 10 ++++-- 5 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 YACReaderLibrary/server/controllers/versioncontroller.cpp create mode 100644 YACReaderLibrary/server/controllers/versioncontroller.h diff --git a/YACReaderLibrary/headless/main.cpp b/YACReaderLibrary/headless/main.cpp index 20fe8a6e..a488174e 100644 --- a/YACReaderLibrary/headless/main.cpp +++ b/YACReaderLibrary/headless/main.cpp @@ -9,6 +9,8 @@ #include "console_ui_library_creator.h" +#include + #include "QsLog.h" #include "QsLogDest.h" @@ -118,7 +120,7 @@ int main( int argc, char ** argv ) QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::tr("\nYACReaderLibraryServer is the headless (no gui) version of YACReaderLibrary")); parser.addHelpOption(); - parser.addVersionOption(); + const QCommandLineOption versionOption = parser.addVersionOption(); parser.addPositionalArgument("command", "The command to execute. [start, create-library, update-library, add-library, remove-library, list-libraries]"); parser.parse(QCoreApplication::arguments()); @@ -126,6 +128,13 @@ int main( int argc, char ** argv ) const QStringList args = parser.positionalArguments(); const QString command = args.isEmpty() ? QString() : args.first(); + if(parser.isSet(versionOption)) + { + qout << "YACReaderLibraryServer" << " " << VERSION << endl; + + return 0; + } + if(command == "start") { QString destLog = YACReader::getSettingsPath()+"/yacreaderlibrary.log"; diff --git a/YACReaderLibrary/server/controllers/versioncontroller.cpp b/YACReaderLibrary/server/controllers/versioncontroller.cpp new file mode 100644 index 00000000..341923c7 --- /dev/null +++ b/YACReaderLibrary/server/controllers/versioncontroller.cpp @@ -0,0 +1,10 @@ +#include "versioncontroller.h" + +VersionController::VersionController() {} + +void VersionController::service(HttpRequest& request, HttpResponse& response) +{ + Q_UNUSED(request); + + response.writeText(SERVER_VERSION_NUMBER,true); +} diff --git a/YACReaderLibrary/server/controllers/versioncontroller.h b/YACReaderLibrary/server/controllers/versioncontroller.h new file mode 100644 index 00000000..d645b110 --- /dev/null +++ b/YACReaderLibrary/server/controllers/versioncontroller.h @@ -0,0 +1,21 @@ +#ifndef VERSIONCONTROLLER_H +#define VERSIONCONTROLLER_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +#include + +class VersionController : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(VersionController); +public: + /** Constructor */ + VersionController(); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); +}; + +#endif // VERSIONCONTROLLER_H diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp index 66b03413..75bbe613 100644 --- a/YACReaderLibrary/server/requestmapper.cpp +++ b/YACReaderLibrary/server/requestmapper.cpp @@ -22,6 +22,7 @@ #include "controllers/errorcontroller.h" #include "controllers/comicdownloadinfocontroller.h" #include "controllers/synccontroller.h" +#include "controllers/versioncontroller.h" #include "db_helper.h" #include "yacreader_libraries.h" @@ -95,20 +96,21 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { QByteArray path=request.getPath(); qDebug("RequestMapper: path=%s",path.data()); - QRegExp folder("/library/.+/folder/[0-9]+/?");//get comic content - QRegExp folderInfo("/library/.+/folder/[0-9]+/info/?"); //get folder info - QRegExp comicDownloadInfo("/library/.+/comic/[0-9]+/?"); //get comic info (basic/download info) - QRegExp comicFullInfo("/library/.+/comic/[0-9]+/info/?"); //get comic info (full info) - QRegExp comicOpen("/library/.+/comic/[0-9]+/remote/?"); //the server will open for reading the comic - QRegExp comicUpdate("/library/.+/comic/[0-9]+/update/?"); //get comic info - QRegExp comicClose("/library/.+/comic/[0-9]+/close/?"); //the server will close the comic and free memory - QRegExp cover("/library/.+/cover/[0-9a-f]+.jpg"); //get comic cover (navigation) - QRegExp comicPage("/library/.+/comic/[0-9]+/page/[0-9]+/?"); //get comic page - QRegExp comicPageRemote("/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); //get comic page (remote reading) + static const QRegExp folder("/library/.+/folder/[0-9]+/?");//get comic content + static const QRegExp folderInfo("/library/.+/folder/[0-9]+/info/?"); //get folder info + static const QRegExp comicDownloadInfo("/library/.+/comic/[0-9]+/?"); //get comic info (basic/download info) + static const QRegExp comicFullInfo("/library/.+/comic/[0-9]+/info/?"); //get comic info (full info) + static const QRegExp comicOpen("/library/.+/comic/[0-9]+/remote/?"); //the server will open for reading the comic + static const QRegExp comicUpdate("/library/.+/comic/[0-9]+/update/?"); //get comic info + static const QRegExp comicClose("/library/.+/comic/[0-9]+/close/?"); //the server will close the comic and free memory + static const QRegExp cover("/library/.+/cover/[0-9a-f]+.jpg"); //get comic cover (navigation) + static const QRegExp comicPage("/library/.+/comic/[0-9]+/page/[0-9]+/?"); //get comic page + static const QRegExp comicPageRemote("/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); //get comic page (remote reading) + static const QRegExp serverVersion("/version/?"); - QRegExp sync("/sync"); + static const QRegExp sync("/sync"); - QRegExp library("/library/([0-9]+)/.+"); //permite verificar que la biblioteca solicitada existe + static const QRegExp library("/library/([0-9]+)/.+"); //permite verificar que la biblioteca solicitada existe path = QUrl::fromPercentEncoding(path).toUtf8(); @@ -122,8 +124,14 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { } else { - if(sync.exactMatch(path)) + if(serverVersion.exactMatch(path)) + { + VersionController().service(request, response); + } + else if(sync.exactMatch(path)) + { SyncController().service(request, response); + } else { //se comprueba que la sesión sea la correcta con el fin de evitar accesos no autorizados diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index 4be20612..19eb50a8 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -15,7 +15,9 @@ HEADERS += \ $$PWD/controllers/covercontroller.h \ $$PWD/controllers/updatecomiccontroller.h \ $$PWD/controllers/comicdownloadinfocontroller.h \ - $$PWD/controllers/synccontroller.h + $$PWD/controllers/synccontroller.h \ + #v2 + $$PWD/controllers/versioncontroller.h SOURCES += \ $$PWD/static.cpp \ @@ -31,8 +33,12 @@ SOURCES += \ $$PWD/controllers/covercontroller.cpp \ $$PWD/controllers/updatecomiccontroller.cpp \ $$PWD/controllers/comicdownloadinfocontroller.cpp \ - $$PWD/controllers/synccontroller.cpp + $$PWD/controllers/synccontroller.cpp \ + #v2 + $$PWD/controllers/versioncontroller.cpp include(lib/bfLogging/bfLogging.pri) include(lib/bfHttpServer/bfHttpServer.pri) include(lib/bfTemplateEngine/bfTemplateEngine.pri) + +DEFINES += SERVER_VERSION_NUMBER=\\\"2.0\\\" From e9210bb366d4c10afc965fafe45c5b71a4483c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 10:04:40 +0100 Subject: [PATCH 002/118] added a new db helper method for knowing the number of elements in a folder --- YACReaderLibrary/db_helper.cpp | 32 ++++++++++++++++++++++++++++++++ YACReaderLibrary/db_helper.h | 1 + 2 files changed, 33 insertions(+) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index f46b589f..c781d01e 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -58,6 +58,38 @@ QList DBHelper::getFolderComicsFromLibrary(qulonglong libraryId, QSqlDatabase::removeDatabase(libraryPath); return list; } + +quint32 DBHelper::getNumChildrenFromFolder(qulonglong libraryId, qulonglong folderId) +{ + QString libraryPath = DBHelper::getLibraries().getPath(libraryId); + QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary"); + + quint32 result = 0; + + { + QSqlQuery selectQuery(db); + selectQuery.prepare("SELECT count(*) FROM folder WHERE parentId = :parentId and id <> 1"); + selectQuery.bindValue(":parentId", folderId); + selectQuery.exec(); + + result += selectQuery.record().value(0).toULongLong(); + } + + { + QSqlQuery selectQuery(db); + selectQuery.prepare("SELECT count(*) FROM comic c WHERE c.parentId = :parentId"); + selectQuery.bindValue(":parentId", folderId); + selectQuery.exec(); + + result += selectQuery.record().value(0).toULongLong(); + } + + db.close(); + QSqlDatabase::removeDatabase(libraryPath); + + return result; +} + qulonglong DBHelper::getParentFromComicFolderId(qulonglong libraryId, qulonglong id) { QString libraryPath = DBHelper::getLibraries().getPath(libraryId); diff --git a/YACReaderLibrary/db_helper.h b/YACReaderLibrary/db_helper.h index 5eb2eae3..19611dc7 100644 --- a/YACReaderLibrary/db_helper.h +++ b/YACReaderLibrary/db_helper.h @@ -23,6 +23,7 @@ public: static QList getFolderSubfoldersFromLibrary(qulonglong libraryId, qulonglong folderId); static QList getFolderComicsFromLibrary(qulonglong libraryId, qulonglong folderId); static QList getFolderComicsFromLibrary(qulonglong libraryId, qulonglong folderId, bool sort); + static quint32 getNumChildrenFromFolder(qulonglong libraryId, qulonglong folderId); static qulonglong getParentFromComicFolderId(qulonglong libraryId, qulonglong id); static ComicDB getComicInfo(qulonglong libraryId, qulonglong id); static QList getSiblings(qulonglong libraryId, qulonglong parentId); From e130698ee9b64118ed97e06da5bcd28f23c8e39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 10:34:54 +0100 Subject: [PATCH 003/118] prepare db update for future 8.6 version --- YACReaderLibrary/db/data_base_management.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/YACReaderLibrary/db/data_base_management.cpp b/YACReaderLibrary/db/data_base_management.cpp index 8d18d478..3e343cda 100644 --- a/YACReaderLibrary/db/data_base_management.cpp +++ b/YACReaderLibrary/db/data_base_management.cpp @@ -686,6 +686,7 @@ bool DataBaseManagement::updateToCurrentVersion(const QString & fullPath) bool pre7 = false; bool pre7_1 = false; bool pre8 = false; + bool pre8_6 = false; if(compareVersions(DataBaseManagement::checkValidDB(fullPath),"7.0.0")<0) pre7 = true; @@ -693,6 +694,8 @@ bool DataBaseManagement::updateToCurrentVersion(const QString & fullPath) pre7_1 = true; if(compareVersions(DataBaseManagement::checkValidDB(fullPath),"8.0.0")<0) pre8 = true; + if(compareVersions(DataBaseManagement::checkValidDB(fullPath),"8.6.0")<0) + pre8_6 = true; QSqlDatabase db = loadDatabaseFromFile(fullPath); bool returnValue = false; @@ -745,6 +748,13 @@ bool DataBaseManagement::updateToCurrentVersion(const QString & fullPath) { returnValue = returnValue && createV8Tables(db); } + + if(pre8_6) + { + QStringList columnDefs; + //TODO + //returnValue = returnValue && addColumns("folder", columnDefs, db); + } } db.close(); From b8031268d050f8bec7cd8563105cc223b2a9c1d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 10:36:19 +0100 Subject: [PATCH 004/118] resort db tables creation --- YACReaderLibrary/db/data_base_management.cpp | 155 +++++++++---------- 1 file changed, 77 insertions(+), 78 deletions(-) diff --git a/YACReaderLibrary/db/data_base_management.cpp b/YACReaderLibrary/db/data_base_management.cpp index 3e343cda..1c29814c 100644 --- a/YACReaderLibrary/db/data_base_management.cpp +++ b/YACReaderLibrary/db/data_base_management.cpp @@ -124,99 +124,98 @@ QSqlDatabase DataBaseManagement::loadDatabaseFromFile(QString filePath) bool DataBaseManagement::createTables(QSqlDatabase & database) { - bool success = true; + bool success = true; - //FOLDER (representa una carpeta en disco) - { - QSqlQuery queryFolder(database); - queryFolder.prepare("CREATE TABLE folder (" - "id INTEGER PRIMARY KEY," - "parentId INTEGER NOT NULL," - "name TEXT NOT NULL," - "path TEXT NOT NULL," - //new 7.1 fields - "finished BOOLEAN DEFAULT 0," //reading - "completed BOOLEAN DEFAULT 1," //collecting - //-- - "FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE)"); - success = success && queryFolder.exec(); + { + //COMIC INFO (representa la información de un cómic, cada cómic tendrá un idéntificador único formado por un hash sha1'de los primeros 512kb' + su tamaño en bytes) + QSqlQuery queryComicInfo(database); + queryComicInfo.prepare("CREATE TABLE comic_info (" + "id INTEGER PRIMARY KEY," + "title TEXT," - //COMIC INFO (representa la información de un cómic, cada cómic tendrá un idéntificador único formado por un hash sha1'de los primeros 512kb' + su tamaño en bytes) - QSqlQuery queryComicInfo(database); - queryComicInfo.prepare("CREATE TABLE comic_info (" - "id INTEGER PRIMARY KEY," - "title TEXT," + "coverPage INTEGER DEFAULT 1," + "numPages INTEGER," - "coverPage INTEGER DEFAULT 1," - "numPages INTEGER," + "number INTEGER," + "isBis BOOLEAN," + "count INTEGER," - "number INTEGER," - "isBis BOOLEAN," - "count INTEGER," + "volume TEXT," + "storyArc TEXT," + "arcNumber INTEGER," + "arcCount INTEGER," - "volume TEXT," - "storyArc TEXT," - "arcNumber INTEGER," - "arcCount INTEGER," + "genere TEXT," - "genere TEXT," + "writer TEXT," + "penciller TEXT," + "inker TEXT," + "colorist TEXT," + "letterer TEXT," + "coverArtist TEXT," - "writer TEXT," - "penciller TEXT," - "inker TEXT," - "colorist TEXT," - "letterer TEXT," - "coverArtist TEXT," + "date TEXT," //dd/mm/yyyy --> se mostrará en 3 campos diferentes + "publisher TEXT," + "format TEXT," + "color BOOLEAN," + "ageRating BOOLEAN," - "date TEXT," //dd/mm/yyyy --> se mostrará en 3 campos diferentes - "publisher TEXT," - "format TEXT," - "color BOOLEAN," - "ageRating BOOLEAN," + "synopsis TEXT," + "characters TEXT," + "notes TEXT," - "synopsis TEXT," - "characters TEXT," - "notes TEXT," + "hash TEXT UNIQUE NOT NULL," + "edited BOOLEAN DEFAULT 0," + "read BOOLEAN DEFAULT 0," + //new 7.0 fields - "hash TEXT UNIQUE NOT NULL," - "edited BOOLEAN DEFAULT 0," - "read BOOLEAN DEFAULT 0," -//new 7.0 fields - - "hasBeenOpened BOOLEAN DEFAULT 0," - "rating INTEGER DEFAULT 0," - "currentPage INTEGER DEFAULT 1, " - "bookmark1 INTEGER DEFAULT -1, " - "bookmark2 INTEGER DEFAULT -1, " - "bookmark3 INTEGER DEFAULT -1, " - "brightness INTEGER DEFAULT -1, " - "contrast INTEGER DEFAULT -1, " - "gamma INTEGER DEFAULT -1, " -//new 7.1 fields - "comicVineID TEXT" + "hasBeenOpened BOOLEAN DEFAULT 0," + "rating INTEGER DEFAULT 0," + "currentPage INTEGER DEFAULT 1, " + "bookmark1 INTEGER DEFAULT -1, " + "bookmark2 INTEGER DEFAULT -1, " + "bookmark3 INTEGER DEFAULT -1, " + "brightness INTEGER DEFAULT -1, " + "contrast INTEGER DEFAULT -1, " + "gamma INTEGER DEFAULT -1, " + //new 7.1 fields + "comicVineID TEXT" - ")"); - success = success && queryComicInfo.exec(); - //queryComicInfo.finish(); + ")"); + success = success && queryComicInfo.exec(); + //queryComicInfo.finish(); - //COMIC (representa un cómic en disco, contiene el nombre de fichero) - QSqlQuery queryComic(database); - queryComic.prepare("CREATE TABLE comic (id INTEGER PRIMARY KEY, parentId INTEGER NOT NULL, comicInfoId INTEGER NOT NULL, fileName TEXT NOT NULL, path TEXT, FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE, FOREIGN KEY(comicInfoId) REFERENCES comic_info(id))"); - success = success && queryComic.exec(); - //queryComic.finish(); - //DB INFO - QSqlQuery queryDBInfo(database); - queryDBInfo.prepare("CREATE TABLE db_info (version TEXT NOT NULL)"); - success = success && queryDBInfo.exec(); - //queryDBInfo.finish(); + //FOLDER (representa una carpeta en disco) + QSqlQuery queryFolder(database); + queryFolder.prepare("CREATE TABLE folder (" + "id INTEGER PRIMARY KEY," + "parentId INTEGER NOT NULL," + "name TEXT NOT NULL," + "path TEXT NOT NULL," + //new 7.1 fields + "finished BOOLEAN DEFAULT 0," //reading + "completed BOOLEAN DEFAULT 1," //collecting + //-- + "FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE)"); + success = success && queryFolder.exec(); - QSqlQuery query("INSERT INTO db_info (version) " - "VALUES ('" VERSION "')",database); - //query.finish(); + //COMIC (representa un cómic en disco, contiene el nombre de fichero) + QSqlQuery queryComic(database); + queryComic.prepare("CREATE TABLE comic (id INTEGER PRIMARY KEY, parentId INTEGER NOT NULL, comicInfoId INTEGER NOT NULL, fileName TEXT NOT NULL, path TEXT, FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE, FOREIGN KEY(comicInfoId) REFERENCES comic_info(id))"); + success = success && queryComic.exec(); + //queryComic.finish(); + //DB INFO + QSqlQuery queryDBInfo(database); + queryDBInfo.prepare("CREATE TABLE db_info (version TEXT NOT NULL)"); + success = success && queryDBInfo.exec(); + //queryDBInfo.finish(); - //8.0> tables - success = success && DataBaseManagement::createV8Tables(database); + QSqlQuery query("INSERT INTO db_info (version) " + "VALUES ('" VERSION "')",database); + //query.finish(); + //8.0> tables + success = success && DataBaseManagement::createV8Tables(database); } return success; From 37d177e65a7d0f4377e8e02166454723aacb6e32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 10:45:07 +0100 Subject: [PATCH 005/118] removed extra comma --- YACReaderLibrary/db/data_base_management.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/db/data_base_management.cpp b/YACReaderLibrary/db/data_base_management.cpp index 1c29814c..5a31ce94 100644 --- a/YACReaderLibrary/db/data_base_management.cpp +++ b/YACReaderLibrary/db/data_base_management.cpp @@ -195,8 +195,13 @@ bool DataBaseManagement::createTables(QSqlDatabase & database) //new 7.1 fields "finished BOOLEAN DEFAULT 0," //reading "completed BOOLEAN DEFAULT 1," //collecting - //-- - "FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE)"); + //new 8.6 fields + "numChildren INTEGER," + "firstChildId INTEGER," + "customImage TEXT," + "FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE), " + //8.6 + "FOREIGN KEY(firstChildId) REFERENCES comic_info(id))"); success = success && queryFolder.exec(); //COMIC (representa un cómic en disco, contiene el nombre de fichero) From b0060795b5ea68f27530dc06f59eae333161cd03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 10:52:12 +0100 Subject: [PATCH 006/118] added new method for adding constraints to an existing table --- YACReaderLibrary/db/data_base_management.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/YACReaderLibrary/db/data_base_management.cpp b/YACReaderLibrary/db/data_base_management.cpp index 5a31ce94..be008518 100644 --- a/YACReaderLibrary/db/data_base_management.cpp +++ b/YACReaderLibrary/db/data_base_management.cpp @@ -620,6 +620,19 @@ bool DataBaseManagement::addColumns(const QString &tableName, const QStringList return returnValue; } +bool DataBaseManagement::addConstraint(const QString &tableName, const QString &constraint, const QSqlDatabase &db) +{ + QString sql = "ALTER TABLE %1 ADD %2"; + bool returnValue = true; + + QSqlQuery alterTable(db); + alterTable.prepare(sql.arg(tableName).arg(constraint)); + alterTable.exec(); + returnValue = returnValue && (alterTable.numRowsAffected() > 0); + + return returnValue; +} + void DataBaseManagement::bindString(const QString & name, const QSqlRecord & record, QSqlQuery & query) { if(!record.value(name).isNull()) From cf79dbad9110aa84472a5b82f201f9434894aff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 10:52:55 +0100 Subject: [PATCH 007/118] update the folder table properly in 8.6 version --- YACReaderLibrary/db/data_base_management.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/YACReaderLibrary/db/data_base_management.cpp b/YACReaderLibrary/db/data_base_management.cpp index be008518..d2c2cc2d 100644 --- a/YACReaderLibrary/db/data_base_management.cpp +++ b/YACReaderLibrary/db/data_base_management.cpp @@ -770,7 +770,11 @@ bool DataBaseManagement::updateToCurrentVersion(const QString & fullPath) { QStringList columnDefs; //TODO + columnDefs << "numChildren INTEGER"; + columnDefs << "firstChildId INTEGER"; + columnDefs << "customImage TEXT"; //returnValue = returnValue && addColumns("folder", columnDefs, db); + //returnValue = returnValue && addConstraint("folder", FOREIGN KEY(firstChildId) REFERENCES comic_info(id), db); } } From 865026e46ffcaad4d2e24bdc0e293e178cac8908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 11:36:12 +0100 Subject: [PATCH 008/118] added new fields to Folder class --- common/folder.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/folder.h b/common/folder.h index 862c05de..22db77e5 100644 --- a/common/folder.h +++ b/common/folder.h @@ -10,6 +10,10 @@ class Folder : public LibraryItem public: bool knownParent; bool knownId; + + qint32 numChildren; //-1 for unknown number of children + qulonglong firstChildId; //0 for unknown first child + QString customImage; //empty for none custom image Folder():knownParent(false), knownId(false){}; Folder(qulonglong sid, qulonglong pid,QString fn, QString fp):knownParent(true), knownId(true){id = sid; parentId = pid;name = fn; path = fp;}; From 2703219a0a38d91787458e41fd1e4fc786e48fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 12:14:31 +0100 Subject: [PATCH 009/118] map new fields on folder table to model class --- YACReaderLibrary/db_helper.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index c781d01e..29ed4a87 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -929,6 +929,10 @@ Folder DBHelper::loadFolder(qulonglong id, QSqlDatabase & db) //new 7.1 folder.setFinished(record.value("finished").toBool()); folder.setCompleted(record.value("completed").toBool()); + //new 8.6 + folder.numChildren = record.value("numChildren").toInt(); + folder.firstChildId = record.value("firstChildId").toULongLong(); + folder.customImage = record.value("customImage").toString(); } return folder; @@ -957,6 +961,11 @@ Folder DBHelper::loadFolder(const QString &folderName, qulonglong parentId, QSql //new 7.1 folder.setFinished(record.value("finished").toBool()); folder.setCompleted(record.value("completed").toBool()); + //new 8.6 + folder.numChildren = record.value("numChildren").toInt(); + folder.firstChildId = record.value("firstChildId").toULongLong(); + folder.customImage = record.value("customImage").toString(); + QLOG_DEBUG() << "FOUND!!"; } From 35b87a9f5931a36c47114313750cc2392bb51101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 12:56:54 +0100 Subject: [PATCH 010/118] added missing method declaration --- YACReaderLibrary/db/data_base_management.h | 1 + 1 file changed, 1 insertion(+) diff --git a/YACReaderLibrary/db/data_base_management.h b/YACReaderLibrary/db/data_base_management.h index 68540339..3d7139e2 100644 --- a/YACReaderLibrary/db/data_base_management.h +++ b/YACReaderLibrary/db/data_base_management.h @@ -38,6 +38,7 @@ private: static void bindValuesFromRecord(const QSqlRecord & record, QSqlQuery & query); static bool addColumns(const QString & tableName, const QStringList & columnDefs, const QSqlDatabase & db); + static bool addConstraint(const QString &tableName, const QString & constraint, const QSqlDatabase & db); public: DataBaseManagement(); From 4b3ffdc665816531fbb51f8fca6d270d11a172d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 14:12:38 +0100 Subject: [PATCH 011/118] set numChildren to -1 if there is no value in the data base --- YACReaderLibrary/db_helper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 29ed4a87..10829cd5 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -930,7 +930,7 @@ Folder DBHelper::loadFolder(qulonglong id, QSqlDatabase & db) folder.setFinished(record.value("finished").toBool()); folder.setCompleted(record.value("completed").toBool()); //new 8.6 - folder.numChildren = record.value("numChildren").toInt(); + folder.numChildren = record.value("numChildren").isNull() ? -1 : record.value("numChildren").toInt(); folder.firstChildId = record.value("firstChildId").toULongLong(); folder.customImage = record.value("customImage").toString(); } @@ -962,7 +962,7 @@ Folder DBHelper::loadFolder(const QString &folderName, qulonglong parentId, QSql folder.setFinished(record.value("finished").toBool()); folder.setCompleted(record.value("completed").toBool()); //new 8.6 - folder.numChildren = record.value("numChildren").toInt(); + folder.numChildren = record.value("numChildren").isNull() ? -1 : record.value("numChildren").toInt(); folder.firstChildId = record.value("firstChildId").toULongLong(); folder.customImage = record.value("customImage").toString(); From 37328edc6448b53fdf718558e55781c4e205799b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 14:15:18 +0100 Subject: [PATCH 012/118] added a new web controller for providing the content information of a folder --- .../controllers/foldercontentcontroller.cpp | 70 +++++++++++++++++++ .../controllers/foldercontentcontroller.h | 22 ++++++ YACReaderLibrary/server/requestmapper.cpp | 6 ++ YACReaderLibrary/server/server.pri | 6 +- 4 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 YACReaderLibrary/server/controllers/foldercontentcontroller.cpp create mode 100644 YACReaderLibrary/server/controllers/foldercontentcontroller.h diff --git a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp new file mode 100644 index 00000000..2fcaf8a4 --- /dev/null +++ b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp @@ -0,0 +1,70 @@ +#include "foldercontentcontroller.h" + +#include + +#include "db_helper.h" +#include "comic_db.h" +#include "folder.h" + +#include "qnaturalsorting.h" + +#include +using namespace std; + +struct LibraryItemSorter +{ + bool operator()(const LibraryItem * a,const LibraryItem * b) const + { + return naturalSortLessThanCI(a->name,b->name); + } +}; + +FolderContentController::FolderContentController() {} + +void FolderContentController::service(HttpRequest& request, HttpResponse& response) +{ + response.setHeader("Content-Type", "plain/text; charset=utf-8"); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + QStringList pathElements = path.split('/'); + int libraryId = pathElements.at(2).toInt(); + qulonglong parentId = pathElements.at(4).toULongLong(); + + serviceContent(libraryId, parentId, response); + + response.writeText("",true); +} + +void FolderContentController::serviceContent(const int &library, const qulonglong &folderId, HttpResponse &response) +{ + clock_t begin = clock(); + + QList folderContent = DBHelper::getFolderSubfoldersFromLibrary(library,folderId); + QList folderComics = DBHelper::getFolderComicsFromLibrary(library,folderId); + + folderContent.append(folderComics); + qSort(folderContent.begin(),folderContent.end(),LibraryItemSorter()); + + folderComics.clear(); + + ComicDB * currentComic; + Folder * currentFolder; + for(QList::const_iterator itr = folderContent.constBegin();itr!=folderContent.constEnd();itr++) + { + if((*itr)->isDir()) + { + currentFolder = (Folder *)(*itr); + response.writeText(QString("f:%1:%2:%3:%4\r\n").arg(library).arg(currentFolder->id).arg(currentFolder->name).arg(currentFolder->numChildren)); + } + else + { + currentComic = (ComicDB *)(*itr); + response.writeText(QString("c:%1:%2:%3:%4:%5\r\n").arg(library).arg(currentComic->id).arg(currentComic->getFileName()).arg(currentComic->getFileSize()).arg(currentComic->info.hash)); + } + } + + clock_t end = clock(); + double msecs = double(end - begin); + + response.writeText(QString("%1ms").arg(msecs)); +} diff --git a/YACReaderLibrary/server/controllers/foldercontentcontroller.h b/YACReaderLibrary/server/controllers/foldercontentcontroller.h new file mode 100644 index 00000000..aa986042 --- /dev/null +++ b/YACReaderLibrary/server/controllers/foldercontentcontroller.h @@ -0,0 +1,22 @@ +#ifndef FOLDERCONTENTCONTROLLER_H +#define FOLDERCONTENTCONTROLLER_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class FolderContentController : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(FolderContentController); +public: + /** Constructor */ + FolderContentController(); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); + +private: + void serviceContent(const int &library, const qulonglong &folderId, HttpResponse &response); +}; + +#endif // FOLDERCONTENTCONTROLLER_H diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp index 75bbe613..97213261 100644 --- a/YACReaderLibrary/server/requestmapper.cpp +++ b/YACReaderLibrary/server/requestmapper.cpp @@ -23,6 +23,7 @@ #include "controllers/comicdownloadinfocontroller.h" #include "controllers/synccontroller.h" #include "controllers/versioncontroller.h" +#include "controllers/foldercontentcontroller.h" #include "db_helper.h" #include "yacreader_libraries.h" @@ -107,6 +108,7 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { static const QRegExp comicPage("/library/.+/comic/[0-9]+/page/[0-9]+/?"); //get comic page static const QRegExp comicPageRemote("/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); //get comic page (remote reading) static const QRegExp serverVersion("/version/?"); + static const QRegExp folderContent("/library/.+/folder/[0-9]+/content/?"); static const QRegExp sync("/sync"); @@ -169,6 +171,10 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { { UpdateComicController().service(request, response); } + else if(folderContent.exactMatch(path)) + { + FolderContentController().service(request, response); + } } else { diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index 19eb50a8..6f1588b4 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -17,7 +17,8 @@ HEADERS += \ $$PWD/controllers/comicdownloadinfocontroller.h \ $$PWD/controllers/synccontroller.h \ #v2 - $$PWD/controllers/versioncontroller.h + $$PWD/controllers/versioncontroller.h \ + $$PWD/controllers/foldercontentcontroller.h SOURCES += \ $$PWD/static.cpp \ @@ -35,7 +36,8 @@ SOURCES += \ $$PWD/controllers/comicdownloadinfocontroller.cpp \ $$PWD/controllers/synccontroller.cpp \ #v2 - $$PWD/controllers/versioncontroller.cpp + $$PWD/controllers/versioncontroller.cpp \ + $$PWD/controllers/foldercontentcontroller.cpp include(lib/bfLogging/bfLogging.pri) include(lib/bfHttpServer/bfHttpServer.pri) From bac7fe13512b55442ff90ed58f9d920096797d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 17:41:26 +0100 Subject: [PATCH 013/118] updated Folder class --- YACReaderLibrary/db_helper.cpp | 22 +++-- .../controllers/foldercontentcontroller.cpp | 2 +- common/comic_db.cpp | 2 +- common/comic_db.h | 2 +- common/folder.cpp | 30 +++++++ common/folder.h | 89 ++++++++++++++++--- common/library_item.h | 4 +- 7 files changed, 125 insertions(+), 26 deletions(-) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 10829cd5..14277d91 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -733,7 +733,13 @@ QList DBHelper::getFoldersFromParent(qulonglong parentId, QSqlDat data << record.value(i); //TODO sort by sort indicator and name currentItem = new Folder(record.value("id").toULongLong(),record.value("parentId").toULongLong(),record.value("name").toString(),record.value("path").toString()); - int lessThan = 0; + + if(!record.value("numChildren").isNull() && record.value("numChildren").isValid()) + currentItem->setNumChildren(record.value("numChildren").toInt()); + currentItem->setFirstChildId(record.value("firstChildId").toULongLong()); + currentItem->setCustomImage(record.value("customImage").toString()); + + int lessThan = 0; if(list.isEmpty() || !sort) list.append(currentItem); @@ -930,9 +936,10 @@ Folder DBHelper::loadFolder(qulonglong id, QSqlDatabase & db) folder.setFinished(record.value("finished").toBool()); folder.setCompleted(record.value("completed").toBool()); //new 8.6 - folder.numChildren = record.value("numChildren").isNull() ? -1 : record.value("numChildren").toInt(); - folder.firstChildId = record.value("firstChildId").toULongLong(); - folder.customImage = record.value("customImage").toString(); + if(!record.value("numChildren").isNull() && record.value("numChildren").isValid()) + folder.setNumChildren(record.value("numChildren").toInt()); + folder.setFirstChildId(record.value("firstChildId").toULongLong()); + folder.setCustomImage(record.value("customImage").toString()); } return folder; @@ -962,9 +969,10 @@ Folder DBHelper::loadFolder(const QString &folderName, qulonglong parentId, QSql folder.setFinished(record.value("finished").toBool()); folder.setCompleted(record.value("completed").toBool()); //new 8.6 - folder.numChildren = record.value("numChildren").isNull() ? -1 : record.value("numChildren").toInt(); - folder.firstChildId = record.value("firstChildId").toULongLong(); - folder.customImage = record.value("customImage").toString(); + if(!record.value("numChildren").isNull() && record.value("numChildren").isValid()) + folder.setNumChildren(record.value("numChildren").toInt()); + folder.setFirstChildId(record.value("firstChildId").toULongLong()); + folder.setCustomImage(record.value("customImage").toString()); QLOG_DEBUG() << "FOUND!!"; } diff --git a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp index 2fcaf8a4..a9eae52f 100644 --- a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp +++ b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp @@ -54,7 +54,7 @@ void FolderContentController::serviceContent(const int &library, const qulonglon if((*itr)->isDir()) { currentFolder = (Folder *)(*itr); - response.writeText(QString("f:%1:%2:%3:%4\r\n").arg(library).arg(currentFolder->id).arg(currentFolder->name).arg(currentFolder->numChildren)); + response.writeText(QString("f:%1:%2:%3:%4\r\n").arg(library).arg(currentFolder->id).arg(currentFolder->name).arg(currentFolder->getNumChildren())); } else { diff --git a/common/comic_db.cpp b/common/comic_db.cpp index 6e92b85a..2cefa5d5 100644 --- a/common/comic_db.cpp +++ b/common/comic_db.cpp @@ -11,7 +11,7 @@ ComicDB::ComicDB() } -bool ComicDB::isDir() +bool ComicDB::isDir() const { return false; } diff --git a/common/comic_db.h b/common/comic_db.h index 417efa4d..e396e718 100644 --- a/common/comic_db.h +++ b/common/comic_db.h @@ -124,7 +124,7 @@ class ComicDB : public LibraryItem public: ComicDB(); - bool isDir(); + bool isDir() const; bool _hasCover; diff --git a/common/folder.cpp b/common/folder.cpp index e69de29b..9a7d3eba 100644 --- a/common/folder.cpp +++ b/common/folder.cpp @@ -0,0 +1,30 @@ +#include "folder.h" + +Folder::Folder() + :knownParent(false), + knownId(false), + numChildren(-1), + firstChildId(0) +{} + +Folder::Folder(qulonglong folderId, qulonglong parentId, const QString &folderName, const QString &folderPath) + :knownParent(true), + knownId(true), + numChildren(-1), + firstChildId(0) +{ + this->id = folderId; + this->parentId = parentId; + this->name = folderName; + this->path = folderPath; +} + +Folder::Folder(const QString & folderName, const QString & folderPath) + :knownParent(false), + knownId(false), + numChildren(-1), + firstChildId(0) +{ + this->name = folderName; + this->path = folderPath; +} diff --git a/common/folder.h b/common/folder.h index 22db77e5..9739a6d6 100644 --- a/common/folder.h +++ b/common/folder.h @@ -10,25 +10,86 @@ class Folder : public LibraryItem public: bool knownParent; bool knownId; - - qint32 numChildren; //-1 for unknown number of children - qulonglong firstChildId; //0 for unknown first child - QString customImage; //empty for none custom image - Folder():knownParent(false), knownId(false){}; - Folder(qulonglong sid, qulonglong pid,QString fn, QString fp):knownParent(true), knownId(true){id = sid; parentId = pid;name = fn; path = fp;}; - Folder(QString fn, QString fp):knownParent(false), knownId(false){name = fn; path = fp;}; - void setId(qulonglong sid){id = sid;knownId = true;}; - void setFather(qulonglong pid){parentId = pid;knownParent = true;}; - bool isDir() {return true;}; - bool isFinished() const {return finished;}; - bool isCompleted() const {return completed;}; - void setFinished(bool b) {finished = b;}; - void setCompleted(bool b) {completed = b;}; + Folder(); + Folder(qulonglong folderId, qulonglong parentId,const QString & folderName, const QString & folderPath); + Folder(const QString & folderName, const QString & folderPath); + + inline void setId(qulonglong sid) + { + id = sid; + knownId = true; + } + + inline void setFather(qulonglong pid) + { + parentId = pid; + knownParent = true; + } + + inline bool isDir() const + { + return true; + } + + inline bool isFinished() const + { + return finished; + } + + inline bool isCompleted() const + { + return completed; + } + + inline void setFinished(bool b) + { + finished = b; + } + + inline void setCompleted(bool b) + { + completed = b; + } + + inline qint32 getNumChildren() const + { + return numChildren; + } + + inline void setNumChildren(const qint32 v) + { + numChildren = v; + } + + inline qulonglong getFirstChildId() const + { + return firstChildId; + } + + inline void setFirstChildId(const qulonglong v) + { + firstChildId = v; + } + + inline qulonglong getCustomImage() const + { + return firstChildId; + } + + inline void setCustomImage(const QString & s) + { + customImage = s; + } private: bool finished; bool completed; + + qint32 numChildren; //-1 for unknown number of children + qulonglong firstChildId; //0 for unknown first child + QString customImage; //empty for none custom image + }; #endif diff --git a/common/library_item.h b/common/library_item.h index 2f6b8d9f..fcc194eb 100644 --- a/common/library_item.h +++ b/common/library_item.h @@ -6,11 +6,11 @@ class LibraryItem { public: - virtual bool isDir() = 0; + virtual bool isDir() const = 0; QString name; QString path; qulonglong parentId; qulonglong id; }; -#endif \ No newline at end of file +#endif From 3cda11eec71b3e33aec4b15f7f317bf66b778527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 18:47:09 +0100 Subject: [PATCH 014/118] fixed db creation --- YACReaderLibrary/db/data_base_management.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YACReaderLibrary/db/data_base_management.cpp b/YACReaderLibrary/db/data_base_management.cpp index d2c2cc2d..5d331440 100644 --- a/YACReaderLibrary/db/data_base_management.cpp +++ b/YACReaderLibrary/db/data_base_management.cpp @@ -199,7 +199,7 @@ bool DataBaseManagement::createTables(QSqlDatabase & database) "numChildren INTEGER," "firstChildId INTEGER," "customImage TEXT," - "FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE), " + "FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE, " //8.6 "FOREIGN KEY(firstChildId) REFERENCES comic_info(id))"); success = success && queryFolder.exec(); From b9e7dbe41ea3b03940479253a51181dd691e163e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 18:52:25 +0100 Subject: [PATCH 015/118] Everytime the content of a folder changes, update numChildren and firstChildId. This could cause performance issues, but the initial tests look good, and it seems that the overhead could be considered negligible. On the other hand, client queries are several times faster. --- YACReaderLibrary/db_helper.cpp | 61 ++++++++++++++++++++++++++++++---- YACReaderLibrary/db_helper.h | 12 ++++--- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 14277d91..2fcb6d98 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -160,26 +160,32 @@ QString DBHelper::getLibraryName(int id) } //objects management //deletes -void DBHelper::removeFromDB(LibraryItem * item, QSqlDatabase & db) +void DBHelper::removeFromDB(LibraryItem * item, QSqlDatabase & db, bool updateParent) { if(item->isDir()) - DBHelper::removeFromDB(dynamic_cast(item),db); + DBHelper::removeFromDB(dynamic_cast(item),db, updateParent); else - DBHelper::removeFromDB(dynamic_cast(item),db); + DBHelper::removeFromDB(dynamic_cast(item),db, updateParent); } -void DBHelper::removeFromDB(Folder * folder, QSqlDatabase & db) +void DBHelper::removeFromDB(Folder * folder, QSqlDatabase & db, bool updateParent) { QSqlQuery query(db); query.prepare("DELETE FROM folder WHERE id = :id"); query.bindValue(":id", folder->id); query.exec(); + + if(updateParent) + DBHelper::updateChildrenInfo(folder->parentId, db); } -void DBHelper::removeFromDB(ComicDB * comic, QSqlDatabase & db) +void DBHelper::removeFromDB(ComicDB * comic, QSqlDatabase & db, bool updateParent) { QSqlQuery query(db); query.prepare("DELETE FROM comic WHERE id = :id"); query.bindValue(":id", comic->id); query.exec(); + + if(updateParent) + DBHelper::updateChildrenInfo(comic->parentId, db); } void DBHelper::removeLabelFromDB(qulonglong id, QSqlDatabase &db) @@ -408,6 +414,39 @@ void DBHelper::update(const Folder & folder, QSqlDatabase &db) updateFolderInfo.exec(); } +void DBHelper::updateChildrenInfo(const Folder & folder, QSqlDatabase & db) +{ + QSqlQuery updateFolderInfo(db); + updateFolderInfo.prepare("UPDATE folder SET " + "numChildren = :numChildren, " + "firstChildId = :firstChildId " + "WHERE id = :id "); + updateFolderInfo.bindValue(":numChildren", folder.getNumChildren()); + updateFolderInfo.bindValue(":firstChildId", folder.getFirstChildId()); + updateFolderInfo.bindValue(":id", folder.id); + updateFolderInfo.exec(); +} + +void DBHelper::updateChildrenInfo(qulonglong folderId, QSqlDatabase & db) +{ + QList subfolders = DBHelper::getFoldersFromParent(folderId,db,false); + QList comics = DBHelper::getComicsFromParent(folderId,db,true); + + ComicDB * firstComic = NULL; + if(comics.count() > 0) + firstComic = static_cast(comics.first()); + + QSqlQuery updateFolderInfo(db); + updateFolderInfo.prepare("UPDATE folder SET " + "numChildren = :numChildren, " + "firstChildId = :firstChildId " + "WHERE id = :id "); + updateFolderInfo.bindValue(":numChildren", subfolders.count() + comics.count()); + updateFolderInfo.bindValue(":firstChildId", firstComic != NULL ? firstComic->info.id : 0); + updateFolderInfo.bindValue(":id", folderId); + updateFolderInfo.exec(); +} + void DBHelper::updateProgress(qulonglong libraryId, const ComicInfo &comicInfo) { QString libraryPath = DBHelper::getLibraries().getPath(libraryId); @@ -571,7 +610,7 @@ void DBHelper::reasignOrderToComicsInReadingList(qulonglong readingListId, QList } //inserts -qulonglong DBHelper::insert(Folder * folder, QSqlDatabase & db) +qulonglong DBHelper::insert(Folder * folder, QSqlDatabase & db, bool updateParent) { QSqlQuery query(db); query.prepare("INSERT INTO folder (parentId, name, path) " @@ -580,10 +619,14 @@ qulonglong DBHelper::insert(Folder * folder, QSqlDatabase & db) query.bindValue(":name", folder->name); query.bindValue(":path", folder->path); query.exec(); + + if(updateParent) + DBHelper::updateChildrenInfo(folder->parentId, db); + return query.lastInsertId().toULongLong(); } -qulonglong DBHelper::insert(ComicDB * comic, QSqlDatabase & db) +qulonglong DBHelper::insert(ComicDB * comic, QSqlDatabase & db, bool updateParent) { if(!comic->info.existOnDb) { @@ -607,6 +650,10 @@ qulonglong DBHelper::insert(ComicDB * comic, QSqlDatabase & db) query.bindValue(":name", comic->name); query.bindValue(":path", comic->path); query.exec(); + + if(updateParent) + DBHelper::updateChildrenInfo(comic->parentId, db); + return query.lastInsertId().toULongLong(); } diff --git a/YACReaderLibrary/db_helper.h b/YACReaderLibrary/db_helper.h index 19611dc7..e7f4d6fe 100644 --- a/YACReaderLibrary/db_helper.h +++ b/YACReaderLibrary/db_helper.h @@ -33,9 +33,9 @@ public: //objects management //deletes - static void removeFromDB(LibraryItem * item, QSqlDatabase & db); - static void removeFromDB(Folder * folder, QSqlDatabase & db); - static void removeFromDB(ComicDB * comic, QSqlDatabase & db); + static void removeFromDB(LibraryItem * item, QSqlDatabase & db, bool updateParent = true); + static void removeFromDB(Folder * folder, QSqlDatabase & db, bool updateParent = true); + static void removeFromDB(ComicDB * comic, QSqlDatabase & db, bool updateParent = true); static void removeLabelFromDB(qulonglong id, QSqlDatabase & db); static void removeListFromDB(qulonglong id, QSqlDatabase & db); //logic deletes @@ -43,8 +43,8 @@ public: static void deleteComicsFromLabel(const QList & comicsList, qulonglong labelId, QSqlDatabase & db); static void deleteComicsFromReadingList(const QList & comicsList, qulonglong readingListId, QSqlDatabase & db); //inserts - static qulonglong insert(Folder * folder, QSqlDatabase & db); - static qulonglong insert(ComicDB * comic, QSqlDatabase & db); + static qulonglong insert(Folder * folder, QSqlDatabase & db, bool updateParent = true); + static qulonglong insert(ComicDB * comic, QSqlDatabase & db, bool updateParent = true); static qulonglong insertLabel(const QString & name, YACReader::LabelColors color , QSqlDatabase & db); static qulonglong insertReadingList(const QString & name, QSqlDatabase & db); static qulonglong insertReadingSubList(const QString & name, qulonglong parentId, int ordering, QSqlDatabase & db); @@ -57,6 +57,8 @@ public: static void update(ComicInfo * comicInfo, QSqlDatabase & db); static void updateRead(ComicInfo * comicInfo, QSqlDatabase & db); static void update(const Folder & folder, QSqlDatabase & db); + static void updateChildrenInfo(const Folder & folder, QSqlDatabase & db); + static void updateChildrenInfo(qulonglong folderId, QSqlDatabase & db); static void updateProgress(qulonglong libraryId,const ComicInfo & comicInfo); static void updateReadingRemoteProgress(const ComicInfo & comicInfo, QSqlDatabase & db); static void updateFromRemoteClient(qulonglong libraryId,const ComicInfo & comicInfo); From e23f6b0bc374412d33d1ee27dae08c3fa977fcc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 19:14:16 +0100 Subject: [PATCH 016/118] store the hash of the first comic in a folder, this is more useful than storing the id --- YACReaderLibrary/db/data_base_management.cpp | 9 +++------ YACReaderLibrary/db_helper.cpp | 14 +++++++------- common/folder.cpp | 9 +++------ common/folder.h | 14 +++++++------- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/YACReaderLibrary/db/data_base_management.cpp b/YACReaderLibrary/db/data_base_management.cpp index 5d331440..fa5dfa0b 100644 --- a/YACReaderLibrary/db/data_base_management.cpp +++ b/YACReaderLibrary/db/data_base_management.cpp @@ -197,11 +197,9 @@ bool DataBaseManagement::createTables(QSqlDatabase & database) "completed BOOLEAN DEFAULT 1," //collecting //new 8.6 fields "numChildren INTEGER," - "firstChildId INTEGER," + "firstChildHash TEXT," "customImage TEXT," - "FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE, " - //8.6 - "FOREIGN KEY(firstChildId) REFERENCES comic_info(id))"); + "FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE)"); success = success && queryFolder.exec(); //COMIC (representa un cómic en disco, contiene el nombre de fichero) @@ -771,10 +769,9 @@ bool DataBaseManagement::updateToCurrentVersion(const QString & fullPath) QStringList columnDefs; //TODO columnDefs << "numChildren INTEGER"; - columnDefs << "firstChildId INTEGER"; + columnDefs << "firstChildHash TEXT"; columnDefs << "customImage TEXT"; //returnValue = returnValue && addColumns("folder", columnDefs, db); - //returnValue = returnValue && addConstraint("folder", FOREIGN KEY(firstChildId) REFERENCES comic_info(id), db); } } diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 2fcb6d98..321cb35e 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -419,10 +419,10 @@ void DBHelper::updateChildrenInfo(const Folder & folder, QSqlDatabase & db) QSqlQuery updateFolderInfo(db); updateFolderInfo.prepare("UPDATE folder SET " "numChildren = :numChildren, " - "firstChildId = :firstChildId " + "firstChildHash = :firstChildHash " "WHERE id = :id "); updateFolderInfo.bindValue(":numChildren", folder.getNumChildren()); - updateFolderInfo.bindValue(":firstChildId", folder.getFirstChildId()); + updateFolderInfo.bindValue(":firstChildHash", folder.getFirstChildHash()); updateFolderInfo.bindValue(":id", folder.id); updateFolderInfo.exec(); } @@ -439,10 +439,10 @@ void DBHelper::updateChildrenInfo(qulonglong folderId, QSqlDatabase & db) QSqlQuery updateFolderInfo(db); updateFolderInfo.prepare("UPDATE folder SET " "numChildren = :numChildren, " - "firstChildId = :firstChildId " + "firstChildHash = :firstChildHash " "WHERE id = :id "); updateFolderInfo.bindValue(":numChildren", subfolders.count() + comics.count()); - updateFolderInfo.bindValue(":firstChildId", firstComic != NULL ? firstComic->info.id : 0); + updateFolderInfo.bindValue(":firstChildHash", firstComic != NULL ? firstComic->info.hash : ""); updateFolderInfo.bindValue(":id", folderId); updateFolderInfo.exec(); } @@ -783,7 +783,7 @@ QList DBHelper::getFoldersFromParent(qulonglong parentId, QSqlDat if(!record.value("numChildren").isNull() && record.value("numChildren").isValid()) currentItem->setNumChildren(record.value("numChildren").toInt()); - currentItem->setFirstChildId(record.value("firstChildId").toULongLong()); + currentItem->setFirstChildHash(record.value("firstChildHash").toString()); currentItem->setCustomImage(record.value("customImage").toString()); int lessThan = 0; @@ -985,7 +985,7 @@ Folder DBHelper::loadFolder(qulonglong id, QSqlDatabase & db) //new 8.6 if(!record.value("numChildren").isNull() && record.value("numChildren").isValid()) folder.setNumChildren(record.value("numChildren").toInt()); - folder.setFirstChildId(record.value("firstChildId").toULongLong()); + folder.setFirstChildHash(record.value("firstChildHash").toString()); folder.setCustomImage(record.value("customImage").toString()); } @@ -1018,7 +1018,7 @@ Folder DBHelper::loadFolder(const QString &folderName, qulonglong parentId, QSql //new 8.6 if(!record.value("numChildren").isNull() && record.value("numChildren").isValid()) folder.setNumChildren(record.value("numChildren").toInt()); - folder.setFirstChildId(record.value("firstChildId").toULongLong()); + folder.setFirstChildHash(record.value("firstChildHash").toString()); folder.setCustomImage(record.value("customImage").toString()); QLOG_DEBUG() << "FOUND!!"; diff --git a/common/folder.cpp b/common/folder.cpp index 9a7d3eba..f3b486c7 100644 --- a/common/folder.cpp +++ b/common/folder.cpp @@ -3,15 +3,13 @@ Folder::Folder() :knownParent(false), knownId(false), - numChildren(-1), - firstChildId(0) + numChildren(-1) {} Folder::Folder(qulonglong folderId, qulonglong parentId, const QString &folderName, const QString &folderPath) :knownParent(true), knownId(true), - numChildren(-1), - firstChildId(0) + numChildren(-1) { this->id = folderId; this->parentId = parentId; @@ -22,8 +20,7 @@ Folder::Folder(qulonglong folderId, qulonglong parentId, const QString &folderNa Folder::Folder(const QString & folderName, const QString & folderPath) :knownParent(false), knownId(false), - numChildren(-1), - firstChildId(0) + numChildren(-1) { this->name = folderName; this->path = folderPath; diff --git a/common/folder.h b/common/folder.h index 9739a6d6..6dd660a6 100644 --- a/common/folder.h +++ b/common/folder.h @@ -62,19 +62,19 @@ public: numChildren = v; } - inline qulonglong getFirstChildId() const + inline QString getFirstChildHash() const { - return firstChildId; + return firstChildHash; } - inline void setFirstChildId(const qulonglong v) + inline void setFirstChildHash(const QString & v) { - firstChildId = v; + firstChildHash = v; } - inline qulonglong getCustomImage() const + inline QString getCustomImage() const { - return firstChildId; + return customImage; } inline void setCustomImage(const QString & s) @@ -87,7 +87,7 @@ private: bool completed; qint32 numChildren; //-1 for unknown number of children - qulonglong firstChildId; //0 for unknown first child + QString firstChildHash; //empty for unknown first child QString customImage; //empty for none custom image }; From 74c64e0280e868420edb53a5812a1316b5a274dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 19:14:54 +0100 Subject: [PATCH 017/118] use the hash of the first comic in a folder for displaying and image for the folder --- YACReaderLibrary/server/controllers/foldercontroller.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/YACReaderLibrary/server/controllers/foldercontroller.cpp b/YACReaderLibrary/server/controllers/foldercontroller.cpp index 30aa657f..3ec241a2 100644 --- a/YACReaderLibrary/server/controllers/foldercontroller.cpp +++ b/YACReaderLibrary/server/controllers/foldercontroller.cpp @@ -167,11 +167,11 @@ void FolderController::service(HttpRequest& request, HttpResponse& response) { t.setVariable(QString("element%1.class").arg(i),"folder"); - QList children = DBHelper::getFolderComicsFromLibrary(libraryId, item->id); - if(children.length()>0) + const Folder * folder = static_cast(item); + + if(folder->getFirstChildHash().length()>0) { - const ComicDB * comic = static_cast(children.at(0)); - t.setVariable(QString("element%1.image.url").arg(i),QString("/library/%1/cover/%2.jpg?folderCover=true").arg(libraryId).arg(comic->info.hash)); + t.setVariable(QString("element%1.image.url").arg(i),QString("/library/%1/cover/%2.jpg?folderCover=true").arg(libraryId).arg(folder->getFirstChildHash())); } else t.setVariable(QString("element%1.image.url").arg(i),"/images/f.png"); From d5d9e5206f0466afbe1f8719a93155c7a3b2075c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 24 Jan 2016 19:20:00 +0100 Subject: [PATCH 018/118] send the hash of the first comic in a folder, so the client can display an image for that folder --- YACReaderLibrary/server/controllers/foldercontentcontroller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp index a9eae52f..776d99cb 100644 --- a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp +++ b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp @@ -54,7 +54,7 @@ void FolderContentController::serviceContent(const int &library, const qulonglon if((*itr)->isDir()) { currentFolder = (Folder *)(*itr); - response.writeText(QString("f:%1:%2:%3:%4\r\n").arg(library).arg(currentFolder->id).arg(currentFolder->name).arg(currentFolder->getNumChildren())); + response.writeText(QString("f:%1:%2:%3:%4:%5\r\n").arg(library).arg(currentFolder->id).arg(currentFolder->name).arg(currentFolder->getNumChildren()).arg(currentFolder->getFirstChildHash())); } else { From 00f45b9a273fef1b7f5913feaee61d469149cc08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 25 Jan 2016 18:40:28 +0100 Subject: [PATCH 019/118] using static regular expresions was a bad idea --- YACReaderLibrary/server/requestmapper.cpp | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp index 97213261..2e887956 100644 --- a/YACReaderLibrary/server/requestmapper.cpp +++ b/YACReaderLibrary/server/requestmapper.cpp @@ -97,22 +97,22 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { QByteArray path=request.getPath(); qDebug("RequestMapper: path=%s",path.data()); - static const QRegExp folder("/library/.+/folder/[0-9]+/?");//get comic content - static const QRegExp folderInfo("/library/.+/folder/[0-9]+/info/?"); //get folder info - static const QRegExp comicDownloadInfo("/library/.+/comic/[0-9]+/?"); //get comic info (basic/download info) - static const QRegExp comicFullInfo("/library/.+/comic/[0-9]+/info/?"); //get comic info (full info) - static const QRegExp comicOpen("/library/.+/comic/[0-9]+/remote/?"); //the server will open for reading the comic - static const QRegExp comicUpdate("/library/.+/comic/[0-9]+/update/?"); //get comic info - static const QRegExp comicClose("/library/.+/comic/[0-9]+/close/?"); //the server will close the comic and free memory - static const QRegExp cover("/library/.+/cover/[0-9a-f]+.jpg"); //get comic cover (navigation) - static const QRegExp comicPage("/library/.+/comic/[0-9]+/page/[0-9]+/?"); //get comic page - static const QRegExp comicPageRemote("/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); //get comic page (remote reading) - static const QRegExp serverVersion("/version/?"); - static const QRegExp folderContent("/library/.+/folder/[0-9]+/content/?"); + QRegExp folder("/library/.+/folder/[0-9]+/?");//get comic content + QRegExp folderInfo("/library/.+/folder/[0-9]+/info/?"); //get folder info + QRegExp comicDownloadInfo("/library/.+/comic/[0-9]+/?"); //get comic info (basic/download info) + QRegExp comicFullInfo("/library/.+/comic/[0-9]+/info/?"); //get comic info (full info) + QRegExp comicOpen("/library/.+/comic/[0-9]+/remote/?"); //the server will open for reading the comic + QRegExp comicUpdate("/library/.+/comic/[0-9]+/update/?"); //get comic info + QRegExp comicClose("/library/.+/comic/[0-9]+/close/?"); //the server will close the comic and free memory + QRegExp cover("/library/.+/cover/[0-9a-f]+.jpg"); //get comic cover (navigation) + QRegExp comicPage("/library/.+/comic/[0-9]+/page/[0-9]+/?"); //get comic page + QRegExp comicPageRemote("/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); //get comic page (remote reading) + QRegExp serverVersion("/version/?"); + QRegExp folderContent("/library/.+/folder/[0-9]+/content/?"); - static const QRegExp sync("/sync"); + QRegExp sync("/sync"); - static const QRegExp library("/library/([0-9]+)/.+"); //permite verificar que la biblioteca solicitada existe + QRegExp library("/library/([0-9]+)/.+"); //permite verificar que la biblioteca solicitada existe path = QUrl::fromPercentEncoding(path).toUtf8(); From 88056c819f01212152368b5cc443076668ef3500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 25 Jan 2016 18:45:50 +0100 Subject: [PATCH 020/118] updating folders' children info for every insert/delete in the db causes big performance issues, so that info is going to be updated explicitly --- YACReaderLibrary/comics_remover.cpp | 5 ++-- YACReaderLibrary/comics_remover.h | 4 ++- YACReaderLibrary/db/comic_model.cpp | 4 +-- YACReaderLibrary/db/comic_model.h | 2 +- YACReaderLibrary/db/folder_model.cpp | 15 ++++++++--- YACReaderLibrary/db/folder_model.h | 4 ++- YACReaderLibrary/db_helper.cpp | 38 ++++++++++++++-------------- YACReaderLibrary/db_helper.h | 11 ++++---- YACReaderLibrary/library_creator.cpp | 11 +++++++- YACReaderLibrary/library_window.cpp | 6 +++-- 10 files changed, 63 insertions(+), 37 deletions(-) diff --git a/YACReaderLibrary/comics_remover.cpp b/YACReaderLibrary/comics_remover.cpp index ef3fd009..7b4eadd7 100644 --- a/YACReaderLibrary/comics_remover.cpp +++ b/YACReaderLibrary/comics_remover.cpp @@ -5,8 +5,8 @@ #include "QsLog.h" -ComicsRemover::ComicsRemover(QModelIndexList & il, QList & ps, QObject *parent) - :QObject(parent),indexList(il), paths(ps) +ComicsRemover::ComicsRemover(QModelIndexList & il, QList & ps, qulonglong parentId, QObject *parent) + :QObject(parent),indexList(il), paths(ps), parentId(parentId) { } @@ -29,6 +29,7 @@ void ComicsRemover::process() } emit finished(); + emit removedItemsFromFolder(parentId); } diff --git a/YACReaderLibrary/comics_remover.h b/YACReaderLibrary/comics_remover.h index ff9d0a21..c844afbb 100644 --- a/YACReaderLibrary/comics_remover.h +++ b/YACReaderLibrary/comics_remover.h @@ -10,12 +10,13 @@ class ComicsRemover : public QObject { Q_OBJECT public: - explicit ComicsRemover(QModelIndexList & indexList, QList & paths, QObject *parent = 0); + explicit ComicsRemover(QModelIndexList & indexList, QList & paths, qulonglong parentId, QObject *parent = 0); signals: void remove(int); void removeError(); void finished(); + void removedItemsFromFolder(qulonglong); public slots: void process(); @@ -23,6 +24,7 @@ public slots: private: QModelIndexList indexList; QList paths; + qulonglong parentId; }; class FoldersRemover : public QObject diff --git a/YACReaderLibrary/db/comic_model.cpp b/YACReaderLibrary/db/comic_model.cpp index 33ddf459..7c3fa3ab 100644 --- a/YACReaderLibrary/db/comic_model.cpp +++ b/YACReaderLibrary/db/comic_model.cpp @@ -965,7 +965,7 @@ void ComicModel::removeInTransaction(int row) endRemoveRows(); } - +/* void ComicModel::remove(ComicDB * comic, int row) { beginRemoveRows(QModelIndex(),row,row); @@ -981,7 +981,7 @@ void ComicModel::remove(ComicDB * comic, int row) QSqlDatabase::removeDatabase(_databasePath); endRemoveRows(); } - +*/ /*ComicDB TableModel::getComic(int row) { return getComic(index(row,0)); diff --git a/YACReaderLibrary/db/comic_model.h b/YACReaderLibrary/db/comic_model.h index ce0de290..bb040a0d 100644 --- a/YACReaderLibrary/db/comic_model.h +++ b/YACReaderLibrary/db/comic_model.h @@ -65,7 +65,7 @@ public: //setComicInfoForSelectedComis(QList list); -->inserta la información común para los comics seleccionados QVector setComicsRead(QList list,YACReaderComicReadStatus read); qint64 asignNumbers(QList list,int startingNumber); - void remove(ComicDB * comic, int row); + //void remove(ComicDB * comic, int row); void removeInTransaction(int row); void reload(const ComicDB & comic); void resetComicRating(const QModelIndex & mi); diff --git a/YACReaderLibrary/db/folder_model.cpp b/YACReaderLibrary/db/folder_model.cpp index 1df7b199..700ca367 100644 --- a/YACReaderLibrary/db/folder_model.cpp +++ b/YACReaderLibrary/db/folder_model.cpp @@ -172,11 +172,12 @@ QVariant FolderModel::data(const QModelIndex &index, int role) const if(role == FolderModel::FinishedRole) return item->data(FolderModel::Finished); + if(role == FolderModel::IdRole) + return item->id; + if (role != Qt::DisplayRole) return QVariant(); - - return item->data(index.column()); } //! [3] @@ -305,7 +306,7 @@ void FolderModel::setupModelData(QSqlQuery &sqlquery, FolderItem *parent) //el diccionario permitir� encontrar cualquier nodo del �rbol r�pidamente, de forma que a�adir un hijo a un padre sea O(1) items.clear(); //se a�ade el nodo 0 - items.insert(parent->id,parent); + items.insert(parent->id,parent); while (sqlquery.next()) { QList data; @@ -535,6 +536,7 @@ QModelIndex FolderModel::addFolderAtParent(const QString &folderName, const QMod QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath); newFolder.id = DBHelper::insert(&newFolder, db); + DBHelper::updateChildrenInfo(parentItem->id, db); QSqlDatabase::removeDatabase(_databasePath); int destRow = 0; @@ -573,11 +575,18 @@ void FolderModel::deleteFolder(const QModelIndex &mi) QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath); DBHelper::removeFromDB(&f,db); + DBHelper::updateChildrenInfo(item->parent()->id, db); QSqlDatabase::removeDatabase(_databasePath); endRemoveRows(); } +void FolderModel::updateFolderChildrenInfo(qulonglong folderId) +{ + QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath); + DBHelper::updateChildrenInfo(folderId, db); + QSqlDatabase::removeDatabase(_databasePath); +} //PROXY diff --git a/YACReaderLibrary/db/folder_model.h b/YACReaderLibrary/db/folder_model.h index 4d0f16bb..ae28a33c 100644 --- a/YACReaderLibrary/db/folder_model.h +++ b/YACReaderLibrary/db/folder_model.h @@ -127,11 +127,13 @@ public: enum Roles { FinishedRole = Qt::UserRole + 1, - CompletedRole + CompletedRole, + IdRole }; public slots: void deleteFolder(const QModelIndex & mi); + void updateFolderChildrenInfo(qulonglong folderId); private: void setupModelData( QSqlQuery &sqlquery, FolderItem *parent); diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 321cb35e..5869a15f 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -160,32 +160,26 @@ QString DBHelper::getLibraryName(int id) } //objects management //deletes -void DBHelper::removeFromDB(LibraryItem * item, QSqlDatabase & db, bool updateParent) +void DBHelper::removeFromDB(LibraryItem * item, QSqlDatabase & db) { if(item->isDir()) - DBHelper::removeFromDB(dynamic_cast(item),db, updateParent); + DBHelper::removeFromDB(dynamic_cast(item),db); else - DBHelper::removeFromDB(dynamic_cast(item),db, updateParent); + DBHelper::removeFromDB(dynamic_cast(item),db); } -void DBHelper::removeFromDB(Folder * folder, QSqlDatabase & db, bool updateParent) +void DBHelper::removeFromDB(Folder * folder, QSqlDatabase & db) { QSqlQuery query(db); query.prepare("DELETE FROM folder WHERE id = :id"); query.bindValue(":id", folder->id); query.exec(); - - if(updateParent) - DBHelper::updateChildrenInfo(folder->parentId, db); } -void DBHelper::removeFromDB(ComicDB * comic, QSqlDatabase & db, bool updateParent) +void DBHelper::removeFromDB(ComicDB * comic, QSqlDatabase & db) { QSqlQuery query(db); query.prepare("DELETE FROM comic WHERE id = :id"); query.bindValue(":id", comic->id); query.exec(); - - if(updateParent) - DBHelper::updateChildrenInfo(comic->parentId, db); } void DBHelper::removeLabelFromDB(qulonglong id, QSqlDatabase &db) @@ -447,6 +441,18 @@ void DBHelper::updateChildrenInfo(qulonglong folderId, QSqlDatabase & db) updateFolderInfo.exec(); } +void DBHelper::updateChildrenInfo(QSqlDatabase & db) +{ + QSqlQuery selectQuery(db); //TODO check + selectQuery.prepare("SELECT id FROM folder"); + selectQuery.exec(); + + while (selectQuery.next()) + { + DBHelper::updateChildrenInfo(selectQuery.record().value(0).toULongLong(), db); + } +} + void DBHelper::updateProgress(qulonglong libraryId, const ComicInfo &comicInfo) { QString libraryPath = DBHelper::getLibraries().getPath(libraryId); @@ -610,7 +616,7 @@ void DBHelper::reasignOrderToComicsInReadingList(qulonglong readingListId, QList } //inserts -qulonglong DBHelper::insert(Folder * folder, QSqlDatabase & db, bool updateParent) +qulonglong DBHelper::insert(Folder * folder, QSqlDatabase & db) { QSqlQuery query(db); query.prepare("INSERT INTO folder (parentId, name, path) " @@ -620,13 +626,10 @@ qulonglong DBHelper::insert(Folder * folder, QSqlDatabase & db, bool updateParen query.bindValue(":path", folder->path); query.exec(); - if(updateParent) - DBHelper::updateChildrenInfo(folder->parentId, db); - return query.lastInsertId().toULongLong(); } -qulonglong DBHelper::insert(ComicDB * comic, QSqlDatabase & db, bool updateParent) +qulonglong DBHelper::insert(ComicDB * comic, QSqlDatabase & db) { if(!comic->info.existOnDb) { @@ -651,9 +654,6 @@ qulonglong DBHelper::insert(ComicDB * comic, QSqlDatabase & db, bool updateParen query.bindValue(":path", comic->path); query.exec(); - if(updateParent) - DBHelper::updateChildrenInfo(comic->parentId, db); - return query.lastInsertId().toULongLong(); } diff --git a/YACReaderLibrary/db_helper.h b/YACReaderLibrary/db_helper.h index e7f4d6fe..27c412e1 100644 --- a/YACReaderLibrary/db_helper.h +++ b/YACReaderLibrary/db_helper.h @@ -33,9 +33,9 @@ public: //objects management //deletes - static void removeFromDB(LibraryItem * item, QSqlDatabase & db, bool updateParent = true); - static void removeFromDB(Folder * folder, QSqlDatabase & db, bool updateParent = true); - static void removeFromDB(ComicDB * comic, QSqlDatabase & db, bool updateParent = true); + static void removeFromDB(LibraryItem * item, QSqlDatabase & db); + static void removeFromDB(Folder * folder, QSqlDatabase & db); + static void removeFromDB(ComicDB * comic, QSqlDatabase & db); static void removeLabelFromDB(qulonglong id, QSqlDatabase & db); static void removeListFromDB(qulonglong id, QSqlDatabase & db); //logic deletes @@ -43,8 +43,8 @@ public: static void deleteComicsFromLabel(const QList & comicsList, qulonglong labelId, QSqlDatabase & db); static void deleteComicsFromReadingList(const QList & comicsList, qulonglong readingListId, QSqlDatabase & db); //inserts - static qulonglong insert(Folder * folder, QSqlDatabase & db, bool updateParent = true); - static qulonglong insert(ComicDB * comic, QSqlDatabase & db, bool updateParent = true); + static qulonglong insert(Folder * folder, QSqlDatabase & db); + static qulonglong insert(ComicDB * comic, QSqlDatabase & db); static qulonglong insertLabel(const QString & name, YACReader::LabelColors color , QSqlDatabase & db); static qulonglong insertReadingList(const QString & name, QSqlDatabase & db); static qulonglong insertReadingSubList(const QString & name, qulonglong parentId, int ordering, QSqlDatabase & db); @@ -59,6 +59,7 @@ public: static void update(const Folder & folder, QSqlDatabase & db); static void updateChildrenInfo(const Folder & folder, QSqlDatabase & db); static void updateChildrenInfo(qulonglong folderId, QSqlDatabase & db); + static void updateChildrenInfo(QSqlDatabase & db); static void updateProgress(qulonglong libraryId,const ComicInfo & comicInfo); static void updateReadingRemoteProgress(const ComicInfo & comicInfo, QSqlDatabase & db); static void updateFromRemoteClient(qulonglong libraryId,const ComicInfo & comicInfo); diff --git a/YACReaderLibrary/library_creator.cpp b/YACReaderLibrary/library_creator.cpp index 9ff6c47b..8fbc14c3 100644 --- a/YACReaderLibrary/library_creator.cpp +++ b/YACReaderLibrary/library_creator.cpp @@ -162,6 +162,9 @@ void LibraryCreator::run() _database.transaction(); //se crea la librería create(QDir(_source)); + + DBHelper::updateChildrenInfo(_database); + _database.commit(); _database.close(); QSqlDatabase::removeDatabase(_database.connectionName()); @@ -199,6 +202,12 @@ void LibraryCreator::run() { update(QDir(_source)); } + + if(partialUpdate) + DBHelper::updateChildrenInfo(folderDestinationModelIndex.data(FolderModel::IdRole).toULongLong(),_database); + else + DBHelper::updateChildrenInfo(_database); + _database.commit(); _database.close(); QSqlDatabase::removeDatabase(_target); @@ -243,7 +252,7 @@ qulonglong LibraryCreator::insertFolders() if(!(i->knownId)) { i->setFather(currentId); - currentId = DBHelper::insert(&(*i),_database);//insertFolder(currentId,*i); + currentId = DBHelper::insert(&(*i),_database);//insertFolder(currentId,*i); i->setId(currentId); } else diff --git a/YACReaderLibrary/library_window.cpp b/YACReaderLibrary/library_window.cpp index f89dfa23..000b9aaa 100644 --- a/YACReaderLibrary/library_window.cpp +++ b/YACReaderLibrary/library_window.cpp @@ -1291,7 +1291,7 @@ void LibraryWindow::loadLibrary(const QString & name) if(d.exists(path+"/library.ydb")) { - QSqlDatabase db = DataBaseManagement::loadDatabase(path); + QSqlDatabase db = DataBaseManagement::loadDatabase(path); manageOpeningLibraryError(db.lastError().databaseText() + "-" + db.lastError().driverText()); //será possible renombrar y borrar estas bibliotecas renameLibraryAction->setEnabled(true); @@ -2579,7 +2579,7 @@ void LibraryWindow::deleteComicsFromDisk() QLOG_INFO() << comic.parentId; } - ComicsRemover * remover = new ComicsRemover(indexList,paths); + ComicsRemover * remover = new ComicsRemover(indexList,paths,comics.at(0).parentId); QThread * thread = NULL; thread = new QThread(this); @@ -2592,6 +2592,8 @@ void LibraryWindow::deleteComicsFromDisk() connect(remover, SIGNAL(remove(int)), comicsModel, SLOT(remove(int))); connect(remover, SIGNAL(removeError()),this,SLOT(setRemoveError())); connect(remover, SIGNAL(finished()), comicsModel, SLOT(finishTransaction())); + connect(remover, SIGNAL(finished()), comicsModel, SLOT(finishTransaction())); + connect(remover, SIGNAL(removedItemsFromFolder(qulonglong)), foldersModel, SLOT(updateFolderChildrenInfo(qulonglong))); connect(remover, SIGNAL(finished()),this,SLOT(checkEmptyFolder())); connect(remover, SIGNAL(finished()),this,SLOT(checkRemoveError())); From cfffc1bae8ad51682bf7dda18326708bd2a9d294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 25 Jan 2016 21:22:39 +0100 Subject: [PATCH 021/118] fixed Content-Type for various controllers --- YACReaderLibrary/server/controllers/comiccontroller.cpp | 2 +- .../server/controllers/comicdownloadinfocontroller.cpp | 2 +- YACReaderLibrary/server/controllers/foldercontentcontroller.cpp | 2 +- YACReaderLibrary/server/controllers/folderinfocontroller.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/YACReaderLibrary/server/controllers/comiccontroller.cpp b/YACReaderLibrary/server/controllers/comiccontroller.cpp index 52ae4e00..f00461ca 100644 --- a/YACReaderLibrary/server/controllers/comiccontroller.cpp +++ b/YACReaderLibrary/server/controllers/comiccontroller.cpp @@ -79,7 +79,7 @@ void ComicController::service(HttpRequest& request, HttpResponse& response) session.setCurrentComic(comic.id, comicFile); } - response.setHeader("Content-Type", "plain/text; charset=utf-8"); + response.setHeader("Content-Type", "text/plain; charset=utf-8"); //TODO this field is not used by the client! response.writeText(QString("library:%1\r\n").arg(libraryName)); response.writeText(QString("libraryId:%1\r\n").arg(libraryId)); diff --git a/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp b/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp index aeab979a..8143ec82 100644 --- a/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp +++ b/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp @@ -10,7 +10,7 @@ ComicDownloadInfoController::ComicDownloadInfoController() {} void ComicDownloadInfoController::service(HttpRequest& request, HttpResponse& response) { - response.setHeader("Content-Type", "plain/text; charset=utf-8"); + response.setHeader("Content-Type", "text/plain; charset=utf-8"); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); diff --git a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp index 776d99cb..f07439ba 100644 --- a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp +++ b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp @@ -23,7 +23,7 @@ FolderContentController::FolderContentController() {} void FolderContentController::service(HttpRequest& request, HttpResponse& response) { - response.setHeader("Content-Type", "plain/text; charset=utf-8"); + response.setHeader("Content-Type", "text/plain; charset=utf-8"); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); diff --git a/YACReaderLibrary/server/controllers/folderinfocontroller.cpp b/YACReaderLibrary/server/controllers/folderinfocontroller.cpp index 0d8f333f..6aaf9029 100644 --- a/YACReaderLibrary/server/controllers/folderinfocontroller.cpp +++ b/YACReaderLibrary/server/controllers/folderinfocontroller.cpp @@ -12,7 +12,7 @@ FolderInfoController::FolderInfoController() {} void FolderInfoController::service(HttpRequest& request, HttpResponse& response) { - response.setHeader("Content-Type", "plain/text; charset=utf-8"); + response.setHeader("Content-Type", "text/plain; charset=utf-8"); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); From 4350d6797e9bad37f6ab34f4fd3726bb16f3dfc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 25 Jan 2016 22:23:49 +0100 Subject: [PATCH 022/118] added a new controller for serving the tags in a library --- YACReaderLibrary/db_helper.cpp | 54 +++++++++++++++++++ YACReaderLibrary/db_helper.h | 4 +- .../server/controllers/tagscontroller.cpp | 30 +++++++++++ .../server/controllers/tagscontroller.h | 22 ++++++++ YACReaderLibrary/server/requestmapper.cpp | 6 +++ YACReaderLibrary/server/server.pri | 6 ++- 6 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 YACReaderLibrary/server/controllers/tagscontroller.cpp create mode 100644 YACReaderLibrary/server/controllers/tagscontroller.h diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 5869a15f..bd633439 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -14,6 +14,7 @@ #include +#include "reading_list_item.h" #include "library_item.h" #include "comic_db.h" #include "data_base_management.h" @@ -961,6 +962,59 @@ QList DBHelper::getComicsFromParent(qulonglong parentId, QSqlData return list; } +QList DBHelper::getLabelItems(qulonglong libraryId) +{ + QString libraryPath = DBHelper::getLibraries().getPath(libraryId); + QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary"); + + QSqlQuery selectQuery("SELECT * FROM label ORDER BY ordering,name",db); //TODO add some kind of + QList labels; + + while(selectQuery.next()) + { + QSqlRecord record = selectQuery.record(); + LabelItem *item = new LabelItem(QList() + << record.value("name") + << record.value("color") + << record.value("id") + << record.value("ordering")); + + if(labels.isEmpty()) + { + labels << item; + } + else + { + int i = 0; + + while (i < labels.count() && (labels.at(i)->colorid() < item->colorid()) ) + i++; + + if(i < labels.count()) + { + if(labels.at(i)->colorid() == item->colorid()) //sort by name + { + while( i < labels.count() && labels.at(i)->colorid() == item->colorid() && naturalSortLessThanCI(labels.at(i)->name(),item->name())) + i++; + } + } + if(i >= labels.count()) + { + labels << item; + } + else + { + labels.insert(i,item); + } + } + } + + db.close(); + QSqlDatabase::removeDatabase(libraryPath); + + return labels; +} + //loads Folder DBHelper::loadFolder(qulonglong id, QSqlDatabase & db) { diff --git a/YACReaderLibrary/db_helper.h b/YACReaderLibrary/db_helper.h index 27c412e1..be2e323e 100644 --- a/YACReaderLibrary/db_helper.h +++ b/YACReaderLibrary/db_helper.h @@ -9,6 +9,7 @@ class QString; class ComicDB; class Folder; class LibraryItem; +class LabelItem; class QSqlDatabase; class ComicInfo; class QSqlRecord; @@ -73,7 +74,8 @@ public: static QList getFoldersFromParent(qulonglong parentId, QSqlDatabase & db, bool sort = true); static QList getSortedComicsFromParent(qulonglong parentId, QSqlDatabase & db); static QList getComicsFromParent(qulonglong parentId, QSqlDatabase & db, bool sort = true); - //load + static QList getLabelItems(qulonglong libraryId); + //load static Folder loadFolder(qulonglong id, QSqlDatabase & db); static Folder loadFolder(const QString & folderName, qulonglong parentId, QSqlDatabase & db); static ComicDB loadComic(qulonglong id, QSqlDatabase & db); diff --git a/YACReaderLibrary/server/controllers/tagscontroller.cpp b/YACReaderLibrary/server/controllers/tagscontroller.cpp new file mode 100644 index 00000000..d1aa877f --- /dev/null +++ b/YACReaderLibrary/server/controllers/tagscontroller.cpp @@ -0,0 +1,30 @@ +#include "tagscontroller.h" + +#include "db_helper.h" +#include "yacreader_libraries.h" + +#include "reading_list_item.h" +#include "../static.h" +#include "yacreader_global.h" + +#include "QsLog.h" + +TagsController::TagsController() {} + +void TagsController::service(HttpRequest& request, HttpResponse& response) +{ + response.setHeader("Content-Type", "text/plain; charset=utf-8"); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + QStringList pathElements = path.split('/'); + int libraryId = pathElements.at(2).toInt(); + + QList tags = DBHelper::getLabelItems(libraryId); + + foreach(LabelItem * tag, tags) + { + response.writeText(QString("%1\t%2\t%3\r\n").arg(tag->getId()).arg(tag->name()).arg(labelColorToRGBString(tag->colorid()))); + } + + response.writeText("",true); +} diff --git a/YACReaderLibrary/server/controllers/tagscontroller.h b/YACReaderLibrary/server/controllers/tagscontroller.h new file mode 100644 index 00000000..eb97ee04 --- /dev/null +++ b/YACReaderLibrary/server/controllers/tagscontroller.h @@ -0,0 +1,22 @@ +#ifndef TAGSCONTROLLER_H +#define TAGSCONTROLLER_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + + + +class TagsController : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(TagsController) +public: + + /** Constructor */ + TagsController(); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); +}; + +#endif // TAGSCONTROLLER_H diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp index 2e887956..d9a98162 100644 --- a/YACReaderLibrary/server/requestmapper.cpp +++ b/YACReaderLibrary/server/requestmapper.cpp @@ -24,6 +24,7 @@ #include "controllers/synccontroller.h" #include "controllers/versioncontroller.h" #include "controllers/foldercontentcontroller.h" +#include "controllers/tagscontroller.h" #include "db_helper.h" #include "yacreader_libraries.h" @@ -109,6 +110,7 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { QRegExp comicPageRemote("/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); //get comic page (remote reading) QRegExp serverVersion("/version/?"); QRegExp folderContent("/library/.+/folder/[0-9]+/content/?"); + QRegExp tags("/library/.+/tags/?"); QRegExp sync("/sync"); @@ -175,6 +177,10 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { { FolderContentController().service(request, response); } + else if(tags.exactMatch(path)) + { + TagsController().service(request, response); + } } else { diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index 6f1588b4..5b903135 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -18,7 +18,8 @@ HEADERS += \ $$PWD/controllers/synccontroller.h \ #v2 $$PWD/controllers/versioncontroller.h \ - $$PWD/controllers/foldercontentcontroller.h + $$PWD/controllers/foldercontentcontroller.h \ + $$PWD/controllers/tagscontroller.h SOURCES += \ $$PWD/static.cpp \ @@ -37,7 +38,8 @@ SOURCES += \ $$PWD/controllers/synccontroller.cpp \ #v2 $$PWD/controllers/versioncontroller.cpp \ - $$PWD/controllers/foldercontentcontroller.cpp + $$PWD/controllers/foldercontentcontroller.cpp \ + $$PWD/controllers/tagscontroller.cpp include(lib/bfLogging/bfLogging.pri) include(lib/bfHttpServer/bfHttpServer.pri) From 3df1e99929c408d9fc6f18ed312e3c2efd9417df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 25 Jan 2016 22:28:15 +0100 Subject: [PATCH 023/118] replaced separator char --- .../server/controllers/foldercontentcontroller.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp index f07439ba..8d88c96a 100644 --- a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp +++ b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp @@ -54,12 +54,12 @@ void FolderContentController::serviceContent(const int &library, const qulonglon if((*itr)->isDir()) { currentFolder = (Folder *)(*itr); - response.writeText(QString("f:%1:%2:%3:%4:%5\r\n").arg(library).arg(currentFolder->id).arg(currentFolder->name).arg(currentFolder->getNumChildren()).arg(currentFolder->getFirstChildHash())); + response.writeText(QString("f\t%1\t%2\t%3\t%4\t%5\r\n").arg(library).arg(currentFolder->id).arg(currentFolder->name).arg(currentFolder->getNumChildren()).arg(currentFolder->getFirstChildHash())); } else { currentComic = (ComicDB *)(*itr); - response.writeText(QString("c:%1:%2:%3:%4:%5\r\n").arg(library).arg(currentComic->id).arg(currentComic->getFileName()).arg(currentComic->getFileSize()).arg(currentComic->info.hash)); + response.writeText(QString("c\t%1\t%2\t%3\t%4\t%5\r\n").arg(library).arg(currentComic->id).arg(currentComic->getFileName()).arg(currentComic->getFileSize()).arg(currentComic->info.hash)); } } From e6d229680f8520de55b3d352e4dbb607d9dbf43b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Thu, 16 Jun 2016 19:46:58 +0200 Subject: [PATCH 024/118] renamed folders and files, preparing the code structure to be updated to the latest version of QtWebApp --- .../httpconnectionhandler.cpp | 0 .../httpconnectionhandler.h | 0 .../httpconnectionhandlerpool.cpp | 0 .../httpconnectionhandlerpool.h | 0 .../httpcookie.cpp | 0 .../{bfHttpServer => httpserver}/httpcookie.h | 0 .../server/lib/httpserver/httpglobal.cpp | 7 +++++ .../server/lib/httpserver/httpglobal.h | 27 +++++++++++++++++++ .../httplistener.cpp | 0 .../httplistener.h | 0 .../httprequest.cpp | 0 .../httprequest.h | 0 .../httprequesthandler.cpp | 0 .../httprequesthandler.h | 0 .../httpresponse.cpp | 0 .../httpresponse.h | 0 .../httpserver.pri} | 0 .../httpsession.cpp | 0 .../httpsession.h | 0 .../httpsessionstore.cpp | 0 .../httpsessionstore.h | 0 .../staticfilecontroller.cpp | 0 .../staticfilecontroller.h | 0 .../{bfLogging => logging}/dualfilelogger.cpp | 0 .../{bfLogging => logging}/dualfilelogger.h | 0 .../lib/{bfLogging => logging}/filelogger.cpp | 0 .../lib/{bfLogging => logging}/filelogger.h | 0 .../lib/{bfLogging => logging}/logger.cpp | 0 .../lib/{bfLogging => logging}/logger.h | 0 .../bfLogging.pri => logging/logging.pri} | 0 .../server/lib/logging/logglobal.h | 24 +++++++++++++++++ .../lib/{bfLogging => logging}/logmessage.cpp | 0 .../lib/{bfLogging => logging}/logmessage.h | 0 .../template.cpp | 0 .../template.h | 0 .../templatecache.cpp | 0 .../templatecache.h | 0 .../templateengine.pri} | 0 .../lib/templateengine/templateglobal.h | 24 +++++++++++++++++ .../templateloader.cpp | 0 .../templateloader.h | 0 YACReaderLibrary/server/server.pri | 6 ++--- 42 files changed, 85 insertions(+), 3 deletions(-) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpconnectionhandler.cpp (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpconnectionhandler.h (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpconnectionhandlerpool.cpp (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpconnectionhandlerpool.h (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpcookie.cpp (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpcookie.h (100%) create mode 100644 YACReaderLibrary/server/lib/httpserver/httpglobal.cpp create mode 100644 YACReaderLibrary/server/lib/httpserver/httpglobal.h rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httplistener.cpp (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httplistener.h (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httprequest.cpp (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httprequest.h (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httprequesthandler.cpp (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httprequesthandler.h (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpresponse.cpp (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpresponse.h (100%) rename YACReaderLibrary/server/lib/{bfHttpServer/bfHttpServer.pri => httpserver/httpserver.pri} (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpsession.cpp (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpsession.h (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpsessionstore.cpp (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/httpsessionstore.h (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/staticfilecontroller.cpp (100%) rename YACReaderLibrary/server/lib/{bfHttpServer => httpserver}/staticfilecontroller.h (100%) rename YACReaderLibrary/server/lib/{bfLogging => logging}/dualfilelogger.cpp (100%) rename YACReaderLibrary/server/lib/{bfLogging => logging}/dualfilelogger.h (100%) rename YACReaderLibrary/server/lib/{bfLogging => logging}/filelogger.cpp (100%) rename YACReaderLibrary/server/lib/{bfLogging => logging}/filelogger.h (100%) rename YACReaderLibrary/server/lib/{bfLogging => logging}/logger.cpp (100%) rename YACReaderLibrary/server/lib/{bfLogging => logging}/logger.h (100%) rename YACReaderLibrary/server/lib/{bfLogging/bfLogging.pri => logging/logging.pri} (100%) create mode 100644 YACReaderLibrary/server/lib/logging/logglobal.h rename YACReaderLibrary/server/lib/{bfLogging => logging}/logmessage.cpp (100%) rename YACReaderLibrary/server/lib/{bfLogging => logging}/logmessage.h (100%) rename YACReaderLibrary/server/lib/{bfTemplateEngine => templateengine}/template.cpp (100%) rename YACReaderLibrary/server/lib/{bfTemplateEngine => templateengine}/template.h (100%) rename YACReaderLibrary/server/lib/{bfTemplateEngine => templateengine}/templatecache.cpp (100%) rename YACReaderLibrary/server/lib/{bfTemplateEngine => templateengine}/templatecache.h (100%) rename YACReaderLibrary/server/lib/{bfTemplateEngine/bfTemplateEngine.pri => templateengine/templateengine.pri} (100%) create mode 100644 YACReaderLibrary/server/lib/templateengine/templateglobal.h rename YACReaderLibrary/server/lib/{bfTemplateEngine => templateengine}/templateloader.cpp (100%) rename YACReaderLibrary/server/lib/{bfTemplateEngine => templateengine}/templateloader.h (100%) diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.cpp b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.cpp rename to YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.cpp diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.h b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.h similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.h rename to YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.h diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.cpp b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.cpp rename to YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.cpp diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.h b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.h similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.h rename to YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.h diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpcookie.cpp b/YACReaderLibrary/server/lib/httpserver/httpcookie.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpcookie.cpp rename to YACReaderLibrary/server/lib/httpserver/httpcookie.cpp diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpcookie.h b/YACReaderLibrary/server/lib/httpserver/httpcookie.h similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpcookie.h rename to YACReaderLibrary/server/lib/httpserver/httpcookie.h diff --git a/YACReaderLibrary/server/lib/httpserver/httpglobal.cpp b/YACReaderLibrary/server/lib/httpserver/httpglobal.cpp new file mode 100644 index 00000000..dfdf9cd4 --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/httpglobal.cpp @@ -0,0 +1,7 @@ +#include "httpglobal.h" + +const char* getQtWebAppLibVersion() +{ + return "1.5.3"; +} + diff --git a/YACReaderLibrary/server/lib/httpserver/httpglobal.h b/YACReaderLibrary/server/lib/httpserver/httpglobal.h new file mode 100644 index 00000000..a4ccbd3e --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/httpglobal.h @@ -0,0 +1,27 @@ +/** + @file + @author Stefan Frings +*/ + +#ifndef HTTPGLOBAL_H +#define HTTPGLOBAL_H + +#include + +// This is specific to Windows dll's +#if defined(Q_OS_WIN) + #if defined(QTWEBAPPLIB_EXPORT) + #define DECLSPEC Q_DECL_EXPORT + #elif defined(QTWEBAPPLIB_IMPORT) + #define DECLSPEC Q_DECL_IMPORT + #endif +#endif +#if !defined(DECLSPEC) + #define DECLSPEC +#endif + +/** Get the library version number */ +DECLSPEC const char* getQtWebAppLibVersion(); + +#endif // HTTPGLOBAL_H + diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httplistener.cpp b/YACReaderLibrary/server/lib/httpserver/httplistener.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httplistener.cpp rename to YACReaderLibrary/server/lib/httpserver/httplistener.cpp diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httplistener.h b/YACReaderLibrary/server/lib/httpserver/httplistener.h similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httplistener.h rename to YACReaderLibrary/server/lib/httpserver/httplistener.h diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httprequest.cpp b/YACReaderLibrary/server/lib/httpserver/httprequest.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httprequest.cpp rename to YACReaderLibrary/server/lib/httpserver/httprequest.cpp diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httprequest.h b/YACReaderLibrary/server/lib/httpserver/httprequest.h similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httprequest.h rename to YACReaderLibrary/server/lib/httpserver/httprequest.h diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httprequesthandler.cpp b/YACReaderLibrary/server/lib/httpserver/httprequesthandler.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httprequesthandler.cpp rename to YACReaderLibrary/server/lib/httpserver/httprequesthandler.cpp diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httprequesthandler.h b/YACReaderLibrary/server/lib/httpserver/httprequesthandler.h similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httprequesthandler.h rename to YACReaderLibrary/server/lib/httpserver/httprequesthandler.h diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpresponse.cpp b/YACReaderLibrary/server/lib/httpserver/httpresponse.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpresponse.cpp rename to YACReaderLibrary/server/lib/httpserver/httpresponse.cpp diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpresponse.h b/YACReaderLibrary/server/lib/httpserver/httpresponse.h similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpresponse.h rename to YACReaderLibrary/server/lib/httpserver/httpresponse.h diff --git a/YACReaderLibrary/server/lib/bfHttpServer/bfHttpServer.pri b/YACReaderLibrary/server/lib/httpserver/httpserver.pri similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/bfHttpServer.pri rename to YACReaderLibrary/server/lib/httpserver/httpserver.pri diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpsession.cpp b/YACReaderLibrary/server/lib/httpserver/httpsession.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpsession.cpp rename to YACReaderLibrary/server/lib/httpserver/httpsession.cpp diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpsession.h b/YACReaderLibrary/server/lib/httpserver/httpsession.h similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpsession.h rename to YACReaderLibrary/server/lib/httpserver/httpsession.h diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.cpp b/YACReaderLibrary/server/lib/httpserver/httpsessionstore.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.cpp rename to YACReaderLibrary/server/lib/httpserver/httpsessionstore.cpp diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.h b/YACReaderLibrary/server/lib/httpserver/httpsessionstore.h similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.h rename to YACReaderLibrary/server/lib/httpserver/httpsessionstore.h diff --git a/YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.cpp b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.cpp rename to YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp diff --git a/YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.h b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h similarity index 100% rename from YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.h rename to YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h diff --git a/YACReaderLibrary/server/lib/bfLogging/dualfilelogger.cpp b/YACReaderLibrary/server/lib/logging/dualfilelogger.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfLogging/dualfilelogger.cpp rename to YACReaderLibrary/server/lib/logging/dualfilelogger.cpp diff --git a/YACReaderLibrary/server/lib/bfLogging/dualfilelogger.h b/YACReaderLibrary/server/lib/logging/dualfilelogger.h similarity index 100% rename from YACReaderLibrary/server/lib/bfLogging/dualfilelogger.h rename to YACReaderLibrary/server/lib/logging/dualfilelogger.h diff --git a/YACReaderLibrary/server/lib/bfLogging/filelogger.cpp b/YACReaderLibrary/server/lib/logging/filelogger.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfLogging/filelogger.cpp rename to YACReaderLibrary/server/lib/logging/filelogger.cpp diff --git a/YACReaderLibrary/server/lib/bfLogging/filelogger.h b/YACReaderLibrary/server/lib/logging/filelogger.h similarity index 100% rename from YACReaderLibrary/server/lib/bfLogging/filelogger.h rename to YACReaderLibrary/server/lib/logging/filelogger.h diff --git a/YACReaderLibrary/server/lib/bfLogging/logger.cpp b/YACReaderLibrary/server/lib/logging/logger.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfLogging/logger.cpp rename to YACReaderLibrary/server/lib/logging/logger.cpp diff --git a/YACReaderLibrary/server/lib/bfLogging/logger.h b/YACReaderLibrary/server/lib/logging/logger.h similarity index 100% rename from YACReaderLibrary/server/lib/bfLogging/logger.h rename to YACReaderLibrary/server/lib/logging/logger.h diff --git a/YACReaderLibrary/server/lib/bfLogging/bfLogging.pri b/YACReaderLibrary/server/lib/logging/logging.pri similarity index 100% rename from YACReaderLibrary/server/lib/bfLogging/bfLogging.pri rename to YACReaderLibrary/server/lib/logging/logging.pri diff --git a/YACReaderLibrary/server/lib/logging/logglobal.h b/YACReaderLibrary/server/lib/logging/logglobal.h new file mode 100644 index 00000000..47b2151e --- /dev/null +++ b/YACReaderLibrary/server/lib/logging/logglobal.h @@ -0,0 +1,24 @@ +/** + @file + @author Stefan Frings +*/ + +#ifndef LOGGLOBAL_H +#define LOGGLOBAL_H + +#include + +// This is specific to Windows dll's +#if defined(Q_OS_WIN) + #if defined(QTWEBAPPLIB_EXPORT) + #define DECLSPEC Q_DECL_EXPORT + #elif defined(QTWEBAPPLIB_IMPORT) + #define DECLSPEC Q_DECL_IMPORT + #endif +#endif +#if !defined(DECLSPEC) + #define DECLSPEC +#endif + +#endif // LOGGLOBAL_H + diff --git a/YACReaderLibrary/server/lib/bfLogging/logmessage.cpp b/YACReaderLibrary/server/lib/logging/logmessage.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfLogging/logmessage.cpp rename to YACReaderLibrary/server/lib/logging/logmessage.cpp diff --git a/YACReaderLibrary/server/lib/bfLogging/logmessage.h b/YACReaderLibrary/server/lib/logging/logmessage.h similarity index 100% rename from YACReaderLibrary/server/lib/bfLogging/logmessage.h rename to YACReaderLibrary/server/lib/logging/logmessage.h diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/template.cpp b/YACReaderLibrary/server/lib/templateengine/template.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfTemplateEngine/template.cpp rename to YACReaderLibrary/server/lib/templateengine/template.cpp diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/template.h b/YACReaderLibrary/server/lib/templateengine/template.h similarity index 100% rename from YACReaderLibrary/server/lib/bfTemplateEngine/template.h rename to YACReaderLibrary/server/lib/templateengine/template.h diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/templatecache.cpp b/YACReaderLibrary/server/lib/templateengine/templatecache.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfTemplateEngine/templatecache.cpp rename to YACReaderLibrary/server/lib/templateengine/templatecache.cpp diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/templatecache.h b/YACReaderLibrary/server/lib/templateengine/templatecache.h similarity index 100% rename from YACReaderLibrary/server/lib/bfTemplateEngine/templatecache.h rename to YACReaderLibrary/server/lib/templateengine/templatecache.h diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/bfTemplateEngine.pri b/YACReaderLibrary/server/lib/templateengine/templateengine.pri similarity index 100% rename from YACReaderLibrary/server/lib/bfTemplateEngine/bfTemplateEngine.pri rename to YACReaderLibrary/server/lib/templateengine/templateengine.pri diff --git a/YACReaderLibrary/server/lib/templateengine/templateglobal.h b/YACReaderLibrary/server/lib/templateengine/templateglobal.h new file mode 100644 index 00000000..c15ffda7 --- /dev/null +++ b/YACReaderLibrary/server/lib/templateengine/templateglobal.h @@ -0,0 +1,24 @@ +/** + @file + @author Stefan Frings +*/ + +#ifndef TEMPLATEGLOBAL_H +#define TEMPLATEGLOBAL_H + +#include + +// This is specific to Windows dll's +#if defined(Q_OS_WIN) + #if defined(QTWEBAPPLIB_EXPORT) + #define DECLSPEC Q_DECL_EXPORT + #elif defined(QTWEBAPPLIB_IMPORT) + #define DECLSPEC Q_DECL_IMPORT + #endif +#endif +#if !defined(DECLSPEC) + #define DECLSPEC +#endif + +#endif // TEMPLATEGLOBAL_H + diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/templateloader.cpp b/YACReaderLibrary/server/lib/templateengine/templateloader.cpp similarity index 100% rename from YACReaderLibrary/server/lib/bfTemplateEngine/templateloader.cpp rename to YACReaderLibrary/server/lib/templateengine/templateloader.cpp diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/templateloader.h b/YACReaderLibrary/server/lib/templateengine/templateloader.h similarity index 100% rename from YACReaderLibrary/server/lib/bfTemplateEngine/templateloader.h rename to YACReaderLibrary/server/lib/templateengine/templateloader.h diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index 5b903135..b47413c6 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -41,8 +41,8 @@ SOURCES += \ $$PWD/controllers/foldercontentcontroller.cpp \ $$PWD/controllers/tagscontroller.cpp -include(lib/bfLogging/bfLogging.pri) -include(lib/bfHttpServer/bfHttpServer.pri) -include(lib/bfTemplateEngine/bfTemplateEngine.pri) +include(lib/logging/logging.pri) +include(lib/httpserver/httpserver.pri) +include(lib/templateengine/templateengine.pri) DEFINES += SERVER_VERSION_NUMBER=\\\"2.0\\\" From 165711162160837388b2e507aa95d46a0c5529d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Thu, 16 Jun 2016 22:16:01 +0200 Subject: [PATCH 025/118] Updated loggin component of QtWebApp to version 1.6.5 --- .../server/lib/logging/dualfilelogger.cpp | 14 ++-- .../server/lib/logging/dualfilelogger.h | 22 ++++-- .../server/lib/logging/filelogger.cpp | 69 ++++++++++++------- .../server/lib/logging/filelogger.h | 3 +- .../server/lib/logging/logger.cpp | 61 ++++++++++------ YACReaderLibrary/server/lib/logging/logger.h | 13 ++-- .../server/lib/logging/logging.pri | 5 +- .../server/lib/logging/logmessage.cpp | 52 ++++++++------ .../server/lib/logging/logmessage.h | 14 ++-- 9 files changed, 162 insertions(+), 91 deletions(-) diff --git a/YACReaderLibrary/server/lib/logging/dualfilelogger.cpp b/YACReaderLibrary/server/lib/logging/dualfilelogger.cpp index 7329cae0..80738e9d 100644 --- a/YACReaderLibrary/server/lib/logging/dualfilelogger.cpp +++ b/YACReaderLibrary/server/lib/logging/dualfilelogger.cpp @@ -13,8 +13,14 @@ DualFileLogger::DualFileLogger(QSettings* firstSettings, QSettings* secondSettin secondLogger=new FileLogger(secondSettings, refreshInterval, this); } - -void DualFileLogger::log(const QtMsgType type, const QString& message) { - firstLogger->log(type, message); - secondLogger->log(type, message); +void DualFileLogger::log(const QtMsgType type, const QString& message, const QString &file, const QString &function, const int line) +{ + firstLogger->log(type,message,file,function,line); + secondLogger->log(type,message,file,function,line); +} + +void DualFileLogger::clear(const bool buffer, const bool variables) +{ + firstLogger->clear(buffer,variables); + secondLogger->clear(buffer,variables); } diff --git a/YACReaderLibrary/server/lib/logging/dualfilelogger.h b/YACReaderLibrary/server/lib/logging/dualfilelogger.h index 39bec859..e65236a0 100644 --- a/YACReaderLibrary/server/lib/logging/dualfilelogger.h +++ b/YACReaderLibrary/server/lib/logging/dualfilelogger.h @@ -6,11 +6,12 @@ #ifndef DUALFILELOGGER_H #define DUALFILELOGGER_H -#include "logger.h" -#include "filelogger.h" #include #include #include +#include "logglobal.h" +#include "logger.h" +#include "filelogger.h" /** Logs messages into two log files simultaneously. @@ -18,7 +19,7 @@ @see FileLogger for a description of the two underlying loggers. */ -class DualFileLogger : public Logger { +class DECLSPEC DualFileLogger : public Logger { Q_OBJECT Q_DISABLE_COPY(DualFileLogger) public: @@ -37,13 +38,24 @@ public: DualFileLogger(QSettings* firstSettings, QSettings* secondSettings, const int refreshInterval=10000, QObject *parent = 0); /** - Decorate and log a message. + Decorate and log the message, if type>=minLevel. This method is thread safe. @param type Message type (level) @param message Message text + @param file Name of the source file where the message was generated (usually filled with the macro __FILE__) + @param function Name of the function where the message was generated (usually filled with the macro __LINE__) + @param line Line Number of the source file, where the message was generated (usually filles with the macro __func__ or __FUNCTION__) @see LogMessage for a description of the message decoration. */ - virtual void log(const QtMsgType type, const QString& message); + virtual void log(const QtMsgType type, const QString& message, const QString &file="", const QString &function="", const int line=0); + + /** + Clear the thread-local data of the current thread. + This method is thread safe. + @param buffer Whether to clear the backtrace buffer + @param variables Whether to clear the log variables + */ + virtual void clear(const bool buffer=true, const bool variables=true); private: diff --git a/YACReaderLibrary/server/lib/logging/filelogger.cpp b/YACReaderLibrary/server/lib/logging/filelogger.cpp index 06cf122d..f12b29be 100644 --- a/YACReaderLibrary/server/lib/logging/filelogger.cpp +++ b/YACReaderLibrary/server/lib/logging/filelogger.cpp @@ -15,7 +15,8 @@ #include #include "yacreader_global.h" -void FileLogger::refreshSettings() { +void FileLogger::refreshSettings() +{ mutex.lock(); // Save old file name for later comparision with new settings QString oldFileName=fileName; @@ -41,7 +42,8 @@ void FileLogger::refreshSettings() { bufferSize=settings->value("bufferSize",0).toInt(); // Create new file if the filename has been changed - if (oldFileName!=fileName) { + if (oldFileName!=fileName) + { fprintf(stderr,"Logging to %s\n",qPrintable(fileName)); close(); open(); @@ -57,7 +59,8 @@ FileLogger::FileLogger(QSettings* settings, const int refreshInterval, QObject* Q_ASSERT(refreshInterval>=0); this->settings=settings; file=0; - if (refreshInterval>0) { + if (refreshInterval>0) + { refreshTimer.start(refreshInterval,this); } flushTimer.start(1000,this); @@ -65,26 +68,31 @@ FileLogger::FileLogger(QSettings* settings, const int refreshInterval, QObject* } -FileLogger::~FileLogger() { +FileLogger::~FileLogger() +{ close(); } -void FileLogger::write(const LogMessage* logMessage) { +void FileLogger::write(const LogMessage* logMessage) +{ // Try to write to the file - if (file) { + if (file) + { // Write the message file->write(qPrintable(logMessage->toString(msgFormat,timestampFormat))); // Flush error messages immediately, to ensure that no important message // gets lost when the program terinates abnormally. - if (logMessage->getType()>=QtCriticalMsg) { + if (logMessage->getType()>=QtCriticalMsg) + { file->flush(); } // Check for success - if (file->error()) { + if (file->error()) + { close(); qWarning("Cannot write to log file %s: %s",qPrintable(fileName),qPrintable(file->errorString())); } @@ -92,19 +100,23 @@ void FileLogger::write(const LogMessage* logMessage) { } // Fall-back to the super class method, if writing failed - if (!file) { + if (!file) + { Logger::write(logMessage); } } -void FileLogger::open() { - if (fileName.isEmpty()) { +void FileLogger::open() +{ + if (fileName.isEmpty()) + { qWarning("Name of logFile is empty"); } else { file=new QFile(fileName); - if (!file->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { + if (!file->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) + { qWarning("Cannot open log file %s: %s",qPrintable(fileName),qPrintable(file->errorString())); file=0; } @@ -112,8 +124,10 @@ void FileLogger::open() { } -void FileLogger::close() { - if (file) { +void FileLogger::close() +{ + if (file) + { file->close(); delete file; file=0; @@ -123,18 +137,22 @@ void FileLogger::close() { void FileLogger::rotate() { // count current number of existing backup files int count=0; - forever { + forever + { QFile bakFile(QString("%1.%2").arg(fileName).arg(count+1)); - if (bakFile.exists()) { + if (bakFile.exists()) + { ++count; } - else { + else + { break; } } // Remove all old backup files that exceed the maximum number - while (maxBackups>0 && count>=maxBackups) { + while (maxBackups>0 && count>=maxBackups) + { QFile::remove(QString("%1.%2").arg(fileName).arg(count)); --count; } @@ -149,21 +167,26 @@ void FileLogger::rotate() { } -void FileLogger::timerEvent(QTimerEvent* event) { - if (!event) { +void FileLogger::timerEvent(QTimerEvent* event) +{ + if (!event) + { return; } - else if (event->timerId()==refreshTimer.timerId()) { + else if (event->timerId()==refreshTimer.timerId()) + { refreshSettings(); } - else if (event->timerId()==flushTimer.timerId() && file) { + else if (event->timerId()==flushTimer.timerId() && file) + { mutex.lock(); // Flush the I/O buffer file->flush(); // Rotate the file if it is too large - if (maxSize>0 && file->size()>=maxSize) { + if (maxSize>0 && file->size()>=maxSize) + { close(); rotate(); open(); diff --git a/YACReaderLibrary/server/lib/logging/filelogger.h b/YACReaderLibrary/server/lib/logging/filelogger.h index 617b5bff..71b840db 100644 --- a/YACReaderLibrary/server/lib/logging/filelogger.h +++ b/YACReaderLibrary/server/lib/logging/filelogger.h @@ -11,6 +11,7 @@ #include #include #include +#include "logglobal.h" #include "logger.h" /** @@ -45,7 +46,7 @@ @see Logger for a descrition of the buffer. */ -class FileLogger : public Logger { +class DECLSPEC FileLogger : public Logger { Q_OBJECT Q_DISABLE_COPY(FileLogger) public: diff --git a/YACReaderLibrary/server/lib/logging/logger.cpp b/YACReaderLibrary/server/lib/logging/logger.cpp index de3ab1a5..f2d17d2a 100644 --- a/YACReaderLibrary/server/lib/logging/logger.cpp +++ b/YACReaderLibrary/server/lib/logging/logger.cpp @@ -9,6 +9,7 @@ #include #include #include +#include Logger* Logger::defaultLogger=0; @@ -16,9 +17,6 @@ Logger* Logger::defaultLogger=0; QThreadStorage*> Logger::logVars; -QThreadStorage*> Logger::buffers; - - QMutex Logger::mutex; @@ -32,7 +30,8 @@ Logger::Logger(QObject* parent) Logger::Logger(const QString msgFormat, const QString timestampFormat, const QtMsgType minLevel, const int bufferSize, QObject* parent) - :QObject(parent) { + :QObject(parent) +{ this->msgFormat=msgFormat; this->timestampFormat=timestampFormat; this->minLevel=minLevel; @@ -40,7 +39,8 @@ Logger::Logger(const QString msgFormat, const QString timestampFormat, const QtM } -void Logger::msgHandler(const QtMsgType type, const QString &message, const QString &file, const QString &function, const int line) { +void Logger::msgHandler(const QtMsgType type, const QString &message, const QString &file, const QString &function, const int line) +{ static QMutex recursiveMutex(QMutex::Recursive); static QMutex nonRecursiveMutex(QMutex::NonRecursive); @@ -50,11 +50,13 @@ void Logger::msgHandler(const QtMsgType type, const QString &message, const QStr recursiveMutex.lock(); // Fall back to stderr when this method has been called recursively. - if (defaultLogger && nonRecursiveMutex.tryLock()) { + if (defaultLogger && nonRecursiveMutex.tryLock()) + { defaultLogger->log(type, message, file, function, line); nonRecursiveMutex.unlock(); } - else { + else + { fputs(qPrintable(message),stderr); fflush(stderr); } @@ -69,19 +71,23 @@ void Logger::msgHandler(const QtMsgType type, const QString &message, const QStr #if QT_VERSION >= 0x050000 - void Logger::msgHandler5(const QtMsgType type, const QMessageLogContext &context, const QString &message) { + void Logger::msgHandler5(const QtMsgType type, const QMessageLogContext &context, const QString &message) + { (void)(context); // suppress "unused parameter" warning msgHandler(type,message,context.file,context.function,context.line); } #else - void Logger::msgHandler4(const QtMsgType type, const char* message) { + void Logger::msgHandler4(const QtMsgType type, const char* message) + { msgHandler(type,message); } #endif -Logger::~Logger() { - if (defaultLogger==this) { +Logger::~Logger() +{ + if (defaultLogger==this) + { #if QT_VERSION >= 0x050000 qInstallMessageHandler(0); #else @@ -92,13 +98,15 @@ Logger::~Logger() { } -void Logger::write(const LogMessage* logMessage) { +void Logger::write(const LogMessage* logMessage) +{ fputs(qPrintable(logMessage->toString(msgFormat,timestampFormat)),stderr); fflush(stderr); } -void Logger::installMsgHandler() { +void Logger::installMsgHandler() +{ defaultLogger=this; #if QT_VERSION >= 0x050000 qInstallMessageHandler(msgHandler5); @@ -108,9 +116,11 @@ void Logger::installMsgHandler() { } -void Logger::set(const QString& name, const QString& value) { +void Logger::set(const QString& name, const QString& value) +{ mutex.lock(); - if (!logVars.hasLocalData()) { + if (!logVars.hasLocalData()) + { logVars.setLocalData(new QHash); } logVars.localData()->insert(name,value); @@ -118,23 +128,27 @@ void Logger::set(const QString& name, const QString& value) { } -void Logger::clear(const bool buffer, const bool variables) { +void Logger::clear(const bool buffer, const bool variables) +{ mutex.lock(); - if (buffer && buffers.hasLocalData()) { + if (buffer && buffers.hasLocalData()) + { QList* buffer=buffers.localData(); while (buffer && !buffer->isEmpty()) { LogMessage* logMessage=buffer->takeLast(); delete logMessage; } } - if (variables && logVars.hasLocalData()) { + if (variables && logVars.hasLocalData()) + { logVars.localData()->clear(); } mutex.unlock(); } -void Logger::log(const QtMsgType type, const QString& message, const QString &file, const QString &function, const int line) { +void Logger::log(const QtMsgType type, const QString& message, const QString &file, const QString &function, const int line) +{ mutex.lock(); // If the buffer is enabled, write the message into it @@ -148,12 +162,14 @@ void Logger::log(const QtMsgType type, const QString& message, const QString &fi LogMessage* logMessage=new LogMessage(type,message,logVars.localData(),file,function,line); buffer->append(logMessage); // Delete oldest message if the buffer became too large - if (buffer->size()>bufferSize) { + if (buffer->size()>bufferSize) + { delete buffer->takeFirst(); } // If the type of the message is high enough, print the whole buffer if (type>=minLevel) { - while (!buffer->isEmpty()) { + while (!buffer->isEmpty()) + { LogMessage* logMessage=buffer->takeFirst(); write(logMessage); delete logMessage; @@ -163,7 +179,8 @@ void Logger::log(const QtMsgType type, const QString& message, const QString &fi // Buffer is disabled, print the message if the type is high enough else { - if (type>=minLevel) { + if (type>=minLevel) + { LogMessage logMessage(type,message,logVars.localData(),file,function,line); write(&logMessage); } diff --git a/YACReaderLibrary/server/lib/logging/logger.h b/YACReaderLibrary/server/lib/logging/logger.h index adcedfd3..0ced8051 100644 --- a/YACReaderLibrary/server/lib/logging/logger.h +++ b/YACReaderLibrary/server/lib/logging/logger.h @@ -6,13 +6,13 @@ #ifndef LOGGER_H #define LOGGER_H -#include - #include #include #include #include #include +#include +#include "logglobal.h" #include "logmessage.h" /** @@ -45,7 +45,7 @@ because logging to the console is less useful. */ -class Logger : public QObject { +class DECLSPEC Logger : public QObject { Q_OBJECT Q_DISABLE_COPY(Logger) public: @@ -100,10 +100,11 @@ public: /** Clear the thread-local data of the current thread. + This method is thread safe. @param buffer Whether to clear the backtrace buffer @param variables Whether to clear the log variables */ - static void clear(const bool buffer=true, const bool variables=true); + virtual void clear(const bool buffer=true, const bool variables=true); protected: @@ -119,7 +120,7 @@ protected: /** Size of backtrace buffer, number of messages per thread. 0=disabled */ int bufferSize; - /** Used to synchronize access to the static members */ + /** Used to synchronize access of concurrent threads */ static QMutex mutex; /** @@ -176,7 +177,7 @@ private: static QThreadStorage*> logVars; /** Thread local backtrace buffers */ - static QThreadStorage*> buffers; + QThreadStorage*> buffers; }; diff --git a/YACReaderLibrary/server/lib/logging/logging.pri b/YACReaderLibrary/server/lib/logging/logging.pri index 17eae35e..13c296c7 100644 --- a/YACReaderLibrary/server/lib/logging/logging.pri +++ b/YACReaderLibrary/server/lib/logging/logging.pri @@ -1,5 +1,6 @@ INCLUDEPATH += $$PWD DEPENDPATH += $$PWD -HEADERS += $$PWD/logmessage.h $$PWD/logger.h $$PWD/filelogger.h $$PWD/dualfilelogger.h -SOURCES += $$PWD/logmessage.cpp $$PWD/logger.cpp $$PWD/filelogger.cpp $$PWD/dualfilelogger.cpp +HEADERS += $$PWD/logglobal.h $$PWD/logmessage.h $$PWD/logger.h $$PWD/filelogger.h $$PWD/dualfilelogger.h + +SOURCES += $$PWD/logmessage.cpp $$PWD/logger.cpp $$PWD/filelogger.cpp $$PWD/dualfilelogger.cpp diff --git a/YACReaderLibrary/server/lib/logging/logmessage.cpp b/YACReaderLibrary/server/lib/logging/logmessage.cpp index 4d610383..ef359302 100644 --- a/YACReaderLibrary/server/lib/logging/logmessage.cpp +++ b/YACReaderLibrary/server/lib/logging/logmessage.cpp @@ -6,7 +6,8 @@ #include "logmessage.h" #include -LogMessage::LogMessage(const QtMsgType type, const QString& message, QHash* logVars, const QString &file, const QString &function, const int line) { +LogMessage::LogMessage(const QtMsgType type, const QString& message, QHash* logVars, const QString &file, const QString &function, const int line) +{ this->type=type; this->message=message; this->file=file; @@ -17,16 +18,19 @@ LogMessage::LogMessage(const QtMsgType type, const QString& message, QHashlogVars=*logVars; } } -QString LogMessage::toString(const QString& msgFormat, const QString& timestampFormat) const { +QString LogMessage::toString(const QString& msgFormat, const QString& timestampFormat) const +{ QString decorated=msgFormat+"\n"; decorated.replace("{msg}",message); - if (decorated.contains("{timestamp}")) { + if (decorated.contains("{timestamp}")) + { decorated.replace("{timestamp}",timestamp.toString(timestampFormat)); } @@ -34,21 +38,22 @@ QString LogMessage::toString(const QString& msgFormat, const QString& timestampF typeNr.setNum(type); decorated.replace("{typeNr}",typeNr); - switch (type) { - case QtDebugMsg: - decorated.replace("{type}","DEBUG"); - break; - case QtWarningMsg: - decorated.replace("{type}","WARNING"); - break; - case QtCriticalMsg: - decorated.replace("{type}","CRITICAL"); - break; - case QtFatalMsg: - decorated.replace("{type}","FATAL"); - break; - default: - decorated.replace("{type}",typeNr); + switch (type) + { + case QtDebugMsg: + decorated.replace("{type}","DEBUG"); + break; + case QtWarningMsg: + decorated.replace("{type}","WARNING"); + break; + case QtCriticalMsg: + decorated.replace("{type}","CRITICAL"); + break; + case QtFatalMsg: + decorated.replace("{type}","FATAL"); + break; + default: + decorated.replace("{type}",typeNr); } decorated.replace("{file}",file); @@ -60,9 +65,11 @@ QString LogMessage::toString(const QString& msgFormat, const QString& timestampF decorated.replace("{thread}",threadId); // Fill in variables - if (decorated.contains("{") && !logVars.isEmpty()) { + if (decorated.contains("{") && !logVars.isEmpty()) + { QList keys=logVars.keys(); - foreach (QString key, keys) { + foreach (QString key, keys) + { decorated.replace("{"+key+"}",logVars.value(key)); } } @@ -70,6 +77,7 @@ QString LogMessage::toString(const QString& msgFormat, const QString& timestampF return decorated; } -QtMsgType LogMessage::getType() const { +QtMsgType LogMessage::getType() const +{ return type; } diff --git a/YACReaderLibrary/server/lib/logging/logmessage.h b/YACReaderLibrary/server/lib/logging/logmessage.h index 433d949d..d6228cba 100644 --- a/YACReaderLibrary/server/lib/logging/logmessage.h +++ b/YACReaderLibrary/server/lib/logging/logmessage.h @@ -9,6 +9,7 @@ #include #include #include +#include "logglobal.h" /** Represents a single log message together with some data @@ -20,16 +21,17 @@ - {typeNr} Type of the message in numeric format (0-3) - {type} Type of the message in string format (DEBUG, WARNING, CRITICAL, FATAL) - {thread} ID number of the thread - - {msg} Message text (only useable in msgFormat) - - {file} Filename where the message was generated # - - {function} Function where the message was generated # - - {line} Line number where the message was generated # + - {msg} Message text - {xxx} For any user-defined logger variable - # The macros qDebug()...qFatal() dont fill these variable in case of QT versions before 5.0. + Plus some new variables since QT 5.0, only filled when compiled in debug mode: + + - {file} Filename where the message was generated + - {function} Function where the message was generated + - {line} Line number where the message was generated */ -class LogMessage +class DECLSPEC LogMessage { Q_DISABLE_COPY(LogMessage) public: From 770aeb2533b1a1162a1ad2a5350af15a4bc1de4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 17 Jun 2016 19:45:24 +0200 Subject: [PATCH 026/118] Extracted default server log settings to Startup, so there is no need to modify QtWebApp base code. --- YACReaderLibrary/server/lib/logging/filelogger.cpp | 13 ++++++------- YACReaderLibrary/server/startup.cpp | 13 +++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/YACReaderLibrary/server/lib/logging/filelogger.cpp b/YACReaderLibrary/server/lib/logging/filelogger.cpp index f12b29be..3e027bc2 100644 --- a/YACReaderLibrary/server/lib/logging/filelogger.cpp +++ b/YACReaderLibrary/server/lib/logging/filelogger.cpp @@ -13,7 +13,6 @@ #include #include #include -#include "yacreader_global.h" void FileLogger::refreshSettings() { @@ -23,7 +22,7 @@ void FileLogger::refreshSettings() // Load new config settings settings->sync(); - fileName=settings->value("fileName","server_log.log").toString(); + fileName=settings->value("fileName").toString(); // Convert relative fileName to absolute, based on the directory of the config file. #ifdef Q_OS_WIN32 if (QDir::isRelativePath(fileName) && settings->format()!=QSettings::NativeFormat) @@ -31,14 +30,14 @@ void FileLogger::refreshSettings() if (QDir::isRelativePath(fileName)) #endif { - QFileInfo configFile(YACReader::getSettingsPath()); - fileName=QFileInfo(YACReader::getSettingsPath(),fileName).absoluteFilePath(); + QFileInfo configFile(settings->fileName()); + fileName=QFileInfo(configFile.absolutePath(),fileName).absoluteFilePath(); } - maxSize=settings->value("maxSize",1048576).toLongLong(); - maxBackups=settings->value("maxBackups",1).toInt(); + maxSize=settings->value("maxSize",0).toLongLong(); + maxBackups=settings->value("maxBackups",0).toInt(); msgFormat=settings->value("msgFormat","{timestamp} {type} {msg}").toString(); timestampFormat=settings->value("timestampFormat","yyyy-MM-dd hh:mm:ss.zzz").toString(); - minLevel=static_cast(settings->value("minLevel",QtCriticalMsg).toInt()); + minLevel=static_cast(settings->value("minLevel",0).toInt()); bufferSize=settings->value("bufferSize",0).toInt(); // Create new file if the filename has been changed diff --git a/YACReaderLibrary/server/startup.cpp b/YACReaderLibrary/server/startup.cpp index fd1e982f..0c1252a3 100644 --- a/YACReaderLibrary/server/startup.cpp +++ b/YACReaderLibrary/server/startup.cpp @@ -35,6 +35,19 @@ void Startup::start() { mainLogSettings->beginGroup("mainLogFile"); //QSettings* debugLogSettings=new QSettings(configFileName,QSettings::IniFormat,app); //debugLogSettings->beginGroup("debugLogFile"); + + if(mainLogSettings->value("fileName").isNull()) + mainLogSettings->setValue("fileName", QFileInfo(YACReader::getSettingsPath(), "server_log.log").absoluteFilePath()); + + if(mainLogSettings->value("maxSize").isNull()) + mainLogSettings->setValue("maxSize",1048576); + + if(mainLogSettings->value("maxBackups").isNull()) + mainLogSettings->setValue("maxBackups",1); + + if(mainLogSettings->value("minLevel").isNull()) + mainLogSettings->value("minLevel",QtCriticalMsg).toInt(); + Logger* logger=new FileLogger(mainLogSettings,10000,app); logger->installMsgHandler(); From 5ae7e9fd3f9fc84d5e03678f46a5e70d713592ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 17 Jun 2016 19:46:45 +0200 Subject: [PATCH 027/118] Restored the default behaviour of exiting the app after a fatal error log. I didn't see fatal log messages, so this should be safe. --- YACReaderLibrary/server/lib/logging/logger.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/server/lib/logging/logger.cpp b/YACReaderLibrary/server/lib/logging/logger.cpp index f2d17d2a..3f6525eb 100644 --- a/YACReaderLibrary/server/lib/logging/logger.cpp +++ b/YACReaderLibrary/server/lib/logging/logger.cpp @@ -62,8 +62,9 @@ void Logger::msgHandler(const QtMsgType type, const QString &message, const QStr } // Abort the program after logging a fatal message - if (type>=QtFatalMsg) { - //abort(); + if (type>=QtFatalMsg) + { + abort(); } recursiveMutex.unlock(); From 3675aebdc543e00e5f0043285bdacb264bfdefbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 17 Jun 2016 19:56:55 +0200 Subject: [PATCH 028/118] Updated templateengine base code to 1.6.5 --- .../server/lib/templateengine/template.cpp | 131 ++++++++++++------ .../server/lib/templateengine/template.h | 5 +- .../lib/templateengine/templatecache.cpp | 6 +- .../server/lib/templateengine/templatecache.h | 9 +- .../lib/templateengine/templateengine.pri | 10 +- .../lib/templateengine/templateloader.cpp | 39 ++++-- .../lib/templateengine/templateloader.h | 9 +- 7 files changed, 142 insertions(+), 67 deletions(-) diff --git a/YACReaderLibrary/server/lib/templateengine/template.cpp b/YACReaderLibrary/server/lib/templateengine/template.cpp index 23abac9e..d099668c 100644 --- a/YACReaderLibrary/server/lib/templateengine/template.cpp +++ b/YACReaderLibrary/server/lib/templateengine/template.cpp @@ -7,131 +7,174 @@ #include Template::Template(QString source, QString sourceName) - : QString(source) { + : QString(source) +{ this->sourceName=sourceName; this->warnings=false; } -Template::Template(QFile& file, QTextCodec* textCodec) { +Template::Template(QFile& file, QTextCodec* textCodec) +{ this->warnings=false; sourceName=QFileInfo(file.fileName()).baseName(); - if (!file.isOpen()) { + if (!file.isOpen()) + { file.open(QFile::ReadOnly | QFile::Text); } QByteArray data=file.readAll(); file.close(); - if (data.size()==0 || file.error()) { + if (data.size()==0 || file.error()) + { qCritical("Template: cannot read from %s, %s",qPrintable(sourceName),qPrintable(file.errorString())); + } + else + { append(textCodec->toUnicode(data)); } } -int Template::setVariable(QString name, QString value) { +int Template::setVariable(QString name, QString value) +{ int count=0; QString variable="{"+name+"}"; int start=indexOf(variable); - while (start>=0) { + while (start>=0) + { replace(start, variable.length(), value); count++; start=indexOf(variable,start+value.length()); } - if (count==0 && warnings) { + if (count==0 && warnings) + { qWarning("Template: missing variable %s in %s",qPrintable(variable),qPrintable(sourceName)); } return count; } -int Template::setCondition(QString name, bool value) { +int Template::setCondition(QString name, bool value) +{ int count=0; QString startTag=QString("{if %1}").arg(name); QString elseTag=QString("{else %1}").arg(name); QString endTag=QString("{end %1}").arg(name); // search for if-else-end int start=indexOf(startTag); - while (start>=0) { + while (start>=0) + { int end=indexOf(endTag,start+startTag.length()); - if (end>=0) { + if (end>=0) + { count++; int ellse=indexOf(elseTag,start+startTag.length()); - if (ellse>start && ellsestart && ellse=0) { + while (start>=0) + { int end=indexOf(endTag,start+startTag2.length()); - if (end>=0) { + if (end>=0) + { count++; int ellse=indexOf(elseTag,start+startTag2.length()); - if (ellse>start && ellsestart && ellse=0); +int Template::loop(QString name, int repetitions) +{ + Q_ASSERT(repetitions>=0); int count=0; QString startTag="{loop "+name+"}"; QString elseTag="{else "+name+"}"; QString endTag="{end "+name+"}"; // search for loop-else-end int start=indexOf(startTag); - while (start>=0) { + while (start>=0) + { int end=indexOf(endTag,start+startTag.length()); - if (end>=0) { + if (end>=0) + { count++; int ellse=indexOf(elseTag,start+startTag.length()); - if (ellse>start && ellse0) { + if (ellse>start && ellse0) + { QString loopPart=mid(start+startTag.length(), ellse-start-startTag.length()); QString insertMe; - for (int i=0; i0) { // and no else part + else if (repetitions>0) + { + // and no else part QString loopPart=mid(start+startTag.length(), end-start-startTag.length()); QString insertMe; - for (int i=0; i #include #include +#include "templateglobal.h" /** Enhanced version of QString for template processing. Templates @@ -37,7 +38,7 @@ Example code to fill this template:

  Template t(QFile("test.tpl"),QTextCode::codecForName("UTF-8"));
- t.setVariable("user", "Stefan");
+ t.setVariable("username", "Stefan");
  t.setCondition("locked",false);
  t.loop("user",2);
  t.setVariable("user0.name,"Markus");
@@ -86,7 +87,7 @@
  @see TemplateCache
 */
 
-class Template : public QString {
+class DECLSPEC Template : public QString {
 public:
 
     /**
diff --git a/YACReaderLibrary/server/lib/templateengine/templatecache.cpp b/YACReaderLibrary/server/lib/templateengine/templatecache.cpp
index 823f4f24..109074f1 100644
--- a/YACReaderLibrary/server/lib/templateengine/templatecache.cpp
+++ b/YACReaderLibrary/server/lib/templateengine/templatecache.cpp
@@ -11,12 +11,14 @@ TemplateCache::TemplateCache(QSettings* settings, QObject* parent)
     qDebug("TemplateCache: timeout=%i, size=%i",cacheTimeout,cache.maxCost());
 }
 
-QString TemplateCache::tryFile(QString localizedName) {
+QString TemplateCache::tryFile(QString localizedName)
+{
     qint64 now=QDateTime::currentMSecsSinceEpoch();
     // search in cache
     qDebug("TemplateCache: trying cached %s",qPrintable(localizedName));
     CacheEntry* entry=cache.object(localizedName);
-    if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout)) {
+    if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout))
+    {
         return entry->document;
     }
     // search on filesystem
diff --git a/YACReaderLibrary/server/lib/templateengine/templatecache.h b/YACReaderLibrary/server/lib/templateengine/templatecache.h
index 6e79f119..7b4da072 100644
--- a/YACReaderLibrary/server/lib/templateengine/templatecache.h
+++ b/YACReaderLibrary/server/lib/templateengine/templatecache.h
@@ -1,8 +1,9 @@
 #ifndef TEMPLATECACHE_H
 #define TEMPLATECACHE_H
 
-#include "templateloader.h"
 #include 
+#include "templateglobal.h"
+#include "templateloader.h"
 
 /**
   Caching template loader, reduces the amount of I/O and improves performance
@@ -25,7 +26,7 @@
   

The following settings are required:

-  path=.
+  path=../templates
   suffix=.tpl
   encoding=UTF-8
   cacheSize=1000000
@@ -38,9 +39,9 @@
   @see TemplateLoader
 */
 
-class TemplateCache : public TemplateLoader {
+class DECLSPEC TemplateCache : public TemplateLoader {
     Q_OBJECT
-    Q_DISABLE_COPY(TemplateCache);
+    Q_DISABLE_COPY(TemplateCache)
 public:
 
     /**
diff --git a/YACReaderLibrary/server/lib/templateengine/templateengine.pri b/YACReaderLibrary/server/lib/templateengine/templateengine.pri
index d3eba98b..53608ae4 100644
--- a/YACReaderLibrary/server/lib/templateengine/templateengine.pri
+++ b/YACReaderLibrary/server/lib/templateengine/templateengine.pri
@@ -1,7 +1,11 @@
 INCLUDEPATH += $$PWD
 DEPENDPATH += $$PWD
 
-HEADERS  += $$PWD/template.h $$PWD/templateloader.h $$PWD/templatecache.h
-SOURCES  += $$PWD/template.cpp $$PWD/templateloader.cpp $$PWD/templatecache.cpp
+HEADERS += $$PWD/templateglobal.h
+HEADERS += $$PWD/template.h 
+HEADERS += $$PWD/templateloader.h 
+HEADERS += $$PWD/templatecache.h
 
-OTHER_FILES += $$PWD/../doc/readme.txt
+SOURCES += $$PWD/template.cpp 
+SOURCES += $$PWD/templateloader.cpp 
+SOURCES += $$PWD/templatecache.cpp
diff --git a/YACReaderLibrary/server/lib/templateengine/templateloader.cpp b/YACReaderLibrary/server/lib/templateengine/templateloader.cpp
index 9ed9cf8f..2abcabd1 100644
--- a/YACReaderLibrary/server/lib/templateengine/templateloader.cpp
+++ b/YACReaderLibrary/server/lib/templateengine/templateloader.cpp
@@ -32,18 +32,22 @@ TemplateLoader::TemplateLoader(QSettings* settings, QObject* parent)
     }
     fileNameSuffix=settings->value("suffix",".tpl").toString();
     QString encoding=settings->value("encoding").toString();
-    if (encoding.isEmpty()) {
+    if (encoding.isEmpty())
+    {
         textCodec=QTextCodec::codecForLocale();
     }
-    else {
+    else
+    {
        textCodec=QTextCodec::codecForName(encoding.toLocal8Bit());
    }
    qDebug("TemplateLoader: path=%s, codec=%s",qPrintable(templatePath),textCodec->name().data());
 }
 
-TemplateLoader::~TemplateLoader() {}
+TemplateLoader::~TemplateLoader()
+{}
 
-QString TemplateLoader::tryFile(QString localizedName) {
+QString TemplateLoader::tryFile(QString localizedName)
+{
     QString fileName=templatePath+"/"+localizedName+fileNameSuffix;
     qDebug("TemplateCache: trying file %s",qPrintable(fileName));
     QFile file(fileName);
@@ -51,28 +55,33 @@ QString TemplateLoader::tryFile(QString localizedName) {
         file.open(QIODevice::ReadOnly);
         QString document=textCodec->toUnicode(file.readAll());
         file.close();
-        if (file.error()) {
+        if (file.error())
+        {
             qCritical("TemplateLoader: cannot load file %s, %s",qPrintable(fileName),qPrintable(file.errorString()));
             return "";
         }
-        else {
+        else
+        {
             return document;
         }
     }
     return "";
 }
 
-Template TemplateLoader::getTemplate(QString templateName, QString locales) {
+Template TemplateLoader::getTemplate(QString templateName, QString locales)
+{
     mutex.lock();
     QSet tried; // used to suppress duplicate attempts
     QStringList locs=locales.split(',',QString::SkipEmptyParts);
 
     // Search for exact match
-    foreach (QString loc,locs) {
+    foreach (QString loc,locs)
+    {
         loc.replace(QRegExp(";.*"),"");
         loc.replace('-','_');
         QString localizedName=templateName+"-"+loc.trimmed();
-        if (!tried.contains(localizedName)) {
+        if (!tried.contains(localizedName))
+        {
             QString document=tryFile(localizedName);
             if (!document.isEmpty()) {
                 mutex.unlock();
@@ -83,12 +92,15 @@ Template TemplateLoader::getTemplate(QString templateName, QString locales) {
     }
 
     // Search for correct language but any country
-    foreach (QString loc,locs) {
+    foreach (QString loc,locs)
+    {
         loc.replace(QRegExp("[;_-].*"),"");
         QString localizedName=templateName+"-"+loc.trimmed();
-        if (!tried.contains(localizedName)) {
+        if (!tried.contains(localizedName))
+        {
             QString document=tryFile(localizedName);
-            if (!document.isEmpty()) {
+            if (!document.isEmpty())
+            {
                 mutex.unlock();
                 return Template(document,localizedName);
             }
@@ -98,7 +110,8 @@ Template TemplateLoader::getTemplate(QString templateName, QString locales) {
 
     // Search for default file
     QString document=tryFile(templateName);
-    if (!document.isEmpty()) {
+    if (!document.isEmpty())
+    {
         mutex.unlock();
         return Template(document,templateName);
     }
diff --git a/YACReaderLibrary/server/lib/templateengine/templateloader.h b/YACReaderLibrary/server/lib/templateengine/templateloader.h
index 5635af40..5b06c609 100644
--- a/YACReaderLibrary/server/lib/templateengine/templateloader.h
+++ b/YACReaderLibrary/server/lib/templateengine/templateloader.h
@@ -9,8 +9,9 @@
 #include 
 #include 
 #include 
-#include "template.h"
 #include 
+#include "templateglobal.h"
+#include "template.h"
 
 /**
   Loads localized versions of template files. If the caller requests a file with the
@@ -25,7 +26,7 @@
 
   The following settings are required:
   
-  path=.
+  path=../templates
   suffix=.tpl
   encoding=UTF-8
   
@@ -34,9 +35,9 @@ @see TemplateCache */ -class TemplateLoader : public QObject { +class DECLSPEC TemplateLoader : public QObject { Q_OBJECT - Q_DISABLE_COPY(TemplateLoader); + Q_DISABLE_COPY(TemplateLoader) public: /** From 232a22489fb647d35b7c6bb5a6d39804727861fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 17 Jun 2016 20:03:06 +0200 Subject: [PATCH 029/118] Fixed setting default value to "minLevel" in log settings. --- YACReaderLibrary/server/startup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YACReaderLibrary/server/startup.cpp b/YACReaderLibrary/server/startup.cpp index 0c1252a3..65f5e7eb 100644 --- a/YACReaderLibrary/server/startup.cpp +++ b/YACReaderLibrary/server/startup.cpp @@ -46,7 +46,7 @@ void Startup::start() { mainLogSettings->setValue("maxBackups",1); if(mainLogSettings->value("minLevel").isNull()) - mainLogSettings->value("minLevel",QtCriticalMsg).toInt(); + mainLogSettings->setValue("minLevel",QtCriticalMsg).toInt(); Logger* logger=new FileLogger(mainLogSettings,10000,app); logger->installMsgHandler(); From 95cb74188069cb139e2dd88212807c0ac2309237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 17 Jun 2016 20:08:56 +0200 Subject: [PATCH 030/118] Fixing copy paste code error. --- YACReaderLibrary/server/startup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YACReaderLibrary/server/startup.cpp b/YACReaderLibrary/server/startup.cpp index 65f5e7eb..00f6901c 100644 --- a/YACReaderLibrary/server/startup.cpp +++ b/YACReaderLibrary/server/startup.cpp @@ -46,7 +46,7 @@ void Startup::start() { mainLogSettings->setValue("maxBackups",1); if(mainLogSettings->value("minLevel").isNull()) - mainLogSettings->setValue("minLevel",QtCriticalMsg).toInt(); + mainLogSettings->setValue("minLevel",QtCriticalMsg); Logger* logger=new FileLogger(mainLogSettings,10000,app); logger->installMsgHandler(); From d2b7487149e0ac2b03b9da00aacf0ba103f57775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 17 Jun 2016 20:11:32 +0200 Subject: [PATCH 031/118] Extracted default templateengine setting to Startup, so base code is no longer modified. --- .../server/lib/templateengine/templatecache.cpp | 2 +- .../server/lib/templateengine/templateloader.cpp | 12 +++--------- YACReaderLibrary/server/startup.cpp | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/YACReaderLibrary/server/lib/templateengine/templatecache.cpp b/YACReaderLibrary/server/lib/templateengine/templatecache.cpp index 109074f1..538aaf22 100644 --- a/YACReaderLibrary/server/lib/templateengine/templatecache.cpp +++ b/YACReaderLibrary/server/lib/templateengine/templatecache.cpp @@ -6,7 +6,7 @@ TemplateCache::TemplateCache(QSettings* settings, QObject* parent) :TemplateLoader(settings,parent) { - cache.setMaxCost(settings->value("cacheSize","160000").toInt());//este tamaño antes era 1000000 + cache.setMaxCost(settings->value("cacheSize","1000000").toInt()); cacheTimeout=settings->value("cacheTime","60000").toInt(); qDebug("TemplateCache: timeout=%i, size=%i",cacheTimeout,cache.maxCost()); } diff --git a/YACReaderLibrary/server/lib/templateengine/templateloader.cpp b/YACReaderLibrary/server/lib/templateengine/templateloader.cpp index 2abcabd1..fbc52efa 100644 --- a/YACReaderLibrary/server/lib/templateengine/templateloader.cpp +++ b/YACReaderLibrary/server/lib/templateengine/templateloader.cpp @@ -9,12 +9,11 @@ #include #include #include -#include TemplateLoader::TemplateLoader(QSettings* settings, QObject* parent) : QObject(parent) { - templatePath=settings->value("path","./server/templates").toString(); + templatePath=settings->value("path",".").toString(); // Convert relative path to absolute, based on the directory of the config file. #ifdef Q_OS_WIN32 if (QDir::isRelativePath(templatePath) && settings->format()!=QSettings::NativeFormat) @@ -22,13 +21,8 @@ TemplateLoader::TemplateLoader(QSettings* settings, QObject* parent) if (QDir::isRelativePath(templatePath)) #endif { -#if defined Q_OS_UNIX && !defined Q_OS_MAC - QFileInfo configFile(QString(DATADIR)+"/yacreader"); - templatePath=QFileInfo(QString(DATADIR)+"/yacreader",templatePath).absoluteFilePath(); -#else - QFileInfo configFile(QCoreApplication::applicationDirPath()); - templatePath=QFileInfo(QCoreApplication::applicationDirPath(),templatePath).absoluteFilePath(); -#endif + QFileInfo configFile(settings->fileName()); + templatePath=QFileInfo(configFile.absolutePath(),templatePath).absoluteFilePath(); } fileNameSuffix=settings->value("suffix",".tpl").toString(); QString encoding=settings->value("encoding").toString(); diff --git a/YACReaderLibrary/server/startup.cpp b/YACReaderLibrary/server/startup.cpp index 00f6901c..d6ce9fa8 100644 --- a/YACReaderLibrary/server/startup.cpp +++ b/YACReaderLibrary/server/startup.cpp @@ -54,6 +54,22 @@ void Startup::start() { // Configure template loader and cache QSettings* templateSettings=new QSettings(configFileName,QSettings::IniFormat,app); templateSettings->beginGroup("templates"); + + if(templateSettings->value("cacheSize").isNull()) + templateSettings->setValue("cacheSize","160000"); + + QString baseTemplatePath = QString("./server/templates"); + QString templatePath; + + #if defined Q_OS_UNIX && !defined Q_OS_MAC + templatePath=QFileInfo(QString(DATADIR)+"/yacreader",baseTemplatePath).absoluteFilePath(); + #else + templatePath=QFileInfo(QCoreApplication::applicationDirPath(),baseTemplatePath).absoluteFilePath(); + #endif + + if(templateSettings->value("path").isNull()) + templateSettings->setValue("path",templatePath); + Static::templateLoader=new TemplateCache(templateSettings,app); // Configure session store From e67d34a51110482c70d1a57911f85a0ec1480ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 21 Jun 2016 21:56:52 +0200 Subject: [PATCH 032/118] Added a new class for storing specific YACReader's http session data. --- YACReaderLibrary/server/server.pri | 6 +- .../server/yacreader_http_session.cpp | 165 ++++++++++++++++++ .../server/yacreader_http_session.h | 71 ++++++++ 3 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 YACReaderLibrary/server/yacreader_http_session.cpp create mode 100644 YACReaderLibrary/server/yacreader_http_session.h diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index b47413c6..4dc0e3ca 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -19,7 +19,8 @@ HEADERS += \ #v2 $$PWD/controllers/versioncontroller.h \ $$PWD/controllers/foldercontentcontroller.h \ - $$PWD/controllers/tagscontroller.h + $$PWD/controllers/tagscontroller.h \ + $$PWD/yacreader_http_session.h SOURCES += \ $$PWD/static.cpp \ @@ -39,7 +40,8 @@ SOURCES += \ #v2 $$PWD/controllers/versioncontroller.cpp \ $$PWD/controllers/foldercontentcontroller.cpp \ - $$PWD/controllers/tagscontroller.cpp + $$PWD/controllers/tagscontroller.cpp \ + $$PWD/yacreader_http_session.cpp include(lib/logging/logging.pri) include(lib/httpserver/httpserver.pri) diff --git a/YACReaderLibrary/server/yacreader_http_session.cpp b/YACReaderLibrary/server/yacreader_http_session.cpp new file mode 100644 index 00000000..7a9c533e --- /dev/null +++ b/YACReaderLibrary/server/yacreader_http_session.cpp @@ -0,0 +1,165 @@ +#include "yacreader_http_session.h" + +YACReaderHttpSession::YACReaderHttpSession(QObject *parent) + : QObject(parent), comic(nullptr), remoteComic(nullptr), comicId(0), remoteComicId(0) +{ + +} + +bool YACReaderHttpSession::isComicOnDevice(const QString & hash) +{ + return comicsOnDevice.contains(hash); +} + +bool YACReaderHttpSession::isComicDownloaded(const QString & hash) +{ + return downloadedComics.contains(hash); +} + +void YACReaderHttpSession::setComicOnDevice(const QString & hash) +{ + comicsOnDevice.insert(hash); +} + +void YACReaderHttpSession::setComicsOnDevice(const QSet & set) +{ + comicsOnDevice = set; +} + +void YACReaderHttpSession::setDownloadedComic(const QString & hash) +{ + downloadedComics.insert(hash); +} + +QSet YACReaderHttpSession::getComicsOnDevice() +{ + return comicsOnDevice ; +} + +QSet YACReaderHttpSession::getDownloadedComics() +{ + return downloadedComics ; +} + +void YACReaderHttpSession::clearComics() +{ + comicsOnDevice.clear(); + downloadedComics.clear(); +} +//current comic (import) +qulonglong YACReaderHttpSession::getCurrentComicId() +{ + return comicId; +} + +Comic* YACReaderHttpSession::getCurrentComic() +{ + comic; +} + +void YACReaderHttpSession::dismissCurrentComic() +{ + if(comic != nullptr) + { + comic->deleteLater(); + comic = nullptr; + } +} + +void YACReaderHttpSession::setCurrentComic(qulonglong id, Comic * comic) +{ + dismissCurrentComic(); + comicId = id; + comic = comic; +} + +//current comic (read) +qulonglong YACReaderHttpSession::getCurrentRemoteComicId() +{ + return remoteComicId ; +} + +Comic* YACReaderHttpSession::getCurrentRemoteComic() +{ + return remoteComic ; +} + +void YACReaderHttpSession::dismissCurrentRemoteComic() +{ + if(remoteComic != nullptr) + { + remoteComic->deleteLater(); + remoteComic = nullptr; + } +} + +void YACReaderHttpSession::setCurrentRemoteComic(qulonglong id, Comic * comic) +{ + dismissCurrentRemoteComic(); + remoteComicId = id; + remoteComic = comic; +} + +QString YACReaderHttpSession::getDeviceType() +{ + return device; +} + +QString YACReaderHttpSession::getDisplayType() +{ + return display; +} + +void YACReaderHttpSession::setDeviceType(const QString & device) +{ + //comicsOnDevice.clear(); //TODO crear un m�todo clear que limpie la sesi�n completamente + //downloadedComics.clear(); + device = device; +} + +void YACReaderHttpSession::setDisplayType(const QString & display) +{ + display = display; +} + +void YACReaderHttpSession::clearNavigationPath() +{ + navigationPath.clear(); +} + +QPair YACReaderHttpSession::popNavigationItem() +{ + if(navigationPath.isEmpty() == false) + return navigationPath.pop(); + return QPair(); +} + +QPair YACReaderHttpSession::topNavigationItem() +{ + if(navigationPath.isEmpty() == false) + return navigationPath.top(); + return QPair(); +} + +void YACReaderHttpSession::pushNavigationItem(const QPair &item) +{ + navigationPath.push(item); +} + +void YACReaderHttpSession::updateTopItem(const QPair &item) +{ + if(navigationPath.isEmpty() == false) + { + navigationPath.pop(); + navigationPath.push(item); + } + else + { + navigationPath.push(item); + } +} + +QStack > YACReaderHttpSession::getNavigationPath() +{ + return navigationPath; +} diff --git a/YACReaderLibrary/server/yacreader_http_session.h b/YACReaderLibrary/server/yacreader_http_session.h new file mode 100644 index 00000000..cc6a3131 --- /dev/null +++ b/YACReaderLibrary/server/yacreader_http_session.h @@ -0,0 +1,71 @@ +#ifndef YACREADERHTTPSESSION_H +#define YACREADERHTTPSESSION_H + +#include + +#include "comic.h" + + + +class YACReaderHttpSession : public QObject +{ + Q_OBJECT +public: + explicit YACReaderHttpSession(QObject *parent = 0); + + void setComicsOnDevice(const QSet & set); + void setComicOnDevice(const QString & hash); + void setDownloadedComic(const QString & hash); + bool isComicOnDevice(const QString & hash); + bool isComicDownloaded(const QString & hash); + QSet getComicsOnDevice(); + QSet getDownloadedComics(); + void clearComics(); + + //current comic (import) + qulonglong getCurrentComicId(); + Comic * getCurrentComic(); + void dismissCurrentComic(); + void setCurrentComic(qulonglong id, Comic * comic); + + //current comic (read) + qulonglong getCurrentRemoteComicId(); + Comic * getCurrentRemoteComic(); + void dismissCurrentRemoteComic(); + void setCurrentRemoteComic(qulonglong id, Comic * comic); + + //device identification + QString getDeviceType(); + QString getDisplayType(); + void setDeviceType(const QString & device); + void setDisplayType(const QString & display); + + void clearNavigationPath(); + QPair popNavigationItem(); + QPair topNavigationItem(); + void pushNavigationItem(const QPair & item); + void updateTopItem(const QPair & item); + + //TODO replace QPair by a custom class for storing folderId, page and folderName(save some DB accesses) + QStack > getNavigationPath(); + +signals: + +public slots: + +private: + QSet comicsOnDevice; + QSet downloadedComics; + + QString device; + QString display; + + qulonglong comicId; + qulonglong remoteComicId; + Comic * comic; + Comic * remoteComic; + + QStack > navigationPath; /* folder_id, page_number */ +}; + +#endif // YACREADERHTTPSESSION_H From 44d7c892c1ec7cb7b0e38488dacf52feed26bcb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 21 Jun 2016 22:00:48 +0200 Subject: [PATCH 033/118] Use YACReaderHttpSession in Static, so YACReader's session data will be available outside HttpSession. --- YACReaderLibrary/server/static.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/YACReaderLibrary/server/static.h b/YACReaderLibrary/server/static.h index 74abf55b..3425ba7b 100644 --- a/YACReaderLibrary/server/static.h +++ b/YACReaderLibrary/server/static.h @@ -11,6 +11,8 @@ #include "httpsessionstore.h" #include "staticfilecontroller.h" +#include "yacreader_http_session.h" + /** This class contains some static resources that are used by the application. */ @@ -51,6 +53,8 @@ public: /** Storage for session cookies */ static HttpSessionStore* sessionStore; + static QMap yacreaderSessionStore; + /** Controller for static files */ static StaticFileController* staticFileController; From 706e0921f3bf670cdc655ac8c78903afc42146f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Wed, 22 Jun 2016 19:09:04 +0200 Subject: [PATCH 034/118] Update httpserver to 1.6.5 --- .../lib/httpserver/httpconnectionhandler.cpp | 221 ++++++--- .../lib/httpserver/httpconnectionhandler.h | 45 +- .../httpserver/httpconnectionhandlerpool.cpp | 122 ++++- .../httpserver/httpconnectionhandlerpool.h | 42 +- .../server/lib/httpserver/httpcookie.cpp | 150 ++++-- .../server/lib/httpserver/httpcookie.h | 19 +- .../server/lib/httpserver/httpglobal.cpp | 2 +- .../server/lib/httpserver/httplistener.cpp | 69 ++- .../server/lib/httpserver/httplistener.h | 40 +- .../server/lib/httpserver/httprequest.cpp | 418 ++++++++++------ .../server/lib/httpserver/httprequest.h | 57 ++- .../lib/httpserver/httprequesthandler.cpp | 8 +- .../lib/httpserver/httprequesthandler.h | 10 +- .../server/lib/httpserver/httpresponse.cpp | 164 +++++-- .../server/lib/httpserver/httpresponse.h | 184 +++---- .../server/lib/httpserver/httpserver.pri | 35 +- .../server/lib/httpserver/httpsession.cpp | 453 +++++------------- .../server/lib/httpserver/httpsession.h | 213 +++----- .../lib/httpserver/httpsessionstore.cpp | 54 ++- .../server/lib/httpserver/httpsessionstore.h | 16 +- .../lib/httpserver/staticfilecontroller.cpp | 296 +++++------- .../lib/httpserver/staticfilecontroller.h | 71 ++- 22 files changed, 1491 insertions(+), 1198 deletions(-) diff --git a/YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.cpp b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.cpp index b1044b4a..d07b593e 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.cpp @@ -5,62 +5,107 @@ #include "httpconnectionhandler.h" #include "httpresponse.h" -#include -#include -HttpConnectionHandler::HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler) +HttpConnectionHandler::HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler, QSslConfiguration* sslConfiguration) : QThread() { Q_ASSERT(settings!=0); Q_ASSERT(requestHandler!=0); this->settings=settings; this->requestHandler=requestHandler; + this->sslConfiguration=sslConfiguration; currentRequest=0; - busy = false; + busy=false; + + // Create TCP or SSL socket + createSocket(); + // execute signals in my own thread moveToThread(this); - socket.moveToThread(this); + socket->moveToThread(this); readTimer.moveToThread(this); - connect(&socket, SIGNAL(readyRead()), SLOT(read())); - connect(&socket, SIGNAL(disconnected()), SLOT(disconnected())); + + // Connect signals + connect(socket, SIGNAL(readyRead()), SLOT(read())); + connect(socket, SIGNAL(disconnected()), SLOT(disconnected())); connect(&readTimer, SIGNAL(timeout()), SLOT(readTimeout())); readTimer.setSingleShot(true); + qDebug("HttpConnectionHandler (%p): constructed", this); this->start(); } -HttpConnectionHandler::~HttpConnectionHandler() { - socket.close(); +HttpConnectionHandler::~HttpConnectionHandler() +{ quit(); wait(); qDebug("HttpConnectionHandler (%p): destroyed", this); } -void HttpConnectionHandler::run() { - qDebug("HttpConnectionHandler (%p): thread started", this); - try { - exec(); - } - catch (...) { - qCritical("HttpConnectionHandler (%p): an uncatched exception occured in the thread",this); - } - qDebug("HttpConnectionHandler (%p): thread stopped", this); - // Change to the main thread, otherwise deleteLater() would not work - moveToThread(QCoreApplication::instance()->thread()); +void HttpConnectionHandler::createSocket() +{ + // If SSL is supported and configured, then create an instance of QSslSocket + #ifndef QT_NO_OPENSSL + if (sslConfiguration) + { + QSslSocket* sslSocket=new QSslSocket(); + sslSocket->setSslConfiguration(*sslConfiguration); + socket=sslSocket; + qDebug("HttpConnectionHandler (%p): SSL is enabled", this); + return; + } + #endif + // else create an instance of QTcpSocket + socket=new QTcpSocket(); } -void HttpConnectionHandler::handleConnection(tSocketDescriptor socketDescriptor) { +void HttpConnectionHandler::run() +{ + qDebug("HttpConnectionHandler (%p): thread started", this); + try + { + exec(); + } + catch (...) + { + qCritical("HttpConnectionHandler (%p): an uncatched exception occured in the thread",this); + } + socket->close(); + delete socket; + readTimer.stop(); + qDebug("HttpConnectionHandler (%p): thread stopped", this); +} + + +void HttpConnectionHandler::handleConnection(tSocketDescriptor socketDescriptor) +{ qDebug("HttpConnectionHandler (%p): handle new connection", this); busy = true; - Q_ASSERT(socket.isOpen()==false); // if not, then the handler is already busy + Q_ASSERT(socket->isOpen()==false); // if not, then the handler is already busy - if (!socket.setSocketDescriptor(socketDescriptor)) { - qCritical("HttpConnectionHandler (%p): cannot initialize socket: %s", this,qPrintable(socket.errorString())); + //UGLY workaround - we need to clear writebuffer before reusing this socket + //https://bugreports.qt-project.org/browse/QTBUG-28914 + socket->connectToHost("",0); + socket->abort(); + + if (!socket->setSocketDescriptor(socketDescriptor)) + { + qCritical("HttpConnectionHandler (%p): cannot initialize socket: %s", this,qPrintable(socket->errorString())); return; } + + #ifndef QT_NO_OPENSSL + // Switch on encryption, if SSL is configured + if (sslConfiguration) + { + qDebug("HttpConnectionHandler (%p): Starting encryption", this); + ((QSslSocket*)socket)->startServerEncryption(); + } + #endif + // Start timer for read timeout int readTimeout=settings->value("readTimeout",10000).toInt(); readTimer.start(readTimeout); @@ -70,49 +115,60 @@ void HttpConnectionHandler::handleConnection(tSocketDescriptor socketDescriptor) } -bool HttpConnectionHandler::isBusy() { +bool HttpConnectionHandler::isBusy() +{ return busy; } -void HttpConnectionHandler::setBusy() { +void HttpConnectionHandler::setBusy() +{ this->busy = true; } -void HttpConnectionHandler::readTimeout() { +void HttpConnectionHandler::readTimeout() +{ qDebug("HttpConnectionHandler (%p): read timeout occured",this); //Commented out because QWebView cannot handle this. - //socket.write("HTTP/1.1 408 request timeout\r\nConnection: close\r\n\r\n408 request timeout\r\n"); + //socket->write("HTTP/1.1 408 request timeout\r\nConnection: close\r\n\r\n408 request timeout\r\n"); - socket.disconnectFromHost(); + socket->flush(); + socket->disconnectFromHost(); delete currentRequest; currentRequest=0; } -void HttpConnectionHandler::disconnected() { +void HttpConnectionHandler::disconnected() +{ qDebug("HttpConnectionHandler (%p): disconnected", this); - socket.close(); + socket->close(); readTimer.stop(); busy = false; } -void HttpConnectionHandler::read() { - while (socket.bytesAvailable()) { -#ifdef SUPERVERBOSE - qDebug("HttpConnectionHandler (%p): read input",this); -#endif +void HttpConnectionHandler::read() +{ + // The loop adds support for HTTP pipelinig + while (socket->bytesAvailable()) + { + #ifdef SUPERVERBOSE + qDebug("HttpConnectionHandler (%p): read input",this); + #endif // Create new HttpRequest object if necessary - if (!currentRequest) { + if (!currentRequest) + { currentRequest=new HttpRequest(settings); } // Collect data for the request object - while (socket.bytesAvailable() && currentRequest->getStatus()!=HttpRequest::complete && currentRequest->getStatus()!=HttpRequest::abort) { + while (socket->bytesAvailable() && currentRequest->getStatus()!=HttpRequest::complete && currentRequest->getStatus()!=HttpRequest::abort) + { currentRequest->readFromSocket(socket); - if (currentRequest->getStatus()==HttpRequest::waitForBody) { + if (currentRequest->getStatus()==HttpRequest::waitForBody) + { // Restart timer for read timeout, otherwise it would // expire during large file uploads. int readTimeout=settings->value("readTimeout",10000).toInt(); @@ -121,50 +177,99 @@ void HttpConnectionHandler::read() { } // If the request is aborted, return error message and close the connection - if (currentRequest->getStatus()==HttpRequest::abort) { - socket.write("HTTP/1.1 413 entity too large\r\nConnection: close\r\n\r\n413 Entity too large\r\n"); - socket.disconnectFromHost(); + if (currentRequest->getStatus()==HttpRequest::abort) + { + socket->write("HTTP/1.1 413 entity too large\r\nConnection: close\r\n\r\n413 Entity too large\r\n"); + socket->flush(); + socket->disconnectFromHost(); delete currentRequest; currentRequest=0; return; } // If the request is complete, let the request mapper dispatch it - if (currentRequest->getStatus()==HttpRequest::complete) { + if (currentRequest->getStatus()==HttpRequest::complete) + { readTimer.stop(); qDebug("HttpConnectionHandler (%p): received request",this); - HttpResponse response(&socket); - //response.setHeader("Connection","close"); No funciona bien con NSURLConnection - try { + + // Copy the Connection:close header to the response + HttpResponse response(socket); + bool closeConnection=QString::compare(currentRequest->getHeader("Connection"),"close",Qt::CaseInsensitive)==0; + if (closeConnection) + { + response.setHeader("Connection","close"); + } + + // In case of HTTP 1.0 protocol add the Connection:close header. + // This ensures that the HttpResponse does not activate chunked mode, which is not spported by HTTP 1.0. + else + { + bool http1_0=QString::compare(currentRequest->getVersion(),"HTTP/1.0",Qt::CaseInsensitive)==0; + if (http1_0) + { + closeConnection=true; + response.setHeader("Connection","close"); + } + } + + // Call the request mapper + try + { requestHandler->service(*currentRequest, response); } - catch (...) { + catch (...) + { qCritical("HttpConnectionHandler (%p): An uncatched exception occured in the request handler",this); } // Finalize sending the response if not already done - if (!response.hasSentLastPart()) { + if (!response.hasSentLastPart()) + { response.write(QByteArray(),true); } - //socket.disconnectFromHost(); //CAMBIADO sólo se van a soportar conexiones NO persistentes + qDebug("HttpConnectionHandler (%p): finished request",this); - // Close the connection after delivering the response, if requested - if (QString::compare(currentRequest->getHeader("Connection"),"close",Qt::CaseInsensitive)==0) { - socket.disconnectFromHost(); + // Find out whether the connection must be closed + if (!closeConnection) + { + // Maybe the request handler or mapper added a Connection:close header in the meantime + bool closeResponse=QString::compare(response.getHeaders().value("Connection"),"close",Qt::CaseInsensitive)==0; + if (closeResponse==true) + { + closeConnection=true; + } + else + { + // If we have no Content-Length header and did not use chunked mode, then we have to close the + // connection to tell the HTTP client that the end of the response has been reached. + bool hasContentLength=response.getHeaders().contains("Content-Length"); + if (!hasContentLength) + { + bool hasChunkedMode=QString::compare(response.getHeaders().value("Transfer-Encoding"),"chunked",Qt::CaseInsensitive)==0; + if (!hasChunkedMode) + { + closeConnection=true; + } + } + } } - else { + + // Close the connection or prepare for the next request on the same connection. + if (closeConnection) + { + socket->flush(); + socket->disconnectFromHost(); + } + else + { // Start timer for next request int readTimeout=settings->value("readTimeout",10000).toInt(); readTimer.start(readTimeout); } - // Prepare for next request delete currentRequest; currentRequest=0; } - else - { - qDebug("HttpConnectionHandler (%p): received request",this); - } } } diff --git a/YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.h b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.h index 0e8b4483..79c0a786 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.h +++ b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandler.h @@ -6,13 +6,29 @@ #ifndef HTTPCONNECTIONHANDLER_H #define HTTPCONNECTIONHANDLER_H +#ifndef QT_NO_OPENSSL + #include +#endif #include #include #include #include +#include "httpglobal.h" #include "httprequest.h" #include "httprequesthandler.h" +/** Alias type definition, for compatibility to different Qt versions */ +#if QT_VERSION >= 0x050000 + typedef qintptr tSocketDescriptor; +#else + typedef int tSocketDescriptor; +#endif + +/** Alias for QSslConfiguration if OpenSSL is not supported */ +#ifdef QT_NO_OPENSSL + #define QSslConfiguration QObject +#endif + /** The connection handler accepts incoming connections and dispatches incoming requests to to a request mapper. Since HTTP clients can send multiple requests before waiting for the response, @@ -26,26 +42,21 @@

The readTimeout value defines the maximum time to wait for a complete HTTP request. - @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize + @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize. */ - -#if QT_VERSION >= 0x050000 - typedef qintptr tSocketDescriptor; -#else - typedef int tSocketDescriptor; -#endif - -class HttpConnectionHandler : public QThread { +class DECLSPEC HttpConnectionHandler : public QThread { Q_OBJECT Q_DISABLE_COPY(HttpConnectionHandler) + public: /** Constructor. @param settings Configuration settings of the HTTP webserver - @param requestHandler handler that will process each incomin HTTP request + @param requestHandler Handler that will process each incoming HTTP request + @param sslConfiguration SSL (HTTPS) will be used if not NULL */ - HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler); + HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler, QSslConfiguration* sslConfiguration=NULL); /** Destructor */ virtual ~HttpConnectionHandler(); @@ -61,8 +72,8 @@ private: /** Configuration settings */ QSettings* settings; - /** TCP socket of the current connection */ - QTcpSocket socket; + /** TCP socket of the current connection */ + QTcpSocket* socket; /** Time for read timeout detection */ QTimer readTimer; @@ -76,9 +87,15 @@ private: /** This shows the busy-state from a very early time */ bool busy; - /** Executes the htreads own event loop */ + /** Configuration for SSL */ + QSslConfiguration* sslConfiguration; + + /** Executes the threads own event loop */ void run(); + /** Create SSL or TCP socket */ + void createSocket(); + public slots: /** diff --git a/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.cpp b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.cpp index fbf70b49..6dd3d8c1 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.cpp @@ -1,3 +1,10 @@ +#ifndef QT_NO_OPENSSL + #include + #include + #include + #include +#endif +#include #include "httpconnectionhandlerpool.h" HttpConnectionHandlerPool::HttpConnectionHandlerPool(QSettings* settings, HttpRequestHandler* requestHandler) @@ -6,35 +13,46 @@ HttpConnectionHandlerPool::HttpConnectionHandlerPool(QSettings* settings, HttpRe Q_ASSERT(settings!=0); this->settings=settings; this->requestHandler=requestHandler; - cleanupTimer.start(settings->value("cleanupInterval",10000).toInt()); + this->sslConfiguration=NULL; + loadSslConfig(); + cleanupTimer.start(settings->value("cleanupInterval",1000).toInt()); connect(&cleanupTimer, SIGNAL(timeout()), SLOT(cleanup())); } -HttpConnectionHandlerPool::~HttpConnectionHandlerPool() { - foreach(HttpConnectionHandler* handler, pool) { - connect(handler,SIGNAL(finished()),handler,SLOT(deleteLater())); - handler->quit(); +HttpConnectionHandlerPool::~HttpConnectionHandlerPool() +{ + // delete all connection handlers and wait until their threads are closed + foreach(HttpConnectionHandler* handler, pool) + { + delete handler; } + delete sslConfiguration; + qDebug("HttpConnectionHandlerPool (%p): destroyed", this); } -HttpConnectionHandler* HttpConnectionHandlerPool::getConnectionHandler() { +HttpConnectionHandler* HttpConnectionHandlerPool::getConnectionHandler() +{ HttpConnectionHandler* freeHandler=0; mutex.lock(); // find a free handler in pool - foreach(HttpConnectionHandler* handler, pool) { - if (!handler->isBusy()) { + foreach(HttpConnectionHandler* handler, pool) + { + if (!handler->isBusy()) + { freeHandler=handler; freeHandler->setBusy(); break; } } // create a new handler, if necessary - if (!freeHandler) { - int maxConnectionHandlers=settings->value("maxThreads",1000).toInt(); - if (pool.count()value("maxThreads",100).toInt(); + if (pool.count()setBusy(); pool.append(freeHandler); } @@ -44,21 +62,85 @@ HttpConnectionHandler* HttpConnectionHandlerPool::getConnectionHandler() { } - -void HttpConnectionHandlerPool::cleanup() { - int maxIdleHandlers=settings->value("minThreads",50).toInt(); +void HttpConnectionHandlerPool::cleanup() +{ + int maxIdleHandlers=settings->value("minThreads",1).toInt(); int idleCounter=0; mutex.lock(); - foreach(HttpConnectionHandler* handler, pool) { - if (!handler->isBusy()) { - if (++idleCounter > maxIdleHandlers) { + foreach(HttpConnectionHandler* handler, pool) + { + if (!handler->isBusy()) + { + if (++idleCounter > maxIdleHandlers) + { + delete handler; pool.removeOne(handler); qDebug("HttpConnectionHandlerPool: Removed connection handler (%p), pool size is now %i",handler,pool.size()); - connect(handler,SIGNAL(finished()),handler,SLOT(deleteLater())); - handler->quit(); break; // remove only one handler in each interval } } } mutex.unlock(); } + + +void HttpConnectionHandlerPool::loadSslConfig() +{ + // If certificate and key files are configured, then load them + QString sslKeyFileName=settings->value("sslKeyFile","").toString(); + QString sslCertFileName=settings->value("sslCertFile","").toString(); + if (!sslKeyFileName.isEmpty() && !sslCertFileName.isEmpty()) + { + #ifdef QT_NO_OPENSSL + qWarning("HttpConnectionHandlerPool: SSL is not supported"); + #else + // Convert relative fileNames to absolute, based on the directory of the config file. + QFileInfo configFile(settings->fileName()); + #ifdef Q_OS_WIN32 + if (QDir::isRelativePath(sslKeyFileName) && settings->format()!=QSettings::NativeFormat) + #else + if (QDir::isRelativePath(sslKeyFileName)) + #endif + { + sslKeyFileName=QFileInfo(configFile.absolutePath(),sslKeyFileName).absoluteFilePath(); + } + #ifdef Q_OS_WIN32 + if (QDir::isRelativePath(sslCertFileName) && settings->format()!=QSettings::NativeFormat) + #else + if (QDir::isRelativePath(sslCertFileName)) + #endif + { + sslCertFileName=QFileInfo(configFile.absolutePath(),sslCertFileName).absoluteFilePath(); + } + + // Load the SSL certificate + QFile certFile(sslCertFileName); + if (!certFile.open(QIODevice::ReadOnly)) + { + qCritical("HttpConnectionHandlerPool: cannot open sslCertFile %s", qPrintable(sslCertFileName)); + return; + } + QSslCertificate certificate(&certFile, QSsl::Pem); + certFile.close(); + + // Load the key file + QFile keyFile(sslKeyFileName); + if (!keyFile.open(QIODevice::ReadOnly)) + { + qCritical("HttpConnectionHandlerPool: cannot open sslKeyFile %s", qPrintable(sslKeyFileName)); + return; + } + QSslKey sslKey(&keyFile, QSsl::Rsa, QSsl::Pem); + keyFile.close(); + + // Create the SSL configuration + sslConfiguration=new QSslConfiguration(); + sslConfiguration->setLocalCertificate(certificate); + sslConfiguration->setPrivateKey(sslKey); + sslConfiguration->setPeerVerifyMode(QSslSocket::VerifyNone); + sslConfiguration->setProtocol(QSsl::TlsV1SslV3); + + qDebug("HttpConnectionHandlerPool: SSL settings loaded"); + #endif + } +} diff --git a/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.h b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.h index 2dc94338..a9b9908b 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.h +++ b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.h @@ -5,29 +5,45 @@ #include #include #include +#include "httpglobal.h" #include "httpconnectionhandler.h" /** - Pool of http connection handlers. Connection handlers are created on demand and idle handlers are - cleaned up in regular time intervals. + Pool of http connection handlers. The size of the pool grows and + shrinks on demand.

Example for the required configuration settings:

-  minThreads=1
+  minThreads=4
   maxThreads=100
-  cleanupInterval=1000
+  cleanupInterval=60000
+  readTimeout=60000
+  ;sslKeyFile=ssl/my.key
+  ;sslCertFile=ssl/my.cert
   maxRequestSize=16000
   maxMultiPartSize=1000000
   
- The pool is empty initially and grows with the number of concurrent - connections. A timer removes one idle connection handler at each - interval, but it leaves some spare handlers in memory to improve - performance. - @see HttpConnectionHandler for description of config settings readTimeout + After server start, the size of the thread pool is always 0. Threads + are started on demand when requests come in. The cleanup timer reduces + the number of idle threads slowly by closing one thread in each interval. + But the configured minimum number of threads are kept running. +

+ For SSL support, you need an OpenSSL certificate file and a key file. + Both can be created with the command +

+      openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout my.key -out my.cert
+  
+

+ Visit http://slproweb.com/products/Win32OpenSSL.html to download the Light version of OpenSSL for Windows. +

+ Please note that a listener with SSL settings can only handle HTTPS protocol. To + support both HTTP and HTTPS simultaneously, you need to start two listeners on different ports - + one with SLL and one without SSL. + @see HttpConnectionHandler for description of the readTimeout @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize */ -class HttpConnectionHandlerPool : public QObject { +class DECLSPEC HttpConnectionHandlerPool : public QObject { Q_OBJECT Q_DISABLE_COPY(HttpConnectionHandlerPool) public: @@ -63,6 +79,12 @@ private: /** Used to synchronize threads */ QMutex mutex; + /** The SSL configuration (certificate, key and other settings) */ + QSslConfiguration* sslConfiguration; + + /** Load SSL configuration */ + void loadSslConfig(); + private slots: /** Received from the clean-up timer. */ diff --git a/YACReaderLibrary/server/lib/httpserver/httpcookie.cpp b/YACReaderLibrary/server/lib/httpserver/httpcookie.cpp index 3f5be929..ce0f5207 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpcookie.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httpcookie.cpp @@ -5,13 +5,15 @@ #include "httpcookie.h" -HttpCookie::HttpCookie() { +HttpCookie::HttpCookie() +{ version=1; maxAge=0; secure=false; } -HttpCookie::HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path, const QByteArray comment, const QByteArray domain, const bool secure) { +HttpCookie::HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path, const QByteArray comment, const QByteArray domain, const bool secure, const bool httpOnly) +{ this->name=name; this->value=value; this->maxAge=maxAge; @@ -19,171 +21,230 @@ HttpCookie::HttpCookie(const QByteArray name, const QByteArray value, const int this->comment=comment; this->domain=domain; this->secure=secure; + this->httpOnly=httpOnly; this->version=1; } -HttpCookie::HttpCookie(const QByteArray source) { +HttpCookie::HttpCookie(const QByteArray source) +{ version=1; maxAge=0; secure=false; QList list=splitCSV(source); - foreach(QByteArray part, list) { + foreach(QByteArray part, list) + { // Split the part into name and value QByteArray name; QByteArray value; int posi=part.indexOf('='); - if (posi) { + if (posi) + { name=part.left(posi).trimmed(); value=part.mid(posi+1).trimmed(); } - else { + else + { name=part.trimmed(); value=""; } // Set fields - if (name=="Comment") { + if (name=="Comment") + { comment=value; } - else if (name=="Domain") { + else if (name=="Domain") + { domain=value; } - else if (name=="Max-Age") { + else if (name=="Max-Age") + { maxAge=value.toInt(); } - else if (name=="Path") { + else if (name=="Path") + { path=value; } - else if (name=="Secure") { + else if (name=="Secure") + { secure=true; } - else if (name=="Version") { + else if (name=="HttpOnly") + { + httpOnly=true; + } + else if (name=="Version") + { version=value.toInt(); } else { - if (this->name.isEmpty()) { + if (this->name.isEmpty()) + { this->name=name; this->value=value; } - else { + else + { qWarning("HttpCookie: Ignoring unknown %s=%s",name.data(),value.data()); } } } } -QByteArray HttpCookie::toByteArray() const { +QByteArray HttpCookie::toByteArray() const +{ QByteArray buffer(name); buffer.append('='); buffer.append(value); - if (!comment.isEmpty()) { + if (!comment.isEmpty()) + { buffer.append("; Comment="); buffer.append(comment); } - if (!domain.isEmpty()) { + if (!domain.isEmpty()) + { buffer.append("; Domain="); buffer.append(domain); } - if (maxAge!=0) { + if (maxAge!=0) + { buffer.append("; Max-Age="); buffer.append(QByteArray::number(maxAge)); } - if (!path.isEmpty()) { + if (!path.isEmpty()) + { buffer.append("; Path="); buffer.append(path); } if (secure) { buffer.append("; Secure"); } + if (httpOnly) { + buffer.append("; HttpOnly"); + } buffer.append("; Version="); buffer.append(QByteArray::number(version)); return buffer; } -void HttpCookie::setName(const QByteArray name){ +void HttpCookie::setName(const QByteArray name) +{ this->name=name; } -void HttpCookie::setValue(const QByteArray value){ +void HttpCookie::setValue(const QByteArray value) +{ this->value=value; } -void HttpCookie::setComment(const QByteArray comment){ +void HttpCookie::setComment(const QByteArray comment) +{ this->comment=comment; } -void HttpCookie::setDomain(const QByteArray domain){ +void HttpCookie::setDomain(const QByteArray domain) +{ this->domain=domain; } -void HttpCookie::setMaxAge(const int maxAge){ +void HttpCookie::setMaxAge(const int maxAge) +{ this->maxAge=maxAge; } -void HttpCookie::setPath(const QByteArray path){ +void HttpCookie::setPath(const QByteArray path) +{ this->path=path; } -void HttpCookie::setSecure(const bool secure){ +void HttpCookie::setSecure(const bool secure) +{ this->secure=secure; } -QByteArray HttpCookie::getName() const { +void HttpCookie::setHttpOnly(const bool httpOnly) +{ + this->httpOnly=httpOnly; +} + +QByteArray HttpCookie::getName() const +{ return name; } -QByteArray HttpCookie::getValue() const { +QByteArray HttpCookie::getValue() const +{ return value; } -QByteArray HttpCookie::getComment() const { +QByteArray HttpCookie::getComment() const +{ return comment; } -QByteArray HttpCookie::getDomain() const { +QByteArray HttpCookie::getDomain() const +{ return domain; } -int HttpCookie::getMaxAge() const { +int HttpCookie::getMaxAge() const +{ return maxAge; } -QByteArray HttpCookie::getPath() const { +QByteArray HttpCookie::getPath() const +{ return path; } -bool HttpCookie::getSecure() const { +bool HttpCookie::getSecure() const +{ return secure; } -int HttpCookie::getVersion() const { +bool HttpCookie::getHttpOnly() const +{ + return httpOnly; +} + +int HttpCookie::getVersion() const +{ return version; } -QList HttpCookie::splitCSV(const QByteArray source) { +QList HttpCookie::splitCSV(const QByteArray source) +{ bool inString=false; QList list; QByteArray buffer; - for (int i=0; i HttpCookie::splitCSV(const QByteArray source) { } } QByteArray trimmed=buffer.trimmed(); - if (!trimmed.isEmpty()) { + if (!trimmed.isEmpty()) + { list.append(trimmed); } return list; diff --git a/YACReaderLibrary/server/lib/httpserver/httpcookie.h b/YACReaderLibrary/server/lib/httpserver/httpcookie.h index 88688f75..d4857fd9 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpcookie.h +++ b/YACReaderLibrary/server/lib/httpserver/httpcookie.h @@ -8,6 +8,7 @@ #include #include +#include "httpglobal.h" /** HTTP cookie as defined in RFC 2109. This class can also parse @@ -15,7 +16,7 @@ 2109. */ -class HttpCookie +class DECLSPEC HttpCookie { public: @@ -30,9 +31,10 @@ public: @param path Path for that the cookie will be sent, default="/" which means the whole domain @param comment Optional comment, may be displayed by the web browser somewhere @param domain Optional domain for that the cookie will be sent. Defaults to the current domain - @param secure If true, the cookie will only be sent on secure connections + @param secure If true, the cookie will be sent by the browser to the server only on secure connections + @param httpOnly If true, the browser does not allow client-side scripts to access the cookie */ - HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path="/", const QByteArray comment=QByteArray(), const QByteArray domain=QByteArray(), const bool secure=false); + HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path="/", const QByteArray comment=QByteArray(), const QByteArray domain=QByteArray(), const bool secure=false, const bool httpOnly=false); /** Create a cookie from a string. @@ -40,7 +42,7 @@ public: */ HttpCookie(const QByteArray source); - /** Convert this cookie to a string that may be used in a Set-Cookie2 header. */ + /** Convert this cookie to a string that may be used in a Set-Cookie header. */ QByteArray toByteArray() const ; /** @@ -67,9 +69,12 @@ public: /** Set the path for that the cookie will be sent, default="/" which means the whole domain */ void setPath(const QByteArray path); - /** Set secure mode, so that the cokkie will only be sent on secure connections */ + /** Set secure mode, so that the cookie will be sent by the browser to the server only on secure connections */ void setSecure(const bool secure); + /** Set secure mode, so that he browser does not allow client-side scripts to access the cookie */ + void setHttpOnly(const bool httpOnly); + /** Get the name of this cookie */ QByteArray getName() const; @@ -91,6 +96,9 @@ public: /** Get the secure flag of this cookie */ bool getSecure() const; + /** Get the httpOnly of this cookie */ + bool getHttpOnly() const; + /** Returns always 1 */ int getVersion() const; @@ -103,6 +111,7 @@ private: int maxAge; QByteArray path; bool secure; + bool httpOnly; int version; }; diff --git a/YACReaderLibrary/server/lib/httpserver/httpglobal.cpp b/YACReaderLibrary/server/lib/httpserver/httpglobal.cpp index dfdf9cd4..088dab14 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpglobal.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httpglobal.cpp @@ -2,6 +2,6 @@ const char* getQtWebAppLibVersion() { - return "1.5.3"; + return "1.6.5"; } diff --git a/YACReaderLibrary/server/lib/httpserver/httplistener.cpp b/YACReaderLibrary/server/lib/httpserver/httplistener.cpp index b79db686..7314ec74 100644 --- a/YACReaderLibrary/server/lib/httpserver/httplistener.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httplistener.cpp @@ -12,53 +12,76 @@ HttpListener::HttpListener(QSettings* settings, HttpRequestHandler* requestHandl : QTcpServer(parent) { Q_ASSERT(settings!=0); + Q_ASSERT(requestHandler!=0); + pool=NULL; + this->settings=settings; + this->requestHandler=requestHandler; // Reqister type of socketDescriptor for signal/slot handling qRegisterMetaType("tSocketDescriptor"); - // Create connection handler pool - this->settings=settings; - pool=new HttpConnectionHandlerPool(settings,requestHandler); // Start listening - int port=settings->value("port",8080).toInt(); - listen(QHostAddress::Any, port); - //Cambiado - int i = 0; - while (!isListening() && i < 1000) { - listen(QHostAddress::Any, (rand() % 45535)+20000); - i++; + listen(); +} + + +HttpListener::~HttpListener() +{ + close(); + qDebug("HttpListener: destroyed"); +} + + +void HttpListener::listen() +{ + if (!pool) + { + pool=new HttpConnectionHandlerPool(settings,requestHandler); + } + QString host = settings->value("host").toString(); + int port=settings->value("port").toInt(); + QTcpServer::listen(host.isEmpty() ? QHostAddress::Any : QHostAddress(host), port); + if (!isListening()) + { + qCritical("HttpListener: Cannot bind on port %i: %s",port,qPrintable(errorString())); } - if(!isListening()) - { - qCritical("HttpListener: Cannot bind on port %i: %s",port,qPrintable(errorString())); - } else { qDebug("HttpListener: Listening on port %i",port); } } -HttpListener::~HttpListener() { - close(); + +void HttpListener::close() { + QTcpServer::close(); qDebug("HttpListener: closed"); - delete pool; - qDebug("HttpListener: destroyed"); + if (pool) { + delete pool; + pool=NULL; + } } void HttpListener::incomingConnection(tSocketDescriptor socketDescriptor) { #ifdef SUPERVERBOSE qDebug("HttpListener: New connection"); #endif - HttpConnectionHandler* freeHandler=pool->getConnectionHandler(); + + HttpConnectionHandler* freeHandler=NULL; + if (pool) + { + freeHandler=pool->getConnectionHandler(); + } // Let the handler process the new connection. - if (freeHandler) { + if (freeHandler) + { // The descriptor is passed via signal/slot because the handler lives in another - // thread and cannot open the socket when called by another thread. + // thread and cannot open the socket when directly called by another thread. connect(this,SIGNAL(handleConnection(tSocketDescriptor)),freeHandler,SLOT(handleConnection(tSocketDescriptor))); emit handleConnection(socketDescriptor); disconnect(this,SIGNAL(handleConnection(tSocketDescriptor)),freeHandler,SLOT(handleConnection(tSocketDescriptor))); } - else { + else + { // Reject the connection - qDebug("HttpListener: Too many connections"); + qDebug("HttpListener: Too many incoming connections"); QTcpSocket* socket=new QTcpSocket(this); socket->setSocketDescriptor(socketDescriptor); connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); diff --git a/YACReaderLibrary/server/lib/httpserver/httplistener.h b/YACReaderLibrary/server/lib/httpserver/httplistener.h index 6ae5d75c..a88176d0 100644 --- a/YACReaderLibrary/server/lib/httpserver/httplistener.h +++ b/YACReaderLibrary/server/lib/httpserver/httplistener.h @@ -3,12 +3,13 @@ @author Stefan Frings */ -#ifndef LISTENER_H -#define LISTENER_H +#ifndef HTTPLISTENER_H +#define HTTPLISTENER_H #include #include #include +#include "httpglobal.h" #include "httpconnectionhandler.h" #include "httpconnectionhandlerpool.h" #include "httprequesthandler.h" @@ -19,36 +20,54 @@

Example for the required settings in the config file:

+  ;host=192.168.0.100
   port=8080
   minThreads=1
   maxThreads=10
   cleanupInterval=1000
   readTimeout=60000
+  ;sslKeyFile=ssl/my.key
+  ;sslCertFile=ssl/my.cert
   maxRequestSize=16000
   maxMultiPartSize=1000000
   
- The port number is the incoming TCP port that this listener listens to. - @see HttpConnectionHandlerPool for description of config settings minThreads, maxThreads and cleanupInterval - @see HttpConnectionHandler for description of config settings readTimeout + The optional host parameter binds the listener to one network interface. + The listener handles all network interfaces if no host is configured. + The port number specifies the incoming TCP port that this listener listens to. + @see HttpConnectionHandlerPool for description of config settings minThreads, maxThreads, cleanupInterval and ssl settings + @see HttpConnectionHandler for description of the readTimeout @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize */ -class HttpListener : public QTcpServer { +class DECLSPEC HttpListener : public QTcpServer { Q_OBJECT Q_DISABLE_COPY(HttpListener) public: /** Constructor. + Creates a connection pool and starts listening on the configured host and port. @param settings Configuration settings for the HTTP server. Must not be 0. @param requestHandler Processes each received HTTP request, usually by dispatching to controller classes. @param parent Parent object. + @warning Ensure to close or delete the listener before deleting the request handler. */ - HttpListener(QSettings* settings, HttpRequestHandler* requestHandler, QObject* parent = 0); + HttpListener(QSettings* settings, HttpRequestHandler* requestHandler, QObject* parent = NULL); /** Destructor */ virtual ~HttpListener(); + /** + Restart listeing after close(). + */ + void listen(); + + /** + Closes the listener, waits until all pending requests are processed, + then closes the connection pool. + */ + void close(); + protected: /** Serves new incoming connection requests */ @@ -59,13 +78,16 @@ private: /** Configuration settings for the HTTP server */ QSettings* settings; + /** Point to the reuqest handler which processes all HTTP requests */ + HttpRequestHandler* requestHandler; + /** Pool of connection handlers */ HttpConnectionHandlerPool* pool; signals: /** - Emitted when the connection handler shall process a new incoming onnection. + Sent to the connection handler to process a new incoming connection. @param socketDescriptor references the accepted connection. */ @@ -73,4 +95,4 @@ signals: }; -#endif // LISTENER_H +#endif // HTTPLISTENER_H diff --git a/YACReaderLibrary/server/lib/httpserver/httprequest.cpp b/YACReaderLibrary/server/lib/httpserver/httprequest.cpp index 8ed427b5..0939bd47 100644 --- a/YACReaderLibrary/server/lib/httpserver/httprequest.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httprequest.cpp @@ -8,144 +8,189 @@ #include #include "httpcookie.h" -HttpRequest::HttpRequest(QSettings* settings) { +HttpRequest::HttpRequest(QSettings* settings) +{ status=waitForRequest; currentSize=0; expectedBodySize=0; - maxSize=settings->value("maxRequestSize","32000000").toInt(); - maxMultiPartSize=settings->value("maxMultiPartSize","32000000").toInt(); + maxSize=settings->value("maxRequestSize","16000").toInt(); + maxMultiPartSize=settings->value("maxMultiPartSize","1000000").toInt(); } -void HttpRequest::readRequest(QTcpSocket& socket) { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: read request"); -#endif +void HttpRequest::readRequest(QTcpSocket* socket) +{ + #ifdef SUPERVERBOSE + qDebug("HttpRequest: read request"); + #endif int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow - QByteArray newData=socket.readLine(toRead).trimmed(); - currentSize+=newData.size(); - if (!newData.isEmpty()) { + lineBuffer.append(socket->readLine(toRead)); + currentSize+=lineBuffer.size(); + if (!lineBuffer.contains('\r') && !lineBuffer.contains('\n')) + { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: collecting more parts until line break"); + #endif + return; + } + QByteArray newData=lineBuffer.trimmed(); + lineBuffer.clear(); + if (!newData.isEmpty()) + { QList list=newData.split(' '); - if (list.count()!=3 || !list.at(2).contains("HTTP")) { + if (list.count()!=3 || !list.at(2).contains("HTTP")) + { qWarning("HttpRequest: received broken HTTP request, invalid first line"); status=abort; } else { - method=list.at(0); + method=list.at(0).trimmed(); path=list.at(1); version=list.at(2); + peerAddress = socket->peerAddress(); status=waitForHeader; } } } -void HttpRequest::readHeader(QTcpSocket& socket) { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: read header"); -#endif +void HttpRequest::readHeader(QTcpSocket* socket) +{ + #ifdef SUPERVERBOSE + qDebug("HttpRequest: read header"); + #endif int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow - QByteArray newData=socket.readLine(toRead).trimmed(); - currentSize+=newData.size(); + lineBuffer.append(socket->readLine(toRead)); + currentSize+=lineBuffer.size(); + if (!lineBuffer.contains('\r') && !lineBuffer.contains('\n')) + { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: collecting more parts until line break"); + #endif + return; + } + QByteArray newData=lineBuffer.trimmed(); + lineBuffer.clear(); int colon=newData.indexOf(':'); - if (colon>0) { + if (colon>0) + { // Received a line with a colon - a header - currentHeader=newData.left(colon); + currentHeader=newData.left(colon).toLower(); QByteArray value=newData.mid(colon+1).trimmed(); headers.insert(currentHeader,value); -#ifdef SUPERVERBOSE - qDebug("HttpRequest: received header %s: %s",currentHeader.data(),value.data()); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: received header %s: %s",currentHeader.data(),value.data()); + #endif } - else if (!newData.isEmpty()) { + else if (!newData.isEmpty()) + { // received another line - belongs to the previous header -#ifdef SUPERVERBOSE - qDebug("HttpRequest: read additional line of header"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: read additional line of header"); + #endif // Received additional line of previous header if (headers.contains(currentHeader)) { headers.insert(currentHeader,headers.value(currentHeader)+" "+newData); } } - else { + else + { // received an empty line - end of headers reached -#ifdef SUPERVERBOSE - qDebug("HttpRequest: headers completed"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: headers completed"); + #endif // Empty line received, that means all headers have been received // Check for multipart/form-data - QByteArray contentType=headers.value("Content-Type"); - if (contentType.startsWith("multipart/form-data")) { + QByteArray contentType=headers.value("content-type"); + if (contentType.startsWith("multipart/form-data")) + { int posi=contentType.indexOf("boundary="); if (posi>=0) { boundary=contentType.mid(posi+9); + if (boundary.startsWith('"') && boundary.endsWith('"')) + { + boundary = boundary.mid(1,boundary.length()-2); + } } } - QByteArray contentLength=getHeader("Content-Length"); - if (!contentLength.isEmpty()) { + QByteArray contentLength=headers.value("content-length"); + if (!contentLength.isEmpty()) + { expectedBodySize=contentLength.toInt(); } - if (expectedBodySize==0) { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: expect no body"); -#endif + if (expectedBodySize==0) + { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: expect no body"); + #endif status=complete; } - else if (boundary.isEmpty() && expectedBodySize+currentSize>maxSize) { + else if (boundary.isEmpty() && expectedBodySize+currentSize>maxSize) + { qWarning("HttpRequest: expected body is too large"); status=abort; } - else if (!boundary.isEmpty() && expectedBodySize>maxMultiPartSize) { + else if (!boundary.isEmpty() && expectedBodySize>maxMultiPartSize) + { qWarning("HttpRequest: expected multipart body is too large"); status=abort; } else { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: expect %i bytes body",expectedBodySize); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: expect %i bytes body",expectedBodySize); + #endif status=waitForBody; } } } -void HttpRequest::readBody(QTcpSocket& socket) { +void HttpRequest::readBody(QTcpSocket* socket) +{ Q_ASSERT(expectedBodySize!=0); - if (boundary.isEmpty()) { + if (boundary.isEmpty()) + { // normal body, no multipart -#ifdef SUPERVERBOSE - qDebug("HttpRequest: receive body"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: receive body"); + #endif int toRead=expectedBodySize-bodyData.size(); - QByteArray newData=socket.read(toRead); + QByteArray newData=socket->read(toRead); currentSize+=newData.size(); bodyData.append(newData); - if (bodyData.size()>=expectedBodySize) { + if (bodyData.size()>=expectedBodySize) + { status=complete; } } - else { + else + { // multipart body, store into temp file -#ifdef SUPERVERBOSE - qDebug("HttpRequest: receiving multipart body"); -#endif - if (!tempFile.isOpen()) { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: receiving multipart body"); + #endif + if (!tempFile.isOpen()) + { tempFile.open(); } // Transfer data in 64kb blocks int fileSize=tempFile.size(); int toRead=expectedBodySize-fileSize; - if (toRead>65536) { + if (toRead>65536) + { toRead=65536; } - fileSize+=tempFile.write(socket.read(toRead)); - if (fileSize>=maxMultiPartSize) { + fileSize+=tempFile.write(socket->read(toRead)); + if (fileSize>=maxMultiPartSize) + { qWarning("HttpRequest: received too many multipart bytes"); status=abort; } - else if (fileSize>=expectedBodySize) { -#ifdef SUPERVERBOSE + else if (fileSize>=expectedBodySize) + { + #ifdef SUPERVERBOSE qDebug("HttpRequest: received whole multipart body"); -#endif + #endif tempFile.flush(); - if (tempFile.error()) { + if (tempFile.error()) + { qCritical("HttpRequest: Error writing temp file for multipart body"); } parseMultiPartFile(); @@ -155,87 +200,106 @@ void HttpRequest::readBody(QTcpSocket& socket) { } } -void HttpRequest::decodeRequestParams() { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: extract and decode request parameters"); -#endif +void HttpRequest::decodeRequestParams() +{ + #ifdef SUPERVERBOSE + qDebug("HttpRequest: extract and decode request parameters"); + #endif // Get URL parameters QByteArray rawParameters; int questionMark=path.indexOf('?'); - if (questionMark>=0) { + if (questionMark>=0) + { rawParameters=path.mid(questionMark+1); path=path.left(questionMark); } // Get request body parameters - QByteArray contentType=headers.value("Content-Type"); - if (!bodyData.isEmpty() && (contentType.isEmpty() || contentType.startsWith("application/x-www-form-urlencoded"))) { - if (rawParameters.isEmpty()) { + QByteArray contentType=headers.value("content-type"); + if (!bodyData.isEmpty() && (contentType.isEmpty() || contentType.startsWith("application/x-www-form-urlencoded"))) + { + if (!rawParameters.isEmpty()) + { rawParameters.append('&'); rawParameters.append(bodyData); } - else { + else + { rawParameters=bodyData; } } // Split the parameters into pairs of value and name QList list=rawParameters.split('&'); - foreach (QByteArray part, list) { + foreach (QByteArray part, list) + { int equalsChar=part.indexOf('='); - if (equalsChar>=0) { + if (equalsChar>=0) + { QByteArray name=part.left(equalsChar).trimmed(); QByteArray value=part.mid(equalsChar+1).trimmed(); parameters.insert(urlDecode(name),urlDecode(value)); } - else if (!part.isEmpty()){ + else if (!part.isEmpty()) + { // Name without value parameters.insert(urlDecode(part),""); } } } -void HttpRequest::extractCookies() { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: extract cookies"); -#endif - foreach(QByteArray cookieStr, headers.values("Cookie")) { +void HttpRequest::extractCookies() +{ + #ifdef SUPERVERBOSE + qDebug("HttpRequest: extract cookies"); + #endif + foreach(QByteArray cookieStr, headers.values("cookie")) + { QList list=HttpCookie::splitCSV(cookieStr); - foreach(QByteArray part, list) { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: found cookie %s",part.data()); -#endif // Split the part into name and value + foreach(QByteArray part, list) + { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: found cookie %s",part.data()); + #endif // Split the part into name and value QByteArray name; QByteArray value; int posi=part.indexOf('='); - if (posi) { + if (posi) + { name=part.left(posi).trimmed(); value=part.mid(posi+1).trimmed(); } - else { + else + { name=part.trimmed(); value=""; } cookies.insert(name,value); } } - headers.remove("Cookie"); + headers.remove("cookie"); } -void HttpRequest::readFromSocket(QTcpSocket& socket) { +void HttpRequest::readFromSocket(QTcpSocket* socket) +{ Q_ASSERT(status!=complete); - if (status==waitForRequest) { + if (status==waitForRequest) + { readRequest(socket); } - else if (status==waitForHeader) { + else if (status==waitForHeader) + { readHeader(socket); } - else if (status==waitForBody) { + else if (status==waitForBody) + { readBody(socket); } - if (currentSize>maxSize) { + if ((boundary.isEmpty() && currentSize>maxSize) || (!boundary.isEmpty() && currentSize>maxMultiPartSize)) + { qWarning("HttpRequest: received too many bytes"); status=abort; } - if (status==complete) { + if (status==complete) + { // Extract and decode request parameters from url and body decodeRequestParams(); // Extract cookies from headers @@ -244,62 +308,82 @@ void HttpRequest::readFromSocket(QTcpSocket& socket) { } -HttpRequest::RequestStatus HttpRequest::getStatus() const { +HttpRequest::RequestStatus HttpRequest::getStatus() const +{ return status; } -QByteArray HttpRequest::getMethod() const { +QByteArray HttpRequest::getMethod() const +{ return method; } -QByteArray HttpRequest::getPath() const { +QByteArray HttpRequest::getPath() const +{ return urlDecode(path); } -QByteArray HttpRequest::getVersion() const { +const QByteArray& HttpRequest::getRawPath() const +{ + return path; +} + + +QByteArray HttpRequest::getVersion() const +{ return version; } -QByteArray HttpRequest::getHeader(const QByteArray& name) const { - return headers.value(name); +QByteArray HttpRequest::getHeader(const QByteArray& name) const +{ + return headers.value(name.toLower()); } -QList HttpRequest::getHeaders(const QByteArray& name) const { - return headers.values(name); +QList HttpRequest::getHeaders(const QByteArray& name) const +{ + return headers.values(name.toLower()); } -QMultiMap HttpRequest::getHeaderMap() const { +QMultiMap HttpRequest::getHeaderMap() const +{ return headers; } -QByteArray HttpRequest::getParameter(const QByteArray& name) const { +QByteArray HttpRequest::getParameter(const QByteArray& name) const +{ return parameters.value(name); } -QList HttpRequest::getParameters(const QByteArray& name) const { +QList HttpRequest::getParameters(const QByteArray& name) const +{ return parameters.values(name); } -QMultiMap HttpRequest::getParameterMap() const { +QMultiMap HttpRequest::getParameterMap() const +{ return parameters; } -QByteArray HttpRequest::getBody() const { +QByteArray HttpRequest::getBody() const +{ return bodyData; } -QByteArray HttpRequest::urlDecode(const QByteArray source) { +QByteArray HttpRequest::urlDecode(const QByteArray source) +{ QByteArray buffer(source); buffer.replace('+',' '); int percentChar=buffer.indexOf('%'); - while (percentChar>=0) { + while (percentChar>=0) + { bool ok; char byte=buffer.mid(percentChar+1,2).toInt(&ok,16); - if (ok) { + if (ok) + { buffer.replace(percentChar,3,(char*)&byte,1); } percentChar=buffer.indexOf('%',percentChar+1); @@ -308,65 +392,77 @@ QByteArray HttpRequest::urlDecode(const QByteArray source) { } -void HttpRequest::parseMultiPartFile() { +void HttpRequest::parseMultiPartFile() +{ qDebug("HttpRequest: parsing multipart temp file"); tempFile.seek(0); bool finished=false; - while (!tempFile.atEnd() && !finished && !tempFile.error()) { - -#ifdef SUPERVERBOSE - qDebug("HttpRequest: reading multpart headers"); -#endif + while (!tempFile.atEnd() && !finished && !tempFile.error()) + { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: reading multpart headers"); + #endif QByteArray fieldName; QByteArray fileName; - while (!tempFile.atEnd() && !finished && !tempFile.error()) { + while (!tempFile.atEnd() && !finished && !tempFile.error()) + { QByteArray line=tempFile.readLine(65536).trimmed(); - if (line.startsWith("Content-Disposition:")) { - if (line.contains("form-data")) { + if (line.startsWith("Content-Disposition:")) + { + if (line.contains("form-data")) + { int start=line.indexOf(" name=\""); int end=line.indexOf("\"",start+7); - if (start>=0 && end>=start) { + if (start>=0 && end>=start) + { fieldName=line.mid(start+7,end-start-7); } start=line.indexOf(" filename=\""); end=line.indexOf("\"",start+11); - if (start>=0 && end>=start) { + if (start>=0 && end>=start) + { fileName=line.mid(start+11,end-start-11); } -#ifdef SUPERVERBOSE - qDebug("HttpRequest: multipart field=%s, filename=%s",fieldName.data(),fileName.data()); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: multipart field=%s, filename=%s",fieldName.data(),fileName.data()); + #endif } - else { + else + { qDebug("HttpRequest: ignoring unsupported content part %s",line.data()); } } - else if (line.isEmpty()) { + else if (line.isEmpty()) + { break; } } -#ifdef SUPERVERBOSE - qDebug("HttpRequest: reading multpart data"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: reading multpart data"); + #endif QTemporaryFile* uploadedFile=0; QByteArray fieldValue; - while (!tempFile.atEnd() && !finished && !tempFile.error()) { + while (!tempFile.atEnd() && !finished && !tempFile.error()) + { QByteArray line=tempFile.readLine(65536); - if (line.startsWith("--"+boundary)) { + if (line.startsWith("--"+boundary)) + { // Boundary found. Until now we have collected 2 bytes too much, // so remove them from the last result - if (fileName.isEmpty() && !fieldName.isEmpty()) { + if (fileName.isEmpty() && !fieldName.isEmpty()) + { // last field was a form field fieldValue.remove(fieldValue.size()-2,2); parameters.insert(fieldName,fieldValue); qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fieldValue.data()); } - else if (!fileName.isEmpty() && !fieldName.isEmpty()) { + else if (!fileName.isEmpty() && !fieldName.isEmpty()) + { // last field was a file -#ifdef SUPERVERBOSE - qDebug("HttpRequest: finishing writing to uploaded file"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: finishing writing to uploaded file"); + #endif uploadedFile->resize(uploadedFile->size()-2); uploadedFile->flush(); uploadedFile->seek(0); @@ -375,57 +471,79 @@ void HttpRequest::parseMultiPartFile() { uploadedFiles.insert(fieldName,uploadedFile); qDebug("HttpRequest: uploaded file size is %i",(int) uploadedFile->size()); } - if (line.contains(boundary+"--")) { + if (line.contains(boundary+"--")) + { finished=true; } break; } - else { - if (fileName.isEmpty() && !fieldName.isEmpty()) { + else + { + if (fileName.isEmpty() && !fieldName.isEmpty()) + { // this is a form field. currentSize+=line.size(); fieldValue.append(line); } - else if (!fileName.isEmpty() && !fieldName.isEmpty()) { + else if (!fileName.isEmpty() && !fieldName.isEmpty()) + { // this is a file - if (!uploadedFile) { + if (!uploadedFile) + { uploadedFile=new QTemporaryFile(); uploadedFile->open(); } uploadedFile->write(line); - if (uploadedFile->error()) { + if (uploadedFile->error()) + { qCritical("HttpRequest: error writing temp file, %s",qPrintable(uploadedFile->errorString())); } } } } } - if (tempFile.error()) { + if (tempFile.error()) + { qCritical("HttpRequest: cannot read temp file, %s",qPrintable(tempFile.errorString())); } -#ifdef SUPERVERBOSE - qDebug("HttpRequest: finished parsing multipart temp file"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: finished parsing multipart temp file"); + #endif } -HttpRequest::~HttpRequest() { - foreach(QByteArray key, uploadedFiles.keys()) { +HttpRequest::~HttpRequest() +{ + foreach(QByteArray key, uploadedFiles.keys()) + { QTemporaryFile* file=uploadedFiles.value(key); file->close(); delete file; } } -QTemporaryFile* HttpRequest::getUploadedFile(const QByteArray fieldName) { +QTemporaryFile* HttpRequest::getUploadedFile(const QByteArray fieldName) const +{ return uploadedFiles.value(fieldName); } -QByteArray HttpRequest::getCookie(const QByteArray& name) const { +QByteArray HttpRequest::getCookie(const QByteArray& name) const +{ return cookies.value(name); } /** Get the map of cookies */ -QMap& HttpRequest::getCookieMap() { +QMap& HttpRequest::getCookieMap() +{ return cookies; } +/** + Get the address of the connected client. + Note that multiple clients may have the same IP address, if they + share an internet connection (which is very common). + */ +QHostAddress HttpRequest::getPeerAddress() const +{ + return peerAddress; +} + diff --git a/YACReaderLibrary/server/lib/httpserver/httprequest.h b/YACReaderLibrary/server/lib/httpserver/httprequest.h index e79fd112..c13846af 100644 --- a/YACReaderLibrary/server/lib/httpserver/httprequest.h +++ b/YACReaderLibrary/server/lib/httpserver/httprequest.h @@ -7,12 +7,14 @@ #define HTTPREQUEST_H #include +#include #include #include #include #include #include #include +#include "httpglobal.h" /** This object represents a single HTTP request. It reads the request @@ -31,9 +33,10 @@ The body is always a little larger than the file itself. */ -class HttpRequest { +class DECLSPEC HttpRequest { Q_DISABLE_COPY(HttpRequest) friend class HttpSessionStore; + public: /** Values for getStatus() */ @@ -51,11 +54,12 @@ public: virtual ~HttpRequest(); /** - Read the request from a socket. This method must be called repeatedly + Read the HTTP request from a socket. + This method is called by the connection handler repeatedly until the status is RequestStatus::complete or RequestStatus::abort. @param socket Source of the data */ - void readFromSocket(QTcpSocket& socket); + void readFromSocket(QTcpSocket* socket); /** Get the status of this reqeust. @@ -69,12 +73,15 @@ public: /** Get the decoded path of the HTPP request (e.g. "/index.html") */ QByteArray getPath() const; + /** Get the raw path of the HTTP request (e.g. "/file%20with%20spaces.html") */ + const QByteArray& getRawPath() const; + /** Get the version of the HTPP request (e.g. "HTTP/1.1") */ QByteArray getVersion() const; /** Get the value of a HTTP request header. - @param name Name of the header + @param name Name of the header, not case-senitive. @return If the header occurs multiple times, only the last one is returned. */ @@ -82,16 +89,19 @@ public: /** Get the values of a HTTP request header. - @param name Name of the header + @param name Name of the header, not case-senitive. */ QList getHeaders(const QByteArray& name) const; - /** Get all HTTP request headers */ + /** + * Get all HTTP request headers. Note that the header names + * are returned in lower-case. + */ QMultiMap getHeaderMap() const; /** Get the value of a HTTP request parameter. - @param name Name of the parameter + @param name Name of the parameter, case-sensitive. @return If the parameter occurs multiple times, only the last one is returned. */ @@ -99,14 +109,14 @@ public: /** Get the values of a HTTP request parameter. - @param name Name of the parameter + @param name Name of the parameter, case-sensitive. */ QList getParameters(const QByteArray& name) const; - /** Get all HTTP request parameters */ + /** Get all HTTP request parameters. */ QMultiMap getParameterMap() const; - /** Get the HTTP request body */ + /** Get the HTTP request body. */ QByteArray getBody() const; /** @@ -125,17 +135,24 @@ public: For uploaded files, the method getParameters() returns the original fileName as provided by the calling web browser. */ - QTemporaryFile* getUploadedFile(const QByteArray fieldName); + QTemporaryFile* getUploadedFile(const QByteArray fieldName) const; /** - Get the value of a cookie + Get the value of a cookie. @param name Name of the cookie */ QByteArray getCookie(const QByteArray& name) const; - /** Get the map of cookies */ + /** Get all cookies. */ QMap& getCookieMap(); + /** + Get the address of the connected client. + Note that multiple clients may have the same IP address, if they + share an internet connection (which is very common). + */ + QHostAddress getPeerAddress() const; + private: /** Request headers */ @@ -163,11 +180,14 @@ private: QByteArray version; /** - Status of this request. + Status of this request. For the state engine. @see RequestStatus */ RequestStatus status; + /** Address of the connected peer. */ + QHostAddress peerAddress; + /** Maximum size of requests in bytes. */ int maxSize; @@ -193,13 +213,13 @@ private: void parseMultiPartFile(); /** Sub-procedure of readFromSocket(), read the first line of a request. */ - void readRequest(QTcpSocket& socket); + void readRequest(QTcpSocket* socket); /** Sub-procedure of readFromSocket(), read header lines. */ - void readHeader(QTcpSocket& socket); + void readHeader(QTcpSocket* socket); /** Sub-procedure of readFromSocket(), read the request body. */ - void readBody(QTcpSocket& socket); + void readBody(QTcpSocket* socket); /** Sub-procedure of readFromSocket(), extract and decode request parameters. */ void decodeRequestParams(); @@ -207,6 +227,9 @@ private: /** Sub-procedure of readFromSocket(), extract cookies from headers */ void extractCookies(); + /** Buffer for collecting characters of request and header lines */ + QByteArray lineBuffer; + }; #endif // HTTPREQUEST_H diff --git a/YACReaderLibrary/server/lib/httpserver/httprequesthandler.cpp b/YACReaderLibrary/server/lib/httpserver/httprequesthandler.cpp index d8ad7caf..879ccd4f 100644 --- a/YACReaderLibrary/server/lib/httpserver/httprequesthandler.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httprequesthandler.cpp @@ -9,10 +9,12 @@ HttpRequestHandler::HttpRequestHandler(QObject* parent) : QObject(parent) {} -HttpRequestHandler::~HttpRequestHandler() {} +HttpRequestHandler::~HttpRequestHandler() +{} -void HttpRequestHandler::service(HttpRequest& request, HttpResponse& response) { - qCritical("HttpRequestHandler: you need to override the dispatch() function"); +void HttpRequestHandler::service(HttpRequest& request, HttpResponse& response) +{ + qCritical("HttpRequestHandler: you need to override the service() function"); qDebug("HttpRequestHandler: request=%s %s %s",request.getMethod().data(),request.getPath().data(),request.getVersion().data()); response.setStatus(501,"not implemented"); response.write("501 not implemented",true); diff --git a/YACReaderLibrary/server/lib/httpserver/httprequesthandler.h b/YACReaderLibrary/server/lib/httpserver/httprequesthandler.h index a5f9f4e2..12ac0cf9 100644 --- a/YACReaderLibrary/server/lib/httpserver/httprequesthandler.h +++ b/YACReaderLibrary/server/lib/httpserver/httprequesthandler.h @@ -6,6 +6,7 @@ #ifndef HTTPREQUESTHANDLER_H #define HTTPREQUESTHANDLER_H +#include "httpglobal.h" #include "httprequest.h" #include "httpresponse.h" @@ -21,13 +22,16 @@ @see StaticFileController which delivers static local files. */ -class HttpRequestHandler : public QObject { +class DECLSPEC HttpRequestHandler : public QObject { Q_OBJECT Q_DISABLE_COPY(HttpRequestHandler) public: - /** Constructor */ - HttpRequestHandler(QObject* parent=0); + /** + * Constructor. + * @param parent Parent object. + */ + HttpRequestHandler(QObject* parent=NULL); /** Destructor */ virtual ~HttpRequestHandler(); diff --git a/YACReaderLibrary/server/lib/httpserver/httpresponse.cpp b/YACReaderLibrary/server/lib/httpserver/httpresponse.cpp index ad8efa29..7dbb600a 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpresponse.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httpresponse.cpp @@ -5,48 +5,62 @@ #include "httpresponse.h" -HttpResponse::HttpResponse(QTcpSocket* socket) { +HttpResponse::HttpResponse(QTcpSocket* socket) +{ this->socket=socket; statusCode=200; statusText="OK"; sentHeaders=false; sentLastPart=false; + chunkedMode=false; } -void HttpResponse::setHeader(QByteArray name, QByteArray value) { - //Q_ASSERT(sentHeaders==false); +void HttpResponse::setHeader(QByteArray name, QByteArray value) +{ + Q_ASSERT(sentHeaders==false); headers.insert(name,value); } -void HttpResponse::setHeader(QByteArray name, int value) { - //Q_ASSERT(sentHeaders==false); +void HttpResponse::setHeader(QByteArray name, int value) +{ + Q_ASSERT(sentHeaders==false); headers.insert(name,QByteArray::number(value)); } -QMap& HttpResponse::getHeaders() { +QMap& HttpResponse::getHeaders() +{ return headers; } -void HttpResponse::setStatus(int statusCode, QByteArray description) { +void HttpResponse::setStatus(int statusCode, QByteArray description) +{ this->statusCode=statusCode; statusText=description; } -void HttpResponse::writeHeaders() { - //Q_ASSERT(sentHeaders==false); +int HttpResponse::getStatusCode() const +{ + return this->statusCode; +} + +void HttpResponse::writeHeaders() +{ + Q_ASSERT(sentHeaders==false); QByteArray buffer; buffer.append("HTTP/1.1 "); buffer.append(QByteArray::number(statusCode)); buffer.append(' '); buffer.append(statusText); buffer.append("\r\n"); - foreach(QByteArray name, headers.keys()) { + foreach(QByteArray name, headers.keys()) + { buffer.append(name); buffer.append(": "); buffer.append(headers.value(name)); buffer.append("\r\n"); } - foreach(HttpCookie cookie,cookies.values()) { + foreach(HttpCookie cookie,cookies.values()) + { buffer.append("Set-Cookie: "); buffer.append(cookie.toByteArray()); buffer.append("\r\n"); @@ -56,14 +70,21 @@ void HttpResponse::writeHeaders() { sentHeaders=true; } -bool HttpResponse::writeToSocket(QByteArray data) { +bool HttpResponse::writeToSocket(QByteArray data) +{ int remaining=data.size(); char* ptr=data.data(); - while (socket->isOpen() && remaining>0) { - // Wait until the previous buffer content is written out, otherwise it could become very large - socket->waitForBytesWritten(-1); + while (socket->isOpen() && remaining>0) + { + // If the output buffer has become large, then wait until it has been sent. + if (socket->bytesToWrite()>16384) + { + socket->waitForBytesWritten(-1); + } + int written=socket->write(ptr,remaining); - if (written==-1) { + if (written==-1) + { return false; } ptr+=written; @@ -72,61 +93,106 @@ bool HttpResponse::writeToSocket(QByteArray data) { return true; } -void HttpResponse::write(QByteArray data, bool lastPart) { - //Q_ASSERT(sentLastPart==false); - if (sentHeaders==false) { - QByteArray connectionMode=headers.value("Connection"); - if (!headers.contains("Content-Length") && !headers.contains("Transfer-Encoding") && connectionMode!="close" && connectionMode!="Close") { - if (!lastPart) { +void HttpResponse::write(QByteArray data, bool lastPart) +{ + Q_ASSERT(sentLastPart==false); + + // Send HTTP headers, if not already done (that happens only on the first call to write()) + if (sentHeaders==false) + { + // If the whole response is generated with a single call to write(), then we know the total + // size of the response and therefore can set the Content-Length header automatically. + if (lastPart) + { + // Automatically set the Content-Length header + headers.insert("Content-Length",QByteArray::number(data.size())); + } + + // else if we will not close the connection at the end, them we must use the chunked mode. + else + { + QByteArray connectionValue=headers.value("Connection",headers.value("connection")); + bool connectionClose=QString::compare(connectionValue,"close",Qt::CaseInsensitive)==0; + if (!connectionClose) + { headers.insert("Transfer-Encoding","chunked"); - } - else { - headers.insert("Content-Length",QByteArray::number(data.size())); + chunkedMode=true; } } + writeHeaders(); } - bool chunked=headers.value("Transfer-Encoding")=="chunked" || headers.value("Transfer-Encoding")=="Chunked"; - if (chunked) { - if (data.size()>0) { - QByteArray buffer=QByteArray::number(data.size(),16); - buffer.append("\r\n"); - writeToSocket(buffer); + + // Send data + if (data.size()>0) + { + if (chunkedMode) + { + if (data.size()>0) + { + QByteArray size=QByteArray::number(data.size(),16); + writeToSocket(size); + writeToSocket("\r\n"); + writeToSocket(data); + writeToSocket("\r\n"); + } + } + else + { writeToSocket(data); - writeToSocket("\r\n"); } } - else { - writeToSocket(data); - } - if (lastPart) { - if (chunked) { + + // Only for the last chunk, send the terminating marker and flush the buffer. + if (lastPart) + { + if (chunkedMode) + { writeToSocket("0\r\n\r\n"); } - else if (!headers.contains("Content-Length")) { - socket->disconnectFromHost(); - } + socket->flush(); sentLastPart=true; } } -void HttpResponse::writeText(QString text, bool lastPart) -{ - write(QByteArray(text.toUtf8()),lastPart); -} -bool HttpResponse::hasSentLastPart() const { +bool HttpResponse::hasSentLastPart() const +{ return sentLastPart; } -void HttpResponse::setCookie(const HttpCookie& cookie) { - //Q_ASSERT(sentHeaders==false); - if (!cookie.getName().isEmpty()) { +void HttpResponse::setCookie(const HttpCookie& cookie) +{ + Q_ASSERT(sentHeaders==false); + if (!cookie.getName().isEmpty()) + { cookies.insert(cookie.getName(),cookie); } } -QMap& HttpResponse::getCookies() { + +QMap& HttpResponse::getCookies() +{ return cookies; } + + +void HttpResponse::redirect(const QByteArray& url) +{ + setStatus(303,"See Other"); + setHeader("Location",url); + write("Redirect",true); +} + + +void HttpResponse::flush() +{ + socket->flush(); +} + + +bool HttpResponse::isConnected() const +{ + return socket->isOpen(); +} diff --git a/YACReaderLibrary/server/lib/httpserver/httpresponse.h b/YACReaderLibrary/server/lib/httpserver/httpresponse.h index 1bdc7733..44054806 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpresponse.h +++ b/YACReaderLibrary/server/lib/httpserver/httpresponse.h @@ -9,12 +9,12 @@ #include #include #include +#include "httpglobal.h" #include "httpcookie.h" /** - This object represents a HTTP response, in particular the response headers. + This object represents a HTTP response, used to return something to the web client.

- Example code for proper response generation:

     response.setStatus(200,"OK"); // optional, because this is the default
     response.writeBody("Hello");
@@ -27,108 +27,132 @@
     response.write("The request cannot be processed because the servers is broken",true);
   

- For performance reason, writing a single or few large packets is better than writing - many small packets. In case of large responses (e.g. file downloads), a Content-Length - header should be set before calling write(). Web Browsers use that information to display - a progress bar. + In case of large responses (e.g. file downloads), a Content-Length header should be set + before calling write(). Web Browsers use that information to display a progress bar. */ -class HttpResponse { +class DECLSPEC HttpResponse { Q_DISABLE_COPY(HttpResponse) public: - /** - Constructor. - @param socket used to write the response - */ - HttpResponse(QTcpSocket* socket); + /** + Constructor. + @param socket used to write the response + */ + HttpResponse(QTcpSocket* socket); - /** - Set a HTTP response header - @param name name of the header - @param value value of the header - */ - void setHeader(QByteArray name, QByteArray value); + /** + Set a HTTP response header. + You must call this method before the first write(). + @param name name of the header + @param value value of the header + */ + void setHeader(QByteArray name, QByteArray value); - /** - Set a HTTP response header - @param name name of the header - @param value value of the header - */ - void setHeader(QByteArray name, int value); + /** + Set a HTTP response header. + You must call this method before the first write(). + @param name name of the header + @param value value of the header + */ + void setHeader(QByteArray name, int value); - /** Get the map of HTTP response headers */ - QMap& getHeaders(); + /** Get the map of HTTP response headers */ + QMap& getHeaders(); - /** Get the map of cookies */ - QMap& getCookies(); + /** Get the map of cookies */ + QMap& getCookies(); - /** - Set status code and description. The default is 200,OK. - */ - void setStatus(int statusCode, QByteArray description=QByteArray()); + /** + Set status code and description. The default is 200,OK. + You must call this method before the first write(). + */ + void setStatus(int statusCode, QByteArray description=QByteArray()); - /** - Write body data to the socket. -

- The HTTP status line and headers are sent automatically before the first - byte of the body gets sent. -

- If the response contains only a single chunk (indicated by lastPart=true), - the response is transferred in traditional mode with a Content-Length - header, which is automatically added if not already set before. -

- Otherwise, each part is transferred in chunked mode. - @param data Data bytes of the body - @param lastPart Indicator, if this is the last part of the response. - */ - void write(QByteArray data, bool lastPart=false); - void writeText(QString text, bool lastPart=false); + /** Return the status code. */ + int getStatusCode() const; - /** - Indicates wheter the body has been sent completely. Used by the connection - handler to terminate the body automatically when necessary. - */ - bool hasSentLastPart() const; + /** + Write body data to the socket. +

+ The HTTP status line, headers and cookies are sent automatically before the body. +

+ If the response contains only a single chunk (indicated by lastPart=true), + then a Content-Length header is automatically set. +

+ Chunked mode is automatically selected if there is no Content-Length header + and also no Connection:close header. + @param data Data bytes of the body + @param lastPart Indicates that this is the last chunk of data and flushes the output buffer. + */ + void write(QByteArray data, bool lastPart=false); - /** - Set a cookie. Cookies are sent together with the headers when the first - call to write() occurs. - */ - void setCookie(const HttpCookie& cookie); + /** + Indicates whether the body has been sent completely (write() has been called with lastPart=true). + */ + bool hasSentLastPart() const; + + /** + Set a cookie. + You must call this method before the first write(). + */ + void setCookie(const HttpCookie& cookie); + + /** + Send a redirect response to the browser. + Cannot be combined with write(). + @param url Destination URL + */ + void redirect(const QByteArray& url); + + /** + * Flush the output buffer (of the underlying socket). + * You normally don't need to call this method because flush is + * automatically called after HttpRequestHandler::service() returns. + */ + void flush(); + + /** + * May be used to check whether the connection to the web client has been lost. + * This might be useful to cancel the generation of large or slow responses. + */ + bool isConnected() const; private: - /** Request headers */ - QMap headers; + /** Request headers */ + QMap headers; - /** Socket for writing output */ - QTcpSocket* socket; + /** Socket for writing output */ + QTcpSocket* socket; - /** HTTP status code*/ - int statusCode; + /** HTTP status code*/ + int statusCode; - /** HTTP status code description */ - QByteArray statusText; + /** HTTP status code description */ + QByteArray statusText; - /** Indicator whether headers have been sent */ - bool sentHeaders; + /** Indicator whether headers have been sent */ + bool sentHeaders; - /** Indicator whether the body has been sent completely */ - bool sentLastPart; + /** Indicator whether the body has been sent completely */ + bool sentLastPart; - /** Cookies */ - QMap cookies; + /** Whether the response is sent in chunked mode */ + bool chunkedMode; - /** Write raw data to the socket. This method blocks until all bytes have been passed to the TCP buffer */ - bool writeToSocket(QByteArray data); + /** Cookies */ + QMap cookies; - /** - Write the response HTTP status and headers to the socket. - Calling this method is optional, because writeBody() calls - it automatically when required. - */ - void writeHeaders(); + /** Write raw data to the socket. This method blocks until all bytes have been passed to the TCP buffer */ + bool writeToSocket(QByteArray data); + + /** + Write the response HTTP status and headers to the socket. + Calling this method is optional, because writeBody() calls + it automatically when required. + */ + void writeHeaders(); }; diff --git a/YACReaderLibrary/server/lib/httpserver/httpserver.pri b/YACReaderLibrary/server/lib/httpserver/httpserver.pri index a109a573..fb78772e 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpserver.pri +++ b/YACReaderLibrary/server/lib/httpserver/httpserver.pri @@ -1,12 +1,33 @@ INCLUDEPATH += $$PWD DEPENDPATH += $$PWD -HEADERS += $$PWD/httplistener.h $$PWD/httpconnectionhandler.h $$PWD/httpconnectionhandlerpool.h $$PWD/httprequest.h $$PWD/httpresponse.h $$PWD/httpcookie.h $$PWD/httprequesthandler.h -HEADERS += $$PWD/httpsession.h $$PWD/httpsessionstore.h -HEADERS += $$PWD/staticfilecontroller.h +QT += network -SOURCES += $$PWD/httplistener.cpp $$PWD/httpconnectionhandler.cpp $$PWD/httpconnectionhandlerpool.cpp $$PWD/httprequest.cpp $$PWD/httpresponse.cpp $$PWD/httpcookie.cpp $$PWD/httprequesthandler.cpp -SOURCES += $$PWD/httpsession.cpp $$PWD/httpsessionstore.cpp -SOURCES += $$PWD/staticfilecontroller.cpp +# Enable very detailed debug messages when compiling the debug version +CONFIG(debug, debug|release) { + DEFINES += SUPERVERBOSE +} -OTHER_FILES += $$PWD/../doc/readme.txt +HEADERS += $$PWD/httpglobal.h \ + $$PWD/httplistener.h \ + $$PWD/httpconnectionhandler.h \ + $$PWD/httpconnectionhandlerpool.h \ + $$PWD/httprequest.h \ + $$PWD/httpresponse.h \ + $$PWD/httpcookie.h \ + $$PWD/httprequesthandler.h \ + $$PWD/httpsession.h \ + $$PWD/httpsessionstore.h \ + $$PWD/staticfilecontroller.h + +SOURCES += $$PWD/httpglobal.cpp \ + $$PWD/httplistener.cpp \ + $$PWD/httpconnectionhandler.cpp \ + $$PWD/httpconnectionhandlerpool.cpp \ + $$PWD/httprequest.cpp \ + $$PWD/httpresponse.cpp \ + $$PWD/httpcookie.cpp \ + $$PWD/httprequesthandler.cpp \ + $$PWD/httpsession.cpp \ + $$PWD/httpsessionstore.cpp \ + $$PWD/staticfilecontroller.cpp diff --git a/YACReaderLibrary/server/lib/httpserver/httpsession.cpp b/YACReaderLibrary/server/lib/httpserver/httpsession.cpp index 1bfe2f07..165f93a2 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpsession.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httpsession.cpp @@ -8,374 +8,179 @@ #include -HttpSession::HttpSession(bool canStore) { - if (canStore) { - dataPtr=new HttpSessionData(); - dataPtr->refCount=1; - dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); - dataPtr->id=QUuid::createUuid().toString().toLatin1(); - dataPtr->yacreaderSessionData.comic = 0; - dataPtr->yacreaderSessionData.comicId = 0; - dataPtr->yacreaderSessionData.remoteComic = 0; - dataPtr->yacreaderSessionData.remoteComicId = 0; +HttpSession::HttpSession(bool canStore) +{ + if (canStore) + { + dataPtr=new HttpSessionData(); + dataPtr->refCount=1; + dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); + dataPtr->id=QUuid::createUuid().toString().toLocal8Bit(); #ifdef SUPERVERBOSE - qDebug("HttpSession: created new session data with id %s",dataPtr->id.data()); + qDebug("HttpSession: created new session data with id %s",dataPtr->id.data()); #endif - } - else { - dataPtr=0; - } + } + else + { + dataPtr=0; + } } -HttpSession::HttpSession(const HttpSession& other) { - dataPtr=other.dataPtr; - if (dataPtr) { - dataPtr->lock.lockForWrite(); - dataPtr->refCount++; +HttpSession::HttpSession(const HttpSession& other) +{ + dataPtr=other.dataPtr; + if (dataPtr) + { + dataPtr->lock.lockForWrite(); + dataPtr->refCount++; #ifdef SUPERVERBOSE - qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); + qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); #endif - dataPtr->lock.unlock(); - } + dataPtr->lock.unlock(); + } } -HttpSession& HttpSession::operator= (const HttpSession& other) { - HttpSessionData* oldPtr=dataPtr; - dataPtr=other.dataPtr; - if (dataPtr) { - dataPtr->lock.lockForWrite(); - dataPtr->refCount++; +HttpSession& HttpSession::operator= (const HttpSession& other) +{ + HttpSessionData* oldPtr=dataPtr; + dataPtr=other.dataPtr; + if (dataPtr) + { + dataPtr->lock.lockForWrite(); + dataPtr->refCount++; #ifdef SUPERVERBOSE - qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); + qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); #endif - dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); - dataPtr->lock.unlock(); - } - if (oldPtr) { - int refCount; - oldPtr->lock.lockForRead(); - refCount=oldPtr->refCount--; + dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); + dataPtr->lock.unlock(); + } + if (oldPtr) + { + int refCount; + oldPtr->lock.lockForRead(); + refCount=oldPtr->refCount--; #ifdef SUPERVERBOSE - qDebug("HttpSession: refCount of %s is %i",oldPtr->id.data(),oldPtr->refCount); + qDebug("HttpSession: refCount of %s is %i",oldPtr->id.data(),oldPtr->refCount); #endif - oldPtr->lock.unlock(); - if (refCount==0) { - delete oldPtr; - } - } - return *this; + oldPtr->lock.unlock(); + if (refCount==0) + { + delete oldPtr; + } + } + return *this; } -HttpSession::~HttpSession() { - if (dataPtr) { - int refCount; - dataPtr->lock.lockForRead(); - refCount=--dataPtr->refCount; +HttpSession::~HttpSession() +{ + if (dataPtr) { + int refCount; + dataPtr->lock.lockForRead(); + refCount=--dataPtr->refCount; #ifdef SUPERVERBOSE - qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); + qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); #endif - dataPtr->lock.unlock(); - if (refCount==0) { - qDebug("HttpSession: deleting data"); - delete dataPtr; - } - } + dataPtr->lock.unlock(); + if (refCount==0) + { + qDebug("HttpSession: deleting data"); + delete dataPtr; + } + } } -QByteArray HttpSession::getId() const { - if (dataPtr) { - return dataPtr->id; - } - else { - return QByteArray(); - } +QByteArray HttpSession::getId() const +{ + if (dataPtr) + { + return dataPtr->id; + } + else + { + return QByteArray(); + } } bool HttpSession::isNull() const { - return dataPtr==0; + return dataPtr==0; } -void HttpSession::set(const QByteArray& key, const QVariant& value) { - if (dataPtr) { - dataPtr->lock.lockForWrite(); - dataPtr->values.insert(key,value); - dataPtr->lock.unlock(); - } -} - -void HttpSession::remove(const QByteArray& key) { - if (dataPtr) { - dataPtr->lock.lockForWrite(); - dataPtr->values.remove(key); - dataPtr->lock.unlock(); - } -} - -QVariant HttpSession::get(const QByteArray& key) const { - QVariant value; - if (dataPtr) { - dataPtr->lock.lockForRead(); - value=dataPtr->values.value(key); - dataPtr->lock.unlock(); - } - return value; -} - -bool HttpSession::contains(const QByteArray& key) const { - bool found=false; - if (dataPtr) { - dataPtr->lock.lockForRead(); - found=dataPtr->values.contains(key); - dataPtr->lock.unlock(); - } - return found; -} - -QMap HttpSession::getAll() const { - QMap values; - if (dataPtr) { - dataPtr->lock.lockForRead(); - values=dataPtr->values; - dataPtr->lock.unlock(); - } - return values; -} - -qint64 HttpSession::getLastAccess() const { - qint64 value=0; - if (dataPtr) { - dataPtr->lock.lockForRead(); - value=dataPtr->lastAccess; - dataPtr->lock.unlock(); - } - return value; -} - - -void HttpSession::setLastAccess() { - if (dataPtr) { - dataPtr->lock.lockForRead(); - dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); - dataPtr->lock.unlock(); - } -} - -//AÑADIDO -//sets -bool HttpSession::isComicOnDevice(const QString & hash) +void HttpSession::set(const QByteArray& key, const QVariant& value) { - if(dataPtr) - return dataPtr->yacreaderSessionData.comicsOnDevice.contains(hash); - else - return false; -} -bool HttpSession::isComicDownloaded(const QString & hash) -{ - if(dataPtr) - return dataPtr->yacreaderSessionData.downloadedComics.contains(hash); - else - return false; -} -void HttpSession::setComicOnDevice(const QString & hash) -{ - if(dataPtr) - { - dataPtr->yacreaderSessionData.comicsOnDevice.insert(hash); - } -} -void HttpSession::setComicsOnDevice(const QSet & set) -{ - if(dataPtr) - { - dataPtr->yacreaderSessionData.comicsOnDevice = set; - } -} -void HttpSession::setDownloadedComic(const QString & hash) -{ - if(dataPtr) - { - dataPtr->yacreaderSessionData.downloadedComics.insert(hash); - } -} -QSet HttpSession::getComicsOnDevice() -{ - if(dataPtr) - return dataPtr->yacreaderSessionData.comicsOnDevice ; - else - return QSet(); -} -QSet HttpSession::getDownloadedComics() -{ - if(dataPtr) - return dataPtr->yacreaderSessionData.downloadedComics ; - else - return QSet(); -} - -void HttpSession::clearComics() -{ - if(dataPtr) + if (dataPtr) { - dataPtr->yacreaderSessionData.comicsOnDevice.clear(); - dataPtr->yacreaderSessionData.downloadedComics.clear(); - } -} -//current comic (import) -qulonglong HttpSession::getCurrentComicId() -{ - if(dataPtr) - return dataPtr->yacreaderSessionData.comicId ; - else - return 0; -} -Comic* HttpSession::getCurrentComic() -{ - if(dataPtr) - { - return dataPtr->yacreaderSessionData.comic ; - } - else - return 0; -} -void HttpSession::dismissCurrentComic() -{ - if(dataPtr) - { - if(dataPtr->yacreaderSessionData.comic != 0) - { - dataPtr->yacreaderSessionData.comic->deleteLater(); - dataPtr->yacreaderSessionData.comic = 0; - } - dataPtr->yacreaderSessionData.comicId = 0; - } -} -void HttpSession::setCurrentComic(qulonglong id, Comic * comic) -{ - if(dataPtr) - { - dismissCurrentComic(); - dataPtr->yacreaderSessionData.comicId = id; - dataPtr->yacreaderSessionData.comic = comic; - } -} - -//current comic (read) -qulonglong HttpSession::getCurrentRemoteComicId() -{ - if(dataPtr) - return dataPtr->yacreaderSessionData.remoteComicId ; - else - return 0; -} -Comic* HttpSession::getCurrentRemoteComic() -{ - if(dataPtr) - { - return dataPtr->yacreaderSessionData.remoteComic ; - } - else - return 0; -} -void HttpSession::dismissCurrentRemoteComic() -{ - if(dataPtr) - { - if(dataPtr->yacreaderSessionData.remoteComic != 0) - { - dataPtr->yacreaderSessionData.remoteComic->deleteLater(); - dataPtr->yacreaderSessionData.remoteComic = 0; - } - dataPtr->yacreaderSessionData.remoteComicId = 0; - } -} -void HttpSession::setCurrentRemoteComic(qulonglong id, Comic * comic) -{ - if(dataPtr) - { - dismissCurrentRemoteComic(); - dataPtr->yacreaderSessionData.remoteComicId = id; - dataPtr->yacreaderSessionData.remoteComic = comic; + dataPtr->lock.lockForWrite(); + dataPtr->values.insert(key,value); + dataPtr->lock.unlock(); } } - -QString HttpSession::getDeviceType() +void HttpSession::remove(const QByteArray& key) { - if(dataPtr) - { - return dataPtr->yacreaderSessionData.device; - } - return ""; -} -QString HttpSession::getDisplayType() -{ - if(dataPtr) - { - return dataPtr->yacreaderSessionData.display; - } - return ""; -} -void HttpSession::setDeviceType(const QString & device) -{ - if(dataPtr) - { - //dataPtr->yacreaderSessionData.comicsOnDevice.clear(); //TODO crear un método clear que limpie la sesión completamente - //dataPtr->yacreaderSessionData.downloadedComics.clear(); - dataPtr->yacreaderSessionData.device = device; - } -} -void HttpSession::setDisplayType(const QString & display) -{ - if(dataPtr) - { - dataPtr->yacreaderSessionData.display = display; - } + if (dataPtr) + { + dataPtr->lock.lockForWrite(); + dataPtr->values.remove(key); + dataPtr->lock.unlock(); + } } -void HttpSession::clearNavigationPath() +QVariant HttpSession::get(const QByteArray& key) const { - if(dataPtr) - dataPtr->yacreaderSessionData.navigationPath.clear(); + QVariant value; + if (dataPtr) + { + dataPtr->lock.lockForRead(); + value=dataPtr->values.value(key); + dataPtr->lock.unlock(); + } + return value; } -QPair HttpSession::popNavigationItem() +bool HttpSession::contains(const QByteArray& key) const { - if(dataPtr && !(dataPtr->yacreaderSessionData.navigationPath.isEmpty())) - return dataPtr->yacreaderSessionData.navigationPath.pop(); - return QPair(); + bool found=false; + if (dataPtr) + { + dataPtr->lock.lockForRead(); + found=dataPtr->values.contains(key); + dataPtr->lock.unlock(); + } + return found; } -QPair HttpSession::topNavigationItem() +QMap HttpSession::getAll() const { - if(dataPtr && !(dataPtr->yacreaderSessionData.navigationPath.isEmpty())) - return dataPtr->yacreaderSessionData.navigationPath.top(); - return QPair(); + QMap values; + if (dataPtr) + { + dataPtr->lock.lockForRead(); + values=dataPtr->values; + dataPtr->lock.unlock(); + } + return values; } -void HttpSession::pushNavigationItem(const QPair &item) +qint64 HttpSession::getLastAccess() const { - if(dataPtr) - dataPtr->yacreaderSessionData.navigationPath.push(item); + qint64 value=0; + if (dataPtr) + { + dataPtr->lock.lockForRead(); + value=dataPtr->lastAccess; + dataPtr->lock.unlock(); + } + return value; } -void HttpSession::updateTopItem(const QPair &item) -{ - if(dataPtr && !(dataPtr->yacreaderSessionData.navigationPath.isEmpty())) - { - dataPtr->yacreaderSessionData.navigationPath.pop(); - dataPtr->yacreaderSessionData.navigationPath.push(item); - } else if(dataPtr) - { - dataPtr->yacreaderSessionData.navigationPath.push(item); - } -} -QStack > HttpSession::getNavigationPath() +void HttpSession::setLastAccess() { - if(dataPtr) - return dataPtr->yacreaderSessionData.navigationPath; - else - return QStack >(); + if (dataPtr) + { + dataPtr->lock.lockForRead(); + dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); + dataPtr->lock.unlock(); + } } - diff --git a/YACReaderLibrary/server/lib/httpserver/httpsession.h b/YACReaderLibrary/server/lib/httpserver/httpsession.h index a95f818f..fa0ee3f9 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpsession.h +++ b/YACReaderLibrary/server/lib/httpserver/httpsession.h @@ -9,10 +9,7 @@ #include #include #include - -#include -#include -#include "comic.h" +#include "httpglobal.h" /** This class stores data for a single HTTP session. @@ -21,172 +18,100 @@ @see HttpSessionStore should be used to create and get instances of this class. */ -class HttpSession { +class DECLSPEC HttpSession { public: - /** - Constructor. - @param canStore The session can store data, if this parameter is true. - Otherwise all calls to set() and remove() do not have any effect. - */ - HttpSession(bool canStore=false); + /** + Constructor. + @param canStore The session can store data, if this parameter is true. + Otherwise all calls to set() and remove() do not have any effect. + */ + HttpSession(bool canStore=false); - /** - Copy constructor. Creates another HttpSession object that shares the - data of the other object. - */ - HttpSession(const HttpSession& other); + /** + Copy constructor. Creates another HttpSession object that shares the + data of the other object. + */ + HttpSession(const HttpSession& other); - /** - Copy operator. Detaches from the current shared data and attaches to - the data of the other object. - */ - HttpSession& operator= (const HttpSession& other); + /** + Copy operator. Detaches from the current shared data and attaches to + the data of the other object. + */ + HttpSession& operator= (const HttpSession& other); - /** - Destructor. Detaches from the shared data. - */ - virtual ~HttpSession(); + /** + Destructor. Detaches from the shared data. + */ + virtual ~HttpSession(); - /** Get the unique ID of this session. This method is thread safe. */ - QByteArray getId() const; + /** Get the unique ID of this session. This method is thread safe. */ + QByteArray getId() const; - /** - Null sessions cannot store data. All calls to set() and remove() - do not have any effect.This method is thread safe. - */ - bool isNull() const; + /** + Null sessions cannot store data. All calls to set() and remove() + do not have any effect.This method is thread safe. + */ + bool isNull() const; - /** Set a value. This method is thread safe. */ - void set(const QByteArray& key, const QVariant& value); + /** Set a value. This method is thread safe. */ + void set(const QByteArray& key, const QVariant& value); - /** Remove a value. This method is thread safe. */ - void remove(const QByteArray& key); + /** Remove a value. This method is thread safe. */ + void remove(const QByteArray& key); - /** Get a value. This method is thread safe. */ - QVariant get(const QByteArray& key) const; + /** Get a value. This method is thread safe. */ + QVariant get(const QByteArray& key) const; - /** Check if a key exists. This method is thread safe. */ - bool contains(const QByteArray& key) const; - - /** - Get a copy of all data stored in this session. - Changes to the session do not affect the copy and vice versa. - This method is thread safe. - */ - QMap getAll() const; - - /** - Get the timestamp of last access. That is the time when the last - HttpSessionStore::getSession() has been called. - This method is thread safe. - */ - qint64 getLastAccess() const; - - /** - Set the timestamp of last access, to renew the timeout period. - Called by HttpSessionStore::getSession(). - This method is thread safe. - */ - void setLastAccess(); - - //AÑADIDO - //sets - void setComicsOnDevice(const QSet & set); - void setComicOnDevice(const QString & hash); - void setDownloadedComic(const QString & hash); - bool isComicOnDevice(const QString & hash); - bool isComicDownloaded(const QString & hash); - QSet getComicsOnDevice(); - QSet getDownloadedComics(); - void clearComics(); - - //current comic (import) - qulonglong getCurrentComicId(); - Comic * getCurrentComic(); - void dismissCurrentComic(); - void setCurrentComic(qulonglong id, Comic * comic); - - //current comic (read) - qulonglong getCurrentRemoteComicId(); - Comic * getCurrentRemoteComic(); - void dismissCurrentRemoteComic(); - void setCurrentRemoteComic(qulonglong id, Comic * comic); - - //device identification - QString getDeviceType(); - QString getDisplayType(); - void setDeviceType(const QString & device); - void setDisplayType(const QString & display); - - - /*int popPage(); - void pushPage(int page); - int topPage(); - - void clearFoldersPath(); - int popFolder(); - void pushFolder(int page); - int topFolder(); - QStack getFoldersPath();*/ - - void clearNavigationPath(); - QPair popNavigationItem(); - QPair topNavigationItem(); - void pushNavigationItem(const QPair & item); - void updateTopItem(const QPair & item); - - //TODO replace QPair by a custom class for storing folderId, page and folderName(save some DB accesses) - QStack > getNavigationPath(); + /** Check if a key exists. This method is thread safe. */ + bool contains(const QByteArray& key) const; + /** + Get a copy of all data stored in this session. + Changes to the session do not affect the copy and vice versa. + This method is thread safe. + */ + QMap getAll() const; + /** + Get the timestamp of last access. That is the time when the last + HttpSessionStore::getSession() has been called. + This method is thread safe. + */ + qint64 getLastAccess() const; + /** + Set the timestamp of last access, to renew the timeout period. + Called by HttpSessionStore::getSession(). + This method is thread safe. + */ + void setLastAccess(); private: - struct YACReaderSessionData { - //cómics disponibles en dispositivo - QSet comicsOnDevice; - //cómics que han sido descargados o están siendo descargados en esta sesión - QSet downloadedComics; - //cómic actual que está siendo descargado - QString device; - QString display; - qulonglong comicId; - qulonglong remoteComicId; + struct HttpSessionData { - //folder_id, page_number - QStack > navigationPath; + /** Unique ID */ + QByteArray id; - Comic * comic; - Comic * remoteComic; - }; + /** Timestamp of last access, set by the HttpSessionStore */ + qint64 lastAccess; - struct HttpSessionData { + /** Reference counter */ + int refCount; - /** Unique ID */ - QByteArray id; + /** Used to synchronize threads */ + QReadWriteLock lock; - /** Timestamp of last access, set by the HttpSessionStore */ - qint64 lastAccess; + /** Storage for the key/value pairs; */ + QMap values; - /** Reference counter */ - int refCount; + }; - /** Used to synchronize threads */ - QReadWriteLock lock; - - /** Storage for the key/value pairs; */ - QMap values; - - YACReaderSessionData yacreaderSessionData; - - }; - - /** Pointer to the shared data. */ - HttpSessionData* dataPtr; + /** Pointer to the shared data. */ + HttpSessionData* dataPtr; }; diff --git a/YACReaderLibrary/server/lib/httpserver/httpsessionstore.cpp b/YACReaderLibrary/server/lib/httpserver/httpsessionstore.cpp index 87ce7d4a..db6e1c2e 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpsessionstore.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httpsessionstore.cpp @@ -11,10 +11,10 @@ HttpSessionStore::HttpSessionStore(QSettings* settings, QObject* parent) :QObject(parent) { this->settings=settings; - connect(&cleanupTimer,SIGNAL(timeout()),this,SLOT(timerEvent())); + connect(&cleanupTimer,SIGNAL(timeout()),this,SLOT(sessionTimerEvent())); cleanupTimer.start(60000); cookieName=settings->value("cookieName","sessionid").toByteArray(); - expirationTime=settings->value("expirationTime",864000000).toInt(); + expirationTime=settings->value("expirationTime",3600000).toInt(); qDebug("HttpSessionStore: Sessions expire after %i milliseconds",expirationTime); } @@ -23,18 +23,22 @@ HttpSessionStore::~HttpSessionStore() cleanupTimer.stop(); } -QByteArray HttpSessionStore::getSessionId(HttpRequest& request, HttpResponse& response) { +QByteArray HttpSessionStore::getSessionId(HttpRequest& request, HttpResponse& response) +{ // The session ID in the response has priority because this one will be used in the next request. mutex.lock(); // Get the session ID from the response cookie QByteArray sessionId=response.getCookies().value(cookieName).getValue(); - if (sessionId.isEmpty()) { + if (sessionId.isEmpty()) + { // Get the session ID from the request cookie sessionId=request.getCookie(cookieName); } // Clear the session ID if there is no such session in the storage. - if (!sessionId.isEmpty()) { - if (!sessions.contains(sessionId)) { + if (!sessionId.isEmpty()) + { + if (!sessions.contains(sessionId)) + { qDebug("HttpSessionStore: received invalid session cookie with ID %s",sessionId.data()); sessionId.clear(); } @@ -43,21 +47,31 @@ QByteArray HttpSessionStore::getSessionId(HttpRequest& request, HttpResponse& re return sessionId; } -HttpSession HttpSessionStore::getSession(HttpRequest& request, HttpResponse& response, bool allowCreate) { +HttpSession HttpSessionStore::getSession(HttpRequest& request, HttpResponse& response, bool allowCreate) +{ QByteArray sessionId=getSessionId(request,response); mutex.lock(); - if (!sessionId.isEmpty()) { + if (!sessionId.isEmpty()) + { HttpSession session=sessions.value(sessionId); - if (!session.isNull()) { + if (!session.isNull()) + { mutex.unlock(); + // Refresh the session cookie + QByteArray cookieName=settings->value("cookieName","sessionid").toByteArray(); + QByteArray cookiePath=settings->value("cookiePath").toByteArray(); + QByteArray cookieComment=settings->value("cookieComment").toByteArray(); + QByteArray cookieDomain=settings->value("cookieDomain").toByteArray(); + response.setCookie(HttpCookie(cookieName,session.getId(),expirationTime/1000,cookiePath,cookieComment,cookieDomain)); session.setLastAccess(); return session; } } // Need to create a new session - if (allowCreate) { + if (allowCreate) + { QByteArray cookieName=settings->value("cookieName","sessionid").toByteArray(); - QByteArray cookiePath=settings->value("cookiePath","/").toByteArray(); + QByteArray cookiePath=settings->value("cookiePath").toByteArray(); QByteArray cookieComment=settings->value("cookieComment").toByteArray(); QByteArray cookieDomain=settings->value("cookieDomain").toByteArray(); HttpSession session(true); @@ -72,7 +86,8 @@ HttpSession HttpSessionStore::getSession(HttpRequest& request, HttpResponse& res return HttpSession(); } -HttpSession HttpSessionStore::getSession(const QByteArray id) { +HttpSession HttpSessionStore::getSession(const QByteArray id) +{ mutex.lock(); HttpSession session=sessions.value(id); mutex.unlock(); @@ -80,19 +95,19 @@ HttpSession HttpSessionStore::getSession(const QByteArray id) { return session; } -void HttpSessionStore::timerEvent() { - // Todo: find a way to delete sessions only if no controller is accessing them +void HttpSessionStore::sessionTimerEvent() +{ mutex.lock(); qint64 now=QDateTime::currentMSecsSinceEpoch(); QMap::iterator i = sessions.begin(); - while (i != sessions.end()) { + while (i != sessions.end()) + { QMap::iterator prev = i; ++i; HttpSession session=prev.value(); qint64 lastAccess=session.getLastAccess(); - if (now-lastAccess>expirationTime) { //TODO cleaning up will cause current opened comic to be deleted, so clients won't be able to download it - //If the cleaning occurs in the midle of a download it going to cause issues - //Temporal fix: use a big expirationTime = 10 days + if (now-lastAccess>expirationTime) + { qDebug("HttpSessionStore: session %s expired",session.getId().data()); sessions.erase(prev); } @@ -102,7 +117,8 @@ void HttpSessionStore::timerEvent() { /** Delete a session */ -void HttpSessionStore::removeSession(HttpSession session) { +void HttpSessionStore::removeSession(HttpSession session) +{ mutex.lock(); sessions.remove(session.getId()); mutex.unlock(); diff --git a/YACReaderLibrary/server/lib/httpserver/httpsessionstore.h b/YACReaderLibrary/server/lib/httpserver/httpsessionstore.h index e65260d7..5f5efdb4 100644 --- a/YACReaderLibrary/server/lib/httpserver/httpsessionstore.h +++ b/YACReaderLibrary/server/lib/httpserver/httpsessionstore.h @@ -10,6 +10,7 @@ #include #include #include +#include "httpglobal.h" #include "httpsession.h" #include "httpresponse.h" #include "httprequest.h" @@ -25,17 +26,17 @@

   cookiePath=/
   cookieComment=Session ID
-  cookieDomain=stefanfrings.de
+  ;cookieDomain=stefanfrings.de
   
*/ -class HttpSessionStore : public QObject { +class DECLSPEC HttpSessionStore : public QObject { Q_OBJECT Q_DISABLE_COPY(HttpSessionStore) public: /** Constructor. */ - HttpSessionStore(QSettings* settings, QObject* parent); + HttpSessionStore(QSettings* settings, QObject* parent=NULL); /** Destructor */ virtual ~HttpSessionStore(); @@ -75,14 +76,15 @@ public: /** Delete a session */ void removeSession(HttpSession session); +protected: + /** Storage for the sessions */ + QMap sessions; + private: /** Configuration settings */ QSettings* settings; - /** Storage for the sessions */ - QMap sessions; - /** Timer to remove expired sessions */ QTimer cleanupTimer; @@ -98,7 +100,7 @@ private: private slots: /** Called every minute to cleanup expired sessions. */ - void timerEvent(); + void sessionTimerEvent(); }; #endif // HTTPSESSIONSTORE_H diff --git a/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp index c16a3c28..b85e175e 100644 --- a/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp +++ b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp @@ -7,31 +7,25 @@ #include #include #include -#include "httpsession.h" -#include "static.h" -#include - StaticFileController::StaticFileController(QSettings* settings, QObject* parent) :HttpRequestHandler(parent) { maxAge=settings->value("maxAge","60000").toInt(); encoding=settings->value("encoding","UTF-8").toString(); - docroot=settings->value("path","./server/docroot").toString(); - // Convert relative path to absolute, based on the directory of the config file. -#ifdef Q_OS_WIN32 - if (QDir::isRelativePath(docroot) && settings->format()!=QSettings::NativeFormat) -#else - if (QDir::isRelativePath(docroot)) -#endif + docroot=settings->value("path",".").toString(); + if(!(docroot.startsWith(":/") || docroot.startsWith("qrc://"))) + { + // Convert relative path to absolute, based on the directory of the config file. + #ifdef Q_OS_WIN32 + if (QDir::isRelativePath(docroot) && settings->format()!=QSettings::NativeFormat) + #else + if (QDir::isRelativePath(docroot)) + #endif { -#if defined Q_OS_UNIX && ! defined Q_OS_MAC - QFileInfo configFile(QString(DATADIR)+"/yacreader"); - docroot=QFileInfo(QString(DATADIR)+"/yacreader",docroot).absoluteFilePath(); -#else - QFileInfo configFile(QCoreApplication::applicationDirPath()); - docroot=QFileInfo(QCoreApplication::applicationDirPath(),docroot).absoluteFilePath(); -#endif + QFileInfo configFile(settings->fileName()); + docroot=QFileInfo(configFile.absolutePath(),docroot).absoluteFilePath(); + } } qDebug("StaticFileController: docroot=%s, encoding=%s, maxAge=%i",qPrintable(docroot),qPrintable(encoding),maxAge); maxCachedFileSize=settings->value("maxCachedFileSize","65536").toInt(); @@ -41,195 +35,151 @@ StaticFileController::StaticFileController(QSettings* settings, QObject* parent) } -void StaticFileController::service(HttpRequest& request, HttpResponse& response) { +void StaticFileController::service(HttpRequest& request, HttpResponse& response) +{ QByteArray path=request.getPath(); - // Forbid access to files outside the docroot directory - if (path.startsWith("/..")) { - qWarning("StaticFileController: somebody attempted to access a file outside the docroot directory"); - response.setStatus(403,"forbidden"); - response.write("403 forbidden",true); - } - - //TODO(DONE) carga sensible al dispositivo y a la localización - QString stringPath = path; - QStringList paths = QString(path).split('/'); - QString fileName = paths.last(); - stringPath.remove(fileName); - HttpSession session=Static::sessionStore->getSession(request,response,false); - QString device = session.getDeviceType(); - QString display = session.getDisplayType(); - if(fileName.endsWith(".png")) - fileName = getDeviceAwareFileName(fileName, device, display, request.getHeader("Accept-Language"), stringPath); - else - fileName = getDeviceAwareFileName(fileName, device, request.getHeader("Accept-Language"), stringPath); - QString newPath = stringPath.append(fileName); - path = newPath.toLocal8Bit(); - - //CAMBIADO - response.setHeader("Connection","close"); - //END_TODO - // Check if we have the file in cache - //qint64 now=QDateTime::currentMSecsSinceEpoch(); - // mutex.lock(); - // CacheEntry* entry=cache.object(path); - //if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout)) { - // QByteArray document=entry->document; //copy the cached document, because other threads may destroy the cached entry immediately after mutex unlock. - // mutex.unlock(); - // qDebug("StaticFileController: Cache hit for %s",path.data()); - // setContentType(path,response); - // response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000)); - // response.write(document); - //} - //else { - - // mutex.unlock(); - //qDebug("StaticFileController: Cache miss for %s",path.data()); + qint64 now=QDateTime::currentMSecsSinceEpoch(); + mutex.lock(); + CacheEntry* entry=cache.object(path); + if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout)) + { + QByteArray document=entry->document; //copy the cached document, because other threads may destroy the cached entry immediately after mutex unlock. + QByteArray filename=entry->filename; + mutex.unlock(); + qDebug("StaticFileController: Cache hit for %s",path.data()); + setContentType(filename,response); + response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000)); + response.write(document); + } + else + { + mutex.unlock(); // The file is not in cache. + qDebug("StaticFileController: Cache miss for %s",path.data()); + // Forbid access to files outside the docroot directory + if (path.contains("/..")) + { + qWarning("StaticFileController: detected forbidden characters in path %s",path.data()); + response.setStatus(403,"forbidden"); + response.write("403 forbidden",true); + return; + } // If the filename is a directory, append index.html. - if (QFileInfo(docroot+path).isDir()) { + if (QFileInfo(docroot+path).isDir()) + { path+="/index.html"; } - - + // Try to open the file QFile file(docroot+path); - if (file.exists()) { - qDebug("StaticFileController: Open file %s",qPrintable(file.fileName())); - if (file.open(QIODevice::ReadOnly)) { - setContentType(path,response); - //response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000)); - //if (file.size()<=maxCachedFileSize) { - // // Return the file content and store it also in the cache - // entry=new CacheEntry(); - // while (!file.atEnd() && !file.error()) { - // QByteArray buffer=file.read(65536); - // response.write(buffer); - // entry->document.append(buffer); - // } - // entry->created=now; - // mutex.lock(); - // cache.insert(request.getPath(),entry,entry->document.size()); - // mutex.unlock(); - //} - //else { - // Return the file content, do not store in cache*/ - while (!file.atEnd() && !file.error()) { - response.write(file.read(131072)); - //} + qDebug("StaticFileController: Open file %s",qPrintable(file.fileName())); + if (file.open(QIODevice::ReadOnly)) + { + setContentType(path,response); + response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000)); + if (file.size()<=maxCachedFileSize) + { + // Return the file content and store it also in the cache + entry=new CacheEntry(); + while (!file.atEnd() && !file.error()) + { + QByteArray buffer=file.read(65536); + response.write(buffer); + entry->document.append(buffer); } - file.close(); + entry->created=now; + entry->filename=path; + mutex.lock(); + cache.insert(request.getPath(),entry,entry->document.size()); + mutex.unlock(); } - else { + else + { + // Return the file content, do not store in cache + while (!file.atEnd() && !file.error()) + { + response.write(file.read(65536)); + } + } + file.close(); + } + else { + if (file.exists()) + { qWarning("StaticFileController: Cannot open existing file %s for reading",qPrintable(file.fileName())); response.setStatus(403,"forbidden"); response.write("403 forbidden",true); } + else + { + response.setStatus(404,"not found"); + response.write("404 not found",true); + } } - else { - response.setStatus(404,"not found"); - response.write("404 not found",true); - } - //} + } } -void StaticFileController::setContentType(QString fileName, HttpResponse& response) const { - if (fileName.endsWith(".png")) { +void StaticFileController::setContentType(QString fileName, HttpResponse& response) const +{ + if (fileName.endsWith(".png")) + { response.setHeader("Content-Type", "image/png"); } - else if (fileName.endsWith(".jpg")) { + else if (fileName.endsWith(".jpg")) + { response.setHeader("Content-Type", "image/jpeg"); } - else if (fileName.endsWith(".gif")) { + else if (fileName.endsWith(".gif")) + { response.setHeader("Content-Type", "image/gif"); } - else if (fileName.endsWith(".pdf")) { + else if (fileName.endsWith(".pdf")) + { response.setHeader("Content-Type", "application/pdf"); } - else if (fileName.endsWith(".txt")) { + else if (fileName.endsWith(".txt")) + { response.setHeader("Content-Type", qPrintable("text/plain; charset="+encoding)); } - else if (fileName.endsWith(".html") || fileName.endsWith(".htm")) { + else if (fileName.endsWith(".html") || fileName.endsWith(".htm")) + { response.setHeader("Content-Type", qPrintable("text/html; charset="+encoding)); } - else if (fileName.endsWith(".css")) { + else if (fileName.endsWith(".css")) + { response.setHeader("Content-Type", "text/css"); } - else if (fileName.endsWith(".js")) { + else if (fileName.endsWith(".js")) + { response.setHeader("Content-Type", "text/javascript"); } + else if (fileName.endsWith(".svg")) + { + response.setHeader("Content-Type", "image/svg+xml"); + } + else if (fileName.endsWith(".woff")) + { + response.setHeader("Content-Type", "font/woff"); + } + else if (fileName.endsWith(".woff2")) + { + response.setHeader("Content-Type", "font/woff2"); + } + else if (fileName.endsWith(".ttf")) + { + response.setHeader("Content-Type", "application/x-font-ttf"); + } + else if (fileName.endsWith(".eot")) + { + response.setHeader("Content-Type", "application/vnd.ms-fontobject"); + } + else if (fileName.endsWith(".otf")) + { + response.setHeader("Content-Type", "application/font-otf"); + } // Todo: add all of your content types -} - -bool StaticFileController::exists(QString localizedName, QString path) const -{ - QString fileName=docroot+"/"+path + localizedName; - QFile file(fileName); - return file.exists(); -} - -//retorna fileName si no se encontró alternativa traducida ó fileName-locale.extensión si se encontró -QString StaticFileController::getLocalizedFileName(QString fileName, QString locales, QString path) const -{ - QSet tried; // used to suppress duplicate attempts - QStringList locs=locales.split(',',QString::SkipEmptyParts); - QStringList fileNameParts = fileName.split('.'); - QString file = fileNameParts.first(); - QString extension = fileNameParts.last(); - // Search for exact match - foreach (QString loc,locs) { - loc.replace(QRegExp(";.*"),""); - loc.replace('-','_'); - QString localizedName=file+"-"+loc.trimmed()+"."+extension; - if (!tried.contains(localizedName)) { - if(exists(localizedName, path)) - return localizedName; - tried.insert(localizedName); - } - } - - // Search for correct language but any country - foreach (QString loc,locs) { - loc.replace(QRegExp("[;_-].*"),""); - QString localizedName=file+"-"+loc.trimmed()+"."+extension; - if (!tried.contains(localizedName)) { - if(exists(localizedName, path)) - return localizedName; - tried.insert(localizedName); - } - } - - return fileName; -} - -QString StaticFileController::getDeviceAwareFileName(QString fileName, QString device, QString locales, QString path) const -{ - QFileInfo fi(fileName); - QString baseName = fi.baseName(); - QString extension = fi.completeSuffix(); - - QString completeFileName = getLocalizedFileName(baseName+"_"+device+"."+extension,locales,path); - - if(QFile(docroot+"/"+path+completeFileName).exists()) - return completeFileName; //existe un archivo específico para este dispositivo y locales - else - return getLocalizedFileName(fileName,locales,path); //no hay archivo específico para el dispositivo, pero puede haberlo para estas locales -} - -QString StaticFileController::getDeviceAwareFileName(QString fileName, QString device, QString display, QString locales, QString path) const -{ - QFileInfo fi(fileName); - QString baseName = fi.baseName(); - QString extension = fi.completeSuffix(); - - QString completeFileName = baseName+display+"."+extension; - if(QFile(docroot+"/"+path+completeFileName).exists()) - return completeFileName; else { - completeFileName = baseName+"_"+device+display+"."+extension; - if((QFile(docroot+"/"+path+completeFileName).exists())) - return completeFileName; + qDebug("StaticFileController: unknown MIME type for filename '%s'", qPrintable(fileName)); } - - return fileName; } diff --git a/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h index 26413398..b28c8ff6 100644 --- a/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h +++ b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h @@ -6,19 +6,20 @@ #ifndef STATICFILECONTROLLER_H #define STATICFILECONTROLLER_H +#include +#include +#include "httpglobal.h" #include "httprequest.h" #include "httpresponse.h" #include "httprequesthandler.h" -#include -#include /** Delivers static files. It is usually called by the applications main request handler when - the caller request a path that is mapped to static files. + the caller requests a path that is mapped to static files.

The following settings are required in the config file:

-  path=docroot
+  path=../docroot
   encoding=UTF-8
   maxAge=60000
   cacheTime=60000
@@ -39,54 +40,48 @@
   received a related HTTP request.
 */
 
-class StaticFileController : public HttpRequestHandler  {
-	Q_OBJECT
-	Q_DISABLE_COPY(StaticFileController);
+class DECLSPEC StaticFileController : public HttpRequestHandler  {
+    Q_OBJECT
+    Q_DISABLE_COPY(StaticFileController)
 public:
 
-	/** Constructor */
-	StaticFileController(QSettings* settings, QObject* parent = 0);
+    /** Constructor */
+    StaticFileController(QSettings* settings, QObject* parent = NULL);
 
-	/** Generates the response */
-	void service(HttpRequest& request, HttpResponse& response);
+    /** Generates the response */
+    void service(HttpRequest& request, HttpResponse& response);
 
 private:
 
-	/** Encoding of text files */
-	QString encoding;
+    /** Encoding of text files */
+    QString encoding;
 
-	/** Root directory of documents */
-	QString docroot;
+    /** Root directory of documents */
+    QString docroot;
 
-	/** Maximum age of files in the browser cache */
-	int maxAge;    
+    /** Maximum age of files in the browser cache */
+    int maxAge;
 
-	struct CacheEntry {
-		QByteArray document;
-		qint64 created;
-	};
+    struct CacheEntry {
+        QByteArray document;
+        qint64 created;
+        QByteArray filename;
+    };
 
-	/** Timeout for each cached file */
-	int cacheTimeout;
+    /** Timeout for each cached file */
+    int cacheTimeout;
 
+    /** Maximum size of files in cache, larger files are not cached */
+    int maxCachedFileSize;
 
-	/** Maximum size of files in cache, larger files are not cached */
-	int maxCachedFileSize;
+    /** Cache storage */
+    QCache cache;
 
-	/** Cache storage */
-	QCache cache;
+    /** Used to synchronize cache access for threads */
+    QMutex mutex;
 
-	/** Used to synchronize cache access for threads */
-	QMutex mutex;
-
-	/** Set a content-type header in the response depending on the ending of the filename */
-	void setContentType(QString file, HttpResponse& response) const;
-
-	QString getLocalizedFileName(QString fileName, QString locales, QString path) const;
-	QString getDeviceAwareFileName(QString fileName, QString device, QString locales, QString path) const;
-    QString getDeviceAwareFileName(QString fileName, QString device, QString display, QString locales, QString path) const;
-
-	bool exists(QString localizedName, QString path) const;
+    /** Set a content-type header in the response depending on the ending of the filename */
+    void setContentType(QString file, HttpResponse& response) const;
 };
 
 #endif // STATICFILECONTROLLER_H

From 507168cb9311bddf5d728bb343be88da2c78be86 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?=
 
Date: Wed, 22 Jun 2016 20:01:36 +0200
Subject: [PATCH 035/118] Fixed some missing resturns and ambiguous assignments
 in YACReaderHttpSession

---
 YACReaderLibrary/server/yacreader_http_session.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/YACReaderLibrary/server/yacreader_http_session.cpp b/YACReaderLibrary/server/yacreader_http_session.cpp
index 7a9c533e..22049735 100644
--- a/YACReaderLibrary/server/yacreader_http_session.cpp
+++ b/YACReaderLibrary/server/yacreader_http_session.cpp
@@ -54,7 +54,7 @@ qulonglong YACReaderHttpSession::getCurrentComicId()
 
 Comic* YACReaderHttpSession::getCurrentComic()
 {
-    comic;
+    return comic;
 }
 
 void YACReaderHttpSession::dismissCurrentComic()
@@ -70,7 +70,7 @@ void YACReaderHttpSession::setCurrentComic(qulonglong id, Comic * comic)
 {
     dismissCurrentComic();
     comicId = id;
-    comic = comic;
+    this->comic = comic;
 }
 
 //current comic (read)
@@ -114,12 +114,12 @@ void YACReaderHttpSession::setDeviceType(const QString & device)
 {
     //comicsOnDevice.clear(); //TODO crear un m�todo clear que limpie la sesi�n completamente
     //downloadedComics.clear();
-    device = device;
+    this->device = device;
 }
 
 void YACReaderHttpSession::setDisplayType(const QString & display)
 {
-    display = display;
+    this->display = display;
 }
 
 void YACReaderHttpSession::clearNavigationPath()

From 0b9e840bf042ede67bc333cf90c91c62198e6e7a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?=
 
Date: Wed, 22 Jun 2016 20:02:02 +0200
Subject: [PATCH 036/118] Fixed static member initialization.

---
 YACReaderLibrary/server/static.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/YACReaderLibrary/server/static.cpp b/YACReaderLibrary/server/static.cpp
index 49e0060e..0f9d7733 100644
--- a/YACReaderLibrary/server/static.cpp
+++ b/YACReaderLibrary/server/static.cpp
@@ -17,6 +17,8 @@ HttpSessionStore* Static::sessionStore=0;
 
 StaticFileController* Static::staticFileController=0;
 
+QMap Static::yacreaderSessionStore;
+
 QString Static::getConfigFileName() {
     return QString("%1/%2.ini").arg(getConfigDir()).arg(QCoreApplication::applicationName());
 }

From d94f9201ee21bcca078b446fe7670e56ec34491f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?=
 
Date: Wed, 22 Jun 2016 20:07:50 +0200
Subject: [PATCH 037/118] Replaced the no longer existing
 `HttpResponse::writeText` with `HttpResponse::write`.

---
 .../server/controllers/comiccontroller.cpp             | 10 +++++-----
 .../server/controllers/comicdownloadinfocontroller.cpp |  4 ++--
 .../server/controllers/foldercontentcontroller.cpp     |  8 ++++----
 .../server/controllers/foldercontroller.cpp            |  3 +--
 .../server/controllers/folderinfocontroller.cpp        |  4 ++--
 .../server/controllers/librariescontroller.cpp         |  2 +-
 YACReaderLibrary/server/controllers/synccontroller.cpp |  2 +-
 YACReaderLibrary/server/controllers/tagscontroller.cpp |  4 ++--
 .../server/controllers/updatecomiccontroller.cpp       |  2 +-
 .../server/controllers/versioncontroller.cpp           |  2 +-
 10 files changed, 20 insertions(+), 21 deletions(-)

diff --git a/YACReaderLibrary/server/controllers/comiccontroller.cpp b/YACReaderLibrary/server/controllers/comiccontroller.cpp
index a77290a4..e3e6daad 100644
--- a/YACReaderLibrary/server/controllers/comiccontroller.cpp
+++ b/YACReaderLibrary/server/controllers/comiccontroller.cpp
@@ -81,8 +81,8 @@ void ComicController::service(HttpRequest& request, HttpResponse& response)
 
         response.setHeader("Content-Type", "text/plain; charset=utf-8");
 		//TODO this field is not used by the client!
-		response.writeText(QString("library:%1\r\n").arg(libraryName));
-        response.writeText(QString("libraryId:%1\r\n").arg(libraryId));
+        response.write(QString("library:%1\r\n").arg(libraryName).toUtf8());
+        response.write(QString("libraryId:%1\r\n").arg(libraryId).toUtf8());
         if(remoteComic) //send previous and next comics id
         {
             QList siblings = DBHelper::getFolderComicsFromLibrary(libraryId, comic.parentId, true);
@@ -99,9 +99,9 @@ void ComicController::service(HttpRequest& request, HttpResponse& response)
             if(found)
             {
                 if(i>0)
-                    response.writeText(QString("previousComic:%1\r\n").arg(siblings.at(i-1)->id));
+                    response.write(QString("previousComic:%1\r\n").arg(siblings.at(i-1)->id).toUtf8());
                 if(iid));
+                    response.write(QString("nextComic:%1\r\n").arg(siblings.at(i+1)->id).toUtf8());
             }
             else
             {
@@ -109,7 +109,7 @@ void ComicController::service(HttpRequest& request, HttpResponse& response)
             }
             qDeleteAll(siblings);
         }
-        response.writeText(comic.toTXT(),true);
+        response.write(comic.toTXT().toUtf8(),true);
 	}
 	else
 	{
diff --git a/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp b/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp
index 8143ec82..2d35ea8e 100644
--- a/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp
+++ b/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp
@@ -21,6 +21,6 @@ void ComicDownloadInfoController::service(HttpRequest& request, HttpResponse& re
     ComicDB comic = DBHelper::getComicInfo(libraryId, comicId);
 
     //TODO: check if the comic wasn't found;
-    response.writeText(QString("fileName:%1\r\n").arg(comic.getFileName()));
-    response.writeText(QString("fileSize:%1\r\n").arg(comic.getFileSize()),true);
+    response.write(QString("fileName:%1\r\n").arg(comic.getFileName()).toUtf8());
+    response.write(QString("fileSize:%1\r\n").arg(comic.getFileSize()).toUtf8(),true);
 }
diff --git a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp
index 8d88c96a..995493a5 100644
--- a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp
+++ b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp
@@ -32,7 +32,7 @@ void FolderContentController::service(HttpRequest& request, HttpResponse& respon
 
     serviceContent(libraryId, parentId, response);
 
-    response.writeText("",true);
+    response.write("",true);
 }
 
 void FolderContentController::serviceContent(const int &library, const qulonglong &folderId, HttpResponse &response)
@@ -54,17 +54,17 @@ void FolderContentController::serviceContent(const int &library, const qulonglon
         if((*itr)->isDir())
         {
             currentFolder = (Folder *)(*itr);
-            response.writeText(QString("f\t%1\t%2\t%3\t%4\t%5\r\n").arg(library).arg(currentFolder->id).arg(currentFolder->name).arg(currentFolder->getNumChildren()).arg(currentFolder->getFirstChildHash()));
+            response.write(QString("f\t%1\t%2\t%3\t%4\t%5\r\n").arg(library).arg(currentFolder->id).arg(currentFolder->name).arg(currentFolder->getNumChildren()).arg(currentFolder->getFirstChildHash()).toUtf8());
         }
         else
         {
             currentComic = (ComicDB *)(*itr);
-            response.writeText(QString("c\t%1\t%2\t%3\t%4\t%5\r\n").arg(library).arg(currentComic->id).arg(currentComic->getFileName()).arg(currentComic->getFileSize()).arg(currentComic->info.hash));
+            response.write(QString("c\t%1\t%2\t%3\t%4\t%5\r\n").arg(library).arg(currentComic->id).arg(currentComic->getFileName()).arg(currentComic->getFileSize()).arg(currentComic->info.hash).toUtf8());
         }
     }
 
     clock_t end = clock();
     double msecs = double(end - begin);
 
-    response.writeText(QString("%1ms").arg(msecs));
+    response.write(QString("%1ms").arg(msecs).toUtf8());
 }
diff --git a/YACReaderLibrary/server/controllers/foldercontroller.cpp b/YACReaderLibrary/server/controllers/foldercontroller.cpp
index 3ec241a2..d279dd7b 100644
--- a/YACReaderLibrary/server/controllers/foldercontroller.cpp
+++ b/YACReaderLibrary/server/controllers/foldercontroller.cpp
@@ -316,6 +316,5 @@ void FolderController::service(HttpRequest& request, HttpResponse& response)
 	t.setVariable("page",QString("%1").arg(page+1));
 	t.setVariable("pages",QString("%1").arg(numPages));
 
-    response.writeText(t, true);
-
+    response.write(t.toUtf8(), true);
 }
diff --git a/YACReaderLibrary/server/controllers/folderinfocontroller.cpp b/YACReaderLibrary/server/controllers/folderinfocontroller.cpp
index 6aaf9029..b88ed0c2 100644
--- a/YACReaderLibrary/server/controllers/folderinfocontroller.cpp
+++ b/YACReaderLibrary/server/controllers/folderinfocontroller.cpp
@@ -22,7 +22,7 @@ void FolderInfoController::service(HttpRequest& request, HttpResponse& response)
 
     serviceComics(libraryId, parentId, response);
 
-    response.writeText("",true);
+    response.write("",true);
 }
 
 void FolderInfoController::serviceComics(const int &library, const qulonglong &folderId, HttpResponse &response)
@@ -34,7 +34,7 @@ void FolderInfoController::serviceComics(const int &library, const qulonglong &f
     for(QList::const_iterator itr = folderComics.constBegin();itr!=folderComics.constEnd();itr++)
     {
         currentComic = (ComicDB *)(*itr);
-        response.writeText(QString("/library/%1/comic/%2:%3:%4\r\n").arg(library).arg(currentComic->id).arg(currentComic->getFileName()).arg(currentComic->getFileSize()));
+        response.write(QString("/library/%1/comic/%2:%3:%4\r\n").arg(library).arg(currentComic->id).arg(currentComic->getFileName()).arg(currentComic->getFileSize()).toUtf8());
         delete currentComic;
     }
 
diff --git a/YACReaderLibrary/server/controllers/librariescontroller.cpp b/YACReaderLibrary/server/controllers/librariescontroller.cpp
index ac66981c..eb65cffb 100644
--- a/YACReaderLibrary/server/controllers/librariescontroller.cpp
+++ b/YACReaderLibrary/server/controllers/librariescontroller.cpp
@@ -36,5 +36,5 @@ void LibrariesController::service(HttpRequest& request, HttpResponse& response)
 	}
 
     response.setStatus(200,"OK");
-    response.writeText(t,true);
+    response.write(t.toUtf8(),true);
 }
diff --git a/YACReaderLibrary/server/controllers/synccontroller.cpp b/YACReaderLibrary/server/controllers/synccontroller.cpp
index 8d945cc8..29cfea66 100644
--- a/YACReaderLibrary/server/controllers/synccontroller.cpp
+++ b/YACReaderLibrary/server/controllers/synccontroller.cpp
@@ -55,7 +55,7 @@ void SyncController::service(HttpRequest &request, HttpResponse &response)
     else
     {
         response.setStatus(412,"No comic info received");
-        response.writeText("",true);
+        response.write("",true);
         return;
     }
 
diff --git a/YACReaderLibrary/server/controllers/tagscontroller.cpp b/YACReaderLibrary/server/controllers/tagscontroller.cpp
index d1aa877f..68f82708 100644
--- a/YACReaderLibrary/server/controllers/tagscontroller.cpp
+++ b/YACReaderLibrary/server/controllers/tagscontroller.cpp
@@ -23,8 +23,8 @@ void TagsController::service(HttpRequest& request, HttpResponse& response)
 
     foreach(LabelItem * tag, tags)
     {
-        response.writeText(QString("%1\t%2\t%3\r\n").arg(tag->getId()).arg(tag->name()).arg(labelColorToRGBString(tag->colorid())));
+        response.write(QString("%1\t%2\t%3\r\n").arg(tag->getId()).arg(tag->name()).arg(labelColorToRGBString(tag->colorid())).toUtf8());
     }
 
-    response.writeText("",true);
+    response.write("",true);
 }
diff --git a/YACReaderLibrary/server/controllers/updatecomiccontroller.cpp b/YACReaderLibrary/server/controllers/updatecomiccontroller.cpp
index 4682630a..edca4ef3 100644
--- a/YACReaderLibrary/server/controllers/updatecomiccontroller.cpp
+++ b/YACReaderLibrary/server/controllers/updatecomiccontroller.cpp
@@ -38,7 +38,7 @@ void UpdateComicController::service(HttpRequest &request, HttpResponse &response
     else
     {
         response.setStatus(412,"No comic info received");
-        response.writeText("",true);
+        response.write("",true);
         return;
     }
 
diff --git a/YACReaderLibrary/server/controllers/versioncontroller.cpp b/YACReaderLibrary/server/controllers/versioncontroller.cpp
index 341923c7..ca699994 100644
--- a/YACReaderLibrary/server/controllers/versioncontroller.cpp
+++ b/YACReaderLibrary/server/controllers/versioncontroller.cpp
@@ -6,5 +6,5 @@ void VersionController::service(HttpRequest& request, HttpResponse& response)
 {
     Q_UNUSED(request);
 
-    response.writeText(SERVER_VERSION_NUMBER,true);
+    response.write(SERVER_VERSION_NUMBER,true);
 }

From b3aca9b5424ad3345c15085719cc1b635f756a06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?=
 
Date: Wed, 22 Jun 2016 20:12:34 +0200
Subject: [PATCH 038/118] Use the YACReaderHttpSession in request mapper. If
 yareader session is not set, create a YACReaderHttpSession for matching the
 current session.

---
 YACReaderLibrary/server/requestmapper.cpp | 30 ++++++++++++++---------
 1 file changed, 19 insertions(+), 11 deletions(-)

diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp
index d9a98162..5380a194 100644
--- a/YACReaderLibrary/server/requestmapper.cpp
+++ b/YACReaderLibrary/server/requestmapper.cpp
@@ -29,6 +29,8 @@
 #include "db_helper.h"
 #include "yacreader_libraries.h"
 
+#include "yacreader_http_session.h"
+
 #include "QsLog.h"
 
 RequestMapper::RequestMapper(QObject* parent)
@@ -39,6 +41,8 @@ void RequestMapper::loadSession(HttpRequest & request, HttpResponse& response)
     HttpSession session=Static::sessionStore->getSession(request,response);
     if(session.contains("ySession")) //session is already alive check if it is needed to update comics
     {
+        YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId());
+
         QString postData = QString::fromUtf8(request.getBody());
 
         if(postData.contains("currentPage"))
@@ -48,26 +52,30 @@ void RequestMapper::loadSession(HttpRequest & request, HttpResponse& response)
 
             QList data = postData.split("\n");
             if(data.length() > 2) {
-                session.setDeviceType(data.at(0).split(":").at(1));
-                session.setDisplayType(data.at(1).split(":").at(1));
+                ySession->setDeviceType(data.at(0).split(":").at(1));
+                ySession->setDisplayType(data.at(1).split(":").at(1));
                 QList comics = data.at(2).split(":").at(1).split("\t");
-                session.clearComics();
+                ySession->clearComics();
                 foreach(QString hash,comics) {
-                    session.setComicOnDevice(hash);
+                    ySession->setComicOnDevice(hash);
                 }
             }
             else
             {
                 if(data.length()>1)
                 {
-                    session.setDeviceType(data.at(0).split(":").at(1));
-                    session.setDisplayType(data.at(1).split(":").at(1));
+                    ySession->setDeviceType(data.at(0).split(":").at(1));
+                    ySession->setDisplayType(data.at(1).split(":").at(1));
                 }
             }
         }
     }
     else
     {
+        YACReaderHttpSession *ySession = new YACReaderHttpSession(this);
+
+        Static::yacreaderSessionStore.insert(session.getId(), ySession);
+
         session.set("ySession","ok");
 
         QString postData = QString::fromUtf8(request.getBody());
@@ -77,18 +85,18 @@ void RequestMapper::loadSession(HttpRequest & request, HttpResponse& response)
 
         if(data.length() > 2)
         {
-            session.setDeviceType(data.at(0).split(":").at(1));
-            session.setDisplayType(data.at(1).split(":").at(1));
+            ySession->setDeviceType(data.at(0).split(":").at(1));
+            ySession->setDisplayType(data.at(1).split(":").at(1));
             QList comics = data.at(2).split(":").at(1).split("\t");
             foreach(QString hash,comics)
             {
-                session.setComicOnDevice(hash);
+                ySession->setComicOnDevice(hash);
             }
         }
         else //values by default, only for debug purposes.
         {
-            session.setDeviceType("ipad");
-            session.setDisplayType("@2x");
+            ySession->setDeviceType("ipad");
+            ySession->setDisplayType("@2x");
         }
 
     }

From d2e3b51089e867665be140f756074cbce80b8265 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?=
 
Date: Wed, 22 Jun 2016 20:14:37 +0200
Subject: [PATCH 039/118] Use the new session object in the all the controllers
 that need yacreader session information.

---
 .../server/controllers/comiccontroller.cpp    |  8 +++--
 .../server/controllers/covercontroller.cpp    |  7 +++--
 .../server/controllers/foldercontroller.cpp   | 29 ++++++++++---------
 .../controllers/librariescontroller.cpp       |  6 ++--
 .../server/controllers/pagecontroller.cpp     | 15 ++++++----
 5 files changed, 38 insertions(+), 27 deletions(-)

diff --git a/YACReaderLibrary/server/controllers/comiccontroller.cpp b/YACReaderLibrary/server/controllers/comiccontroller.cpp
index e3e6daad..7ffcec58 100644
--- a/YACReaderLibrary/server/controllers/comiccontroller.cpp
+++ b/YACReaderLibrary/server/controllers/comiccontroller.cpp
@@ -2,6 +2,7 @@
 
 #include "db_helper.h"
 #include "yacreader_libraries.h"
+#include "yacreader_http_session.h"
 
 #include "template.h"
 #include "../static.h"
@@ -18,6 +19,7 @@ ComicController::ComicController() {}
 void ComicController::service(HttpRequest& request, HttpResponse& response)
 {
 	HttpSession session=Static::sessionStore->getSession(request,response,false);
+    YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId());
 
     QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8();
 	QStringList pathElements = path.split('/');
@@ -44,7 +46,7 @@ void ComicController::service(HttpRequest& request, HttpResponse& response)
     ComicDB comic = DBHelper::getComicInfo(libraryId, comicId);
 
     if(!remoteComic)
-        session.setDownloadedComic(comic.info.hash);
+        ySession->setDownloadedComic(comic.info.hash);
 
     Comic * comicFile = FactoryComic::newComic(libraries.getPath(libraryId)+comic.path);
 
@@ -70,13 +72,13 @@ void ComicController::service(HttpRequest& request, HttpResponse& response)
         if(remoteComic)
         {
             QLOG_TRACE() << "remote comic requested";
-            session.setCurrentRemoteComic(comic.id, comicFile);
+            ySession->setCurrentRemoteComic(comic.id, comicFile);
 
         }
         else
         {
             QLOG_TRACE() << "comic requested";
-            session.setCurrentComic(comic.id, comicFile);
+            ySession->setCurrentComic(comic.id, comicFile);
         }
 
         response.setHeader("Content-Type", "text/plain; charset=utf-8");
diff --git a/YACReaderLibrary/server/controllers/covercontroller.cpp b/YACReaderLibrary/server/controllers/covercontroller.cpp
index da414c80..b8181e9e 100644
--- a/YACReaderLibrary/server/controllers/covercontroller.cpp
+++ b/YACReaderLibrary/server/controllers/covercontroller.cpp
@@ -1,6 +1,7 @@
 #include "covercontroller.h"
 #include "db_helper.h"  //get libraries
 #include "yacreader_libraries.h"
+#include "yacreader_http_session.h"
 
 #include "template.h"
 #include "../static.h"
@@ -9,8 +10,8 @@ CoverController::CoverController() {}
 
 void CoverController::service(HttpRequest& request, HttpResponse& response)
 {
-
 	HttpSession session=Static::sessionStore->getSession(request,response,false);
+    YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId());
 
 	response.setHeader("Content-Type", "image/jpeg");
 	response.setHeader("Connection","close");
@@ -47,7 +48,7 @@ void CoverController::service(HttpRequest& request, HttpResponse& response)
 	if (!img.isNull()) {
 
         int width = 80, height = 120;
-        if(session.getDisplayType()=="@2x")
+        if(ySession->getDisplayType()=="@2x")
         {
 			width = 160;
             height = 240;
@@ -66,7 +67,7 @@ void CoverController::service(HttpRequest& request, HttpResponse& response)
 
         if(folderCover)
         {
-             if(session.getDisplayType()=="@2x")
+             if(ySession->getDisplayType()=="@2x")
                 p.drawImage(0,0,QImage(":/images/f_overlayed_retina.png"));
              else
                 p.drawImage(0,0,QImage(":/images/f_overlayed.png"));
diff --git a/YACReaderLibrary/server/controllers/foldercontroller.cpp b/YACReaderLibrary/server/controllers/foldercontroller.cpp
index d279dd7b..a8eea99b 100644
--- a/YACReaderLibrary/server/controllers/foldercontroller.cpp
+++ b/YACReaderLibrary/server/controllers/foldercontroller.cpp
@@ -11,6 +11,8 @@
 
 #include "qnaturalsorting.h"
 
+#include "yacreader_http_session.h"
+
 #include "QsLog.h"
 
 struct LibraryItemSorter
@@ -26,6 +28,7 @@ FolderController::FolderController() {}
 void FolderController::service(HttpRequest& request, HttpResponse& response)
 {
 	HttpSession session=Static::sessionStore->getSession(request,response,false);
+    YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId());
 
     response.setHeader("Content-Type", "text/html; charset=utf-8");
 	response.setHeader("Connection","close");
@@ -33,7 +36,7 @@ void FolderController::service(HttpRequest& request, HttpResponse& response)
 	//QString y = session.get("xxx").toString();
 	//response.writeText(QString("session xxx : %1 
").arg(y)); - Template t=Static::templateLoader->getTemplate("folder_"+session.getDeviceType(),request.getHeader("Accept-Language")); + Template t = Static::templateLoader->getTemplate("folder_"+ySession->getDeviceType(),request.getHeader("Accept-Language")); t.enableWarnings(); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); @@ -85,17 +88,17 @@ void FolderController::service(HttpRequest& request, HttpResponse& response) if(folderId == 1) { - session.clearNavigationPath(); - session.pushNavigationItem(QPair(folderId,page)); + ySession->clearNavigationPath(); + ySession->pushNavigationItem(QPair(folderId,page)); t.setVariable(QString("upurl"),"/"); } else { if(fromUp) - session.popNavigationItem(); + ySession->popNavigationItem(); else //drill down or direct access { - QStack > path = session.getNavigationPath(); + QStack > path = ySession->getNavigationPath(); bool found=false; for(QStack >::const_iterator itr = path.begin(); itr!=path.end(); itr++) if(itr->first == folderId) @@ -106,16 +109,16 @@ void FolderController::service(HttpRequest& request, HttpResponse& response) if(found) { - while(session.topNavigationItem().first != folderId) - session.popNavigationItem(); + while(ySession->topNavigationItem().first != folderId) + ySession->popNavigationItem(); - session.updateTopItem(QPair(folderId,page)); + ySession->updateTopItem(QPair(folderId,page)); } else - session.pushNavigationItem(QPair(folderId,page)); + ySession->pushNavigationItem(QPair(folderId,page)); } - QStack > path = session.getNavigationPath(); + QStack > path = ySession->getNavigationPath(); if(path.count()>1) { QPair parentItem = path.at(path.count()-2); @@ -146,7 +149,7 @@ void FolderController::service(HttpRequest& request, HttpResponse& response) int numFoldersAtCurrentPage = qMax(0,qMin(numFolders - indexCurrentPage, elementsPerPage)); //PATH - QStack > foldersPath = session.getNavigationPath(); + QStack > foldersPath = ySession->getNavigationPath(); t.setVariable(QString("library.name"),libraryName); t.setVariable(QString("library.url"),QString("/library/%1/folder/1").arg(libraryId)); t.loop("path",foldersPath.count()-1); @@ -195,9 +198,9 @@ void FolderController::service(HttpRequest& request, HttpResponse& response) const ComicDB * comic = (ComicDB *)item; t.setVariable(QString("element%1.browse").arg(i),""); //t.setVariable(QString("element%1.downloadurl").arg(i),"/library/"+libraryName+"/comic/"+QString("%1").arg(comic->id)); - if(!session.isComicOnDevice(comic->info.hash) && !session.isComicDownloaded(comic->info.hash)) + if(!ySession->isComicOnDevice(comic->info.hash) && !ySession->isComicDownloaded(comic->info.hash)) t.setVariable(QString("element%1.download").arg(i),QString("IMPORT").arg("/library/"+QString::number(libraryId)+"/comic/"+QString("%1").arg(comic->id))); - else if (session.isComicOnDevice(comic->info.hash)) + else if (ySession->isComicOnDevice(comic->info.hash)) t.setVariable(QString("element%1.download").arg(i),QString("
IMPORTED
")); else t.setVariable(QString("element%1.download").arg(i),QString("
IMPORTING
")); diff --git a/YACReaderLibrary/server/controllers/librariescontroller.cpp b/YACReaderLibrary/server/controllers/librariescontroller.cpp index eb65cffb..24d12612 100644 --- a/YACReaderLibrary/server/controllers/librariescontroller.cpp +++ b/YACReaderLibrary/server/controllers/librariescontroller.cpp @@ -1,6 +1,7 @@ #include "librariescontroller.h" #include "db_helper.h" //get libraries #include "yacreader_libraries.h" +#include "yacreader_http_session.h" #include "template.h" #include "../static.h" @@ -12,13 +13,14 @@ LibrariesController::LibrariesController() {} void LibrariesController::service(HttpRequest& request, HttpResponse& response) { HttpSession session=Static::sessionStore->getSession(request,response,false); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId()); response.setHeader("Content-Type", "text/html; charset=utf-8"); response.setHeader("Connection","close"); - session.clearNavigationPath(); + ySession->clearNavigationPath(); - Template t=Static::templateLoader->getTemplate("libraries_"+session.getDeviceType(),request.getHeader("Accept-Language")); + Template t=Static::templateLoader->getTemplate("libraries_"+ySession->getDeviceType(),request.getHeader("Accept-Language")); t.enableWarnings(); YACReaderLibraries libraries = DBHelper::getLibraries(); diff --git a/YACReaderLibrary/server/controllers/pagecontroller.cpp b/YACReaderLibrary/server/controllers/pagecontroller.cpp index 1ab15543..eda6fee2 100644 --- a/YACReaderLibrary/server/controllers/pagecontroller.cpp +++ b/YACReaderLibrary/server/controllers/pagecontroller.cpp @@ -4,6 +4,8 @@ #include "comic.h" #include "comiccontroller.h" +#include "yacreader_http_session.h" + #include #include @@ -16,6 +18,7 @@ PageController::PageController() {} void PageController::service(HttpRequest& request, HttpResponse& response) { HttpSession session=Static::sessionStore->getSession(request,response,false); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId()); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); bool remote = path.endsWith("remote"); @@ -35,14 +38,14 @@ void PageController::service(HttpRequest& request, HttpResponse& response) if(remote) { QLOG_TRACE() << "se recupera comic remoto para servir páginas"; - comicFile = session.getCurrentRemoteComic(); - currentComicId = session.getCurrentRemoteComicId(); + comicFile = ySession->getCurrentRemoteComic(); + currentComicId = ySession->getCurrentRemoteComicId(); } else { QLOG_TRACE() << "se recupera comic para servir páginas"; - comicFile = session.getCurrentComic(); - currentComicId = session.getCurrentComicId(); + comicFile = ySession->getCurrentComic(); + currentComicId = ySession->getCurrentComicId(); } if(currentComicId != 0 && !QPointer(comicFile).isNull()) @@ -77,9 +80,9 @@ void PageController::service(HttpRequest& request, HttpResponse& response) { //delete comicFile; if(remote) - session.dismissCurrentRemoteComic(); + ySession->dismissCurrentRemoteComic(); else - session.dismissCurrentComic(); + ySession->dismissCurrentComic(); } response.setStatus(404,"not found"); //TODO qué mensaje enviar response.write("404 not found",true); From 45ab9bc71fcf884bdbbac2ee3577f79f158687ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 24 Jun 2016 17:26:51 +0200 Subject: [PATCH 040/118] Added a new class for storing YACReaderHttpSession objects. --- YACReaderLibrary/server/server.pri | 6 ++++-- .../server/yacreader_http_session_store.cpp | 6 ++++++ .../server/yacreader_http_session_store.h | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 YACReaderLibrary/server/yacreader_http_session_store.cpp create mode 100644 YACReaderLibrary/server/yacreader_http_session_store.h diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index 4dc0e3ca..99114153 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -20,7 +20,8 @@ HEADERS += \ $$PWD/controllers/versioncontroller.h \ $$PWD/controllers/foldercontentcontroller.h \ $$PWD/controllers/tagscontroller.h \ - $$PWD/yacreader_http_session.h + $$PWD/yacreader_http_session.h \ + $$PWD/yacreader_http_session_store.h SOURCES += \ $$PWD/static.cpp \ @@ -41,7 +42,8 @@ SOURCES += \ $$PWD/controllers/versioncontroller.cpp \ $$PWD/controllers/foldercontentcontroller.cpp \ $$PWD/controllers/tagscontroller.cpp \ - $$PWD/yacreader_http_session.cpp + $$PWD/yacreader_http_session.cpp \ + $$PWD/yacreader_http_session_store.cpp include(lib/logging/logging.pri) include(lib/httpserver/httpserver.pri) diff --git a/YACReaderLibrary/server/yacreader_http_session_store.cpp b/YACReaderLibrary/server/yacreader_http_session_store.cpp new file mode 100644 index 00000000..e1458141 --- /dev/null +++ b/YACReaderLibrary/server/yacreader_http_session_store.cpp @@ -0,0 +1,6 @@ +#include "yacreader_http_session_store.h" + +YACReaderHttpSessionStore::YACReaderHttpSessionStore(QObject *parent) : QObject(parent) +{ + +} diff --git a/YACReaderLibrary/server/yacreader_http_session_store.h b/YACReaderLibrary/server/yacreader_http_session_store.h new file mode 100644 index 00000000..56cc3abe --- /dev/null +++ b/YACReaderLibrary/server/yacreader_http_session_store.h @@ -0,0 +1,17 @@ +#ifndef YACREADERHTTPSESSIONSTORE_H +#define YACREADERHTTPSESSIONSTORE_H + +#include + +class YACReaderHttpSessionStore : public QObject +{ + Q_OBJECT +public: + explicit YACReaderHttpSessionStore(QObject *parent = 0); + +signals: + +public slots: +}; + +#endif // YACREADERHTTPSESSIONSTORE_H \ No newline at end of file From 0788ae12a26bede78995f22fa803f580c1038ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 24 Jun 2016 18:28:22 +0200 Subject: [PATCH 041/118] Implemented YACReaderHttpSessionStore. --- .../server/yacreader_http_session_store.cpp | 40 ++++++++++++++++++- .../server/yacreader_http_session_store.h | 26 +++++++++++- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/YACReaderLibrary/server/yacreader_http_session_store.cpp b/YACReaderLibrary/server/yacreader_http_session_store.cpp index e1458141..10f71105 100644 --- a/YACReaderLibrary/server/yacreader_http_session_store.cpp +++ b/YACReaderLibrary/server/yacreader_http_session_store.cpp @@ -1,6 +1,42 @@ #include "yacreader_http_session_store.h" -YACReaderHttpSessionStore::YACReaderHttpSessionStore(QObject *parent) : QObject(parent) -{ +#include "httpsessionstore.h" + + +YACReaderHttpSessionStore::YACReaderHttpSessionStore(HttpSessionStore *sessionStore, QObject *parent) + : QObject(parent), sessionStore(sessionStore) +{ + connect(&cleanupTimer,SIGNAL(timeout()),this,SLOT(sessionTimerEvent())); + cleanupTimer.start(60000); +} + +void YACReaderHttpSessionStore::setYACReaderHttpSession(const QByteArray &httpSessionId, YACReaderHttpSession *yacreaderHttpSession) +{ + QMutexLocker locker(&mutex); + + sessions.insert(httpSessionId, yacreaderHttpSession); +} + +YACReaderHttpSession *YACReaderHttpSessionStore::getYACReaderSessionHttpSession(const QByteArray &httpSessionId) +{ + QMutexLocker locker(&mutex); + + return sessions.value(id, nullptr); +} + +void YACReaderHttpSessionStore::sessionTimerEvent() +{ + QMutexLocker locker(&mutex); + for(const QByteArray &id : sessions.keys()) + { + if(sessionStore->getSession(id).isNull()) + { + YACReaderHttpSession *session = sessions.value(id, nullptr); + if(session != nullptr) + delete session; + + sessions.remove(id); + } + } } diff --git a/YACReaderLibrary/server/yacreader_http_session_store.h b/YACReaderLibrary/server/yacreader_http_session_store.h index 56cc3abe..fe88cbdf 100644 --- a/YACReaderLibrary/server/yacreader_http_session_store.h +++ b/YACReaderLibrary/server/yacreader_http_session_store.h @@ -2,16 +2,38 @@ #define YACREADERHTTPSESSIONSTORE_H #include +#include + + + +class HttpSessionStore; +class YACReaderHttpSession; + + class YACReaderHttpSessionStore : public QObject { Q_OBJECT public: - explicit YACReaderHttpSessionStore(QObject *parent = 0); + explicit YACReaderHttpSessionStore(HttpSessionStore *sessionStore, QObject *parent = 0); + + void setYACReaderHttpSession(const QByteArray & httpSessionId, YACReaderHttpSession *yacreaderHttpSession); + YACReaderHttpSession *getYACReaderSessionHttpSession(const QByteArray & httpSessionId); signals: public slots: + +private: + QMap sessions; + HttpSessionStore *sessionStore; + QTimer cleanupTimer; + + QMutex mutex; + +private slots: + + void sessionTimerEvent(); }; -#endif // YACREADERHTTPSESSIONSTORE_H \ No newline at end of file +#endif // YACREADERHTTPSESSIONSTORE_H From 0358fab47ad8e2cd2b1d1c7436f486f597e8c42c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 24 Jun 2016 18:28:44 +0200 Subject: [PATCH 042/118] Replace the yacreader sessions map with the new store. --- YACReaderLibrary/server/static.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/server/static.h b/YACReaderLibrary/server/static.h index 3425ba7b..154f2ad9 100644 --- a/YACReaderLibrary/server/static.h +++ b/YACReaderLibrary/server/static.h @@ -11,7 +11,7 @@ #include "httpsessionstore.h" #include "staticfilecontroller.h" -#include "yacreader_http_session.h" +#include "yacreader_http_session_store.h" /** This class contains some static resources that are used by the application. @@ -53,7 +53,7 @@ public: /** Storage for session cookies */ static HttpSessionStore* sessionStore; - static QMap yacreaderSessionStore; + static YACReaderHttpSessionStore* yacreaderSessionStore; /** Controller for static files */ static StaticFileController* staticFileController; From c408a77e85d4be32f79f7aedd98791b78c8fab78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 24 Jun 2016 18:29:08 +0200 Subject: [PATCH 043/118] Initialize the static pointer to the yacreader session store. --- YACReaderLibrary/server/static.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YACReaderLibrary/server/static.cpp b/YACReaderLibrary/server/static.cpp index 0f9d7733..0e8693f7 100644 --- a/YACReaderLibrary/server/static.cpp +++ b/YACReaderLibrary/server/static.cpp @@ -17,7 +17,7 @@ HttpSessionStore* Static::sessionStore=0; StaticFileController* Static::staticFileController=0; -QMap Static::yacreaderSessionStore; +YACReaderHttpSessionStore* Static::yacreaderSessionStore=0; QString Static::getConfigFileName() { return QString("%1/%2.ini").arg(getConfigDir()).arg(QCoreApplication::applicationName()); From bde712d54baa322c5682a47e8543e24611095589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 24 Jun 2016 18:29:44 +0200 Subject: [PATCH 044/118] Initialize `Static::yacreaderSessionStore`. --- YACReaderLibrary/server/startup.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/YACReaderLibrary/server/startup.cpp b/YACReaderLibrary/server/startup.cpp index d6ce9fa8..321035dc 100644 --- a/YACReaderLibrary/server/startup.cpp +++ b/YACReaderLibrary/server/startup.cpp @@ -77,6 +77,8 @@ void Startup::start() { sessionSettings->beginGroup("sessions"); Static::sessionStore=new HttpSessionStore(sessionSettings,app); + Static::yacreaderSessionStore = new YACReaderHttpSessionStore(Static::sessionStore, sessionSettings, app); + // Configure static file controller QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat,app); fileSettings->beginGroup("docroot"); From d29719a98dbe53886d9944404ed30144677b59e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 24 Jun 2016 18:32:22 +0200 Subject: [PATCH 045/118] Renamed the method used for adding sessions. --- YACReaderLibrary/server/yacreader_http_session_store.cpp | 2 +- YACReaderLibrary/server/yacreader_http_session_store.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/server/yacreader_http_session_store.cpp b/YACReaderLibrary/server/yacreader_http_session_store.cpp index 10f71105..122c7b9c 100644 --- a/YACReaderLibrary/server/yacreader_http_session_store.cpp +++ b/YACReaderLibrary/server/yacreader_http_session_store.cpp @@ -11,7 +11,7 @@ YACReaderHttpSessionStore::YACReaderHttpSessionStore(HttpSessionStore *sessionSt cleanupTimer.start(60000); } -void YACReaderHttpSessionStore::setYACReaderHttpSession(const QByteArray &httpSessionId, YACReaderHttpSession *yacreaderHttpSession) +void YACReaderHttpSessionStore::addYACReaderHttpSession(const QByteArray &httpSessionId, YACReaderHttpSession *yacreaderHttpSession) { QMutexLocker locker(&mutex); diff --git a/YACReaderLibrary/server/yacreader_http_session_store.h b/YACReaderLibrary/server/yacreader_http_session_store.h index fe88cbdf..6001c7ab 100644 --- a/YACReaderLibrary/server/yacreader_http_session_store.h +++ b/YACReaderLibrary/server/yacreader_http_session_store.h @@ -17,7 +17,7 @@ class YACReaderHttpSessionStore : public QObject public: explicit YACReaderHttpSessionStore(HttpSessionStore *sessionStore, QObject *parent = 0); - void setYACReaderHttpSession(const QByteArray & httpSessionId, YACReaderHttpSession *yacreaderHttpSession); + void addYACReaderHttpSession(const QByteArray & httpSessionId, YACReaderHttpSession *yacreaderHttpSession); YACReaderHttpSession *getYACReaderSessionHttpSession(const QByteArray & httpSessionId); signals: From 0f247d8641b217f76a1c41e62633f9377b2cbe61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 24 Jun 2016 18:51:30 +0200 Subject: [PATCH 046/118] Added destructor to YACReaderHttpSession --- YACReaderLibrary/server/yacreader_http_session.cpp | 9 +++++++++ YACReaderLibrary/server/yacreader_http_session.h | 1 + 2 files changed, 10 insertions(+) diff --git a/YACReaderLibrary/server/yacreader_http_session.cpp b/YACReaderLibrary/server/yacreader_http_session.cpp index 22049735..24ad65d7 100644 --- a/YACReaderLibrary/server/yacreader_http_session.cpp +++ b/YACReaderLibrary/server/yacreader_http_session.cpp @@ -6,6 +6,15 @@ YACReaderHttpSession::YACReaderHttpSession(QObject *parent) } +YACReaderHttpSession::~YACReaderHttpSession() +{ + if(comic != nullptr) + delete comic; + + if(remoteComic != nullptr) + delete remoteComic; +} + bool YACReaderHttpSession::isComicOnDevice(const QString & hash) { return comicsOnDevice.contains(hash); diff --git a/YACReaderLibrary/server/yacreader_http_session.h b/YACReaderLibrary/server/yacreader_http_session.h index cc6a3131..7b1b677a 100644 --- a/YACReaderLibrary/server/yacreader_http_session.h +++ b/YACReaderLibrary/server/yacreader_http_session.h @@ -12,6 +12,7 @@ class YACReaderHttpSession : public QObject Q_OBJECT public: explicit YACReaderHttpSession(QObject *parent = 0); + ~YACReaderHttpSession(); void setComicsOnDevice(const QSet & set); void setComicOnDevice(const QString & hash); From 31018a4c38601626b3af6f0ba551a8e154114808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 24 Jun 2016 18:51:50 +0200 Subject: [PATCH 047/118] Fixed compilation errors. --- YACReaderLibrary/server/startup.cpp | 2 +- YACReaderLibrary/server/yacreader_http_session_store.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/server/startup.cpp b/YACReaderLibrary/server/startup.cpp index 321035dc..a983ab89 100644 --- a/YACReaderLibrary/server/startup.cpp +++ b/YACReaderLibrary/server/startup.cpp @@ -77,7 +77,7 @@ void Startup::start() { sessionSettings->beginGroup("sessions"); Static::sessionStore=new HttpSessionStore(sessionSettings,app); - Static::yacreaderSessionStore = new YACReaderHttpSessionStore(Static::sessionStore, sessionSettings, app); + Static::yacreaderSessionStore = new YACReaderHttpSessionStore(Static::sessionStore, app); // Configure static file controller QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat,app); diff --git a/YACReaderLibrary/server/yacreader_http_session_store.cpp b/YACReaderLibrary/server/yacreader_http_session_store.cpp index 122c7b9c..355ecba9 100644 --- a/YACReaderLibrary/server/yacreader_http_session_store.cpp +++ b/YACReaderLibrary/server/yacreader_http_session_store.cpp @@ -22,7 +22,7 @@ YACReaderHttpSession *YACReaderHttpSessionStore::getYACReaderSessionHttpSession( { QMutexLocker locker(&mutex); - return sessions.value(id, nullptr); + return sessions.value(httpSessionId, nullptr); } void YACReaderHttpSessionStore::sessionTimerEvent() From 10c261c77aad6cc8487617b3e0fb7f34702a7dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 24 Jun 2016 18:54:31 +0200 Subject: [PATCH 048/118] Fixed access to `Static::yacreaderSessionStore` members. --- YACReaderLibrary/server/controllers/comiccontroller.cpp | 2 +- YACReaderLibrary/server/controllers/covercontroller.cpp | 2 +- YACReaderLibrary/server/controllers/foldercontroller.cpp | 2 +- YACReaderLibrary/server/controllers/librariescontroller.cpp | 2 +- YACReaderLibrary/server/controllers/pagecontroller.cpp | 2 +- YACReaderLibrary/server/requestmapper.cpp | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/YACReaderLibrary/server/controllers/comiccontroller.cpp b/YACReaderLibrary/server/controllers/comiccontroller.cpp index 7ffcec58..7538067d 100644 --- a/YACReaderLibrary/server/controllers/comiccontroller.cpp +++ b/YACReaderLibrary/server/controllers/comiccontroller.cpp @@ -19,7 +19,7 @@ ComicController::ComicController() {} void ComicController::service(HttpRequest& request, HttpResponse& response) { HttpSession session=Static::sessionStore->getSession(request,response,false); - YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId()); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); diff --git a/YACReaderLibrary/server/controllers/covercontroller.cpp b/YACReaderLibrary/server/controllers/covercontroller.cpp index b8181e9e..09251bef 100644 --- a/YACReaderLibrary/server/controllers/covercontroller.cpp +++ b/YACReaderLibrary/server/controllers/covercontroller.cpp @@ -11,7 +11,7 @@ CoverController::CoverController() {} void CoverController::service(HttpRequest& request, HttpResponse& response) { HttpSession session=Static::sessionStore->getSession(request,response,false); - YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId()); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); response.setHeader("Content-Type", "image/jpeg"); response.setHeader("Connection","close"); diff --git a/YACReaderLibrary/server/controllers/foldercontroller.cpp b/YACReaderLibrary/server/controllers/foldercontroller.cpp index a8eea99b..275418b8 100644 --- a/YACReaderLibrary/server/controllers/foldercontroller.cpp +++ b/YACReaderLibrary/server/controllers/foldercontroller.cpp @@ -28,7 +28,7 @@ FolderController::FolderController() {} void FolderController::service(HttpRequest& request, HttpResponse& response) { HttpSession session=Static::sessionStore->getSession(request,response,false); - YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId()); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); response.setHeader("Content-Type", "text/html; charset=utf-8"); response.setHeader("Connection","close"); diff --git a/YACReaderLibrary/server/controllers/librariescontroller.cpp b/YACReaderLibrary/server/controllers/librariescontroller.cpp index 24d12612..547fb578 100644 --- a/YACReaderLibrary/server/controllers/librariescontroller.cpp +++ b/YACReaderLibrary/server/controllers/librariescontroller.cpp @@ -13,7 +13,7 @@ LibrariesController::LibrariesController() {} void LibrariesController::service(HttpRequest& request, HttpResponse& response) { HttpSession session=Static::sessionStore->getSession(request,response,false); - YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId()); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); response.setHeader("Content-Type", "text/html; charset=utf-8"); response.setHeader("Connection","close"); diff --git a/YACReaderLibrary/server/controllers/pagecontroller.cpp b/YACReaderLibrary/server/controllers/pagecontroller.cpp index eda6fee2..b0af2613 100644 --- a/YACReaderLibrary/server/controllers/pagecontroller.cpp +++ b/YACReaderLibrary/server/controllers/pagecontroller.cpp @@ -18,7 +18,7 @@ PageController::PageController() {} void PageController::service(HttpRequest& request, HttpResponse& response) { HttpSession session=Static::sessionStore->getSession(request,response,false); - YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId()); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); bool remote = path.endsWith("remote"); diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp index 5380a194..a04b4556 100644 --- a/YACReaderLibrary/server/requestmapper.cpp +++ b/YACReaderLibrary/server/requestmapper.cpp @@ -41,7 +41,7 @@ void RequestMapper::loadSession(HttpRequest & request, HttpResponse& response) HttpSession session=Static::sessionStore->getSession(request,response); if(session.contains("ySession")) //session is already alive check if it is needed to update comics { - YACReaderHttpSession *ySession = Static::yacreaderSessionStore.value(session.getId()); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); QString postData = QString::fromUtf8(request.getBody()); @@ -74,7 +74,7 @@ void RequestMapper::loadSession(HttpRequest & request, HttpResponse& response) { YACReaderHttpSession *ySession = new YACReaderHttpSession(this); - Static::yacreaderSessionStore.insert(session.getId(), ySession); + Static::yacreaderSessionStore->addYACReaderHttpSession(session.getId(), ySession); session.set("ySession","ok"); From 3baeac6bac35177e3182eac716e6611592d3a5e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 24 Jun 2016 21:09:41 +0200 Subject: [PATCH 049/118] Set the path of the docs root. --- YACReaderLibrary/server/startup.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/YACReaderLibrary/server/startup.cpp b/YACReaderLibrary/server/startup.cpp index a983ab89..6970c6e9 100644 --- a/YACReaderLibrary/server/startup.cpp +++ b/YACReaderLibrary/server/startup.cpp @@ -82,6 +82,21 @@ void Startup::start() { // Configure static file controller QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat,app); fileSettings->beginGroup("docroot"); + + QString basedocroot = "./server/docroot"; + QString docroot; + + #if defined Q_OS_UNIX && ! defined Q_OS_MAC + QFileInfo configFile(QString(DATADIR)+"/yacreader"); + docroot=QFileInfo(QString(DATADIR)+"/yacreader",basedocroot).absoluteFilePath(); + #else + QFileInfo configFile(QCoreApplication::applicationDirPath()); + docroot=QFileInfo(QCoreApplication::applicationDirPath(),basedocroot).absoluteFilePath(); + #endif + + if(fileSettings->value("path").isNull()) + fileSettings->setValue("path",docroot); + Static::staticFileController=new StaticFileController(fileSettings,app); // Configure and start the TCP listener From 9cd592622a526ded37c1568ba4fdaf5d750c258b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 24 Jun 2016 23:03:12 +0200 Subject: [PATCH 050/118] Restored StaticFileController custom code. --- .../lib/httpserver/staticfilecontroller.cpp | 103 ++++++++++++++++++ .../lib/httpserver/staticfilecontroller.h | 7 ++ 2 files changed, 110 insertions(+) diff --git a/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp index b85e175e..dd940d3e 100644 --- a/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp +++ b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp @@ -8,6 +8,12 @@ #include #include +//YACReader----- +#include "httpsession.h" +#include "yacreader_http_session.h" +#include "static.h" +//-- + StaticFileController::StaticFileController(QSettings* settings, QObject* parent) :HttpRequestHandler(parent) { @@ -55,6 +61,27 @@ void StaticFileController::service(HttpRequest& request, HttpResponse& response) else { mutex.unlock(); + + //TODO(DONE) carga sensible al dispositivo y a la localización + QString stringPath = path; + QStringList paths = QString(path).split('/'); + QString fileName = paths.last(); + stringPath.remove(fileName); + HttpSession session=Static::sessionStore->getSession(request,response,false); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); + QString device = ySession->getDeviceType(); + QString display = ySession->getDisplayType(); + if(fileName.endsWith(".png")) + fileName = getDeviceAwareFileName(fileName, device, display, request.getHeader("Accept-Language"), stringPath); + else + fileName = getDeviceAwareFileName(fileName, device, request.getHeader("Accept-Language"), stringPath); + QString newPath = stringPath.append(fileName); + path = newPath.toLocal8Bit(); + + //CAMBIADO + //response.setHeader("Connection","close"); + //END_TODO + // The file is not in cache. qDebug("StaticFileController: Cache miss for %s",path.data()); // Forbid access to files outside the docroot directory @@ -183,3 +210,79 @@ void StaticFileController::setContentType(QString fileName, HttpResponse& respon qDebug("StaticFileController: unknown MIME type for filename '%s'", qPrintable(fileName)); } } + +//YACReader------------------------------------------------------------------------ + +bool StaticFileController::exists(QString localizedName, QString path) const +{ + QString fileName=docroot+"/"+path + localizedName; + QFile file(fileName); + return file.exists(); +} + +//retorna fileName si no se encontró alternativa traducida ó fileName-locale.extensión si se encontró +QString StaticFileController::getLocalizedFileName(QString fileName, QString locales, QString path) const +{ + QSet tried; // used to suppress duplicate attempts + QStringList locs=locales.split(',',QString::SkipEmptyParts); + QStringList fileNameParts = fileName.split('.'); + QString file = fileNameParts.first(); + QString extension = fileNameParts.last(); + // Search for exact match + foreach (QString loc,locs) { + loc.replace(QRegExp(";.*"),""); + loc.replace('-','_'); + QString localizedName=file+"-"+loc.trimmed()+"."+extension; + if (!tried.contains(localizedName)) { + if(exists(localizedName, path)) + return localizedName; + tried.insert(localizedName); + } + } + + // Search for correct language but any country + foreach (QString loc,locs) { + loc.replace(QRegExp("[;_-].*"),""); + QString localizedName=file+"-"+loc.trimmed()+"."+extension; + if (!tried.contains(localizedName)) { + if(exists(localizedName, path)) + return localizedName; + tried.insert(localizedName); + } + } + + return fileName; +} + +QString StaticFileController::getDeviceAwareFileName(QString fileName, QString device, QString locales, QString path) const +{ + QFileInfo fi(fileName); + QString baseName = fi.baseName(); + QString extension = fi.completeSuffix(); + + QString completeFileName = getLocalizedFileName(baseName+"_"+device+"."+extension,locales,path); + + if(QFile(docroot+"/"+path+completeFileName).exists()) + return completeFileName; //existe un archivo específico para este dispositivo y locales + else + return getLocalizedFileName(fileName,locales,path); //no hay archivo específico para el dispositivo, pero puede haberlo para estas locales +} + +QString StaticFileController::getDeviceAwareFileName(QString fileName, QString device, QString display, QString locales, QString path) const +{ + QFileInfo fi(fileName); + QString baseName = fi.baseName(); + QString extension = fi.completeSuffix(); + + QString completeFileName = baseName+display+"."+extension; + if(QFile(docroot+"/"+path+completeFileName).exists()) + return completeFileName; + else + { + completeFileName = baseName+"_"+device+display+"."+extension; + if((QFile(docroot+"/"+path+completeFileName).exists())) + return completeFileName; + } + + return fileName; +} diff --git a/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h index b28c8ff6..47424d75 100644 --- a/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h +++ b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h @@ -82,6 +82,13 @@ private: /** Set a content-type header in the response depending on the ending of the filename */ void setContentType(QString file, HttpResponse& response) const; + + //YACReader------------------------------------------------------------------------ + QString getLocalizedFileName(QString fileName, QString locales, QString path) const; + QString getDeviceAwareFileName(QString fileName, QString device, QString locales, QString path) const; + QString getDeviceAwareFileName(QString fileName, QString device, QString display, QString locales, QString path) const; + + bool exists(QString localizedName, QString path) const; }; #endif // STATICFILECONTROLLER_H From ed130010e636788a5a74a6a6417e004d45a55375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sat, 25 Jun 2016 08:32:58 +0200 Subject: [PATCH 051/118] Set more default parameters for the server. --- YACReaderLibrary/server/startup.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/YACReaderLibrary/server/startup.cpp b/YACReaderLibrary/server/startup.cpp index 6970c6e9..379e5137 100644 --- a/YACReaderLibrary/server/startup.cpp +++ b/YACReaderLibrary/server/startup.cpp @@ -75,6 +75,10 @@ void Startup::start() { // Configure session store QSettings* sessionSettings=new QSettings(configFileName,QSettings::IniFormat,app); sessionSettings->beginGroup("sessions"); + + if(sessionSettings->value("expirationTime").isNull()) + sessionSettings->setValue("expirationTime",864000000); + Static::sessionStore=new HttpSessionStore(sessionSettings,app); Static::yacreaderSessionStore = new YACReaderHttpSessionStore(Static::sessionStore, app); @@ -103,6 +107,22 @@ void Startup::start() { qDebug("ServiceHelper: Starting service"); QSettings* listenerSettings=new QSettings(configFileName,QSettings::IniFormat,app); listenerSettings->beginGroup("listener"); + + if(listenerSettings->value("maxRequestSize").isNull()) + listenerSettings->setValue("maxRequestSize","32000000"); + + if(listenerSettings->value("maxMultiPartSize").isNull()) + listenerSettings->setValue("maxMultiPartSize","32000000"); + + if(listenerSettings->value("cleanupInterval").isNull()) + listenerSettings->setValue("cleanupInterval",10000); + + if(listenerSettings->value("minThreads").isNull()) + listenerSettings->setValue("maxThreads",1000); + + if(listenerSettings->value("minThreads").isNull()) + listenerSettings->setValue("minThreads",50); + listener = new HttpListener(listenerSettings,new RequestMapper(app),app); qDebug("ServiceHelper: Service has started"); From a8932463f73097968c074eaa6eb54efd21530977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sat, 25 Jun 2016 08:37:48 +0200 Subject: [PATCH 052/118] Try to listen even if the default port is no available. --- .../server/lib/httpserver/httplistener.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/YACReaderLibrary/server/lib/httpserver/httplistener.cpp b/YACReaderLibrary/server/lib/httpserver/httplistener.cpp index 7314ec74..3f638f31 100644 --- a/YACReaderLibrary/server/lib/httpserver/httplistener.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httplistener.cpp @@ -39,9 +39,19 @@ void HttpListener::listen() QString host = settings->value("host").toString(); int port=settings->value("port").toInt(); QTcpServer::listen(host.isEmpty() ? QHostAddress::Any : QHostAddress(host), port); + + //YACReader--------------------------------------------- + //try to listen even if the default port is no available + int i = 0; + while (!isListening() && i < 1000) { + listen(QHostAddress::Any, (rand() % 45535)+20000); + i++; + } + //------------------------------------------------------ + if (!isListening()) { - qCritical("HttpListener: Cannot bind on port %i: %s",port,qPrintable(errorString())); + qDebug("HttpListener: Cannot bind on port %i: %s",port,qPrintable(errorString())); } else { qDebug("HttpListener: Listening on port %i",port); From c3a0cafcc024143e30031b806425e148e76eb55a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sat, 25 Jun 2016 08:50:37 +0200 Subject: [PATCH 053/118] fixed compilation --- YACReaderLibrary/server/lib/httpserver/httplistener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YACReaderLibrary/server/lib/httpserver/httplistener.cpp b/YACReaderLibrary/server/lib/httpserver/httplistener.cpp index 3f638f31..59596064 100644 --- a/YACReaderLibrary/server/lib/httpserver/httplistener.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httplistener.cpp @@ -44,7 +44,7 @@ void HttpListener::listen() //try to listen even if the default port is no available int i = 0; while (!isListening() && i < 1000) { - listen(QHostAddress::Any, (rand() % 45535)+20000); + QTcpServer::listen(QHostAddress::Any, (rand() % 45535)+20000); i++; } //------------------------------------------------------ From 9cfb4e0f38362453c6bf1841545c3942407b60e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 22 Aug 2016 13:44:43 +0200 Subject: [PATCH 054/118] Fixed warning. --- YACReaderLibrary/server/yacreader_http_session_store.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/YACReaderLibrary/server/yacreader_http_session_store.cpp b/YACReaderLibrary/server/yacreader_http_session_store.cpp index 355ecba9..52a55bd3 100644 --- a/YACReaderLibrary/server/yacreader_http_session_store.cpp +++ b/YACReaderLibrary/server/yacreader_http_session_store.cpp @@ -1,5 +1,7 @@ #include "yacreader_http_session_store.h" +#include "yacreader_http_session.h" + #include "httpsessionstore.h" From b0b13de52db04553e35cdc72f09b159485fdf54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 22 Aug 2016 23:03:18 +0200 Subject: [PATCH 055/118] New method for getting the comics in a tag (aka label). --- YACReaderLibrary/db_helper.cpp | 42 ++++++++++++++++++++++++++++++++++ YACReaderLibrary/db_helper.h | 2 ++ 2 files changed, 44 insertions(+) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 3af2b8b4..973d7af1 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -159,6 +159,48 @@ QString DBHelper::getLibraryName(int id) { return getLibraries().getName(id); } + +QList DBHelper::getLabelComics(qulonglong libraryId, qulonglong labelId) +{ + QString libraryPath = DBHelper::getLibraries().getPath(libraryId); + QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary"); + + QList list; + + { + QSqlQuery selectQuery(db); + selectQuery.prepare("SELECT ci.number,ci.title,c.fileName,ci.numPages,c.id,c.parentId,c.path,ci.hash,ci.read,ci.isBis,ci.currentPage,ci.rating,ci.hasBeenOpened " + "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) " + "INNER JOIN comic_label cl ON (c.id == cl.comic_id) " + "WHERE cl.label_id = :parentLabelId " + "ORDER BY cl.ordering"); + selectQuery.bindValue(":parentLabelId", labelId); + selectQuery.exec(); + + while (selectQuery.next()) + { + ComicDB comic; + + QList data; + QSqlRecord record = selectQuery.record(); + for(int i=0;i getLibrariesNames(); static QString getLibraryName(int id); + static QList getLabelComics(qulonglong libraryId, qulonglong labelId); //objects management //deletes @@ -75,6 +76,7 @@ public: static QList getSortedComicsFromParent(qulonglong parentId, QSqlDatabase & db); static QList getComicsFromParent(qulonglong parentId, QSqlDatabase & db, bool sort = true); static QList getLabelItems(qulonglong libraryId); + //load static Folder loadFolder(qulonglong id, QSqlDatabase & db); static Folder loadFolder(const QString & folderName, qulonglong parentId, QSqlDatabase & db); From de4540367f65fd875234e0153aba2b4ef5c820d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 22 Aug 2016 23:04:43 +0200 Subject: [PATCH 056/118] Extracted the data generation for services to a helper class. --- .../server/yacreader_server_data_helper.cpp | 26 +++++++++++++++++++ .../server/yacreader_server_data_helper.h | 19 ++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 YACReaderLibrary/server/yacreader_server_data_helper.cpp create mode 100644 YACReaderLibrary/server/yacreader_server_data_helper.h diff --git a/YACReaderLibrary/server/yacreader_server_data_helper.cpp b/YACReaderLibrary/server/yacreader_server_data_helper.cpp new file mode 100644 index 00000000..bbfe6624 --- /dev/null +++ b/YACReaderLibrary/server/yacreader_server_data_helper.cpp @@ -0,0 +1,26 @@ +#include "yacreader_server_data_helper.h" + +QString YACReaderServerDataHelper::folderToYSFormat(const qulonglong libraryId, const Folder & folder) +{ + return QString("f\t%1\t%2\t%3\t%4\t%5\r\n") + .arg(libraryId) + .arg(folder.id) + .arg(folder.name) + .arg(folder.getNumChildren()) + .arg(folder.getFirstChildHash()); +} + +QString YACReaderServerDataHelper::comicToYSFormat(const qulonglong libraryId,const ComicDB & comic) +{ + return QString("c\t%1\t%2\t%3\t%4\t%5\r\n") + .arg(libraryId) + .arg(comic.id) + .arg(comic.getFileName()) + .arg(comic.getFileSize()) + .arg(comic.info.hash); +} + +YACReaderServerDataHelper::YACReaderServerDataHelper() +{ + +} diff --git a/YACReaderLibrary/server/yacreader_server_data_helper.h b/YACReaderLibrary/server/yacreader_server_data_helper.h new file mode 100644 index 00000000..b09da28d --- /dev/null +++ b/YACReaderLibrary/server/yacreader_server_data_helper.h @@ -0,0 +1,19 @@ +#ifndef YACREADERSERVERDATAHELPER_H +#define YACREADERSERVERDATAHELPER_H + +#include +#include "folder.h" +#include "comic_db.h" + +class YACReaderServerDataHelper +{ +public: + static QString folderToYSFormat(const qulonglong libraryId, const Folder & folder); + static QString comicToYSFormat(const qulonglong libraryId, const ComicDB & comic); + +private: + YACReaderServerDataHelper(); + +}; + +#endif // YACREADERSERVERDATAHELPER_H From 1d73d1f1bf7c1a3c8e5e700da1de98f4b7a7f126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 22 Aug 2016 23:05:07 +0200 Subject: [PATCH 057/118] Use the new helper class in FolderContentController. --- .../server/controllers/foldercontentcontroller.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp index 995493a5..2411947e 100644 --- a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp +++ b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp @@ -6,6 +6,8 @@ #include "comic_db.h" #include "folder.h" +#include "yacreader_server_data_helper.h" + #include "qnaturalsorting.h" #include @@ -54,12 +56,12 @@ void FolderContentController::serviceContent(const int &library, const qulonglon if((*itr)->isDir()) { currentFolder = (Folder *)(*itr); - response.write(QString("f\t%1\t%2\t%3\t%4\t%5\r\n").arg(library).arg(currentFolder->id).arg(currentFolder->name).arg(currentFolder->getNumChildren()).arg(currentFolder->getFirstChildHash()).toUtf8()); + response.write(YACReaderServerDataHelper::folderToYSFormat(library, *currentFolder).toUtf8()); } else { currentComic = (ComicDB *)(*itr); - response.write(QString("c\t%1\t%2\t%3\t%4\t%5\r\n").arg(library).arg(currentComic->id).arg(currentComic->getFileName()).arg(currentComic->getFileSize()).arg(currentComic->info.hash).toUtf8()); + response.write(YACReaderServerDataHelper::comicToYSFormat(library, *currentComic).toUtf8()); } } From 4e50c5628dfeb4c61cb2e40d90721beaf8713827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 22 Aug 2016 23:05:36 +0200 Subject: [PATCH 058/118] Add new class for serving the content of tag. --- .../controllers/tagcontentcontroller.cpp | 37 +++++++++++++++++++ .../server/controllers/tagcontentcontroller.h | 22 +++++++++++ 2 files changed, 59 insertions(+) create mode 100644 YACReaderLibrary/server/controllers/tagcontentcontroller.cpp create mode 100644 YACReaderLibrary/server/controllers/tagcontentcontroller.h diff --git a/YACReaderLibrary/server/controllers/tagcontentcontroller.cpp b/YACReaderLibrary/server/controllers/tagcontentcontroller.cpp new file mode 100644 index 00000000..6f077b8f --- /dev/null +++ b/YACReaderLibrary/server/controllers/tagcontentcontroller.cpp @@ -0,0 +1,37 @@ +#include "tagcontentcontroller.h" + +#include "db_helper.h" +#include "comic_db.h" + +#include "yacreader_server_data_helper.h" + +#include + +TagContentController::TagContentController() +{ + +} + +void TagContentController::service(HttpRequest &request, HttpResponse &response) +{ + response.setHeader("Content-Type", "text/plain; charset=utf-8"); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + QStringList pathElements = path.split('/'); + int libraryId = pathElements.at(2).toInt(); + qulonglong tagId = pathElements.at(4).toULongLong(); + + serviceContent(libraryId, tagId, response); + + response.write("",true); +} + +void TagContentController::serviceContent(const int &library, const qulonglong &tagId, HttpResponse &response) +{ + QList tagComics = DBHelper::getLabelComics(library, tagId); + + for(const ComicDB &comic : tagComics) + { + response.write(YACReaderServerDataHelper::comicToYSFormat(library, comic).toUtf8()); + } +} diff --git a/YACReaderLibrary/server/controllers/tagcontentcontroller.h b/YACReaderLibrary/server/controllers/tagcontentcontroller.h new file mode 100644 index 00000000..ab43646a --- /dev/null +++ b/YACReaderLibrary/server/controllers/tagcontentcontroller.h @@ -0,0 +1,22 @@ +#ifndef TAGCONTENTCONTROLLER_H +#define TAGCONTENTCONTROLLER_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class TagContentController : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(TagContentController); +public: + /** Constructor */ + TagContentController(); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); + +private: + void serviceContent(const int &library, const qulonglong &tagId, HttpResponse &response); +}; + +#endif // TAGCONTENTCONTROLLER_H From df87a14dde343132d9fe41e9c7421ff3f009a2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 22 Aug 2016 23:06:33 +0200 Subject: [PATCH 059/118] Include new classes in server.pri --- YACReaderLibrary/server/server.pri | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index 99114153..5378df73 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -21,7 +21,9 @@ HEADERS += \ $$PWD/controllers/foldercontentcontroller.h \ $$PWD/controllers/tagscontroller.h \ $$PWD/yacreader_http_session.h \ - $$PWD/yacreader_http_session_store.h + $$PWD/yacreader_http_session_store.h \ + $$PWD/controllers/tagcontentcontroller.h \ + $$PWD/yacreader_server_data_helper.h SOURCES += \ $$PWD/static.cpp \ @@ -43,7 +45,9 @@ SOURCES += \ $$PWD/controllers/foldercontentcontroller.cpp \ $$PWD/controllers/tagscontroller.cpp \ $$PWD/yacreader_http_session.cpp \ - $$PWD/yacreader_http_session_store.cpp + $$PWD/yacreader_http_session_store.cpp \ + $$PWD/controllers/tagcontentcontroller.cpp \ + $$PWD/yacreader_server_data_helper.cpp include(lib/logging/logging.pri) include(lib/httpserver/httpserver.pri) From db3f2b64312ec197b7734fc1659f90d9e7475ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 22 Aug 2016 23:07:48 +0200 Subject: [PATCH 060/118] Use the new tag content controller in the request mapper. --- YACReaderLibrary/server/requestmapper.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp index a04b4556..22a554c0 100644 --- a/YACReaderLibrary/server/requestmapper.cpp +++ b/YACReaderLibrary/server/requestmapper.cpp @@ -25,6 +25,7 @@ #include "controllers/versioncontroller.h" #include "controllers/foldercontentcontroller.h" #include "controllers/tagscontroller.h" +#include "controllers/tagcontentcontroller.h" #include "db_helper.h" #include "yacreader_libraries.h" @@ -119,6 +120,7 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { QRegExp serverVersion("/version/?"); QRegExp folderContent("/library/.+/folder/[0-9]+/content/?"); QRegExp tags("/library/.+/tags/?"); + QRegExp tagContent("/library/.+/tag/[0-9]+/content/?"); QRegExp sync("/sync"); @@ -189,6 +191,10 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { { TagsController().service(request, response); } + else if(tagContent.exactMatch(path)) + { + TagContentController().service(request, response); + } } else { From ab795212c657134d8c88f0c8d8f27008ac72f649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 23 Aug 2016 18:03:03 +0200 Subject: [PATCH 061/118] Spacing. --- YACReaderLibrary/server/yacreader_server_data_helper.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/YACReaderLibrary/server/yacreader_server_data_helper.cpp b/YACReaderLibrary/server/yacreader_server_data_helper.cpp index bbfe6624..105511bd 100644 --- a/YACReaderLibrary/server/yacreader_server_data_helper.cpp +++ b/YACReaderLibrary/server/yacreader_server_data_helper.cpp @@ -20,7 +20,4 @@ QString YACReaderServerDataHelper::comicToYSFormat(const qulonglong libraryId,co .arg(comic.info.hash); } -YACReaderServerDataHelper::YACReaderServerDataHelper() -{ - -} +YACReaderServerDataHelper::YACReaderServerDataHelper() {} From 2aa5c00d3452280e1171b54f09e33a8c987ddd69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 23 Aug 2016 18:03:31 +0200 Subject: [PATCH 062/118] Include missing info in YSFormat for comics. --- YACReaderLibrary/server/yacreader_server_data_helper.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/server/yacreader_server_data_helper.cpp b/YACReaderLibrary/server/yacreader_server_data_helper.cpp index 105511bd..494f8a78 100644 --- a/YACReaderLibrary/server/yacreader_server_data_helper.cpp +++ b/YACReaderLibrary/server/yacreader_server_data_helper.cpp @@ -12,12 +12,15 @@ QString YACReaderServerDataHelper::folderToYSFormat(const qulonglong libraryId, QString YACReaderServerDataHelper::comicToYSFormat(const qulonglong libraryId,const ComicDB & comic) { - return QString("c\t%1\t%2\t%3\t%4\t%5\r\n") + return QString("c\t%1\t%2\t%3\t%4\t%5\t%6\t%7\t%8\r\n") .arg(libraryId) .arg(comic.id) .arg(comic.getFileName()) .arg(comic.getFileSize()) - .arg(comic.info.hash); + .arg(comic.info.hash) + .arg(comic.info.currentPage) + .arg(comic.info.numPages.toInt()) + .arg(comic.info.read?1:0); } YACReaderServerDataHelper::YACReaderServerDataHelper() {} From e5671806fc06e17f03e535d34124ffc4e13f9c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 23 Aug 2016 18:04:20 +0200 Subject: [PATCH 063/118] Optimize comic info loading for the server. --- YACReaderLibrary/db_helper.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 973d7af1..72c82017 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -169,7 +169,7 @@ QList DBHelper::getLabelComics(qulonglong libraryId, qulonglong labelId { QSqlQuery selectQuery(db); - selectQuery.prepare("SELECT ci.number,ci.title,c.fileName,ci.numPages,c.id,c.parentId,c.path,ci.hash,ci.read,ci.isBis,ci.currentPage,ci.rating,ci.hasBeenOpened " + selectQuery.prepare("SELECT c.id,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash " "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) " "INNER JOIN comic_label cl ON (c.id == cl.comic_id) " "WHERE cl.label_id = :parentLabelId " @@ -186,11 +186,13 @@ QList DBHelper::getLabelComics(qulonglong libraryId, qulonglong labelId for(int i=0;i Date: Tue, 23 Aug 2016 18:07:19 +0200 Subject: [PATCH 064/118] New method for loading the comics in favorites (server listing). --- YACReaderLibrary/db_helper.cpp | 42 ++++++++++++++++++++++++++++++++++ YACReaderLibrary/db_helper.h | 1 + 2 files changed, 43 insertions(+) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 72c82017..54d45b1c 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -203,6 +203,48 @@ QList DBHelper::getLabelComics(qulonglong libraryId, qulonglong labelId return list; } +QList DBHelper::getFavorites(qulonglong libraryId) +{ + QString libraryPath = DBHelper::getLibraries().getPath(libraryId); + QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary"); + + QList list; + + const int FAV_ID = 1; + + { + QSqlQuery selectQuery(db); + selectQuery.prepare("SELECT c.id,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash " + "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) " + "INNER JOIN comic_default_reading_list cdrl ON (c.id == cdrl.comic_id) " + "WHERE cdrl.default_reading_list_id = :parentDefaultListId " + "ORDER BY cdrl.ordering"); + selectQuery.bindValue(":parentDefaultListId", 1); + selectQuery.exec(); + + while (selectQuery.next()) + { + ComicDB comic; + + QSqlRecord record = selectQuery.record(); + + comic.id = record.value(0).toULongLong(); + comic.parentId = FAV_ID; + comic.name = record.value(1).toString(); + comic.info.title = record.value(2).toString(); + comic.info.currentPage = record.value(3).toInt(); + comic.info.numPages = record.value(4).toInt(); + comic.info.hash = record.value(5).toString(); + + list.append(comic); + } + + db.close(); + } + + return list; +} + //objects management //deletes void DBHelper::removeFromDB(LibraryItem * item, QSqlDatabase & db) diff --git a/YACReaderLibrary/db_helper.h b/YACReaderLibrary/db_helper.h index f5e8aa26..e41f64be 100644 --- a/YACReaderLibrary/db_helper.h +++ b/YACReaderLibrary/db_helper.h @@ -32,6 +32,7 @@ public: static QList getLibrariesNames(); static QString getLibraryName(int id); static QList getLabelComics(qulonglong libraryId, qulonglong labelId); + static QList getFavorites(qulonglong libraryId); //objects management //deletes From 69b83dae5b40fc74d9b885984a95dd11a2c938ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 23 Aug 2016 18:20:16 +0200 Subject: [PATCH 065/118] New controller for serving favoritess. --- .../controllers/favoritescontroller.cpp | 33 +++++++++++++++++++ .../server/controllers/favoritescontroller.h | 21 ++++++++++++ YACReaderLibrary/server/server.pri | 6 ++-- 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 YACReaderLibrary/server/controllers/favoritescontroller.cpp create mode 100644 YACReaderLibrary/server/controllers/favoritescontroller.h diff --git a/YACReaderLibrary/server/controllers/favoritescontroller.cpp b/YACReaderLibrary/server/controllers/favoritescontroller.cpp new file mode 100644 index 00000000..dbb44556 --- /dev/null +++ b/YACReaderLibrary/server/controllers/favoritescontroller.cpp @@ -0,0 +1,33 @@ +#include "favoritescontroller.h" + +#include "db_helper.h" +#include "comic_db.h" + +#include "yacreader_server_data_helper.h" + +FavoritesController::FavoritesController() {} + +void FavoritesController::service(HttpRequest &request, HttpResponse &response) +{ + response.setHeader("Content-Type", "text/plain; charset=utf-8"); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + QStringList pathElements = path.split('/'); + int libraryId = pathElements.at(2).toInt(); + + serviceContent(libraryId, response); + + response.write("",true); +} + +void FavoritesController::serviceContent(const int library, HttpResponse &response) +{ + QList tagComics = DBHelper::getFavorites(library); + + for(const ComicDB &comic : tagComics) + { + response.write(YACReaderServerDataHelper::comicToYSFormat(library, comic).toUtf8()); + } +} + + diff --git a/YACReaderLibrary/server/controllers/favoritescontroller.h b/YACReaderLibrary/server/controllers/favoritescontroller.h new file mode 100644 index 00000000..bf7efa78 --- /dev/null +++ b/YACReaderLibrary/server/controllers/favoritescontroller.h @@ -0,0 +1,21 @@ +#ifndef FAVORITESCONTROLLER_H +#define FAVORITESCONTROLLER_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class FavoritesController : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(FavoritesController); +public: + FavoritesController(); + + void service(HttpRequest& request, HttpResponse& response); + +private: + void serviceContent(const int library, HttpResponse &response); +}; + + +#endif // FAVORITESCONTROLLER_H diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index 5378df73..d54d0a33 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -23,7 +23,8 @@ HEADERS += \ $$PWD/yacreader_http_session.h \ $$PWD/yacreader_http_session_store.h \ $$PWD/controllers/tagcontentcontroller.h \ - $$PWD/yacreader_server_data_helper.h + $$PWD/yacreader_server_data_helper.h \ + $$PWD/controllers/favoritescontroller.h SOURCES += \ $$PWD/static.cpp \ @@ -47,7 +48,8 @@ SOURCES += \ $$PWD/yacreader_http_session.cpp \ $$PWD/yacreader_http_session_store.cpp \ $$PWD/controllers/tagcontentcontroller.cpp \ - $$PWD/yacreader_server_data_helper.cpp + $$PWD/yacreader_server_data_helper.cpp \ + $$PWD/controllers/favoritescontroller.cpp include(lib/logging/logging.pri) include(lib/httpserver/httpserver.pri) From 036fad4a77aa057a59432ff2df6ed9e0d9b10c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 23 Aug 2016 18:21:14 +0200 Subject: [PATCH 066/118] Use the new crotroller in request mapper. --- YACReaderLibrary/server/requestmapper.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp index 22a554c0..d27c435b 100644 --- a/YACReaderLibrary/server/requestmapper.cpp +++ b/YACReaderLibrary/server/requestmapper.cpp @@ -26,6 +26,7 @@ #include "controllers/foldercontentcontroller.h" #include "controllers/tagscontroller.h" #include "controllers/tagcontentcontroller.h" +#include "controllers/favoritescontroller.h" #include "db_helper.h" #include "yacreader_libraries.h" @@ -119,6 +120,7 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { QRegExp comicPageRemote("/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); //get comic page (remote reading) QRegExp serverVersion("/version/?"); QRegExp folderContent("/library/.+/folder/[0-9]+/content/?"); + QRegExp favs("/library/.+/favs/?"); QRegExp tags("/library/.+/tags/?"); QRegExp tagContent("/library/.+/tag/[0-9]+/content/?"); @@ -195,6 +197,10 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { { TagContentController().service(request, response); } + else if(favs.exactMatch(path)) + { + FavoritesController().service(request, response); + } } else { From 94251d30452481ac6d6567a3d4e92c0dc9af6b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 23 Aug 2016 22:03:20 +0200 Subject: [PATCH 067/118] Fixed how Favorites are loaded by using the right sorting. --- YACReaderLibrary/db/comic_model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YACReaderLibrary/db/comic_model.cpp b/YACReaderLibrary/db/comic_model.cpp index 33346196..ec758603 100644 --- a/YACReaderLibrary/db/comic_model.cpp +++ b/YACReaderLibrary/db/comic_model.cpp @@ -582,7 +582,7 @@ void ComicModel::setupFavoritesModelData(const QString &databasePath) "ORDER BY cdrl.ordering"); selectQuery.bindValue(":parentDefaultListId", 1); selectQuery.exec(); - setupModelData(selectQuery); + setupModelDataForList(selectQuery); } db.close(); QSqlDatabase::removeDatabase(_databasePath); From a6204bcc6552f8956d44e0a7946cc2f0976dfdb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 23 Aug 2016 22:03:56 +0200 Subject: [PATCH 068/118] Fixed Favorites resorting. --- YACReaderLibrary/db_helper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 54d45b1c..0b6903ba 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -650,7 +650,7 @@ void DBHelper::reasignOrderToComicsInFavorites(QList comicIds, QSqlD QSqlQuery updateOrdering(db); updateOrdering.prepare("UPDATE comic_default_reading_list SET " "ordering = :ordering " - "WHERE comic_id = :comic_id AND default_reading_list_id = 0"); + "WHERE comic_id = :comic_id AND default_reading_list_id = 1"); db.transaction(); int order = 0; foreach(qulonglong id, comicIds) From 983a035c312c5321511e6cab261e15a53261defd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 23 Aug 2016 22:22:46 +0200 Subject: [PATCH 069/118] New method for getting the comics being read, plus added the read state to comics loaded for being served. --- YACReaderLibrary/db_helper.cpp | 44 ++++++++++++++++++++++++++++++++-- YACReaderLibrary/db_helper.h | 1 + 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 0b6903ba..d9d31aff 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -169,7 +169,7 @@ QList DBHelper::getLabelComics(qulonglong libraryId, qulonglong labelId { QSqlQuery selectQuery(db); - selectQuery.prepare("SELECT c.id,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash " + selectQuery.prepare("SELECT c.id,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash,ci.read " "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) " "INNER JOIN comic_label cl ON (c.id == cl.comic_id) " "WHERE cl.label_id = :parentLabelId " @@ -193,6 +193,7 @@ QList DBHelper::getLabelComics(qulonglong libraryId, qulonglong labelId comic.info.currentPage = record.value(3).toInt(); comic.info.numPages = record.value(4).toInt(); comic.info.hash = record.value(5).toString(); + comic.info.read = record.value(6).toBool(); list.append(comic); } @@ -214,7 +215,7 @@ QList DBHelper::getFavorites(qulonglong libraryId) { QSqlQuery selectQuery(db); - selectQuery.prepare("SELECT c.id,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash " + selectQuery.prepare("SELECT c.id,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash,ci.read " "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) " "INNER JOIN comic_default_reading_list cdrl ON (c.id == cdrl.comic_id) " "WHERE cdrl.default_reading_list_id = :parentDefaultListId " @@ -235,6 +236,45 @@ QList DBHelper::getFavorites(qulonglong libraryId) comic.info.currentPage = record.value(3).toInt(); comic.info.numPages = record.value(4).toInt(); comic.info.hash = record.value(5).toString(); + comic.info.read = record.value(6).toBool(); + + list.append(comic); + } + + db.close(); + } + + return list; +} + +QList DBHelper::getReading(qulonglong libraryId) +{ + QString libraryPath = DBHelper::getLibraries().getPath(libraryId); + QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary"); + + QList list; + + { + QSqlQuery selectQuery(db); + selectQuery.prepare("SELECT c.id,c.parentId,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash,ci.read " + "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) " + "WHERE ci.hasBeenOpened = 1 AND ci.read = 0 AND ci.currentPage != ci.numPages AND ci.currentPage != 1"); + selectQuery.exec(); + + while (selectQuery.next()) + { + ComicDB comic; + + QSqlRecord record = selectQuery.record(); + + comic.id = record.value(0).toULongLong(); + comic.parentId = record.value(1).toULongLong(); + comic.name = record.value(2).toString(); + comic.info.title = record.value(3).toString(); + comic.info.currentPage = record.value(4).toInt(); + comic.info.numPages = record.value(5).toInt(); + comic.info.hash = record.value(6).toString(); + comic.info.read = record.value(7).toBool(); list.append(comic); } diff --git a/YACReaderLibrary/db_helper.h b/YACReaderLibrary/db_helper.h index e41f64be..9bf99c63 100644 --- a/YACReaderLibrary/db_helper.h +++ b/YACReaderLibrary/db_helper.h @@ -33,6 +33,7 @@ public: static QString getLibraryName(int id); static QList getLabelComics(qulonglong libraryId, qulonglong labelId); static QList getFavorites(qulonglong libraryId); + static QList getReading(qulonglong libraryId); //objects management //deletes From 87e9cf92ff294b3de539482b143aa49f8883860a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 23 Aug 2016 22:23:06 +0200 Subject: [PATCH 070/118] Use FAV_ID instead of hardcoded value. --- YACReaderLibrary/db_helper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index d9d31aff..8667cf1e 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -220,7 +220,7 @@ QList DBHelper::getFavorites(qulonglong libraryId) "INNER JOIN comic_default_reading_list cdrl ON (c.id == cdrl.comic_id) " "WHERE cdrl.default_reading_list_id = :parentDefaultListId " "ORDER BY cdrl.ordering"); - selectQuery.bindValue(":parentDefaultListId", 1); + selectQuery.bindValue(":parentDefaultListId", FAV_ID); selectQuery.exec(); while (selectQuery.next()) From dbc9c9848ae7e13a8a9fc0ea8a983cbf35b82206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 23 Aug 2016 22:23:26 +0200 Subject: [PATCH 071/118] New controller for serving the comics being read. --- .../controllers/readingcomicscontroller.cpp | 37 +++++++++++++++++++ .../controllers/readingcomicscontroller.h | 20 ++++++++++ YACReaderLibrary/server/server.pri | 6 ++- 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 YACReaderLibrary/server/controllers/readingcomicscontroller.cpp create mode 100644 YACReaderLibrary/server/controllers/readingcomicscontroller.h diff --git a/YACReaderLibrary/server/controllers/readingcomicscontroller.cpp b/YACReaderLibrary/server/controllers/readingcomicscontroller.cpp new file mode 100644 index 00000000..77f0124e --- /dev/null +++ b/YACReaderLibrary/server/controllers/readingcomicscontroller.cpp @@ -0,0 +1,37 @@ +#include "readingcomicscontroller.h" + +#include "db_helper.h" +#include "comic_db.h" + +#include "yacreader_server_data_helper.h" + +ReadingComicsController::ReadingComicsController() +{ + +} + +void ReadingComicsController::service(HttpRequest &request, HttpResponse &response) +{ + response.setHeader("Content-Type", "text/plain; charset=utf-8"); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + QStringList pathElements = path.split('/'); + int libraryId = pathElements.at(2).toInt(); + + serviceContent(libraryId, response); + + response.write("",true); +} + +void ReadingComicsController::serviceContent(const int &library, HttpResponse &response) +{ + //TODO sort comics before serving them + int FIXME; + + QList tagComics = DBHelper::getReading(library); + + for(const ComicDB &comic : tagComics) + { + response.write(YACReaderServerDataHelper::comicToYSFormat(library, comic).toUtf8()); + } +} diff --git a/YACReaderLibrary/server/controllers/readingcomicscontroller.h b/YACReaderLibrary/server/controllers/readingcomicscontroller.h new file mode 100644 index 00000000..da20aab5 --- /dev/null +++ b/YACReaderLibrary/server/controllers/readingcomicscontroller.h @@ -0,0 +1,20 @@ +#ifndef READINGCOMICSCONTROLLER_H +#define READINGCOMICSCONTROLLER_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class ReadingComicsController : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(ReadingComicsController) +public: + ReadingComicsController(); + + void service(HttpRequest& request, HttpResponse& response); + +private: + void serviceContent(const int &library, HttpResponse &response); +}; + +#endif // READINGCOMICSCONTROLLER_H diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index d54d0a33..b64ee632 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -24,7 +24,8 @@ HEADERS += \ $$PWD/yacreader_http_session_store.h \ $$PWD/controllers/tagcontentcontroller.h \ $$PWD/yacreader_server_data_helper.h \ - $$PWD/controllers/favoritescontroller.h + $$PWD/controllers/favoritescontroller.h \ + $$PWD/controllers/readingcomicscontroller.h SOURCES += \ $$PWD/static.cpp \ @@ -49,7 +50,8 @@ SOURCES += \ $$PWD/yacreader_http_session_store.cpp \ $$PWD/controllers/tagcontentcontroller.cpp \ $$PWD/yacreader_server_data_helper.cpp \ - $$PWD/controllers/favoritescontroller.cpp + $$PWD/controllers/favoritescontroller.cpp \ + $$PWD/controllers/readingcomicscontroller.cpp include(lib/logging/logging.pri) include(lib/httpserver/httpserver.pri) From 58bc6ae6bfacb8622109cea56fd0dfcf86e19d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 23 Aug 2016 22:23:53 +0200 Subject: [PATCH 072/118] Map the reading uri to the new controller in the request mapper. --- YACReaderLibrary/server/requestmapper.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp index d27c435b..f83b8bf5 100644 --- a/YACReaderLibrary/server/requestmapper.cpp +++ b/YACReaderLibrary/server/requestmapper.cpp @@ -27,6 +27,7 @@ #include "controllers/tagscontroller.h" #include "controllers/tagcontentcontroller.h" #include "controllers/favoritescontroller.h" +#include "controllers/readingcomicscontroller.h" #include "db_helper.h" #include "yacreader_libraries.h" @@ -121,6 +122,7 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { QRegExp serverVersion("/version/?"); QRegExp folderContent("/library/.+/folder/[0-9]+/content/?"); QRegExp favs("/library/.+/favs/?"); + QRegExp reading("/library/.+/reading/?"); QRegExp tags("/library/.+/tags/?"); QRegExp tagContent("/library/.+/tag/[0-9]+/content/?"); @@ -201,6 +203,10 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { { FavoritesController().service(request, response); } + else if(reading.exactMatch(path)) + { + ReadingComicsController().service(request, response); + } } else { From ff4befb60e0262c14ef8abca7432e72434fe9ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 30 Oct 2016 09:15:16 +0100 Subject: [PATCH 073/118] Removed extraneous semicolon. --- YACReaderLibrary/server/controllers/favoritescontroller.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YACReaderLibrary/server/controllers/favoritescontroller.h b/YACReaderLibrary/server/controllers/favoritescontroller.h index bf7efa78..ca103950 100644 --- a/YACReaderLibrary/server/controllers/favoritescontroller.h +++ b/YACReaderLibrary/server/controllers/favoritescontroller.h @@ -7,7 +7,7 @@ class FavoritesController : public HttpRequestHandler { Q_OBJECT - Q_DISABLE_COPY(FavoritesController); + Q_DISABLE_COPY(FavoritesController) public: FavoritesController(); From 42a16a273b8d5692475cfb6d0801bafd13b3a877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 30 Oct 2016 09:16:28 +0100 Subject: [PATCH 074/118] Added new class for serving a list of reading lists, service is not implemented yet. --- .../controllers/readinglistscontroller.cpp | 24 +++++++++++++++++++ .../controllers/readinglistscontroller.h | 20 ++++++++++++++++ YACReaderLibrary/server/server.pri | 6 +++-- 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 YACReaderLibrary/server/controllers/readinglistscontroller.cpp create mode 100644 YACReaderLibrary/server/controllers/readinglistscontroller.h diff --git a/YACReaderLibrary/server/controllers/readinglistscontroller.cpp b/YACReaderLibrary/server/controllers/readinglistscontroller.cpp new file mode 100644 index 00000000..095832b7 --- /dev/null +++ b/YACReaderLibrary/server/controllers/readinglistscontroller.cpp @@ -0,0 +1,24 @@ +#include "readinglistscontroller.h" + +ReadingListsController::ReadingListsController() +{ + +} + +void ReadingListsController::service(HttpRequest &request, HttpResponse &response) +{ + response.setHeader("Content-Type", "text/plain; charset=utf-8"); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + QStringList pathElements = path.split('/'); + int libraryId = pathElements.at(2).toInt(); + + serviceContent(libraryId, response); + + response.write("",true); +} + +void ReadingListsController::serviceContent(const int library, HttpResponse &response) +{ + +} diff --git a/YACReaderLibrary/server/controllers/readinglistscontroller.h b/YACReaderLibrary/server/controllers/readinglistscontroller.h new file mode 100644 index 00000000..5bb3ba27 --- /dev/null +++ b/YACReaderLibrary/server/controllers/readinglistscontroller.h @@ -0,0 +1,20 @@ +#ifndef READINGLISTSCONTROLLER_H +#define READINGLISTSCONTROLLER_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class ReadingListsController : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(ReadingListsController) +public: + ReadingListsController(); + + void service(HttpRequest& request, HttpResponse& response); + +private: + void serviceContent(const int library, HttpResponse &response); +}; + +#endif // READINGLISTSCONTROLLER_H diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index b64ee632..535989cb 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -25,7 +25,8 @@ HEADERS += \ $$PWD/controllers/tagcontentcontroller.h \ $$PWD/yacreader_server_data_helper.h \ $$PWD/controllers/favoritescontroller.h \ - $$PWD/controllers/readingcomicscontroller.h + $$PWD/controllers/readingcomicscontroller.h \ + $$PWD/controllers/readinglistscontroller.h SOURCES += \ $$PWD/static.cpp \ @@ -51,7 +52,8 @@ SOURCES += \ $$PWD/controllers/tagcontentcontroller.cpp \ $$PWD/yacreader_server_data_helper.cpp \ $$PWD/controllers/favoritescontroller.cpp \ - $$PWD/controllers/readingcomicscontroller.cpp + $$PWD/controllers/readingcomicscontroller.cpp \ + $$PWD/controllers/readinglistscontroller.cpp include(lib/logging/logging.pri) include(lib/httpserver/httpserver.pri) From 6d4ce1eabd34a815be2655f694b7b8d753a19212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 30 Oct 2016 09:16:53 +0100 Subject: [PATCH 075/118] Added new regexps for matching requests related to reading lists. --- YACReaderLibrary/server/requestmapper.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp index f83b8bf5..d703f086 100644 --- a/YACReaderLibrary/server/requestmapper.cpp +++ b/YACReaderLibrary/server/requestmapper.cpp @@ -125,6 +125,8 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { QRegExp reading("/library/.+/reading/?"); QRegExp tags("/library/.+/tags/?"); QRegExp tagContent("/library/.+/tag/[0-9]+/content/?"); + QRegExp readingLists("/library/.+/reading_lists/?"); + QRegExp readingListContent("/library/.+/reading_list/[0-9]+/content/?"); QRegExp sync("/sync"); @@ -207,6 +209,14 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { { ReadingComicsController().service(request, response); } + else if(readingLists.exactMatch(path)) + { + //ReadingListsController().service(request, response); + } + else if(readingListContent.exactMatch(path)) + { + //ReadingListContentController().service(request, response); + } } else { From a789d192d1588f8218f8b1627829e2bf7f29f5c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 28 Mar 2017 18:38:05 +0200 Subject: [PATCH 076/118] Added lastTimeOpened as a new property to ComicDB. --- common/comic_db.cpp | 6 ++++++ common/comic_db.h | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/common/comic_db.cpp b/common/comic_db.cpp index b7bbf7d8..1f136a58 100644 --- a/common/comic_db.cpp +++ b/common/comic_db.cpp @@ -235,6 +235,8 @@ ComicInfo & ComicInfo::operator=(const ComicInfo & comicInfo) notes = comicInfo.notes; comicVineID = comicInfo.comicVineID; + lastTimeOpened = comicInfo.lastTimeOpened; + return *this; } @@ -547,6 +549,8 @@ QDataStream &operator<<(QDataStream & stream, const ComicInfo & comicInfo) stream << comicInfo.comicVineID; + stream << comicInfo.lastTimeOpened; + return stream; } @@ -602,6 +606,8 @@ QDataStream &operator>>(QDataStream & stream, ComicInfo & comicInfo) stream >> comicInfo.notes; stream >> comicInfo.comicVineID; + + stream >> comicInfo.lastTimeOpened; return stream; } diff --git a/common/comic_db.h b/common/comic_db.h index e4cfc6b9..51627243 100644 --- a/common/comic_db.h +++ b/common/comic_db.h @@ -83,6 +83,8 @@ public: QImage cover; + QVariant lastTimeOpened;//integer/date + /*void setTitle(QVariant value); void setCoverPage(QVariant value); @@ -186,6 +188,8 @@ public: Q_PROPERTY(QImage cover MEMBER cover CONSTANT) + Q_PROPERTY(QVariant lastTimeOpened MEMBER lastTimeOpened CONSTANT) + //-new properties, not loaded from the DB automatically bool isFavorite; Q_PROPERTY(bool isFavorite MEMBER isFavorite WRITE setFavorite NOTIFY favoriteChanged) From 3a5415c3352e61eb5abc1c6e5b7c67cb98f95757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 28 Mar 2017 18:39:59 +0200 Subject: [PATCH 077/118] Add new field to the comic info table in the DB and updated the migration process. --- YACReaderLibrary/db/data_base_management.cpp | 36 +++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/YACReaderLibrary/db/data_base_management.cpp b/YACReaderLibrary/db/data_base_management.cpp index fa5dfa0b..25edf1d3 100644 --- a/YACReaderLibrary/db/data_base_management.cpp +++ b/YACReaderLibrary/db/data_base_management.cpp @@ -179,7 +179,9 @@ bool DataBaseManagement::createTables(QSqlDatabase & database) "contrast INTEGER DEFAULT -1, " "gamma INTEGER DEFAULT -1, " //new 7.1 fields - "comicVineID TEXT" + "comicVineID TEXT," + //new 8.6 fields + "lastTimeOpened INTEGER" ")"); success = success && queryComicInfo.exec(); @@ -417,7 +419,9 @@ bool DataBaseManagement::importComicsInfo(QString source, QString dest) "edited = :edited," - "comicVineID = :comicVineID" + "comicVineID = :comicVineID," + + "lastTimeOpened = :lastTimeOpened" " WHERE hash = :hash "); @@ -451,6 +455,7 @@ bool DataBaseManagement::importComicsInfo(QString source, QString dest) "read," "edited," "comicVineID," + "lastTimeOpened," "hash)" "VALUES (:title," @@ -488,6 +493,8 @@ bool DataBaseManagement::importComicsInfo(QString source, QString dest) ":edited," ":comicVineID," + ":lastTimeOpened," + ":hash )"); QSqlRecord record = newInfo.record(); @@ -598,6 +605,8 @@ void DataBaseManagement::bindValuesFromRecord(const QSqlRecord & record, QSqlQue bindString("comicVineID",record,query); + bindString("lastTimeOpened",record,query); + bindString("hash",record,query); } @@ -766,12 +775,23 @@ bool DataBaseManagement::updateToCurrentVersion(const QString & fullPath) if(pre8_6) { - QStringList columnDefs; - //TODO - columnDefs << "numChildren INTEGER"; - columnDefs << "firstChildHash TEXT"; - columnDefs << "customImage TEXT"; - //returnValue = returnValue && addColumns("folder", columnDefs, db); + {//folder + QStringList columnDefs; + //a full library update is needed after updating the table + columnDefs << "numChildren INTEGER"; + columnDefs << "firstChildHash TEXT"; + columnDefs << "customImage TEXT"; + returnValue = returnValue && addColumns("folder", columnDefs, db); + } + + {//comic_info + QStringList columnDefs; + columnDefs << "lastTimeOpened INTEGER"; + returnValue = returnValue && addColumns("comic_info", columnDefs, db); + + QSqlQuery queryIndexLastTimeOpened(db); + returnValue = returnValue && queryIndexLastTimeOpened.exec("CREATE INDEX last_time_opened_index ON comic_info (lastTimeOpened)"); + } } } From e2a8d520a90cf12cbb7bee86f206f130534c8144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 28 Mar 2017 18:42:25 +0200 Subject: [PATCH 078/118] Update the new lastTimeOpened value in the DB when it is needed in the DBHelper. Plus updated the getReading method for ordering the comics by the last time they were opened (descending order). --- YACReaderLibrary/db_helper.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 8667cf1e..ef52846a 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -258,7 +258,8 @@ QList DBHelper::getReading(qulonglong libraryId) QSqlQuery selectQuery(db); selectQuery.prepare("SELECT c.id,c.parentId,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash,ci.read " "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) " - "WHERE ci.hasBeenOpened = 1 AND ci.read = 0 AND ci.currentPage != ci.numPages AND ci.currentPage != 1"); + "WHERE ci.hasBeenOpened = 1 AND ci.read = 0 AND ci.currentPage != ci.numPages AND ci.currentPage != 1 " + "ORDER BY ci.lastTimeOpened DESC"); selectQuery.exec(); while (selectQuery.next()) @@ -452,7 +453,10 @@ void DBHelper::update(ComicInfo * comicInfo, QSqlDatabase & db) "rating = :rating," //new 7.1 fields - "comicVineID = :comicVineID" + "comicVineID = :comicVineID," + + //new 8.6 fields + "lastTimeOpened = :lastTimeOpened" //-- " WHERE id = :id "); @@ -507,6 +511,8 @@ void DBHelper::update(ComicInfo * comicInfo, QSqlDatabase & db) updateComicInfo.bindValue(":comicVineID", comicInfo->comicVineID); + updateComicInfo.bindValue(":lastTimeOpened", comicInfo->lastTimeOpened); + updateComicInfo.exec(); } @@ -589,6 +595,8 @@ void DBHelper::updateProgress(qulonglong libraryId, const ComicInfo &comicInfo) comic.info.currentPage = comicInfo.currentPage; comic.info.hasBeenOpened = true; + comic.info.lastTimeOpened = QDateTime::currentSecsSinceEpoch(); + DBHelper::update(&comic.info,db); db.close(); @@ -602,12 +610,14 @@ void DBHelper::updateReadingRemoteProgress(const ComicInfo &comicInfo, QSqlDatab "read = :read, " "currentPage = :currentPage, " "hasBeenOpened = :hasBeenOpened, " + "lastTimeOpened = :lastTimeOpened" "rating = :rating" " WHERE id = :id "); updateComicInfo.bindValue(":read", comicInfo.read?1:0); updateComicInfo.bindValue(":currentPage", comicInfo.currentPage); updateComicInfo.bindValue(":hasBeenOpened", comicInfo.hasBeenOpened?1:0); + updateComicInfo.bindValue(":lastTimeOpened", QDateTime::currentSecsSinceEpoch()); updateComicInfo.bindValue(":id", comicInfo.id); updateComicInfo.bindValue(":rating", comicInfo.rating); updateComicInfo.exec(); @@ -1283,6 +1293,10 @@ ComicInfo DBHelper::loadComicInfo(QString hash, QSqlDatabase & db) comicInfo.rating = record.value("rating").toInt(); //-- + //new 8.6 fields + comicInfo.lastTimeOpened = record.value("lastTimeOpened"); + //-- + comicInfo.title = record.value("title"); comicInfo.numPages = record.value("numPages"); From 30982be0a92bc09e33cb0c754338495e8f38e675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 28 Mar 2017 18:43:15 +0200 Subject: [PATCH 079/118] Updated the comic model for getting the reading comics properly sorted. //TODO This should be using the method in the DBHelper. --- YACReaderLibrary/db/comic_model.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/YACReaderLibrary/db/comic_model.cpp b/YACReaderLibrary/db/comic_model.cpp index ec758603..2995c92e 100644 --- a/YACReaderLibrary/db/comic_model.cpp +++ b/YACReaderLibrary/db/comic_model.cpp @@ -607,9 +607,11 @@ void ComicModel::setupReadingModelData(const QString &databasePath) QSqlQuery selectQuery(db); selectQuery.prepare("SELECT ci.number,ci.title,c.fileName,ci.numPages,c.id,c.parentId,c.path,ci.hash,ci.read,ci.isBis,ci.currentPage,ci.rating,ci.hasBeenOpened " "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) " - "WHERE ci.hasBeenOpened = 1 AND ci.read = 0 AND ci.currentPage != ci.numPages AND ci.currentPage != 1"); + "WHERE ci.hasBeenOpened = 1 AND ci.read = 0 AND ci.currentPage != ci.numPages AND ci.currentPage != 1 " + "ORDER BY ci.lastTimeOpened DESC"); selectQuery.exec(); - setupModelData(selectQuery); + + setupModelDataForList(selectQuery); } db.close(); QSqlDatabase::removeDatabase(_databasePath); From 169c6c16dda1d9716a6bfe5aaa75c07b4afe6bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 28 Mar 2017 18:43:59 +0200 Subject: [PATCH 080/118] Fixed reading comics web controller. --- .../server/controllers/readingcomicscontroller.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/YACReaderLibrary/server/controllers/readingcomicscontroller.cpp b/YACReaderLibrary/server/controllers/readingcomicscontroller.cpp index 77f0124e..78e22073 100644 --- a/YACReaderLibrary/server/controllers/readingcomicscontroller.cpp +++ b/YACReaderLibrary/server/controllers/readingcomicscontroller.cpp @@ -25,12 +25,9 @@ void ReadingComicsController::service(HttpRequest &request, HttpResponse &respon void ReadingComicsController::serviceContent(const int &library, HttpResponse &response) { - //TODO sort comics before serving them - int FIXME; + QList readingComics = DBHelper::getReading(library); - QList tagComics = DBHelper::getReading(library); - - for(const ComicDB &comic : tagComics) + for(const ComicDB &comic : readingComics) { response.write(YACReaderServerDataHelper::comicToYSFormat(library, comic).toUtf8()); } From 0f9396ea9ee4c70cd0f1c49641543f4892e69419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 28 Mar 2017 18:44:52 +0200 Subject: [PATCH 081/118] Commented performance measuring code from folder content controller. --- .../server/controllers/foldercontentcontroller.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp index 2411947e..3c81e7f0 100644 --- a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp +++ b/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp @@ -39,7 +39,7 @@ void FolderContentController::service(HttpRequest& request, HttpResponse& respon void FolderContentController::serviceContent(const int &library, const qulonglong &folderId, HttpResponse &response) { - clock_t begin = clock(); + //clock_t begin = clock(); QList folderContent = DBHelper::getFolderSubfoldersFromLibrary(library,folderId); QList folderComics = DBHelper::getFolderComicsFromLibrary(library,folderId); @@ -65,8 +65,8 @@ void FolderContentController::serviceContent(const int &library, const qulonglon } } - clock_t end = clock(); + /*clock_t end = clock(); double msecs = double(end - begin); - response.write(QString("%1ms").arg(msecs).toUtf8()); + response.write(QString("%1ms").arg(msecs).toUtf8());*/ } From 1da9745e1cd64da02ec871b04c578a05f43b93f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Tue, 28 Mar 2017 18:45:24 +0200 Subject: [PATCH 082/118] Update the last time opened value in YACReader when a comic info is synchronized back to YACReaderLibrary. --- YACReader/main_window_viewer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/YACReader/main_window_viewer.cpp b/YACReader/main_window_viewer.cpp index e3da7210..a031eae2 100644 --- a/YACReader/main_window_viewer.cpp +++ b/YACReader/main_window_viewer.cpp @@ -1658,6 +1658,7 @@ void MainWindowViewer::sendComic() { YACReaderLocalClient * client = new YACReaderLocalClient; currentComicDB.info.hasBeenOpened = true; + currentComicDB.info.lastTimeOpened = QDateTime::currentSecsSinceEpoch(); viewer->updateComic(currentComicDB); int retries = 1; while(!client->sendComicInfo(libraryId,currentComicDB) && retries!=0) From 8f7af955c24c1089fcfc4b15289580fa6ee096df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 26 May 2017 17:18:50 +0200 Subject: [PATCH 083/118] Fixed warning --- compressed_archive/unarr/compressed_archive.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compressed_archive/unarr/compressed_archive.cpp b/compressed_archive/unarr/compressed_archive.cpp index 98709cb0..f2c682ba 100644 --- a/compressed_archive/unarr/compressed_archive.cpp +++ b/compressed_archive/unarr/compressed_archive.cpp @@ -10,7 +10,7 @@ extern"C" { } CompressedArchive::CompressedArchive(const QString & filePath, QObject *parent) : - QObject(parent),valid(false),tools(true),numFiles(0),ar(NULL),stream(NULL) + QObject(parent),tools(true),valid(false),numFiles(0),ar(NULL),stream(NULL) { //open file stream = ar_open_file(filePath.toStdString().c_str()); From 1988bbb58e4c5b6adc742498cc79e7b5ccc39801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 26 May 2017 17:19:55 +0200 Subject: [PATCH 084/118] Make `naturalComprare` public in case we need to user the int return value instead of a bool. --- common/qnaturalsorting.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/qnaturalsorting.h b/common/qnaturalsorting.h index 9a84f96a..614e419e 100644 --- a/common/qnaturalsorting.h +++ b/common/qnaturalsorting.h @@ -7,6 +7,7 @@ #include #include "library_item.h" +int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity caseSensitivity); bool naturalSortLessThanCS( const QString &left, const QString &right ); bool naturalSortLessThanCI( const QString &left, const QString &right ); bool naturalSortLessThanCIFileInfo(const QFileInfo & left,const QFileInfo & right); From d20277736dc84bbbee093c8912bc2d432cfc1360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 26 May 2017 17:24:56 +0200 Subject: [PATCH 085/118] Added model classes for reading lists and labels. --- YACReaderLibrary/YACReaderLibrary.pro | 6 ++- YACReaderLibrary/db/reading_list.cpp | 44 +++++++++++++++++++ YACReaderLibrary/db/reading_list.h | 36 +++++++++++++++ .../YACReaderLibraryServer.pro | 6 ++- 4 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 YACReaderLibrary/db/reading_list.cpp create mode 100644 YACReaderLibrary/db/reading_list.h diff --git a/YACReaderLibrary/YACReaderLibrary.pro b/YACReaderLibrary/YACReaderLibrary.pro index fa1f7a7e..7b26afa2 100644 --- a/YACReaderLibrary/YACReaderLibrary.pro +++ b/YACReaderLibrary/YACReaderLibrary.pro @@ -146,7 +146,8 @@ HEADERS += comic_flow.h \ yacreader_comics_views_manager.h \ info_comics_view.h \ yacreader_comics_selection_helper.h \ - yacreader_comic_info_helper.h + yacreader_comic_info_helper.h \ + db/reading_list.cpp !CONFIG(no_opengl) { CONFIG(legacy_gl_widget) { @@ -220,7 +221,8 @@ SOURCES += comic_flow.cpp \ yacreader_comics_views_manager.cpp \ info_comics_view.cpp \ yacreader_comics_selection_helper.cpp \ - yacreader_comic_info_helper.cpp + yacreader_comic_info_helper.cpp \ + db/reading_list.cpp !CONFIG(no_opengl) { CONFIG(legacy_gl_widget) { diff --git a/YACReaderLibrary/db/reading_list.cpp b/YACReaderLibrary/db/reading_list.cpp new file mode 100644 index 00000000..b27b6650 --- /dev/null +++ b/YACReaderLibrary/db/reading_list.cpp @@ -0,0 +1,44 @@ +#include "reading_list.h" + +ReadingList::ReadingList(const QString &name, qulonglong id, int ordering) + :name(name), id(id), ordering(ordering) +{ + +} + +qulonglong ReadingList::getId() const +{ + return id; +} + +QString ReadingList::getName() const +{ + return name; +} + +int ReadingList::getOrdering() const +{ + return ordering; +} + + +Label::Label(const QString &name, qulonglong id, YACReader::LabelColors colorid) + :name(name), id(id), colorid(colorid) +{ + +} + +YACReader::LabelColors Label::getColorID() const +{ + return colorid; +} + +QString Label::getName() const +{ + return name; +} + +qulonglong Label::getId() const +{ + return id; +} diff --git a/YACReaderLibrary/db/reading_list.h b/YACReaderLibrary/db/reading_list.h new file mode 100644 index 00000000..84e41846 --- /dev/null +++ b/YACReaderLibrary/db/reading_list.h @@ -0,0 +1,36 @@ +#ifndef READING_LIST_H +#define READING_LIST_H + +#include "yacreader_global.h" + +class ReadingList +{ +public: + ReadingList(const QString &name, qulonglong id, int ordering); + + qulonglong getId() const; + QString getName() const; + int getOrdering() const; +private: + QString name; + qulonglong id; + int ordering; +}; + +class Label +{ +public: + Label(const QString &name, qulonglong id, YACReader::LabelColors colorid); + + YACReader::LabelColors getColorID() const; + QString getName() const; + qulonglong getId() const; + +private: + QString name; + qulonglong id; + YACReader::LabelColors colorid; + +}; + +#endif // READING_LIST_H diff --git a/YACReaderLibraryServer/YACReaderLibraryServer.pro b/YACReaderLibraryServer/YACReaderLibraryServer.pro index 59e8ad18..a6fcae8b 100644 --- a/YACReaderLibraryServer/YACReaderLibraryServer.pro +++ b/YACReaderLibraryServer/YACReaderLibraryServer.pro @@ -60,7 +60,8 @@ HEADERS += ../YACReaderLibrary/library_creator.h \ ../common/http_worker.h \ ../YACReaderLibrary/yacreader_libraries.h \ ../YACReaderLibrary/comic_files_manager.h \ - console_ui_library_creator.h + console_ui_library_creator.h \ + ../YACReaderLibrary/db/reading_list.h SOURCES += ../YACReaderLibrary/library_creator.cpp \ @@ -81,6 +82,7 @@ SOURCES += ../YACReaderLibrary/library_creator.cpp \ ../YACReaderLibrary/yacreader_libraries.cpp \ ../YACReaderLibrary/comic_files_manager.cpp \ console_ui_library_creator.cpp \ + ../YACReaderLibrary/db/reading_list.cpp \ main.cpp include(../YACReaderLibrary/server/server.pri) @@ -151,4 +153,4 @@ translation.files = ../release/languages/yacreaderlibrary_* #manpage.path = $$DATADIR/man/man1 #manpage.files = ../YACReaderLibrary.1 -} \ No newline at end of file +} From 85fba7e8f536220492ff97af2309892c601d802b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Fri, 26 May 2017 17:27:08 +0200 Subject: [PATCH 086/118] Added new methods for getting reading lists and comics in a reading list. Fixed DBHelper for not using GUI related classes (reading_list_item). --- YACReaderLibrary/db_helper.cpp | 108 +++++++++++++++++++++++++++++---- YACReaderLibrary/db_helper.h | 7 ++- 2 files changed, 101 insertions(+), 14 deletions(-) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index ef52846a..1643a6b9 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -14,7 +14,7 @@ #include -#include "reading_list_item.h" +#include "reading_list.h" #include "library_item.h" #include "comic_db.h" #include "data_base_management.h" @@ -286,6 +286,94 @@ QList DBHelper::getReading(qulonglong libraryId) return list; } +QList DBHelper::getReadingLists(qulonglong libraryId) +{ + QString libraryPath = DBHelper::getLibraries().getPath(libraryId); + QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary"); + + QList list; + + QSqlQuery selectQuery("SELECT * from reading_list WHERE parentId IS NULL ORDER BY name DESC",db); + + selectQuery.exec(); + + while (selectQuery.next()) + { + QSqlRecord record = selectQuery.record(); + + ReadingList item(record.value("name").toString(), record.value("id").toLongLong(),record.value("ordering").toInt()); + + if(list.isEmpty()) + { + list.append(item); + } + else + { + int i= 0; + while(i DBHelper::getReadingListFullContent(qulonglong libraryId, qulonglong readingListId) +{ + QString libraryPath = DBHelper::getLibraries().getPath(libraryId); + QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary"); + + QList list; + + { + QList ids; + ids << readingListId; + + QSqlQuery subfolders(db); + subfolders.prepare("SELECT id " + "FROM reading_list " + "WHERE parentId = :parentId " + "ORDER BY ordering ASC"); + subfolders.bindValue(":parentId", readingListId); + subfolders.exec(); + while(subfolders.next()) + ids << subfolders.record().value(0).toULongLong(); + + foreach(qulonglong id, ids) + { + QSqlQuery selectQuery(db); + selectQuery.prepare("SELECT c.id,c.parentId,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash,ci.read " + "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) " + "INNER JOIN comic_reading_list crl ON (c.id == crl.comic_id) " + "WHERE crl.reading_list_id = :parentReadingList " + "ORDER BY crl.ordering"); + selectQuery.bindValue(":parentReadingList", id); + selectQuery.exec(); + + while (selectQuery.next()) + { + ComicDB comic; + + QSqlRecord record = selectQuery.record(); + + comic.id = record.value(0).toULongLong(); + comic.parentId = record.value(1).toULongLong(); + comic.name = record.value(2).toString(); + comic.info.title = record.value(3).toString(); + comic.info.currentPage = record.value(4).toInt(); + comic.info.numPages = record.value(5).toInt(); + comic.info.hash = record.value(6).toString(); + comic.info.read = record.value(7).toBool(); + + list.append(comic); + } + } + } + + return list; +} + //objects management //deletes void DBHelper::removeFromDB(LibraryItem * item, QSqlDatabase & db) @@ -935,7 +1023,7 @@ QList DBHelper::getFoldersFromParent(qulonglong parentId, QSqlDat QList::iterator i; i = list.end(); i--; - while ((0 > (lessThan = naturalSortLessThanCI(nameCurrent,nameLast))) && i != list.begin()) + while ((0 > (lessThan = naturalCompare(nameCurrent,nameLast,Qt::CaseInsensitive))) && i != list.begin()) { i--; nameLast = (*i)->name; @@ -1098,22 +1186,18 @@ QList DBHelper::getComicsFromParent(qulonglong parentId, QSqlData return list; } -QList DBHelper::getLabelItems(qulonglong libraryId) +QList

The readTimeout value defines the maximum time to wait for a complete HTTP request. - @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize + @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize. */ - -#if QT_VERSION >= 0x050000 - typedef qintptr tSocketDescriptor; -#else - typedef int tSocketDescriptor; -#endif - -class HttpConnectionHandler : public QThread { +class DECLSPEC HttpConnectionHandler : public QThread { Q_OBJECT Q_DISABLE_COPY(HttpConnectionHandler) + public: /** Constructor. @param settings Configuration settings of the HTTP webserver - @param requestHandler handler that will process each incomin HTTP request + @param requestHandler Handler that will process each incoming HTTP request + @param sslConfiguration SSL (HTTPS) will be used if not NULL */ - HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler); + HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler, QSslConfiguration* sslConfiguration=NULL); /** Destructor */ virtual ~HttpConnectionHandler(); @@ -61,8 +72,8 @@ private: /** Configuration settings */ QSettings* settings; - /** TCP socket of the current connection */ - QTcpSocket socket; + /** TCP socket of the current connection */ + QTcpSocket* socket; /** Time for read timeout detection */ QTimer readTimer; @@ -76,9 +87,15 @@ private: /** This shows the busy-state from a very early time */ bool busy; - /** Executes the htreads own event loop */ + /** Configuration for SSL */ + QSslConfiguration* sslConfiguration; + + /** Executes the threads own event loop */ void run(); + /** Create SSL or TCP socket */ + void createSocket(); + public slots: /** diff --git a/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.cpp b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.cpp new file mode 100644 index 00000000..6dd3d8c1 --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.cpp @@ -0,0 +1,146 @@ +#ifndef QT_NO_OPENSSL + #include + #include + #include + #include +#endif +#include +#include "httpconnectionhandlerpool.h" + +HttpConnectionHandlerPool::HttpConnectionHandlerPool(QSettings* settings, HttpRequestHandler* requestHandler) + : QObject() +{ + Q_ASSERT(settings!=0); + this->settings=settings; + this->requestHandler=requestHandler; + this->sslConfiguration=NULL; + loadSslConfig(); + cleanupTimer.start(settings->value("cleanupInterval",1000).toInt()); + connect(&cleanupTimer, SIGNAL(timeout()), SLOT(cleanup())); +} + + +HttpConnectionHandlerPool::~HttpConnectionHandlerPool() +{ + // delete all connection handlers and wait until their threads are closed + foreach(HttpConnectionHandler* handler, pool) + { + delete handler; + } + delete sslConfiguration; + qDebug("HttpConnectionHandlerPool (%p): destroyed", this); +} + + +HttpConnectionHandler* HttpConnectionHandlerPool::getConnectionHandler() +{ + HttpConnectionHandler* freeHandler=0; + mutex.lock(); + // find a free handler in pool + foreach(HttpConnectionHandler* handler, pool) + { + if (!handler->isBusy()) + { + freeHandler=handler; + freeHandler->setBusy(); + break; + } + } + // create a new handler, if necessary + if (!freeHandler) + { + int maxConnectionHandlers=settings->value("maxThreads",100).toInt(); + if (pool.count()setBusy(); + pool.append(freeHandler); + } + } + mutex.unlock(); + return freeHandler; +} + + +void HttpConnectionHandlerPool::cleanup() +{ + int maxIdleHandlers=settings->value("minThreads",1).toInt(); + int idleCounter=0; + mutex.lock(); + foreach(HttpConnectionHandler* handler, pool) + { + if (!handler->isBusy()) + { + if (++idleCounter > maxIdleHandlers) + { + delete handler; + pool.removeOne(handler); + qDebug("HttpConnectionHandlerPool: Removed connection handler (%p), pool size is now %i",handler,pool.size()); + break; // remove only one handler in each interval + } + } + } + mutex.unlock(); +} + + +void HttpConnectionHandlerPool::loadSslConfig() +{ + // If certificate and key files are configured, then load them + QString sslKeyFileName=settings->value("sslKeyFile","").toString(); + QString sslCertFileName=settings->value("sslCertFile","").toString(); + if (!sslKeyFileName.isEmpty() && !sslCertFileName.isEmpty()) + { + #ifdef QT_NO_OPENSSL + qWarning("HttpConnectionHandlerPool: SSL is not supported"); + #else + // Convert relative fileNames to absolute, based on the directory of the config file. + QFileInfo configFile(settings->fileName()); + #ifdef Q_OS_WIN32 + if (QDir::isRelativePath(sslKeyFileName) && settings->format()!=QSettings::NativeFormat) + #else + if (QDir::isRelativePath(sslKeyFileName)) + #endif + { + sslKeyFileName=QFileInfo(configFile.absolutePath(),sslKeyFileName).absoluteFilePath(); + } + #ifdef Q_OS_WIN32 + if (QDir::isRelativePath(sslCertFileName) && settings->format()!=QSettings::NativeFormat) + #else + if (QDir::isRelativePath(sslCertFileName)) + #endif + { + sslCertFileName=QFileInfo(configFile.absolutePath(),sslCertFileName).absoluteFilePath(); + } + + // Load the SSL certificate + QFile certFile(sslCertFileName); + if (!certFile.open(QIODevice::ReadOnly)) + { + qCritical("HttpConnectionHandlerPool: cannot open sslCertFile %s", qPrintable(sslCertFileName)); + return; + } + QSslCertificate certificate(&certFile, QSsl::Pem); + certFile.close(); + + // Load the key file + QFile keyFile(sslKeyFileName); + if (!keyFile.open(QIODevice::ReadOnly)) + { + qCritical("HttpConnectionHandlerPool: cannot open sslKeyFile %s", qPrintable(sslKeyFileName)); + return; + } + QSslKey sslKey(&keyFile, QSsl::Rsa, QSsl::Pem); + keyFile.close(); + + // Create the SSL configuration + sslConfiguration=new QSslConfiguration(); + sslConfiguration->setLocalCertificate(certificate); + sslConfiguration->setPrivateKey(sslKey); + sslConfiguration->setPeerVerifyMode(QSslSocket::VerifyNone); + sslConfiguration->setProtocol(QSsl::TlsV1SslV3); + + qDebug("HttpConnectionHandlerPool: SSL settings loaded"); + #endif + } +} diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.h b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.h similarity index 53% rename from YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.h rename to YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.h index 2dc94338..a9b9908b 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.h +++ b/YACReaderLibrary/server/lib/httpserver/httpconnectionhandlerpool.h @@ -5,29 +5,45 @@ #include #include #include +#include "httpglobal.h" #include "httpconnectionhandler.h" /** - Pool of http connection handlers. Connection handlers are created on demand and idle handlers are - cleaned up in regular time intervals. + Pool of http connection handlers. The size of the pool grows and + shrinks on demand.

Example for the required configuration settings:

-  minThreads=1
+  minThreads=4
   maxThreads=100
-  cleanupInterval=1000
+  cleanupInterval=60000
+  readTimeout=60000
+  ;sslKeyFile=ssl/my.key
+  ;sslCertFile=ssl/my.cert
   maxRequestSize=16000
   maxMultiPartSize=1000000
   
- The pool is empty initially and grows with the number of concurrent - connections. A timer removes one idle connection handler at each - interval, but it leaves some spare handlers in memory to improve - performance. - @see HttpConnectionHandler for description of config settings readTimeout + After server start, the size of the thread pool is always 0. Threads + are started on demand when requests come in. The cleanup timer reduces + the number of idle threads slowly by closing one thread in each interval. + But the configured minimum number of threads are kept running. +

+ For SSL support, you need an OpenSSL certificate file and a key file. + Both can be created with the command +

+      openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout my.key -out my.cert
+  
+

+ Visit http://slproweb.com/products/Win32OpenSSL.html to download the Light version of OpenSSL for Windows. +

+ Please note that a listener with SSL settings can only handle HTTPS protocol. To + support both HTTP and HTTPS simultaneously, you need to start two listeners on different ports - + one with SLL and one without SSL. + @see HttpConnectionHandler for description of the readTimeout @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize */ -class HttpConnectionHandlerPool : public QObject { +class DECLSPEC HttpConnectionHandlerPool : public QObject { Q_OBJECT Q_DISABLE_COPY(HttpConnectionHandlerPool) public: @@ -63,6 +79,12 @@ private: /** Used to synchronize threads */ QMutex mutex; + /** The SSL configuration (certificate, key and other settings) */ + QSslConfiguration* sslConfiguration; + + /** Load SSL configuration */ + void loadSslConfig(); + private slots: /** Received from the clean-up timer. */ diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpcookie.cpp b/YACReaderLibrary/server/lib/httpserver/httpcookie.cpp similarity index 53% rename from YACReaderLibrary/server/lib/bfHttpServer/httpcookie.cpp rename to YACReaderLibrary/server/lib/httpserver/httpcookie.cpp index 3f5be929..ce0f5207 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpcookie.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httpcookie.cpp @@ -5,13 +5,15 @@ #include "httpcookie.h" -HttpCookie::HttpCookie() { +HttpCookie::HttpCookie() +{ version=1; maxAge=0; secure=false; } -HttpCookie::HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path, const QByteArray comment, const QByteArray domain, const bool secure) { +HttpCookie::HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path, const QByteArray comment, const QByteArray domain, const bool secure, const bool httpOnly) +{ this->name=name; this->value=value; this->maxAge=maxAge; @@ -19,171 +21,230 @@ HttpCookie::HttpCookie(const QByteArray name, const QByteArray value, const int this->comment=comment; this->domain=domain; this->secure=secure; + this->httpOnly=httpOnly; this->version=1; } -HttpCookie::HttpCookie(const QByteArray source) { +HttpCookie::HttpCookie(const QByteArray source) +{ version=1; maxAge=0; secure=false; QList list=splitCSV(source); - foreach(QByteArray part, list) { + foreach(QByteArray part, list) + { // Split the part into name and value QByteArray name; QByteArray value; int posi=part.indexOf('='); - if (posi) { + if (posi) + { name=part.left(posi).trimmed(); value=part.mid(posi+1).trimmed(); } - else { + else + { name=part.trimmed(); value=""; } // Set fields - if (name=="Comment") { + if (name=="Comment") + { comment=value; } - else if (name=="Domain") { + else if (name=="Domain") + { domain=value; } - else if (name=="Max-Age") { + else if (name=="Max-Age") + { maxAge=value.toInt(); } - else if (name=="Path") { + else if (name=="Path") + { path=value; } - else if (name=="Secure") { + else if (name=="Secure") + { secure=true; } - else if (name=="Version") { + else if (name=="HttpOnly") + { + httpOnly=true; + } + else if (name=="Version") + { version=value.toInt(); } else { - if (this->name.isEmpty()) { + if (this->name.isEmpty()) + { this->name=name; this->value=value; } - else { + else + { qWarning("HttpCookie: Ignoring unknown %s=%s",name.data(),value.data()); } } } } -QByteArray HttpCookie::toByteArray() const { +QByteArray HttpCookie::toByteArray() const +{ QByteArray buffer(name); buffer.append('='); buffer.append(value); - if (!comment.isEmpty()) { + if (!comment.isEmpty()) + { buffer.append("; Comment="); buffer.append(comment); } - if (!domain.isEmpty()) { + if (!domain.isEmpty()) + { buffer.append("; Domain="); buffer.append(domain); } - if (maxAge!=0) { + if (maxAge!=0) + { buffer.append("; Max-Age="); buffer.append(QByteArray::number(maxAge)); } - if (!path.isEmpty()) { + if (!path.isEmpty()) + { buffer.append("; Path="); buffer.append(path); } if (secure) { buffer.append("; Secure"); } + if (httpOnly) { + buffer.append("; HttpOnly"); + } buffer.append("; Version="); buffer.append(QByteArray::number(version)); return buffer; } -void HttpCookie::setName(const QByteArray name){ +void HttpCookie::setName(const QByteArray name) +{ this->name=name; } -void HttpCookie::setValue(const QByteArray value){ +void HttpCookie::setValue(const QByteArray value) +{ this->value=value; } -void HttpCookie::setComment(const QByteArray comment){ +void HttpCookie::setComment(const QByteArray comment) +{ this->comment=comment; } -void HttpCookie::setDomain(const QByteArray domain){ +void HttpCookie::setDomain(const QByteArray domain) +{ this->domain=domain; } -void HttpCookie::setMaxAge(const int maxAge){ +void HttpCookie::setMaxAge(const int maxAge) +{ this->maxAge=maxAge; } -void HttpCookie::setPath(const QByteArray path){ +void HttpCookie::setPath(const QByteArray path) +{ this->path=path; } -void HttpCookie::setSecure(const bool secure){ +void HttpCookie::setSecure(const bool secure) +{ this->secure=secure; } -QByteArray HttpCookie::getName() const { +void HttpCookie::setHttpOnly(const bool httpOnly) +{ + this->httpOnly=httpOnly; +} + +QByteArray HttpCookie::getName() const +{ return name; } -QByteArray HttpCookie::getValue() const { +QByteArray HttpCookie::getValue() const +{ return value; } -QByteArray HttpCookie::getComment() const { +QByteArray HttpCookie::getComment() const +{ return comment; } -QByteArray HttpCookie::getDomain() const { +QByteArray HttpCookie::getDomain() const +{ return domain; } -int HttpCookie::getMaxAge() const { +int HttpCookie::getMaxAge() const +{ return maxAge; } -QByteArray HttpCookie::getPath() const { +QByteArray HttpCookie::getPath() const +{ return path; } -bool HttpCookie::getSecure() const { +bool HttpCookie::getSecure() const +{ return secure; } -int HttpCookie::getVersion() const { +bool HttpCookie::getHttpOnly() const +{ + return httpOnly; +} + +int HttpCookie::getVersion() const +{ return version; } -QList HttpCookie::splitCSV(const QByteArray source) { +QList HttpCookie::splitCSV(const QByteArray source) +{ bool inString=false; QList list; QByteArray buffer; - for (int i=0; i HttpCookie::splitCSV(const QByteArray source) { } } QByteArray trimmed=buffer.trimmed(); - if (!trimmed.isEmpty()) { + if (!trimmed.isEmpty()) + { list.append(trimmed); } return list; diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpcookie.h b/YACReaderLibrary/server/lib/httpserver/httpcookie.h similarity index 78% rename from YACReaderLibrary/server/lib/bfHttpServer/httpcookie.h rename to YACReaderLibrary/server/lib/httpserver/httpcookie.h index 88688f75..d4857fd9 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpcookie.h +++ b/YACReaderLibrary/server/lib/httpserver/httpcookie.h @@ -8,6 +8,7 @@ #include #include +#include "httpglobal.h" /** HTTP cookie as defined in RFC 2109. This class can also parse @@ -15,7 +16,7 @@ 2109. */ -class HttpCookie +class DECLSPEC HttpCookie { public: @@ -30,9 +31,10 @@ public: @param path Path for that the cookie will be sent, default="/" which means the whole domain @param comment Optional comment, may be displayed by the web browser somewhere @param domain Optional domain for that the cookie will be sent. Defaults to the current domain - @param secure If true, the cookie will only be sent on secure connections + @param secure If true, the cookie will be sent by the browser to the server only on secure connections + @param httpOnly If true, the browser does not allow client-side scripts to access the cookie */ - HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path="/", const QByteArray comment=QByteArray(), const QByteArray domain=QByteArray(), const bool secure=false); + HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path="/", const QByteArray comment=QByteArray(), const QByteArray domain=QByteArray(), const bool secure=false, const bool httpOnly=false); /** Create a cookie from a string. @@ -40,7 +42,7 @@ public: */ HttpCookie(const QByteArray source); - /** Convert this cookie to a string that may be used in a Set-Cookie2 header. */ + /** Convert this cookie to a string that may be used in a Set-Cookie header. */ QByteArray toByteArray() const ; /** @@ -67,9 +69,12 @@ public: /** Set the path for that the cookie will be sent, default="/" which means the whole domain */ void setPath(const QByteArray path); - /** Set secure mode, so that the cokkie will only be sent on secure connections */ + /** Set secure mode, so that the cookie will be sent by the browser to the server only on secure connections */ void setSecure(const bool secure); + /** Set secure mode, so that he browser does not allow client-side scripts to access the cookie */ + void setHttpOnly(const bool httpOnly); + /** Get the name of this cookie */ QByteArray getName() const; @@ -91,6 +96,9 @@ public: /** Get the secure flag of this cookie */ bool getSecure() const; + /** Get the httpOnly of this cookie */ + bool getHttpOnly() const; + /** Returns always 1 */ int getVersion() const; @@ -103,6 +111,7 @@ private: int maxAge; QByteArray path; bool secure; + bool httpOnly; int version; }; diff --git a/YACReaderLibrary/server/lib/httpserver/httpglobal.cpp b/YACReaderLibrary/server/lib/httpserver/httpglobal.cpp new file mode 100644 index 00000000..088dab14 --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/httpglobal.cpp @@ -0,0 +1,7 @@ +#include "httpglobal.h" + +const char* getQtWebAppLibVersion() +{ + return "1.6.5"; +} + diff --git a/YACReaderLibrary/server/lib/httpserver/httpglobal.h b/YACReaderLibrary/server/lib/httpserver/httpglobal.h new file mode 100644 index 00000000..a4ccbd3e --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/httpglobal.h @@ -0,0 +1,27 @@ +/** + @file + @author Stefan Frings +*/ + +#ifndef HTTPGLOBAL_H +#define HTTPGLOBAL_H + +#include + +// This is specific to Windows dll's +#if defined(Q_OS_WIN) + #if defined(QTWEBAPPLIB_EXPORT) + #define DECLSPEC Q_DECL_EXPORT + #elif defined(QTWEBAPPLIB_IMPORT) + #define DECLSPEC Q_DECL_IMPORT + #endif +#endif +#if !defined(DECLSPEC) + #define DECLSPEC +#endif + +/** Get the library version number */ +DECLSPEC const char* getQtWebAppLibVersion(); + +#endif // HTTPGLOBAL_H + diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httplistener.cpp b/YACReaderLibrary/server/lib/httpserver/httplistener.cpp similarity index 56% rename from YACReaderLibrary/server/lib/bfHttpServer/httplistener.cpp rename to YACReaderLibrary/server/lib/httpserver/httplistener.cpp index b79db686..59596064 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httplistener.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httplistener.cpp @@ -12,53 +12,86 @@ HttpListener::HttpListener(QSettings* settings, HttpRequestHandler* requestHandl : QTcpServer(parent) { Q_ASSERT(settings!=0); + Q_ASSERT(requestHandler!=0); + pool=NULL; + this->settings=settings; + this->requestHandler=requestHandler; // Reqister type of socketDescriptor for signal/slot handling qRegisterMetaType("tSocketDescriptor"); - // Create connection handler pool - this->settings=settings; - pool=new HttpConnectionHandlerPool(settings,requestHandler); // Start listening - int port=settings->value("port",8080).toInt(); - listen(QHostAddress::Any, port); - //Cambiado - int i = 0; - while (!isListening() && i < 1000) { - listen(QHostAddress::Any, (rand() % 45535)+20000); - i++; + listen(); +} + + +HttpListener::~HttpListener() +{ + close(); + qDebug("HttpListener: destroyed"); +} + + +void HttpListener::listen() +{ + if (!pool) + { + pool=new HttpConnectionHandlerPool(settings,requestHandler); + } + QString host = settings->value("host").toString(); + int port=settings->value("port").toInt(); + QTcpServer::listen(host.isEmpty() ? QHostAddress::Any : QHostAddress(host), port); + + //YACReader--------------------------------------------- + //try to listen even if the default port is no available + int i = 0; + while (!isListening() && i < 1000) { + QTcpServer::listen(QHostAddress::Any, (rand() % 45535)+20000); + i++; + } + //------------------------------------------------------ + + if (!isListening()) + { + qDebug("HttpListener: Cannot bind on port %i: %s",port,qPrintable(errorString())); } - if(!isListening()) - { - qCritical("HttpListener: Cannot bind on port %i: %s",port,qPrintable(errorString())); - } else { qDebug("HttpListener: Listening on port %i",port); } } -HttpListener::~HttpListener() { - close(); + +void HttpListener::close() { + QTcpServer::close(); qDebug("HttpListener: closed"); - delete pool; - qDebug("HttpListener: destroyed"); + if (pool) { + delete pool; + pool=NULL; + } } void HttpListener::incomingConnection(tSocketDescriptor socketDescriptor) { #ifdef SUPERVERBOSE qDebug("HttpListener: New connection"); #endif - HttpConnectionHandler* freeHandler=pool->getConnectionHandler(); + + HttpConnectionHandler* freeHandler=NULL; + if (pool) + { + freeHandler=pool->getConnectionHandler(); + } // Let the handler process the new connection. - if (freeHandler) { + if (freeHandler) + { // The descriptor is passed via signal/slot because the handler lives in another - // thread and cannot open the socket when called by another thread. + // thread and cannot open the socket when directly called by another thread. connect(this,SIGNAL(handleConnection(tSocketDescriptor)),freeHandler,SLOT(handleConnection(tSocketDescriptor))); emit handleConnection(socketDescriptor); disconnect(this,SIGNAL(handleConnection(tSocketDescriptor)),freeHandler,SLOT(handleConnection(tSocketDescriptor))); } - else { + else + { // Reject the connection - qDebug("HttpListener: Too many connections"); + qDebug("HttpListener: Too many incoming connections"); QTcpSocket* socket=new QTcpSocket(this); socket->setSocketDescriptor(socketDescriptor); connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httplistener.h b/YACReaderLibrary/server/lib/httpserver/httplistener.h similarity index 58% rename from YACReaderLibrary/server/lib/bfHttpServer/httplistener.h rename to YACReaderLibrary/server/lib/httpserver/httplistener.h index 6ae5d75c..a88176d0 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httplistener.h +++ b/YACReaderLibrary/server/lib/httpserver/httplistener.h @@ -3,12 +3,13 @@ @author Stefan Frings */ -#ifndef LISTENER_H -#define LISTENER_H +#ifndef HTTPLISTENER_H +#define HTTPLISTENER_H #include #include #include +#include "httpglobal.h" #include "httpconnectionhandler.h" #include "httpconnectionhandlerpool.h" #include "httprequesthandler.h" @@ -19,36 +20,54 @@

Example for the required settings in the config file:

+  ;host=192.168.0.100
   port=8080
   minThreads=1
   maxThreads=10
   cleanupInterval=1000
   readTimeout=60000
+  ;sslKeyFile=ssl/my.key
+  ;sslCertFile=ssl/my.cert
   maxRequestSize=16000
   maxMultiPartSize=1000000
   
- The port number is the incoming TCP port that this listener listens to. - @see HttpConnectionHandlerPool for description of config settings minThreads, maxThreads and cleanupInterval - @see HttpConnectionHandler for description of config settings readTimeout + The optional host parameter binds the listener to one network interface. + The listener handles all network interfaces if no host is configured. + The port number specifies the incoming TCP port that this listener listens to. + @see HttpConnectionHandlerPool for description of config settings minThreads, maxThreads, cleanupInterval and ssl settings + @see HttpConnectionHandler for description of the readTimeout @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize */ -class HttpListener : public QTcpServer { +class DECLSPEC HttpListener : public QTcpServer { Q_OBJECT Q_DISABLE_COPY(HttpListener) public: /** Constructor. + Creates a connection pool and starts listening on the configured host and port. @param settings Configuration settings for the HTTP server. Must not be 0. @param requestHandler Processes each received HTTP request, usually by dispatching to controller classes. @param parent Parent object. + @warning Ensure to close or delete the listener before deleting the request handler. */ - HttpListener(QSettings* settings, HttpRequestHandler* requestHandler, QObject* parent = 0); + HttpListener(QSettings* settings, HttpRequestHandler* requestHandler, QObject* parent = NULL); /** Destructor */ virtual ~HttpListener(); + /** + Restart listeing after close(). + */ + void listen(); + + /** + Closes the listener, waits until all pending requests are processed, + then closes the connection pool. + */ + void close(); + protected: /** Serves new incoming connection requests */ @@ -59,13 +78,16 @@ private: /** Configuration settings for the HTTP server */ QSettings* settings; + /** Point to the reuqest handler which processes all HTTP requests */ + HttpRequestHandler* requestHandler; + /** Pool of connection handlers */ HttpConnectionHandlerPool* pool; signals: /** - Emitted when the connection handler shall process a new incoming onnection. + Sent to the connection handler to process a new incoming connection. @param socketDescriptor references the accepted connection. */ @@ -73,4 +95,4 @@ signals: }; -#endif // LISTENER_H +#endif // HTTPLISTENER_H diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httprequest.cpp b/YACReaderLibrary/server/lib/httpserver/httprequest.cpp similarity index 54% rename from YACReaderLibrary/server/lib/bfHttpServer/httprequest.cpp rename to YACReaderLibrary/server/lib/httpserver/httprequest.cpp index 8ed427b5..0939bd47 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httprequest.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httprequest.cpp @@ -8,144 +8,189 @@ #include #include "httpcookie.h" -HttpRequest::HttpRequest(QSettings* settings) { +HttpRequest::HttpRequest(QSettings* settings) +{ status=waitForRequest; currentSize=0; expectedBodySize=0; - maxSize=settings->value("maxRequestSize","32000000").toInt(); - maxMultiPartSize=settings->value("maxMultiPartSize","32000000").toInt(); + maxSize=settings->value("maxRequestSize","16000").toInt(); + maxMultiPartSize=settings->value("maxMultiPartSize","1000000").toInt(); } -void HttpRequest::readRequest(QTcpSocket& socket) { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: read request"); -#endif +void HttpRequest::readRequest(QTcpSocket* socket) +{ + #ifdef SUPERVERBOSE + qDebug("HttpRequest: read request"); + #endif int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow - QByteArray newData=socket.readLine(toRead).trimmed(); - currentSize+=newData.size(); - if (!newData.isEmpty()) { + lineBuffer.append(socket->readLine(toRead)); + currentSize+=lineBuffer.size(); + if (!lineBuffer.contains('\r') && !lineBuffer.contains('\n')) + { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: collecting more parts until line break"); + #endif + return; + } + QByteArray newData=lineBuffer.trimmed(); + lineBuffer.clear(); + if (!newData.isEmpty()) + { QList list=newData.split(' '); - if (list.count()!=3 || !list.at(2).contains("HTTP")) { + if (list.count()!=3 || !list.at(2).contains("HTTP")) + { qWarning("HttpRequest: received broken HTTP request, invalid first line"); status=abort; } else { - method=list.at(0); + method=list.at(0).trimmed(); path=list.at(1); version=list.at(2); + peerAddress = socket->peerAddress(); status=waitForHeader; } } } -void HttpRequest::readHeader(QTcpSocket& socket) { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: read header"); -#endif +void HttpRequest::readHeader(QTcpSocket* socket) +{ + #ifdef SUPERVERBOSE + qDebug("HttpRequest: read header"); + #endif int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow - QByteArray newData=socket.readLine(toRead).trimmed(); - currentSize+=newData.size(); + lineBuffer.append(socket->readLine(toRead)); + currentSize+=lineBuffer.size(); + if (!lineBuffer.contains('\r') && !lineBuffer.contains('\n')) + { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: collecting more parts until line break"); + #endif + return; + } + QByteArray newData=lineBuffer.trimmed(); + lineBuffer.clear(); int colon=newData.indexOf(':'); - if (colon>0) { + if (colon>0) + { // Received a line with a colon - a header - currentHeader=newData.left(colon); + currentHeader=newData.left(colon).toLower(); QByteArray value=newData.mid(colon+1).trimmed(); headers.insert(currentHeader,value); -#ifdef SUPERVERBOSE - qDebug("HttpRequest: received header %s: %s",currentHeader.data(),value.data()); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: received header %s: %s",currentHeader.data(),value.data()); + #endif } - else if (!newData.isEmpty()) { + else if (!newData.isEmpty()) + { // received another line - belongs to the previous header -#ifdef SUPERVERBOSE - qDebug("HttpRequest: read additional line of header"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: read additional line of header"); + #endif // Received additional line of previous header if (headers.contains(currentHeader)) { headers.insert(currentHeader,headers.value(currentHeader)+" "+newData); } } - else { + else + { // received an empty line - end of headers reached -#ifdef SUPERVERBOSE - qDebug("HttpRequest: headers completed"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: headers completed"); + #endif // Empty line received, that means all headers have been received // Check for multipart/form-data - QByteArray contentType=headers.value("Content-Type"); - if (contentType.startsWith("multipart/form-data")) { + QByteArray contentType=headers.value("content-type"); + if (contentType.startsWith("multipart/form-data")) + { int posi=contentType.indexOf("boundary="); if (posi>=0) { boundary=contentType.mid(posi+9); + if (boundary.startsWith('"') && boundary.endsWith('"')) + { + boundary = boundary.mid(1,boundary.length()-2); + } } } - QByteArray contentLength=getHeader("Content-Length"); - if (!contentLength.isEmpty()) { + QByteArray contentLength=headers.value("content-length"); + if (!contentLength.isEmpty()) + { expectedBodySize=contentLength.toInt(); } - if (expectedBodySize==0) { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: expect no body"); -#endif + if (expectedBodySize==0) + { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: expect no body"); + #endif status=complete; } - else if (boundary.isEmpty() && expectedBodySize+currentSize>maxSize) { + else if (boundary.isEmpty() && expectedBodySize+currentSize>maxSize) + { qWarning("HttpRequest: expected body is too large"); status=abort; } - else if (!boundary.isEmpty() && expectedBodySize>maxMultiPartSize) { + else if (!boundary.isEmpty() && expectedBodySize>maxMultiPartSize) + { qWarning("HttpRequest: expected multipart body is too large"); status=abort; } else { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: expect %i bytes body",expectedBodySize); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: expect %i bytes body",expectedBodySize); + #endif status=waitForBody; } } } -void HttpRequest::readBody(QTcpSocket& socket) { +void HttpRequest::readBody(QTcpSocket* socket) +{ Q_ASSERT(expectedBodySize!=0); - if (boundary.isEmpty()) { + if (boundary.isEmpty()) + { // normal body, no multipart -#ifdef SUPERVERBOSE - qDebug("HttpRequest: receive body"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: receive body"); + #endif int toRead=expectedBodySize-bodyData.size(); - QByteArray newData=socket.read(toRead); + QByteArray newData=socket->read(toRead); currentSize+=newData.size(); bodyData.append(newData); - if (bodyData.size()>=expectedBodySize) { + if (bodyData.size()>=expectedBodySize) + { status=complete; } } - else { + else + { // multipart body, store into temp file -#ifdef SUPERVERBOSE - qDebug("HttpRequest: receiving multipart body"); -#endif - if (!tempFile.isOpen()) { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: receiving multipart body"); + #endif + if (!tempFile.isOpen()) + { tempFile.open(); } // Transfer data in 64kb blocks int fileSize=tempFile.size(); int toRead=expectedBodySize-fileSize; - if (toRead>65536) { + if (toRead>65536) + { toRead=65536; } - fileSize+=tempFile.write(socket.read(toRead)); - if (fileSize>=maxMultiPartSize) { + fileSize+=tempFile.write(socket->read(toRead)); + if (fileSize>=maxMultiPartSize) + { qWarning("HttpRequest: received too many multipart bytes"); status=abort; } - else if (fileSize>=expectedBodySize) { -#ifdef SUPERVERBOSE + else if (fileSize>=expectedBodySize) + { + #ifdef SUPERVERBOSE qDebug("HttpRequest: received whole multipart body"); -#endif + #endif tempFile.flush(); - if (tempFile.error()) { + if (tempFile.error()) + { qCritical("HttpRequest: Error writing temp file for multipart body"); } parseMultiPartFile(); @@ -155,87 +200,106 @@ void HttpRequest::readBody(QTcpSocket& socket) { } } -void HttpRequest::decodeRequestParams() { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: extract and decode request parameters"); -#endif +void HttpRequest::decodeRequestParams() +{ + #ifdef SUPERVERBOSE + qDebug("HttpRequest: extract and decode request parameters"); + #endif // Get URL parameters QByteArray rawParameters; int questionMark=path.indexOf('?'); - if (questionMark>=0) { + if (questionMark>=0) + { rawParameters=path.mid(questionMark+1); path=path.left(questionMark); } // Get request body parameters - QByteArray contentType=headers.value("Content-Type"); - if (!bodyData.isEmpty() && (contentType.isEmpty() || contentType.startsWith("application/x-www-form-urlencoded"))) { - if (rawParameters.isEmpty()) { + QByteArray contentType=headers.value("content-type"); + if (!bodyData.isEmpty() && (contentType.isEmpty() || contentType.startsWith("application/x-www-form-urlencoded"))) + { + if (!rawParameters.isEmpty()) + { rawParameters.append('&'); rawParameters.append(bodyData); } - else { + else + { rawParameters=bodyData; } } // Split the parameters into pairs of value and name QList list=rawParameters.split('&'); - foreach (QByteArray part, list) { + foreach (QByteArray part, list) + { int equalsChar=part.indexOf('='); - if (equalsChar>=0) { + if (equalsChar>=0) + { QByteArray name=part.left(equalsChar).trimmed(); QByteArray value=part.mid(equalsChar+1).trimmed(); parameters.insert(urlDecode(name),urlDecode(value)); } - else if (!part.isEmpty()){ + else if (!part.isEmpty()) + { // Name without value parameters.insert(urlDecode(part),""); } } } -void HttpRequest::extractCookies() { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: extract cookies"); -#endif - foreach(QByteArray cookieStr, headers.values("Cookie")) { +void HttpRequest::extractCookies() +{ + #ifdef SUPERVERBOSE + qDebug("HttpRequest: extract cookies"); + #endif + foreach(QByteArray cookieStr, headers.values("cookie")) + { QList list=HttpCookie::splitCSV(cookieStr); - foreach(QByteArray part, list) { -#ifdef SUPERVERBOSE - qDebug("HttpRequest: found cookie %s",part.data()); -#endif // Split the part into name and value + foreach(QByteArray part, list) + { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: found cookie %s",part.data()); + #endif // Split the part into name and value QByteArray name; QByteArray value; int posi=part.indexOf('='); - if (posi) { + if (posi) + { name=part.left(posi).trimmed(); value=part.mid(posi+1).trimmed(); } - else { + else + { name=part.trimmed(); value=""; } cookies.insert(name,value); } } - headers.remove("Cookie"); + headers.remove("cookie"); } -void HttpRequest::readFromSocket(QTcpSocket& socket) { +void HttpRequest::readFromSocket(QTcpSocket* socket) +{ Q_ASSERT(status!=complete); - if (status==waitForRequest) { + if (status==waitForRequest) + { readRequest(socket); } - else if (status==waitForHeader) { + else if (status==waitForHeader) + { readHeader(socket); } - else if (status==waitForBody) { + else if (status==waitForBody) + { readBody(socket); } - if (currentSize>maxSize) { + if ((boundary.isEmpty() && currentSize>maxSize) || (!boundary.isEmpty() && currentSize>maxMultiPartSize)) + { qWarning("HttpRequest: received too many bytes"); status=abort; } - if (status==complete) { + if (status==complete) + { // Extract and decode request parameters from url and body decodeRequestParams(); // Extract cookies from headers @@ -244,62 +308,82 @@ void HttpRequest::readFromSocket(QTcpSocket& socket) { } -HttpRequest::RequestStatus HttpRequest::getStatus() const { +HttpRequest::RequestStatus HttpRequest::getStatus() const +{ return status; } -QByteArray HttpRequest::getMethod() const { +QByteArray HttpRequest::getMethod() const +{ return method; } -QByteArray HttpRequest::getPath() const { +QByteArray HttpRequest::getPath() const +{ return urlDecode(path); } -QByteArray HttpRequest::getVersion() const { +const QByteArray& HttpRequest::getRawPath() const +{ + return path; +} + + +QByteArray HttpRequest::getVersion() const +{ return version; } -QByteArray HttpRequest::getHeader(const QByteArray& name) const { - return headers.value(name); +QByteArray HttpRequest::getHeader(const QByteArray& name) const +{ + return headers.value(name.toLower()); } -QList HttpRequest::getHeaders(const QByteArray& name) const { - return headers.values(name); +QList HttpRequest::getHeaders(const QByteArray& name) const +{ + return headers.values(name.toLower()); } -QMultiMap HttpRequest::getHeaderMap() const { +QMultiMap HttpRequest::getHeaderMap() const +{ return headers; } -QByteArray HttpRequest::getParameter(const QByteArray& name) const { +QByteArray HttpRequest::getParameter(const QByteArray& name) const +{ return parameters.value(name); } -QList HttpRequest::getParameters(const QByteArray& name) const { +QList HttpRequest::getParameters(const QByteArray& name) const +{ return parameters.values(name); } -QMultiMap HttpRequest::getParameterMap() const { +QMultiMap HttpRequest::getParameterMap() const +{ return parameters; } -QByteArray HttpRequest::getBody() const { +QByteArray HttpRequest::getBody() const +{ return bodyData; } -QByteArray HttpRequest::urlDecode(const QByteArray source) { +QByteArray HttpRequest::urlDecode(const QByteArray source) +{ QByteArray buffer(source); buffer.replace('+',' '); int percentChar=buffer.indexOf('%'); - while (percentChar>=0) { + while (percentChar>=0) + { bool ok; char byte=buffer.mid(percentChar+1,2).toInt(&ok,16); - if (ok) { + if (ok) + { buffer.replace(percentChar,3,(char*)&byte,1); } percentChar=buffer.indexOf('%',percentChar+1); @@ -308,65 +392,77 @@ QByteArray HttpRequest::urlDecode(const QByteArray source) { } -void HttpRequest::parseMultiPartFile() { +void HttpRequest::parseMultiPartFile() +{ qDebug("HttpRequest: parsing multipart temp file"); tempFile.seek(0); bool finished=false; - while (!tempFile.atEnd() && !finished && !tempFile.error()) { - -#ifdef SUPERVERBOSE - qDebug("HttpRequest: reading multpart headers"); -#endif + while (!tempFile.atEnd() && !finished && !tempFile.error()) + { + #ifdef SUPERVERBOSE + qDebug("HttpRequest: reading multpart headers"); + #endif QByteArray fieldName; QByteArray fileName; - while (!tempFile.atEnd() && !finished && !tempFile.error()) { + while (!tempFile.atEnd() && !finished && !tempFile.error()) + { QByteArray line=tempFile.readLine(65536).trimmed(); - if (line.startsWith("Content-Disposition:")) { - if (line.contains("form-data")) { + if (line.startsWith("Content-Disposition:")) + { + if (line.contains("form-data")) + { int start=line.indexOf(" name=\""); int end=line.indexOf("\"",start+7); - if (start>=0 && end>=start) { + if (start>=0 && end>=start) + { fieldName=line.mid(start+7,end-start-7); } start=line.indexOf(" filename=\""); end=line.indexOf("\"",start+11); - if (start>=0 && end>=start) { + if (start>=0 && end>=start) + { fileName=line.mid(start+11,end-start-11); } -#ifdef SUPERVERBOSE - qDebug("HttpRequest: multipart field=%s, filename=%s",fieldName.data(),fileName.data()); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: multipart field=%s, filename=%s",fieldName.data(),fileName.data()); + #endif } - else { + else + { qDebug("HttpRequest: ignoring unsupported content part %s",line.data()); } } - else if (line.isEmpty()) { + else if (line.isEmpty()) + { break; } } -#ifdef SUPERVERBOSE - qDebug("HttpRequest: reading multpart data"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: reading multpart data"); + #endif QTemporaryFile* uploadedFile=0; QByteArray fieldValue; - while (!tempFile.atEnd() && !finished && !tempFile.error()) { + while (!tempFile.atEnd() && !finished && !tempFile.error()) + { QByteArray line=tempFile.readLine(65536); - if (line.startsWith("--"+boundary)) { + if (line.startsWith("--"+boundary)) + { // Boundary found. Until now we have collected 2 bytes too much, // so remove them from the last result - if (fileName.isEmpty() && !fieldName.isEmpty()) { + if (fileName.isEmpty() && !fieldName.isEmpty()) + { // last field was a form field fieldValue.remove(fieldValue.size()-2,2); parameters.insert(fieldName,fieldValue); qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fieldValue.data()); } - else if (!fileName.isEmpty() && !fieldName.isEmpty()) { + else if (!fileName.isEmpty() && !fieldName.isEmpty()) + { // last field was a file -#ifdef SUPERVERBOSE - qDebug("HttpRequest: finishing writing to uploaded file"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: finishing writing to uploaded file"); + #endif uploadedFile->resize(uploadedFile->size()-2); uploadedFile->flush(); uploadedFile->seek(0); @@ -375,57 +471,79 @@ void HttpRequest::parseMultiPartFile() { uploadedFiles.insert(fieldName,uploadedFile); qDebug("HttpRequest: uploaded file size is %i",(int) uploadedFile->size()); } - if (line.contains(boundary+"--")) { + if (line.contains(boundary+"--")) + { finished=true; } break; } - else { - if (fileName.isEmpty() && !fieldName.isEmpty()) { + else + { + if (fileName.isEmpty() && !fieldName.isEmpty()) + { // this is a form field. currentSize+=line.size(); fieldValue.append(line); } - else if (!fileName.isEmpty() && !fieldName.isEmpty()) { + else if (!fileName.isEmpty() && !fieldName.isEmpty()) + { // this is a file - if (!uploadedFile) { + if (!uploadedFile) + { uploadedFile=new QTemporaryFile(); uploadedFile->open(); } uploadedFile->write(line); - if (uploadedFile->error()) { + if (uploadedFile->error()) + { qCritical("HttpRequest: error writing temp file, %s",qPrintable(uploadedFile->errorString())); } } } } } - if (tempFile.error()) { + if (tempFile.error()) + { qCritical("HttpRequest: cannot read temp file, %s",qPrintable(tempFile.errorString())); } -#ifdef SUPERVERBOSE - qDebug("HttpRequest: finished parsing multipart temp file"); -#endif + #ifdef SUPERVERBOSE + qDebug("HttpRequest: finished parsing multipart temp file"); + #endif } -HttpRequest::~HttpRequest() { - foreach(QByteArray key, uploadedFiles.keys()) { +HttpRequest::~HttpRequest() +{ + foreach(QByteArray key, uploadedFiles.keys()) + { QTemporaryFile* file=uploadedFiles.value(key); file->close(); delete file; } } -QTemporaryFile* HttpRequest::getUploadedFile(const QByteArray fieldName) { +QTemporaryFile* HttpRequest::getUploadedFile(const QByteArray fieldName) const +{ return uploadedFiles.value(fieldName); } -QByteArray HttpRequest::getCookie(const QByteArray& name) const { +QByteArray HttpRequest::getCookie(const QByteArray& name) const +{ return cookies.value(name); } /** Get the map of cookies */ -QMap& HttpRequest::getCookieMap() { +QMap& HttpRequest::getCookieMap() +{ return cookies; } +/** + Get the address of the connected client. + Note that multiple clients may have the same IP address, if they + share an internet connection (which is very common). + */ +QHostAddress HttpRequest::getPeerAddress() const +{ + return peerAddress; +} + diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httprequest.h b/YACReaderLibrary/server/lib/httpserver/httprequest.h similarity index 77% rename from YACReaderLibrary/server/lib/bfHttpServer/httprequest.h rename to YACReaderLibrary/server/lib/httpserver/httprequest.h index e79fd112..c13846af 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httprequest.h +++ b/YACReaderLibrary/server/lib/httpserver/httprequest.h @@ -7,12 +7,14 @@ #define HTTPREQUEST_H #include +#include #include #include #include #include #include #include +#include "httpglobal.h" /** This object represents a single HTTP request. It reads the request @@ -31,9 +33,10 @@ The body is always a little larger than the file itself. */ -class HttpRequest { +class DECLSPEC HttpRequest { Q_DISABLE_COPY(HttpRequest) friend class HttpSessionStore; + public: /** Values for getStatus() */ @@ -51,11 +54,12 @@ public: virtual ~HttpRequest(); /** - Read the request from a socket. This method must be called repeatedly + Read the HTTP request from a socket. + This method is called by the connection handler repeatedly until the status is RequestStatus::complete or RequestStatus::abort. @param socket Source of the data */ - void readFromSocket(QTcpSocket& socket); + void readFromSocket(QTcpSocket* socket); /** Get the status of this reqeust. @@ -69,12 +73,15 @@ public: /** Get the decoded path of the HTPP request (e.g. "/index.html") */ QByteArray getPath() const; + /** Get the raw path of the HTTP request (e.g. "/file%20with%20spaces.html") */ + const QByteArray& getRawPath() const; + /** Get the version of the HTPP request (e.g. "HTTP/1.1") */ QByteArray getVersion() const; /** Get the value of a HTTP request header. - @param name Name of the header + @param name Name of the header, not case-senitive. @return If the header occurs multiple times, only the last one is returned. */ @@ -82,16 +89,19 @@ public: /** Get the values of a HTTP request header. - @param name Name of the header + @param name Name of the header, not case-senitive. */ QList getHeaders(const QByteArray& name) const; - /** Get all HTTP request headers */ + /** + * Get all HTTP request headers. Note that the header names + * are returned in lower-case. + */ QMultiMap getHeaderMap() const; /** Get the value of a HTTP request parameter. - @param name Name of the parameter + @param name Name of the parameter, case-sensitive. @return If the parameter occurs multiple times, only the last one is returned. */ @@ -99,14 +109,14 @@ public: /** Get the values of a HTTP request parameter. - @param name Name of the parameter + @param name Name of the parameter, case-sensitive. */ QList getParameters(const QByteArray& name) const; - /** Get all HTTP request parameters */ + /** Get all HTTP request parameters. */ QMultiMap getParameterMap() const; - /** Get the HTTP request body */ + /** Get the HTTP request body. */ QByteArray getBody() const; /** @@ -125,17 +135,24 @@ public: For uploaded files, the method getParameters() returns the original fileName as provided by the calling web browser. */ - QTemporaryFile* getUploadedFile(const QByteArray fieldName); + QTemporaryFile* getUploadedFile(const QByteArray fieldName) const; /** - Get the value of a cookie + Get the value of a cookie. @param name Name of the cookie */ QByteArray getCookie(const QByteArray& name) const; - /** Get the map of cookies */ + /** Get all cookies. */ QMap& getCookieMap(); + /** + Get the address of the connected client. + Note that multiple clients may have the same IP address, if they + share an internet connection (which is very common). + */ + QHostAddress getPeerAddress() const; + private: /** Request headers */ @@ -163,11 +180,14 @@ private: QByteArray version; /** - Status of this request. + Status of this request. For the state engine. @see RequestStatus */ RequestStatus status; + /** Address of the connected peer. */ + QHostAddress peerAddress; + /** Maximum size of requests in bytes. */ int maxSize; @@ -193,13 +213,13 @@ private: void parseMultiPartFile(); /** Sub-procedure of readFromSocket(), read the first line of a request. */ - void readRequest(QTcpSocket& socket); + void readRequest(QTcpSocket* socket); /** Sub-procedure of readFromSocket(), read header lines. */ - void readHeader(QTcpSocket& socket); + void readHeader(QTcpSocket* socket); /** Sub-procedure of readFromSocket(), read the request body. */ - void readBody(QTcpSocket& socket); + void readBody(QTcpSocket* socket); /** Sub-procedure of readFromSocket(), extract and decode request parameters. */ void decodeRequestParams(); @@ -207,6 +227,9 @@ private: /** Sub-procedure of readFromSocket(), extract cookies from headers */ void extractCookies(); + /** Buffer for collecting characters of request and header lines */ + QByteArray lineBuffer; + }; #endif // HTTPREQUEST_H diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httprequesthandler.cpp b/YACReaderLibrary/server/lib/httpserver/httprequesthandler.cpp similarity index 75% rename from YACReaderLibrary/server/lib/bfHttpServer/httprequesthandler.cpp rename to YACReaderLibrary/server/lib/httpserver/httprequesthandler.cpp index d8ad7caf..879ccd4f 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httprequesthandler.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httprequesthandler.cpp @@ -9,10 +9,12 @@ HttpRequestHandler::HttpRequestHandler(QObject* parent) : QObject(parent) {} -HttpRequestHandler::~HttpRequestHandler() {} +HttpRequestHandler::~HttpRequestHandler() +{} -void HttpRequestHandler::service(HttpRequest& request, HttpResponse& response) { - qCritical("HttpRequestHandler: you need to override the dispatch() function"); +void HttpRequestHandler::service(HttpRequest& request, HttpResponse& response) +{ + qCritical("HttpRequestHandler: you need to override the service() function"); qDebug("HttpRequestHandler: request=%s %s %s",request.getMethod().data(),request.getPath().data(),request.getVersion().data()); response.setStatus(501,"not implemented"); response.write("501 not implemented",true); diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httprequesthandler.h b/YACReaderLibrary/server/lib/httpserver/httprequesthandler.h similarity index 85% rename from YACReaderLibrary/server/lib/bfHttpServer/httprequesthandler.h rename to YACReaderLibrary/server/lib/httpserver/httprequesthandler.h index a5f9f4e2..12ac0cf9 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httprequesthandler.h +++ b/YACReaderLibrary/server/lib/httpserver/httprequesthandler.h @@ -6,6 +6,7 @@ #ifndef HTTPREQUESTHANDLER_H #define HTTPREQUESTHANDLER_H +#include "httpglobal.h" #include "httprequest.h" #include "httpresponse.h" @@ -21,13 +22,16 @@ @see StaticFileController which delivers static local files. */ -class HttpRequestHandler : public QObject { +class DECLSPEC HttpRequestHandler : public QObject { Q_OBJECT Q_DISABLE_COPY(HttpRequestHandler) public: - /** Constructor */ - HttpRequestHandler(QObject* parent=0); + /** + * Constructor. + * @param parent Parent object. + */ + HttpRequestHandler(QObject* parent=NULL); /** Destructor */ virtual ~HttpRequestHandler(); diff --git a/YACReaderLibrary/server/lib/httpserver/httpresponse.cpp b/YACReaderLibrary/server/lib/httpserver/httpresponse.cpp new file mode 100644 index 00000000..7dbb600a --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/httpresponse.cpp @@ -0,0 +1,198 @@ +/** + @file + @author Stefan Frings +*/ + +#include "httpresponse.h" + +HttpResponse::HttpResponse(QTcpSocket* socket) +{ + this->socket=socket; + statusCode=200; + statusText="OK"; + sentHeaders=false; + sentLastPart=false; + chunkedMode=false; +} + +void HttpResponse::setHeader(QByteArray name, QByteArray value) +{ + Q_ASSERT(sentHeaders==false); + headers.insert(name,value); +} + +void HttpResponse::setHeader(QByteArray name, int value) +{ + Q_ASSERT(sentHeaders==false); + headers.insert(name,QByteArray::number(value)); +} + +QMap& HttpResponse::getHeaders() +{ + return headers; +} + +void HttpResponse::setStatus(int statusCode, QByteArray description) +{ + this->statusCode=statusCode; + statusText=description; +} + +int HttpResponse::getStatusCode() const +{ + return this->statusCode; +} + +void HttpResponse::writeHeaders() +{ + Q_ASSERT(sentHeaders==false); + QByteArray buffer; + buffer.append("HTTP/1.1 "); + buffer.append(QByteArray::number(statusCode)); + buffer.append(' '); + buffer.append(statusText); + buffer.append("\r\n"); + foreach(QByteArray name, headers.keys()) + { + buffer.append(name); + buffer.append(": "); + buffer.append(headers.value(name)); + buffer.append("\r\n"); + } + foreach(HttpCookie cookie,cookies.values()) + { + buffer.append("Set-Cookie: "); + buffer.append(cookie.toByteArray()); + buffer.append("\r\n"); + } + buffer.append("\r\n"); + writeToSocket(buffer); + sentHeaders=true; +} + +bool HttpResponse::writeToSocket(QByteArray data) +{ + int remaining=data.size(); + char* ptr=data.data(); + while (socket->isOpen() && remaining>0) + { + // If the output buffer has become large, then wait until it has been sent. + if (socket->bytesToWrite()>16384) + { + socket->waitForBytesWritten(-1); + } + + int written=socket->write(ptr,remaining); + if (written==-1) + { + return false; + } + ptr+=written; + remaining-=written; + } + return true; +} + +void HttpResponse::write(QByteArray data, bool lastPart) +{ + Q_ASSERT(sentLastPart==false); + + // Send HTTP headers, if not already done (that happens only on the first call to write()) + if (sentHeaders==false) + { + // If the whole response is generated with a single call to write(), then we know the total + // size of the response and therefore can set the Content-Length header automatically. + if (lastPart) + { + // Automatically set the Content-Length header + headers.insert("Content-Length",QByteArray::number(data.size())); + } + + // else if we will not close the connection at the end, them we must use the chunked mode. + else + { + QByteArray connectionValue=headers.value("Connection",headers.value("connection")); + bool connectionClose=QString::compare(connectionValue,"close",Qt::CaseInsensitive)==0; + if (!connectionClose) + { + headers.insert("Transfer-Encoding","chunked"); + chunkedMode=true; + } + } + + writeHeaders(); + } + + // Send data + if (data.size()>0) + { + if (chunkedMode) + { + if (data.size()>0) + { + QByteArray size=QByteArray::number(data.size(),16); + writeToSocket(size); + writeToSocket("\r\n"); + writeToSocket(data); + writeToSocket("\r\n"); + } + } + else + { + writeToSocket(data); + } + } + + // Only for the last chunk, send the terminating marker and flush the buffer. + if (lastPart) + { + if (chunkedMode) + { + writeToSocket("0\r\n\r\n"); + } + socket->flush(); + sentLastPart=true; + } +} + + +bool HttpResponse::hasSentLastPart() const +{ + return sentLastPart; +} + + +void HttpResponse::setCookie(const HttpCookie& cookie) +{ + Q_ASSERT(sentHeaders==false); + if (!cookie.getName().isEmpty()) + { + cookies.insert(cookie.getName(),cookie); + } +} + + +QMap& HttpResponse::getCookies() +{ + return cookies; +} + + +void HttpResponse::redirect(const QByteArray& url) +{ + setStatus(303,"See Other"); + setHeader("Location",url); + write("Redirect",true); +} + + +void HttpResponse::flush() +{ + socket->flush(); +} + + +bool HttpResponse::isConnected() const +{ + return socket->isOpen(); +} diff --git a/YACReaderLibrary/server/lib/httpserver/httpresponse.h b/YACReaderLibrary/server/lib/httpserver/httpresponse.h new file mode 100644 index 00000000..44054806 --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/httpresponse.h @@ -0,0 +1,159 @@ +/** + @file + @author Stefan Frings +*/ + +#ifndef HTTPRESPONSE_H +#define HTTPRESPONSE_H + +#include +#include +#include +#include "httpglobal.h" +#include "httpcookie.h" + +/** + This object represents a HTTP response, used to return something to the web client. +

+

+    response.setStatus(200,"OK"); // optional, because this is the default
+    response.writeBody("Hello");
+    response.writeBody("World!",true);
+  
+

+ Example how to return an error: +

+    response.setStatus(500,"server error");
+    response.write("The request cannot be processed because the servers is broken",true);
+  
+

+ In case of large responses (e.g. file downloads), a Content-Length header should be set + before calling write(). Web Browsers use that information to display a progress bar. +*/ + +class DECLSPEC HttpResponse { + Q_DISABLE_COPY(HttpResponse) +public: + + /** + Constructor. + @param socket used to write the response + */ + HttpResponse(QTcpSocket* socket); + + /** + Set a HTTP response header. + You must call this method before the first write(). + @param name name of the header + @param value value of the header + */ + void setHeader(QByteArray name, QByteArray value); + + /** + Set a HTTP response header. + You must call this method before the first write(). + @param name name of the header + @param value value of the header + */ + void setHeader(QByteArray name, int value); + + /** Get the map of HTTP response headers */ + QMap& getHeaders(); + + /** Get the map of cookies */ + QMap& getCookies(); + + /** + Set status code and description. The default is 200,OK. + You must call this method before the first write(). + */ + void setStatus(int statusCode, QByteArray description=QByteArray()); + + /** Return the status code. */ + int getStatusCode() const; + + /** + Write body data to the socket. +

+ The HTTP status line, headers and cookies are sent automatically before the body. +

+ If the response contains only a single chunk (indicated by lastPart=true), + then a Content-Length header is automatically set. +

+ Chunked mode is automatically selected if there is no Content-Length header + and also no Connection:close header. + @param data Data bytes of the body + @param lastPart Indicates that this is the last chunk of data and flushes the output buffer. + */ + void write(QByteArray data, bool lastPart=false); + + /** + Indicates whether the body has been sent completely (write() has been called with lastPart=true). + */ + bool hasSentLastPart() const; + + /** + Set a cookie. + You must call this method before the first write(). + */ + void setCookie(const HttpCookie& cookie); + + /** + Send a redirect response to the browser. + Cannot be combined with write(). + @param url Destination URL + */ + void redirect(const QByteArray& url); + + /** + * Flush the output buffer (of the underlying socket). + * You normally don't need to call this method because flush is + * automatically called after HttpRequestHandler::service() returns. + */ + void flush(); + + /** + * May be used to check whether the connection to the web client has been lost. + * This might be useful to cancel the generation of large or slow responses. + */ + bool isConnected() const; + +private: + + /** Request headers */ + QMap headers; + + /** Socket for writing output */ + QTcpSocket* socket; + + /** HTTP status code*/ + int statusCode; + + /** HTTP status code description */ + QByteArray statusText; + + /** Indicator whether headers have been sent */ + bool sentHeaders; + + /** Indicator whether the body has been sent completely */ + bool sentLastPart; + + /** Whether the response is sent in chunked mode */ + bool chunkedMode; + + /** Cookies */ + QMap cookies; + + /** Write raw data to the socket. This method blocks until all bytes have been passed to the TCP buffer */ + bool writeToSocket(QByteArray data); + + /** + Write the response HTTP status and headers to the socket. + Calling this method is optional, because writeBody() calls + it automatically when required. + */ + void writeHeaders(); + +}; + +#endif // HTTPRESPONSE_H diff --git a/YACReaderLibrary/server/lib/httpserver/httpserver.pri b/YACReaderLibrary/server/lib/httpserver/httpserver.pri new file mode 100644 index 00000000..fb78772e --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/httpserver.pri @@ -0,0 +1,33 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +QT += network + +# Enable very detailed debug messages when compiling the debug version +CONFIG(debug, debug|release) { + DEFINES += SUPERVERBOSE +} + +HEADERS += $$PWD/httpglobal.h \ + $$PWD/httplistener.h \ + $$PWD/httpconnectionhandler.h \ + $$PWD/httpconnectionhandlerpool.h \ + $$PWD/httprequest.h \ + $$PWD/httpresponse.h \ + $$PWD/httpcookie.h \ + $$PWD/httprequesthandler.h \ + $$PWD/httpsession.h \ + $$PWD/httpsessionstore.h \ + $$PWD/staticfilecontroller.h + +SOURCES += $$PWD/httpglobal.cpp \ + $$PWD/httplistener.cpp \ + $$PWD/httpconnectionhandler.cpp \ + $$PWD/httpconnectionhandlerpool.cpp \ + $$PWD/httprequest.cpp \ + $$PWD/httpresponse.cpp \ + $$PWD/httpcookie.cpp \ + $$PWD/httprequesthandler.cpp \ + $$PWD/httpsession.cpp \ + $$PWD/httpsessionstore.cpp \ + $$PWD/staticfilecontroller.cpp diff --git a/YACReaderLibrary/server/lib/httpserver/httpsession.cpp b/YACReaderLibrary/server/lib/httpserver/httpsession.cpp new file mode 100644 index 00000000..165f93a2 --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/httpsession.cpp @@ -0,0 +1,186 @@ +/** + @file + @author Stefan Frings +*/ + +#include "httpsession.h" +#include +#include + + +HttpSession::HttpSession(bool canStore) +{ + if (canStore) + { + dataPtr=new HttpSessionData(); + dataPtr->refCount=1; + dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); + dataPtr->id=QUuid::createUuid().toString().toLocal8Bit(); +#ifdef SUPERVERBOSE + qDebug("HttpSession: created new session data with id %s",dataPtr->id.data()); +#endif + } + else + { + dataPtr=0; + } +} + +HttpSession::HttpSession(const HttpSession& other) +{ + dataPtr=other.dataPtr; + if (dataPtr) + { + dataPtr->lock.lockForWrite(); + dataPtr->refCount++; +#ifdef SUPERVERBOSE + qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); +#endif + dataPtr->lock.unlock(); + } +} + +HttpSession& HttpSession::operator= (const HttpSession& other) +{ + HttpSessionData* oldPtr=dataPtr; + dataPtr=other.dataPtr; + if (dataPtr) + { + dataPtr->lock.lockForWrite(); + dataPtr->refCount++; +#ifdef SUPERVERBOSE + qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); +#endif + dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); + dataPtr->lock.unlock(); + } + if (oldPtr) + { + int refCount; + oldPtr->lock.lockForRead(); + refCount=oldPtr->refCount--; +#ifdef SUPERVERBOSE + qDebug("HttpSession: refCount of %s is %i",oldPtr->id.data(),oldPtr->refCount); +#endif + oldPtr->lock.unlock(); + if (refCount==0) + { + delete oldPtr; + } + } + return *this; +} + +HttpSession::~HttpSession() +{ + if (dataPtr) { + int refCount; + dataPtr->lock.lockForRead(); + refCount=--dataPtr->refCount; +#ifdef SUPERVERBOSE + qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); +#endif + dataPtr->lock.unlock(); + if (refCount==0) + { + qDebug("HttpSession: deleting data"); + delete dataPtr; + } + } +} + + +QByteArray HttpSession::getId() const +{ + if (dataPtr) + { + return dataPtr->id; + } + else + { + return QByteArray(); + } +} + +bool HttpSession::isNull() const { + return dataPtr==0; +} + +void HttpSession::set(const QByteArray& key, const QVariant& value) +{ + if (dataPtr) + { + dataPtr->lock.lockForWrite(); + dataPtr->values.insert(key,value); + dataPtr->lock.unlock(); + } +} + +void HttpSession::remove(const QByteArray& key) +{ + if (dataPtr) + { + dataPtr->lock.lockForWrite(); + dataPtr->values.remove(key); + dataPtr->lock.unlock(); + } +} + +QVariant HttpSession::get(const QByteArray& key) const +{ + QVariant value; + if (dataPtr) + { + dataPtr->lock.lockForRead(); + value=dataPtr->values.value(key); + dataPtr->lock.unlock(); + } + return value; +} + +bool HttpSession::contains(const QByteArray& key) const +{ + bool found=false; + if (dataPtr) + { + dataPtr->lock.lockForRead(); + found=dataPtr->values.contains(key); + dataPtr->lock.unlock(); + } + return found; +} + +QMap HttpSession::getAll() const +{ + QMap values; + if (dataPtr) + { + dataPtr->lock.lockForRead(); + values=dataPtr->values; + dataPtr->lock.unlock(); + } + return values; +} + +qint64 HttpSession::getLastAccess() const +{ + qint64 value=0; + if (dataPtr) + { + dataPtr->lock.lockForRead(); + value=dataPtr->lastAccess; + dataPtr->lock.unlock(); + } + return value; +} + + +void HttpSession::setLastAccess() +{ + if (dataPtr) + { + dataPtr->lock.lockForRead(); + dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); + dataPtr->lock.unlock(); + } +} diff --git a/YACReaderLibrary/server/lib/httpserver/httpsession.h b/YACReaderLibrary/server/lib/httpserver/httpsession.h new file mode 100644 index 00000000..fa0ee3f9 --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/httpsession.h @@ -0,0 +1,118 @@ +/** + @file + @author Stefan Frings +*/ + +#ifndef HTTPSESSION_H +#define HTTPSESSION_H + +#include +#include +#include +#include "httpglobal.h" + +/** + This class stores data for a single HTTP session. + A session can store any number of key/value pairs. This class uses implicit + sharing for read and write access. This class is thread safe. + @see HttpSessionStore should be used to create and get instances of this class. +*/ + +class DECLSPEC HttpSession { + +public: + + /** + Constructor. + @param canStore The session can store data, if this parameter is true. + Otherwise all calls to set() and remove() do not have any effect. + */ + HttpSession(bool canStore=false); + + /** + Copy constructor. Creates another HttpSession object that shares the + data of the other object. + */ + HttpSession(const HttpSession& other); + + /** + Copy operator. Detaches from the current shared data and attaches to + the data of the other object. + */ + HttpSession& operator= (const HttpSession& other); + + + /** + Destructor. Detaches from the shared data. + */ + virtual ~HttpSession(); + + /** Get the unique ID of this session. This method is thread safe. */ + QByteArray getId() const; + + /** + Null sessions cannot store data. All calls to set() and remove() + do not have any effect.This method is thread safe. + */ + bool isNull() const; + + /** Set a value. This method is thread safe. */ + void set(const QByteArray& key, const QVariant& value); + + /** Remove a value. This method is thread safe. */ + void remove(const QByteArray& key); + + /** Get a value. This method is thread safe. */ + QVariant get(const QByteArray& key) const; + + /** Check if a key exists. This method is thread safe. */ + bool contains(const QByteArray& key) const; + + /** + Get a copy of all data stored in this session. + Changes to the session do not affect the copy and vice versa. + This method is thread safe. + */ + QMap getAll() const; + + /** + Get the timestamp of last access. That is the time when the last + HttpSessionStore::getSession() has been called. + This method is thread safe. + */ + qint64 getLastAccess() const; + + /** + Set the timestamp of last access, to renew the timeout period. + Called by HttpSessionStore::getSession(). + This method is thread safe. + */ + void setLastAccess(); + +private: + + struct HttpSessionData { + + /** Unique ID */ + QByteArray id; + + /** Timestamp of last access, set by the HttpSessionStore */ + qint64 lastAccess; + + /** Reference counter */ + int refCount; + + /** Used to synchronize threads */ + QReadWriteLock lock; + + /** Storage for the key/value pairs; */ + QMap values; + + }; + + /** Pointer to the shared data. */ + HttpSessionData* dataPtr; + +}; + +#endif // HTTPSESSION_H diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.cpp b/YACReaderLibrary/server/lib/httpserver/httpsessionstore.cpp similarity index 67% rename from YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.cpp rename to YACReaderLibrary/server/lib/httpserver/httpsessionstore.cpp index 87ce7d4a..db6e1c2e 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.cpp +++ b/YACReaderLibrary/server/lib/httpserver/httpsessionstore.cpp @@ -11,10 +11,10 @@ HttpSessionStore::HttpSessionStore(QSettings* settings, QObject* parent) :QObject(parent) { this->settings=settings; - connect(&cleanupTimer,SIGNAL(timeout()),this,SLOT(timerEvent())); + connect(&cleanupTimer,SIGNAL(timeout()),this,SLOT(sessionTimerEvent())); cleanupTimer.start(60000); cookieName=settings->value("cookieName","sessionid").toByteArray(); - expirationTime=settings->value("expirationTime",864000000).toInt(); + expirationTime=settings->value("expirationTime",3600000).toInt(); qDebug("HttpSessionStore: Sessions expire after %i milliseconds",expirationTime); } @@ -23,18 +23,22 @@ HttpSessionStore::~HttpSessionStore() cleanupTimer.stop(); } -QByteArray HttpSessionStore::getSessionId(HttpRequest& request, HttpResponse& response) { +QByteArray HttpSessionStore::getSessionId(HttpRequest& request, HttpResponse& response) +{ // The session ID in the response has priority because this one will be used in the next request. mutex.lock(); // Get the session ID from the response cookie QByteArray sessionId=response.getCookies().value(cookieName).getValue(); - if (sessionId.isEmpty()) { + if (sessionId.isEmpty()) + { // Get the session ID from the request cookie sessionId=request.getCookie(cookieName); } // Clear the session ID if there is no such session in the storage. - if (!sessionId.isEmpty()) { - if (!sessions.contains(sessionId)) { + if (!sessionId.isEmpty()) + { + if (!sessions.contains(sessionId)) + { qDebug("HttpSessionStore: received invalid session cookie with ID %s",sessionId.data()); sessionId.clear(); } @@ -43,21 +47,31 @@ QByteArray HttpSessionStore::getSessionId(HttpRequest& request, HttpResponse& re return sessionId; } -HttpSession HttpSessionStore::getSession(HttpRequest& request, HttpResponse& response, bool allowCreate) { +HttpSession HttpSessionStore::getSession(HttpRequest& request, HttpResponse& response, bool allowCreate) +{ QByteArray sessionId=getSessionId(request,response); mutex.lock(); - if (!sessionId.isEmpty()) { + if (!sessionId.isEmpty()) + { HttpSession session=sessions.value(sessionId); - if (!session.isNull()) { + if (!session.isNull()) + { mutex.unlock(); + // Refresh the session cookie + QByteArray cookieName=settings->value("cookieName","sessionid").toByteArray(); + QByteArray cookiePath=settings->value("cookiePath").toByteArray(); + QByteArray cookieComment=settings->value("cookieComment").toByteArray(); + QByteArray cookieDomain=settings->value("cookieDomain").toByteArray(); + response.setCookie(HttpCookie(cookieName,session.getId(),expirationTime/1000,cookiePath,cookieComment,cookieDomain)); session.setLastAccess(); return session; } } // Need to create a new session - if (allowCreate) { + if (allowCreate) + { QByteArray cookieName=settings->value("cookieName","sessionid").toByteArray(); - QByteArray cookiePath=settings->value("cookiePath","/").toByteArray(); + QByteArray cookiePath=settings->value("cookiePath").toByteArray(); QByteArray cookieComment=settings->value("cookieComment").toByteArray(); QByteArray cookieDomain=settings->value("cookieDomain").toByteArray(); HttpSession session(true); @@ -72,7 +86,8 @@ HttpSession HttpSessionStore::getSession(HttpRequest& request, HttpResponse& res return HttpSession(); } -HttpSession HttpSessionStore::getSession(const QByteArray id) { +HttpSession HttpSessionStore::getSession(const QByteArray id) +{ mutex.lock(); HttpSession session=sessions.value(id); mutex.unlock(); @@ -80,19 +95,19 @@ HttpSession HttpSessionStore::getSession(const QByteArray id) { return session; } -void HttpSessionStore::timerEvent() { - // Todo: find a way to delete sessions only if no controller is accessing them +void HttpSessionStore::sessionTimerEvent() +{ mutex.lock(); qint64 now=QDateTime::currentMSecsSinceEpoch(); QMap::iterator i = sessions.begin(); - while (i != sessions.end()) { + while (i != sessions.end()) + { QMap::iterator prev = i; ++i; HttpSession session=prev.value(); qint64 lastAccess=session.getLastAccess(); - if (now-lastAccess>expirationTime) { //TODO cleaning up will cause current opened comic to be deleted, so clients won't be able to download it - //If the cleaning occurs in the midle of a download it going to cause issues - //Temporal fix: use a big expirationTime = 10 days + if (now-lastAccess>expirationTime) + { qDebug("HttpSessionStore: session %s expired",session.getId().data()); sessions.erase(prev); } @@ -102,7 +117,8 @@ void HttpSessionStore::timerEvent() { /** Delete a session */ -void HttpSessionStore::removeSession(HttpSession session) { +void HttpSessionStore::removeSession(HttpSession session) +{ mutex.lock(); sessions.remove(session.getId()); mutex.unlock(); diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.h b/YACReaderLibrary/server/lib/httpserver/httpsessionstore.h similarity index 89% rename from YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.h rename to YACReaderLibrary/server/lib/httpserver/httpsessionstore.h index e65260d7..5f5efdb4 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.h +++ b/YACReaderLibrary/server/lib/httpserver/httpsessionstore.h @@ -10,6 +10,7 @@ #include #include #include +#include "httpglobal.h" #include "httpsession.h" #include "httpresponse.h" #include "httprequest.h" @@ -25,17 +26,17 @@

   cookiePath=/
   cookieComment=Session ID
-  cookieDomain=stefanfrings.de
+  ;cookieDomain=stefanfrings.de
   
*/ -class HttpSessionStore : public QObject { +class DECLSPEC HttpSessionStore : public QObject { Q_OBJECT Q_DISABLE_COPY(HttpSessionStore) public: /** Constructor. */ - HttpSessionStore(QSettings* settings, QObject* parent); + HttpSessionStore(QSettings* settings, QObject* parent=NULL); /** Destructor */ virtual ~HttpSessionStore(); @@ -75,14 +76,15 @@ public: /** Delete a session */ void removeSession(HttpSession session); +protected: + /** Storage for the sessions */ + QMap sessions; + private: /** Configuration settings */ QSettings* settings; - /** Storage for the sessions */ - QMap sessions; - /** Timer to remove expired sessions */ QTimer cleanupTimer; @@ -98,7 +100,7 @@ private: private slots: /** Called every minute to cleanup expired sessions. */ - void timerEvent(); + void sessionTimerEvent(); }; #endif // HTTPSESSIONSTORE_H diff --git a/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp new file mode 100644 index 00000000..dd940d3e --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.cpp @@ -0,0 +1,288 @@ +/** + @file + @author Stefan Frings +*/ + +#include "staticfilecontroller.h" +#include +#include +#include + +//YACReader----- +#include "httpsession.h" +#include "yacreader_http_session.h" +#include "static.h" +//-- + +StaticFileController::StaticFileController(QSettings* settings, QObject* parent) + :HttpRequestHandler(parent) +{ + maxAge=settings->value("maxAge","60000").toInt(); + encoding=settings->value("encoding","UTF-8").toString(); + docroot=settings->value("path",".").toString(); + if(!(docroot.startsWith(":/") || docroot.startsWith("qrc://"))) + { + // Convert relative path to absolute, based on the directory of the config file. + #ifdef Q_OS_WIN32 + if (QDir::isRelativePath(docroot) && settings->format()!=QSettings::NativeFormat) + #else + if (QDir::isRelativePath(docroot)) + #endif + { + QFileInfo configFile(settings->fileName()); + docroot=QFileInfo(configFile.absolutePath(),docroot).absoluteFilePath(); + } + } + qDebug("StaticFileController: docroot=%s, encoding=%s, maxAge=%i",qPrintable(docroot),qPrintable(encoding),maxAge); + maxCachedFileSize=settings->value("maxCachedFileSize","65536").toInt(); + cache.setMaxCost(settings->value("cacheSize","1000000").toInt()); + cacheTimeout=settings->value("cacheTime","60000").toInt(); + qDebug("StaticFileController: cache timeout=%i, size=%i",cacheTimeout,cache.maxCost()); +} + + +void StaticFileController::service(HttpRequest& request, HttpResponse& response) +{ + QByteArray path=request.getPath(); + // Check if we have the file in cache + qint64 now=QDateTime::currentMSecsSinceEpoch(); + mutex.lock(); + CacheEntry* entry=cache.object(path); + if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout)) + { + QByteArray document=entry->document; //copy the cached document, because other threads may destroy the cached entry immediately after mutex unlock. + QByteArray filename=entry->filename; + mutex.unlock(); + qDebug("StaticFileController: Cache hit for %s",path.data()); + setContentType(filename,response); + response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000)); + response.write(document); + } + else + { + mutex.unlock(); + + //TODO(DONE) carga sensible al dispositivo y a la localización + QString stringPath = path; + QStringList paths = QString(path).split('/'); + QString fileName = paths.last(); + stringPath.remove(fileName); + HttpSession session=Static::sessionStore->getSession(request,response,false); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); + QString device = ySession->getDeviceType(); + QString display = ySession->getDisplayType(); + if(fileName.endsWith(".png")) + fileName = getDeviceAwareFileName(fileName, device, display, request.getHeader("Accept-Language"), stringPath); + else + fileName = getDeviceAwareFileName(fileName, device, request.getHeader("Accept-Language"), stringPath); + QString newPath = stringPath.append(fileName); + path = newPath.toLocal8Bit(); + + //CAMBIADO + //response.setHeader("Connection","close"); + //END_TODO + + // The file is not in cache. + qDebug("StaticFileController: Cache miss for %s",path.data()); + // Forbid access to files outside the docroot directory + if (path.contains("/..")) + { + qWarning("StaticFileController: detected forbidden characters in path %s",path.data()); + response.setStatus(403,"forbidden"); + response.write("403 forbidden",true); + return; + } + // If the filename is a directory, append index.html. + if (QFileInfo(docroot+path).isDir()) + { + path+="/index.html"; + } + // Try to open the file + QFile file(docroot+path); + qDebug("StaticFileController: Open file %s",qPrintable(file.fileName())); + if (file.open(QIODevice::ReadOnly)) + { + setContentType(path,response); + response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000)); + if (file.size()<=maxCachedFileSize) + { + // Return the file content and store it also in the cache + entry=new CacheEntry(); + while (!file.atEnd() && !file.error()) + { + QByteArray buffer=file.read(65536); + response.write(buffer); + entry->document.append(buffer); + } + entry->created=now; + entry->filename=path; + mutex.lock(); + cache.insert(request.getPath(),entry,entry->document.size()); + mutex.unlock(); + } + else + { + // Return the file content, do not store in cache + while (!file.atEnd() && !file.error()) + { + response.write(file.read(65536)); + } + } + file.close(); + } + else { + if (file.exists()) + { + qWarning("StaticFileController: Cannot open existing file %s for reading",qPrintable(file.fileName())); + response.setStatus(403,"forbidden"); + response.write("403 forbidden",true); + } + else + { + response.setStatus(404,"not found"); + response.write("404 not found",true); + } + } + } +} + +void StaticFileController::setContentType(QString fileName, HttpResponse& response) const +{ + if (fileName.endsWith(".png")) + { + response.setHeader("Content-Type", "image/png"); + } + else if (fileName.endsWith(".jpg")) + { + response.setHeader("Content-Type", "image/jpeg"); + } + else if (fileName.endsWith(".gif")) + { + response.setHeader("Content-Type", "image/gif"); + } + else if (fileName.endsWith(".pdf")) + { + response.setHeader("Content-Type", "application/pdf"); + } + else if (fileName.endsWith(".txt")) + { + response.setHeader("Content-Type", qPrintable("text/plain; charset="+encoding)); + } + else if (fileName.endsWith(".html") || fileName.endsWith(".htm")) + { + response.setHeader("Content-Type", qPrintable("text/html; charset="+encoding)); + } + else if (fileName.endsWith(".css")) + { + response.setHeader("Content-Type", "text/css"); + } + else if (fileName.endsWith(".js")) + { + response.setHeader("Content-Type", "text/javascript"); + } + else if (fileName.endsWith(".svg")) + { + response.setHeader("Content-Type", "image/svg+xml"); + } + else if (fileName.endsWith(".woff")) + { + response.setHeader("Content-Type", "font/woff"); + } + else if (fileName.endsWith(".woff2")) + { + response.setHeader("Content-Type", "font/woff2"); + } + else if (fileName.endsWith(".ttf")) + { + response.setHeader("Content-Type", "application/x-font-ttf"); + } + else if (fileName.endsWith(".eot")) + { + response.setHeader("Content-Type", "application/vnd.ms-fontobject"); + } + else if (fileName.endsWith(".otf")) + { + response.setHeader("Content-Type", "application/font-otf"); + } + // Todo: add all of your content types + else + { + qDebug("StaticFileController: unknown MIME type for filename '%s'", qPrintable(fileName)); + } +} + +//YACReader------------------------------------------------------------------------ + +bool StaticFileController::exists(QString localizedName, QString path) const +{ + QString fileName=docroot+"/"+path + localizedName; + QFile file(fileName); + return file.exists(); +} + +//retorna fileName si no se encontró alternativa traducida ó fileName-locale.extensión si se encontró +QString StaticFileController::getLocalizedFileName(QString fileName, QString locales, QString path) const +{ + QSet tried; // used to suppress duplicate attempts + QStringList locs=locales.split(',',QString::SkipEmptyParts); + QStringList fileNameParts = fileName.split('.'); + QString file = fileNameParts.first(); + QString extension = fileNameParts.last(); + // Search for exact match + foreach (QString loc,locs) { + loc.replace(QRegExp(";.*"),""); + loc.replace('-','_'); + QString localizedName=file+"-"+loc.trimmed()+"."+extension; + if (!tried.contains(localizedName)) { + if(exists(localizedName, path)) + return localizedName; + tried.insert(localizedName); + } + } + + // Search for correct language but any country + foreach (QString loc,locs) { + loc.replace(QRegExp("[;_-].*"),""); + QString localizedName=file+"-"+loc.trimmed()+"."+extension; + if (!tried.contains(localizedName)) { + if(exists(localizedName, path)) + return localizedName; + tried.insert(localizedName); + } + } + + return fileName; +} + +QString StaticFileController::getDeviceAwareFileName(QString fileName, QString device, QString locales, QString path) const +{ + QFileInfo fi(fileName); + QString baseName = fi.baseName(); + QString extension = fi.completeSuffix(); + + QString completeFileName = getLocalizedFileName(baseName+"_"+device+"."+extension,locales,path); + + if(QFile(docroot+"/"+path+completeFileName).exists()) + return completeFileName; //existe un archivo específico para este dispositivo y locales + else + return getLocalizedFileName(fileName,locales,path); //no hay archivo específico para el dispositivo, pero puede haberlo para estas locales +} + +QString StaticFileController::getDeviceAwareFileName(QString fileName, QString device, QString display, QString locales, QString path) const +{ + QFileInfo fi(fileName); + QString baseName = fi.baseName(); + QString extension = fi.completeSuffix(); + + QString completeFileName = baseName+display+"."+extension; + if(QFile(docroot+"/"+path+completeFileName).exists()) + return completeFileName; + else + { + completeFileName = baseName+"_"+device+display+"."+extension; + if((QFile(docroot+"/"+path+completeFileName).exists())) + return completeFileName; + } + + return fileName; +} diff --git a/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h new file mode 100644 index 00000000..47424d75 --- /dev/null +++ b/YACReaderLibrary/server/lib/httpserver/staticfilecontroller.h @@ -0,0 +1,94 @@ +/** + @file + @author Stefan Frings +*/ + +#ifndef STATICFILECONTROLLER_H +#define STATICFILECONTROLLER_H + +#include +#include +#include "httpglobal.h" +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +/** + Delivers static files. It is usually called by the applications main request handler when + the caller requests a path that is mapped to static files. +

+ The following settings are required in the config file: +

+  path=../docroot
+  encoding=UTF-8
+  maxAge=60000
+  cacheTime=60000
+  cacheSize=1000000
+  maxCachedFileSize=65536
+  
+ The path is relative to the directory of the config file. In case of windows, if the + settings are in the registry, the path is relative to the current working directory. +

+ The encoding is sent to the web browser in case of text and html files. +

+ The cache improves performance of small files when loaded from a network + drive. Large files are not cached. Files are cached as long as possible, + when cacheTime=0. The maxAge value (in msec!) controls the remote browsers cache. +

+ Do not instantiate this class in each request, because this would make the file cache + useless. Better create one instance during start-up and call it when the application + received a related HTTP request. +*/ + +class DECLSPEC StaticFileController : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(StaticFileController) +public: + + /** Constructor */ + StaticFileController(QSettings* settings, QObject* parent = NULL); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); + +private: + + /** Encoding of text files */ + QString encoding; + + /** Root directory of documents */ + QString docroot; + + /** Maximum age of files in the browser cache */ + int maxAge; + + struct CacheEntry { + QByteArray document; + qint64 created; + QByteArray filename; + }; + + /** Timeout for each cached file */ + int cacheTimeout; + + /** Maximum size of files in cache, larger files are not cached */ + int maxCachedFileSize; + + /** Cache storage */ + QCache cache; + + /** Used to synchronize cache access for threads */ + QMutex mutex; + + /** Set a content-type header in the response depending on the ending of the filename */ + void setContentType(QString file, HttpResponse& response) const; + + //YACReader------------------------------------------------------------------------ + QString getLocalizedFileName(QString fileName, QString locales, QString path) const; + QString getDeviceAwareFileName(QString fileName, QString device, QString locales, QString path) const; + QString getDeviceAwareFileName(QString fileName, QString device, QString display, QString locales, QString path) const; + + bool exists(QString localizedName, QString path) const; +}; + +#endif // STATICFILECONTROLLER_H diff --git a/YACReaderLibrary/server/lib/bfLogging/dualfilelogger.cpp b/YACReaderLibrary/server/lib/logging/dualfilelogger.cpp similarity index 54% rename from YACReaderLibrary/server/lib/bfLogging/dualfilelogger.cpp rename to YACReaderLibrary/server/lib/logging/dualfilelogger.cpp index 7329cae0..80738e9d 100644 --- a/YACReaderLibrary/server/lib/bfLogging/dualfilelogger.cpp +++ b/YACReaderLibrary/server/lib/logging/dualfilelogger.cpp @@ -13,8 +13,14 @@ DualFileLogger::DualFileLogger(QSettings* firstSettings, QSettings* secondSettin secondLogger=new FileLogger(secondSettings, refreshInterval, this); } - -void DualFileLogger::log(const QtMsgType type, const QString& message) { - firstLogger->log(type, message); - secondLogger->log(type, message); +void DualFileLogger::log(const QtMsgType type, const QString& message, const QString &file, const QString &function, const int line) +{ + firstLogger->log(type,message,file,function,line); + secondLogger->log(type,message,file,function,line); +} + +void DualFileLogger::clear(const bool buffer, const bool variables) +{ + firstLogger->clear(buffer,variables); + secondLogger->clear(buffer,variables); } diff --git a/YACReaderLibrary/server/lib/bfLogging/dualfilelogger.h b/YACReaderLibrary/server/lib/logging/dualfilelogger.h similarity index 63% rename from YACReaderLibrary/server/lib/bfLogging/dualfilelogger.h rename to YACReaderLibrary/server/lib/logging/dualfilelogger.h index 39bec859..e65236a0 100644 --- a/YACReaderLibrary/server/lib/bfLogging/dualfilelogger.h +++ b/YACReaderLibrary/server/lib/logging/dualfilelogger.h @@ -6,11 +6,12 @@ #ifndef DUALFILELOGGER_H #define DUALFILELOGGER_H -#include "logger.h" -#include "filelogger.h" #include #include #include +#include "logglobal.h" +#include "logger.h" +#include "filelogger.h" /** Logs messages into two log files simultaneously. @@ -18,7 +19,7 @@ @see FileLogger for a description of the two underlying loggers. */ -class DualFileLogger : public Logger { +class DECLSPEC DualFileLogger : public Logger { Q_OBJECT Q_DISABLE_COPY(DualFileLogger) public: @@ -37,13 +38,24 @@ public: DualFileLogger(QSettings* firstSettings, QSettings* secondSettings, const int refreshInterval=10000, QObject *parent = 0); /** - Decorate and log a message. + Decorate and log the message, if type>=minLevel. This method is thread safe. @param type Message type (level) @param message Message text + @param file Name of the source file where the message was generated (usually filled with the macro __FILE__) + @param function Name of the function where the message was generated (usually filled with the macro __LINE__) + @param line Line Number of the source file, where the message was generated (usually filles with the macro __func__ or __FUNCTION__) @see LogMessage for a description of the message decoration. */ - virtual void log(const QtMsgType type, const QString& message); + virtual void log(const QtMsgType type, const QString& message, const QString &file="", const QString &function="", const int line=0); + + /** + Clear the thread-local data of the current thread. + This method is thread safe. + @param buffer Whether to clear the backtrace buffer + @param variables Whether to clear the log variables + */ + virtual void clear(const bool buffer=true, const bool variables=true); private: diff --git a/YACReaderLibrary/server/lib/bfLogging/filelogger.cpp b/YACReaderLibrary/server/lib/logging/filelogger.cpp similarity index 70% rename from YACReaderLibrary/server/lib/bfLogging/filelogger.cpp rename to YACReaderLibrary/server/lib/logging/filelogger.cpp index 06cf122d..3e027bc2 100644 --- a/YACReaderLibrary/server/lib/bfLogging/filelogger.cpp +++ b/YACReaderLibrary/server/lib/logging/filelogger.cpp @@ -13,16 +13,16 @@ #include #include #include -#include "yacreader_global.h" -void FileLogger::refreshSettings() { +void FileLogger::refreshSettings() +{ mutex.lock(); // Save old file name for later comparision with new settings QString oldFileName=fileName; // Load new config settings settings->sync(); - fileName=settings->value("fileName","server_log.log").toString(); + fileName=settings->value("fileName").toString(); // Convert relative fileName to absolute, based on the directory of the config file. #ifdef Q_OS_WIN32 if (QDir::isRelativePath(fileName) && settings->format()!=QSettings::NativeFormat) @@ -30,18 +30,19 @@ void FileLogger::refreshSettings() { if (QDir::isRelativePath(fileName)) #endif { - QFileInfo configFile(YACReader::getSettingsPath()); - fileName=QFileInfo(YACReader::getSettingsPath(),fileName).absoluteFilePath(); + QFileInfo configFile(settings->fileName()); + fileName=QFileInfo(configFile.absolutePath(),fileName).absoluteFilePath(); } - maxSize=settings->value("maxSize",1048576).toLongLong(); - maxBackups=settings->value("maxBackups",1).toInt(); + maxSize=settings->value("maxSize",0).toLongLong(); + maxBackups=settings->value("maxBackups",0).toInt(); msgFormat=settings->value("msgFormat","{timestamp} {type} {msg}").toString(); timestampFormat=settings->value("timestampFormat","yyyy-MM-dd hh:mm:ss.zzz").toString(); - minLevel=static_cast(settings->value("minLevel",QtCriticalMsg).toInt()); + minLevel=static_cast(settings->value("minLevel",0).toInt()); bufferSize=settings->value("bufferSize",0).toInt(); // Create new file if the filename has been changed - if (oldFileName!=fileName) { + if (oldFileName!=fileName) + { fprintf(stderr,"Logging to %s\n",qPrintable(fileName)); close(); open(); @@ -57,7 +58,8 @@ FileLogger::FileLogger(QSettings* settings, const int refreshInterval, QObject* Q_ASSERT(refreshInterval>=0); this->settings=settings; file=0; - if (refreshInterval>0) { + if (refreshInterval>0) + { refreshTimer.start(refreshInterval,this); } flushTimer.start(1000,this); @@ -65,26 +67,31 @@ FileLogger::FileLogger(QSettings* settings, const int refreshInterval, QObject* } -FileLogger::~FileLogger() { +FileLogger::~FileLogger() +{ close(); } -void FileLogger::write(const LogMessage* logMessage) { +void FileLogger::write(const LogMessage* logMessage) +{ // Try to write to the file - if (file) { + if (file) + { // Write the message file->write(qPrintable(logMessage->toString(msgFormat,timestampFormat))); // Flush error messages immediately, to ensure that no important message // gets lost when the program terinates abnormally. - if (logMessage->getType()>=QtCriticalMsg) { + if (logMessage->getType()>=QtCriticalMsg) + { file->flush(); } // Check for success - if (file->error()) { + if (file->error()) + { close(); qWarning("Cannot write to log file %s: %s",qPrintable(fileName),qPrintable(file->errorString())); } @@ -92,19 +99,23 @@ void FileLogger::write(const LogMessage* logMessage) { } // Fall-back to the super class method, if writing failed - if (!file) { + if (!file) + { Logger::write(logMessage); } } -void FileLogger::open() { - if (fileName.isEmpty()) { +void FileLogger::open() +{ + if (fileName.isEmpty()) + { qWarning("Name of logFile is empty"); } else { file=new QFile(fileName); - if (!file->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { + if (!file->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) + { qWarning("Cannot open log file %s: %s",qPrintable(fileName),qPrintable(file->errorString())); file=0; } @@ -112,8 +123,10 @@ void FileLogger::open() { } -void FileLogger::close() { - if (file) { +void FileLogger::close() +{ + if (file) + { file->close(); delete file; file=0; @@ -123,18 +136,22 @@ void FileLogger::close() { void FileLogger::rotate() { // count current number of existing backup files int count=0; - forever { + forever + { QFile bakFile(QString("%1.%2").arg(fileName).arg(count+1)); - if (bakFile.exists()) { + if (bakFile.exists()) + { ++count; } - else { + else + { break; } } // Remove all old backup files that exceed the maximum number - while (maxBackups>0 && count>=maxBackups) { + while (maxBackups>0 && count>=maxBackups) + { QFile::remove(QString("%1.%2").arg(fileName).arg(count)); --count; } @@ -149,21 +166,26 @@ void FileLogger::rotate() { } -void FileLogger::timerEvent(QTimerEvent* event) { - if (!event) { +void FileLogger::timerEvent(QTimerEvent* event) +{ + if (!event) + { return; } - else if (event->timerId()==refreshTimer.timerId()) { + else if (event->timerId()==refreshTimer.timerId()) + { refreshSettings(); } - else if (event->timerId()==flushTimer.timerId() && file) { + else if (event->timerId()==flushTimer.timerId() && file) + { mutex.lock(); // Flush the I/O buffer file->flush(); // Rotate the file if it is too large - if (maxSize>0 && file->size()>=maxSize) { + if (maxSize>0 && file->size()>=maxSize) + { close(); rotate(); open(); diff --git a/YACReaderLibrary/server/lib/bfLogging/filelogger.h b/YACReaderLibrary/server/lib/logging/filelogger.h similarity index 95% rename from YACReaderLibrary/server/lib/bfLogging/filelogger.h rename to YACReaderLibrary/server/lib/logging/filelogger.h index 617b5bff..71b840db 100644 --- a/YACReaderLibrary/server/lib/bfLogging/filelogger.h +++ b/YACReaderLibrary/server/lib/logging/filelogger.h @@ -11,6 +11,7 @@ #include #include #include +#include "logglobal.h" #include "logger.h" /** @@ -45,7 +46,7 @@ @see Logger for a descrition of the buffer. */ -class FileLogger : public Logger { +class DECLSPEC FileLogger : public Logger { Q_OBJECT Q_DISABLE_COPY(FileLogger) public: diff --git a/YACReaderLibrary/server/lib/bfLogging/logger.cpp b/YACReaderLibrary/server/lib/logging/logger.cpp similarity index 79% rename from YACReaderLibrary/server/lib/bfLogging/logger.cpp rename to YACReaderLibrary/server/lib/logging/logger.cpp index de3ab1a5..3f6525eb 100644 --- a/YACReaderLibrary/server/lib/bfLogging/logger.cpp +++ b/YACReaderLibrary/server/lib/logging/logger.cpp @@ -9,6 +9,7 @@ #include #include #include +#include Logger* Logger::defaultLogger=0; @@ -16,9 +17,6 @@ Logger* Logger::defaultLogger=0; QThreadStorage*> Logger::logVars; -QThreadStorage*> Logger::buffers; - - QMutex Logger::mutex; @@ -32,7 +30,8 @@ Logger::Logger(QObject* parent) Logger::Logger(const QString msgFormat, const QString timestampFormat, const QtMsgType minLevel, const int bufferSize, QObject* parent) - :QObject(parent) { + :QObject(parent) +{ this->msgFormat=msgFormat; this->timestampFormat=timestampFormat; this->minLevel=minLevel; @@ -40,7 +39,8 @@ Logger::Logger(const QString msgFormat, const QString timestampFormat, const QtM } -void Logger::msgHandler(const QtMsgType type, const QString &message, const QString &file, const QString &function, const int line) { +void Logger::msgHandler(const QtMsgType type, const QString &message, const QString &file, const QString &function, const int line) +{ static QMutex recursiveMutex(QMutex::Recursive); static QMutex nonRecursiveMutex(QMutex::NonRecursive); @@ -50,18 +50,21 @@ void Logger::msgHandler(const QtMsgType type, const QString &message, const QStr recursiveMutex.lock(); // Fall back to stderr when this method has been called recursively. - if (defaultLogger && nonRecursiveMutex.tryLock()) { + if (defaultLogger && nonRecursiveMutex.tryLock()) + { defaultLogger->log(type, message, file, function, line); nonRecursiveMutex.unlock(); } - else { + else + { fputs(qPrintable(message),stderr); fflush(stderr); } // Abort the program after logging a fatal message - if (type>=QtFatalMsg) { - //abort(); + if (type>=QtFatalMsg) + { + abort(); } recursiveMutex.unlock(); @@ -69,19 +72,23 @@ void Logger::msgHandler(const QtMsgType type, const QString &message, const QStr #if QT_VERSION >= 0x050000 - void Logger::msgHandler5(const QtMsgType type, const QMessageLogContext &context, const QString &message) { + void Logger::msgHandler5(const QtMsgType type, const QMessageLogContext &context, const QString &message) + { (void)(context); // suppress "unused parameter" warning msgHandler(type,message,context.file,context.function,context.line); } #else - void Logger::msgHandler4(const QtMsgType type, const char* message) { + void Logger::msgHandler4(const QtMsgType type, const char* message) + { msgHandler(type,message); } #endif -Logger::~Logger() { - if (defaultLogger==this) { +Logger::~Logger() +{ + if (defaultLogger==this) + { #if QT_VERSION >= 0x050000 qInstallMessageHandler(0); #else @@ -92,13 +99,15 @@ Logger::~Logger() { } -void Logger::write(const LogMessage* logMessage) { +void Logger::write(const LogMessage* logMessage) +{ fputs(qPrintable(logMessage->toString(msgFormat,timestampFormat)),stderr); fflush(stderr); } -void Logger::installMsgHandler() { +void Logger::installMsgHandler() +{ defaultLogger=this; #if QT_VERSION >= 0x050000 qInstallMessageHandler(msgHandler5); @@ -108,9 +117,11 @@ void Logger::installMsgHandler() { } -void Logger::set(const QString& name, const QString& value) { +void Logger::set(const QString& name, const QString& value) +{ mutex.lock(); - if (!logVars.hasLocalData()) { + if (!logVars.hasLocalData()) + { logVars.setLocalData(new QHash); } logVars.localData()->insert(name,value); @@ -118,23 +129,27 @@ void Logger::set(const QString& name, const QString& value) { } -void Logger::clear(const bool buffer, const bool variables) { +void Logger::clear(const bool buffer, const bool variables) +{ mutex.lock(); - if (buffer && buffers.hasLocalData()) { + if (buffer && buffers.hasLocalData()) + { QList* buffer=buffers.localData(); while (buffer && !buffer->isEmpty()) { LogMessage* logMessage=buffer->takeLast(); delete logMessage; } } - if (variables && logVars.hasLocalData()) { + if (variables && logVars.hasLocalData()) + { logVars.localData()->clear(); } mutex.unlock(); } -void Logger::log(const QtMsgType type, const QString& message, const QString &file, const QString &function, const int line) { +void Logger::log(const QtMsgType type, const QString& message, const QString &file, const QString &function, const int line) +{ mutex.lock(); // If the buffer is enabled, write the message into it @@ -148,12 +163,14 @@ void Logger::log(const QtMsgType type, const QString& message, const QString &fi LogMessage* logMessage=new LogMessage(type,message,logVars.localData(),file,function,line); buffer->append(logMessage); // Delete oldest message if the buffer became too large - if (buffer->size()>bufferSize) { + if (buffer->size()>bufferSize) + { delete buffer->takeFirst(); } // If the type of the message is high enough, print the whole buffer if (type>=minLevel) { - while (!buffer->isEmpty()) { + while (!buffer->isEmpty()) + { LogMessage* logMessage=buffer->takeFirst(); write(logMessage); delete logMessage; @@ -163,7 +180,8 @@ void Logger::log(const QtMsgType type, const QString& message, const QString &fi // Buffer is disabled, print the message if the type is high enough else { - if (type>=minLevel) { + if (type>=minLevel) + { LogMessage logMessage(type,message,logVars.localData(),file,function,line); write(&logMessage); } diff --git a/YACReaderLibrary/server/lib/bfLogging/logger.h b/YACReaderLibrary/server/lib/logging/logger.h similarity index 92% rename from YACReaderLibrary/server/lib/bfLogging/logger.h rename to YACReaderLibrary/server/lib/logging/logger.h index adcedfd3..0ced8051 100644 --- a/YACReaderLibrary/server/lib/bfLogging/logger.h +++ b/YACReaderLibrary/server/lib/logging/logger.h @@ -6,13 +6,13 @@ #ifndef LOGGER_H #define LOGGER_H -#include - #include #include #include #include #include +#include +#include "logglobal.h" #include "logmessage.h" /** @@ -45,7 +45,7 @@ because logging to the console is less useful. */ -class Logger : public QObject { +class DECLSPEC Logger : public QObject { Q_OBJECT Q_DISABLE_COPY(Logger) public: @@ -100,10 +100,11 @@ public: /** Clear the thread-local data of the current thread. + This method is thread safe. @param buffer Whether to clear the backtrace buffer @param variables Whether to clear the log variables */ - static void clear(const bool buffer=true, const bool variables=true); + virtual void clear(const bool buffer=true, const bool variables=true); protected: @@ -119,7 +120,7 @@ protected: /** Size of backtrace buffer, number of messages per thread. 0=disabled */ int bufferSize; - /** Used to synchronize access to the static members */ + /** Used to synchronize access of concurrent threads */ static QMutex mutex; /** @@ -176,7 +177,7 @@ private: static QThreadStorage*> logVars; /** Thread local backtrace buffers */ - static QThreadStorage*> buffers; + QThreadStorage*> buffers; }; diff --git a/YACReaderLibrary/server/lib/logging/logging.pri b/YACReaderLibrary/server/lib/logging/logging.pri new file mode 100644 index 00000000..13c296c7 --- /dev/null +++ b/YACReaderLibrary/server/lib/logging/logging.pri @@ -0,0 +1,6 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +HEADERS += $$PWD/logglobal.h $$PWD/logmessage.h $$PWD/logger.h $$PWD/filelogger.h $$PWD/dualfilelogger.h + +SOURCES += $$PWD/logmessage.cpp $$PWD/logger.cpp $$PWD/filelogger.cpp $$PWD/dualfilelogger.cpp diff --git a/YACReaderLibrary/server/lib/logging/logglobal.h b/YACReaderLibrary/server/lib/logging/logglobal.h new file mode 100644 index 00000000..47b2151e --- /dev/null +++ b/YACReaderLibrary/server/lib/logging/logglobal.h @@ -0,0 +1,24 @@ +/** + @file + @author Stefan Frings +*/ + +#ifndef LOGGLOBAL_H +#define LOGGLOBAL_H + +#include + +// This is specific to Windows dll's +#if defined(Q_OS_WIN) + #if defined(QTWEBAPPLIB_EXPORT) + #define DECLSPEC Q_DECL_EXPORT + #elif defined(QTWEBAPPLIB_IMPORT) + #define DECLSPEC Q_DECL_IMPORT + #endif +#endif +#if !defined(DECLSPEC) + #define DECLSPEC +#endif + +#endif // LOGGLOBAL_H + diff --git a/YACReaderLibrary/server/lib/bfLogging/logmessage.cpp b/YACReaderLibrary/server/lib/logging/logmessage.cpp similarity index 62% rename from YACReaderLibrary/server/lib/bfLogging/logmessage.cpp rename to YACReaderLibrary/server/lib/logging/logmessage.cpp index 4d610383..ef359302 100644 --- a/YACReaderLibrary/server/lib/bfLogging/logmessage.cpp +++ b/YACReaderLibrary/server/lib/logging/logmessage.cpp @@ -6,7 +6,8 @@ #include "logmessage.h" #include -LogMessage::LogMessage(const QtMsgType type, const QString& message, QHash* logVars, const QString &file, const QString &function, const int line) { +LogMessage::LogMessage(const QtMsgType type, const QString& message, QHash* logVars, const QString &file, const QString &function, const int line) +{ this->type=type; this->message=message; this->file=file; @@ -17,16 +18,19 @@ LogMessage::LogMessage(const QtMsgType type, const QString& message, QHashlogVars=*logVars; } } -QString LogMessage::toString(const QString& msgFormat, const QString& timestampFormat) const { +QString LogMessage::toString(const QString& msgFormat, const QString& timestampFormat) const +{ QString decorated=msgFormat+"\n"; decorated.replace("{msg}",message); - if (decorated.contains("{timestamp}")) { + if (decorated.contains("{timestamp}")) + { decorated.replace("{timestamp}",timestamp.toString(timestampFormat)); } @@ -34,21 +38,22 @@ QString LogMessage::toString(const QString& msgFormat, const QString& timestampF typeNr.setNum(type); decorated.replace("{typeNr}",typeNr); - switch (type) { - case QtDebugMsg: - decorated.replace("{type}","DEBUG"); - break; - case QtWarningMsg: - decorated.replace("{type}","WARNING"); - break; - case QtCriticalMsg: - decorated.replace("{type}","CRITICAL"); - break; - case QtFatalMsg: - decorated.replace("{type}","FATAL"); - break; - default: - decorated.replace("{type}",typeNr); + switch (type) + { + case QtDebugMsg: + decorated.replace("{type}","DEBUG"); + break; + case QtWarningMsg: + decorated.replace("{type}","WARNING"); + break; + case QtCriticalMsg: + decorated.replace("{type}","CRITICAL"); + break; + case QtFatalMsg: + decorated.replace("{type}","FATAL"); + break; + default: + decorated.replace("{type}",typeNr); } decorated.replace("{file}",file); @@ -60,9 +65,11 @@ QString LogMessage::toString(const QString& msgFormat, const QString& timestampF decorated.replace("{thread}",threadId); // Fill in variables - if (decorated.contains("{") && !logVars.isEmpty()) { + if (decorated.contains("{") && !logVars.isEmpty()) + { QList keys=logVars.keys(); - foreach (QString key, keys) { + foreach (QString key, keys) + { decorated.replace("{"+key+"}",logVars.value(key)); } } @@ -70,6 +77,7 @@ QString LogMessage::toString(const QString& msgFormat, const QString& timestampF return decorated; } -QtMsgType LogMessage::getType() const { +QtMsgType LogMessage::getType() const +{ return type; } diff --git a/YACReaderLibrary/server/lib/bfLogging/logmessage.h b/YACReaderLibrary/server/lib/logging/logmessage.h similarity index 84% rename from YACReaderLibrary/server/lib/bfLogging/logmessage.h rename to YACReaderLibrary/server/lib/logging/logmessage.h index 433d949d..d6228cba 100644 --- a/YACReaderLibrary/server/lib/bfLogging/logmessage.h +++ b/YACReaderLibrary/server/lib/logging/logmessage.h @@ -9,6 +9,7 @@ #include #include #include +#include "logglobal.h" /** Represents a single log message together with some data @@ -20,16 +21,17 @@ - {typeNr} Type of the message in numeric format (0-3) - {type} Type of the message in string format (DEBUG, WARNING, CRITICAL, FATAL) - {thread} ID number of the thread - - {msg} Message text (only useable in msgFormat) - - {file} Filename where the message was generated # - - {function} Function where the message was generated # - - {line} Line number where the message was generated # + - {msg} Message text - {xxx} For any user-defined logger variable - # The macros qDebug()...qFatal() dont fill these variable in case of QT versions before 5.0. + Plus some new variables since QT 5.0, only filled when compiled in debug mode: + + - {file} Filename where the message was generated + - {function} Function where the message was generated + - {line} Line number where the message was generated */ -class LogMessage +class DECLSPEC LogMessage { Q_DISABLE_COPY(LogMessage) public: diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/template.cpp b/YACReaderLibrary/server/lib/templateengine/template.cpp similarity index 73% rename from YACReaderLibrary/server/lib/bfTemplateEngine/template.cpp rename to YACReaderLibrary/server/lib/templateengine/template.cpp index 23abac9e..d099668c 100644 --- a/YACReaderLibrary/server/lib/bfTemplateEngine/template.cpp +++ b/YACReaderLibrary/server/lib/templateengine/template.cpp @@ -7,131 +7,174 @@ #include Template::Template(QString source, QString sourceName) - : QString(source) { + : QString(source) +{ this->sourceName=sourceName; this->warnings=false; } -Template::Template(QFile& file, QTextCodec* textCodec) { +Template::Template(QFile& file, QTextCodec* textCodec) +{ this->warnings=false; sourceName=QFileInfo(file.fileName()).baseName(); - if (!file.isOpen()) { + if (!file.isOpen()) + { file.open(QFile::ReadOnly | QFile::Text); } QByteArray data=file.readAll(); file.close(); - if (data.size()==0 || file.error()) { + if (data.size()==0 || file.error()) + { qCritical("Template: cannot read from %s, %s",qPrintable(sourceName),qPrintable(file.errorString())); + } + else + { append(textCodec->toUnicode(data)); } } -int Template::setVariable(QString name, QString value) { +int Template::setVariable(QString name, QString value) +{ int count=0; QString variable="{"+name+"}"; int start=indexOf(variable); - while (start>=0) { + while (start>=0) + { replace(start, variable.length(), value); count++; start=indexOf(variable,start+value.length()); } - if (count==0 && warnings) { + if (count==0 && warnings) + { qWarning("Template: missing variable %s in %s",qPrintable(variable),qPrintable(sourceName)); } return count; } -int Template::setCondition(QString name, bool value) { +int Template::setCondition(QString name, bool value) +{ int count=0; QString startTag=QString("{if %1}").arg(name); QString elseTag=QString("{else %1}").arg(name); QString endTag=QString("{end %1}").arg(name); // search for if-else-end int start=indexOf(startTag); - while (start>=0) { + while (start>=0) + { int end=indexOf(endTag,start+startTag.length()); - if (end>=0) { + if (end>=0) + { count++; int ellse=indexOf(elseTag,start+startTag.length()); - if (ellse>start && ellsestart && ellse=0) { + while (start>=0) + { int end=indexOf(endTag,start+startTag2.length()); - if (end>=0) { + if (end>=0) + { count++; int ellse=indexOf(elseTag,start+startTag2.length()); - if (ellse>start && ellsestart && ellse=0); +int Template::loop(QString name, int repetitions) +{ + Q_ASSERT(repetitions>=0); int count=0; QString startTag="{loop "+name+"}"; QString elseTag="{else "+name+"}"; QString endTag="{end "+name+"}"; // search for loop-else-end int start=indexOf(startTag); - while (start>=0) { + while (start>=0) + { int end=indexOf(endTag,start+startTag.length()); - if (end>=0) { + if (end>=0) + { count++; int ellse=indexOf(elseTag,start+startTag.length()); - if (ellse>start && ellse0) { + if (ellse>start && ellse0) + { QString loopPart=mid(start+startTag.length(), ellse-start-startTag.length()); QString insertMe; - for (int i=0; i0) { // and no else part + else if (repetitions>0) + { + // and no else part QString loopPart=mid(start+startTag.length(), end-start-startTag.length()); QString insertMe; - for (int i=0; i #include #include +#include "templateglobal.h" /** Enhanced version of QString for template processing. Templates @@ -37,7 +38,7 @@ Example code to fill this template:

  Template t(QFile("test.tpl"),QTextCode::codecForName("UTF-8"));
- t.setVariable("user", "Stefan");
+ t.setVariable("username", "Stefan");
  t.setCondition("locked",false);
  t.loop("user",2);
  t.setVariable("user0.name,"Markus");
@@ -86,7 +87,7 @@
  @see TemplateCache
 */
 
-class Template : public QString {
+class DECLSPEC Template : public QString {
 public:
 
     /**
diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/templatecache.cpp b/YACReaderLibrary/server/lib/templateengine/templatecache.cpp
similarity index 83%
rename from YACReaderLibrary/server/lib/bfTemplateEngine/templatecache.cpp
rename to YACReaderLibrary/server/lib/templateengine/templatecache.cpp
index 823f4f24..538aaf22 100644
--- a/YACReaderLibrary/server/lib/bfTemplateEngine/templatecache.cpp
+++ b/YACReaderLibrary/server/lib/templateengine/templatecache.cpp
@@ -6,17 +6,19 @@
 TemplateCache::TemplateCache(QSettings* settings, QObject* parent)
     :TemplateLoader(settings,parent)
 {
-    cache.setMaxCost(settings->value("cacheSize","160000").toInt());//este tamaño antes era 1000000
+    cache.setMaxCost(settings->value("cacheSize","1000000").toInt());
     cacheTimeout=settings->value("cacheTime","60000").toInt();
     qDebug("TemplateCache: timeout=%i, size=%i",cacheTimeout,cache.maxCost());
 }
 
-QString TemplateCache::tryFile(QString localizedName) {
+QString TemplateCache::tryFile(QString localizedName)
+{
     qint64 now=QDateTime::currentMSecsSinceEpoch();
     // search in cache
     qDebug("TemplateCache: trying cached %s",qPrintable(localizedName));
     CacheEntry* entry=cache.object(localizedName);
-    if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout)) {
+    if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout))
+    {
         return entry->document;
     }
     // search on filesystem
diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/templatecache.h b/YACReaderLibrary/server/lib/templateengine/templatecache.h
similarity index 90%
rename from YACReaderLibrary/server/lib/bfTemplateEngine/templatecache.h
rename to YACReaderLibrary/server/lib/templateengine/templatecache.h
index 6e79f119..7b4da072 100644
--- a/YACReaderLibrary/server/lib/bfTemplateEngine/templatecache.h
+++ b/YACReaderLibrary/server/lib/templateengine/templatecache.h
@@ -1,8 +1,9 @@
 #ifndef TEMPLATECACHE_H
 #define TEMPLATECACHE_H
 
-#include "templateloader.h"
 #include 
+#include "templateglobal.h"
+#include "templateloader.h"
 
 /**
   Caching template loader, reduces the amount of I/O and improves performance
@@ -25,7 +26,7 @@
   

The following settings are required:

-  path=.
+  path=../templates
   suffix=.tpl
   encoding=UTF-8
   cacheSize=1000000
@@ -38,9 +39,9 @@
   @see TemplateLoader
 */
 
-class TemplateCache : public TemplateLoader {
+class DECLSPEC TemplateCache : public TemplateLoader {
     Q_OBJECT
-    Q_DISABLE_COPY(TemplateCache);
+    Q_DISABLE_COPY(TemplateCache)
 public:
 
     /**
diff --git a/YACReaderLibrary/server/lib/templateengine/templateengine.pri b/YACReaderLibrary/server/lib/templateengine/templateengine.pri
new file mode 100644
index 00000000..53608ae4
--- /dev/null
+++ b/YACReaderLibrary/server/lib/templateengine/templateengine.pri
@@ -0,0 +1,11 @@
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+
+HEADERS += $$PWD/templateglobal.h
+HEADERS += $$PWD/template.h 
+HEADERS += $$PWD/templateloader.h 
+HEADERS += $$PWD/templatecache.h
+
+SOURCES += $$PWD/template.cpp 
+SOURCES += $$PWD/templateloader.cpp 
+SOURCES += $$PWD/templatecache.cpp
diff --git a/YACReaderLibrary/server/lib/templateengine/templateglobal.h b/YACReaderLibrary/server/lib/templateengine/templateglobal.h
new file mode 100644
index 00000000..c15ffda7
--- /dev/null
+++ b/YACReaderLibrary/server/lib/templateengine/templateglobal.h
@@ -0,0 +1,24 @@
+/**
+  @file
+  @author Stefan Frings
+*/
+
+#ifndef TEMPLATEGLOBAL_H
+#define TEMPLATEGLOBAL_H
+
+#include 
+
+// This is specific to Windows dll's
+#if defined(Q_OS_WIN)
+    #if defined(QTWEBAPPLIB_EXPORT)
+        #define DECLSPEC Q_DECL_EXPORT
+    #elif defined(QTWEBAPPLIB_IMPORT)
+        #define DECLSPEC Q_DECL_IMPORT
+    #endif
+#endif
+#if !defined(DECLSPEC)
+    #define DECLSPEC
+#endif
+
+#endif // TEMPLATEGLOBAL_H
+
diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/templateloader.cpp b/YACReaderLibrary/server/lib/templateengine/templateloader.cpp
similarity index 72%
rename from YACReaderLibrary/server/lib/bfTemplateEngine/templateloader.cpp
rename to YACReaderLibrary/server/lib/templateengine/templateloader.cpp
index 9ed9cf8f..fbc52efa 100644
--- a/YACReaderLibrary/server/lib/bfTemplateEngine/templateloader.cpp
+++ b/YACReaderLibrary/server/lib/templateengine/templateloader.cpp
@@ -9,12 +9,11 @@
 #include 
 #include 
 #include 
-#include 
 
 TemplateLoader::TemplateLoader(QSettings* settings, QObject* parent)
     : QObject(parent)
 {
-    templatePath=settings->value("path","./server/templates").toString();
+    templatePath=settings->value("path",".").toString();
     // Convert relative path to absolute, based on the directory of the config file.
 #ifdef Q_OS_WIN32
     if (QDir::isRelativePath(templatePath) && settings->format()!=QSettings::NativeFormat)
@@ -22,28 +21,27 @@ TemplateLoader::TemplateLoader(QSettings* settings, QObject* parent)
     if (QDir::isRelativePath(templatePath))
 #endif
     {
-#if defined Q_OS_UNIX && !defined Q_OS_MAC
-        QFileInfo configFile(QString(DATADIR)+"/yacreader");
-        templatePath=QFileInfo(QString(DATADIR)+"/yacreader",templatePath).absoluteFilePath();
-#else
-        QFileInfo configFile(QCoreApplication::applicationDirPath());
-        templatePath=QFileInfo(QCoreApplication::applicationDirPath(),templatePath).absoluteFilePath();
-#endif
+        QFileInfo configFile(settings->fileName());
+        templatePath=QFileInfo(configFile.absolutePath(),templatePath).absoluteFilePath();
     }
     fileNameSuffix=settings->value("suffix",".tpl").toString();
     QString encoding=settings->value("encoding").toString();
-    if (encoding.isEmpty()) {
+    if (encoding.isEmpty())
+    {
         textCodec=QTextCodec::codecForLocale();
     }
-    else {
+    else
+    {
        textCodec=QTextCodec::codecForName(encoding.toLocal8Bit());
    }
    qDebug("TemplateLoader: path=%s, codec=%s",qPrintable(templatePath),textCodec->name().data());
 }
 
-TemplateLoader::~TemplateLoader() {}
+TemplateLoader::~TemplateLoader()
+{}
 
-QString TemplateLoader::tryFile(QString localizedName) {
+QString TemplateLoader::tryFile(QString localizedName)
+{
     QString fileName=templatePath+"/"+localizedName+fileNameSuffix;
     qDebug("TemplateCache: trying file %s",qPrintable(fileName));
     QFile file(fileName);
@@ -51,28 +49,33 @@ QString TemplateLoader::tryFile(QString localizedName) {
         file.open(QIODevice::ReadOnly);
         QString document=textCodec->toUnicode(file.readAll());
         file.close();
-        if (file.error()) {
+        if (file.error())
+        {
             qCritical("TemplateLoader: cannot load file %s, %s",qPrintable(fileName),qPrintable(file.errorString()));
             return "";
         }
-        else {
+        else
+        {
             return document;
         }
     }
     return "";
 }
 
-Template TemplateLoader::getTemplate(QString templateName, QString locales) {
+Template TemplateLoader::getTemplate(QString templateName, QString locales)
+{
     mutex.lock();
     QSet tried; // used to suppress duplicate attempts
     QStringList locs=locales.split(',',QString::SkipEmptyParts);
 
     // Search for exact match
-    foreach (QString loc,locs) {
+    foreach (QString loc,locs)
+    {
         loc.replace(QRegExp(";.*"),"");
         loc.replace('-','_');
         QString localizedName=templateName+"-"+loc.trimmed();
-        if (!tried.contains(localizedName)) {
+        if (!tried.contains(localizedName))
+        {
             QString document=tryFile(localizedName);
             if (!document.isEmpty()) {
                 mutex.unlock();
@@ -83,12 +86,15 @@ Template TemplateLoader::getTemplate(QString templateName, QString locales) {
     }
 
     // Search for correct language but any country
-    foreach (QString loc,locs) {
+    foreach (QString loc,locs)
+    {
         loc.replace(QRegExp("[;_-].*"),"");
         QString localizedName=templateName+"-"+loc.trimmed();
-        if (!tried.contains(localizedName)) {
+        if (!tried.contains(localizedName))
+        {
             QString document=tryFile(localizedName);
-            if (!document.isEmpty()) {
+            if (!document.isEmpty())
+            {
                 mutex.unlock();
                 return Template(document,localizedName);
             }
@@ -98,7 +104,8 @@ Template TemplateLoader::getTemplate(QString templateName, QString locales) {
 
     // Search for default file
     QString document=tryFile(templateName);
-    if (!document.isEmpty()) {
+    if (!document.isEmpty())
+    {
         mutex.unlock();
         return Template(document,templateName);
     }
diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/templateloader.h b/YACReaderLibrary/server/lib/templateengine/templateloader.h
similarity index 90%
rename from YACReaderLibrary/server/lib/bfTemplateEngine/templateloader.h
rename to YACReaderLibrary/server/lib/templateengine/templateloader.h
index 5635af40..5b06c609 100644
--- a/YACReaderLibrary/server/lib/bfTemplateEngine/templateloader.h
+++ b/YACReaderLibrary/server/lib/templateengine/templateloader.h
@@ -9,8 +9,9 @@
 #include 
 #include 
 #include 
-#include "template.h"
 #include 
+#include "templateglobal.h"
+#include "template.h"
 
 /**
   Loads localized versions of template files. If the caller requests a file with the
@@ -25,7 +26,7 @@
 
   The following settings are required:
   
-  path=.
+  path=../templates
   suffix=.tpl
   encoding=UTF-8
   
@@ -34,9 +35,9 @@ @see TemplateCache */ -class TemplateLoader : public QObject { +class DECLSPEC TemplateLoader : public QObject { Q_OBJECT - Q_DISABLE_COPY(TemplateLoader); + Q_DISABLE_COPY(TemplateLoader) public: /** diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp index 66b03413..d703f086 100644 --- a/YACReaderLibrary/server/requestmapper.cpp +++ b/YACReaderLibrary/server/requestmapper.cpp @@ -22,10 +22,18 @@ #include "controllers/errorcontroller.h" #include "controllers/comicdownloadinfocontroller.h" #include "controllers/synccontroller.h" +#include "controllers/versioncontroller.h" +#include "controllers/foldercontentcontroller.h" +#include "controllers/tagscontroller.h" +#include "controllers/tagcontentcontroller.h" +#include "controllers/favoritescontroller.h" +#include "controllers/readingcomicscontroller.h" #include "db_helper.h" #include "yacreader_libraries.h" +#include "yacreader_http_session.h" + #include "QsLog.h" RequestMapper::RequestMapper(QObject* parent) @@ -36,6 +44,8 @@ void RequestMapper::loadSession(HttpRequest & request, HttpResponse& response) HttpSession session=Static::sessionStore->getSession(request,response); if(session.contains("ySession")) //session is already alive check if it is needed to update comics { + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); + QString postData = QString::fromUtf8(request.getBody()); if(postData.contains("currentPage")) @@ -45,26 +55,30 @@ void RequestMapper::loadSession(HttpRequest & request, HttpResponse& response) QList data = postData.split("\n"); if(data.length() > 2) { - session.setDeviceType(data.at(0).split(":").at(1)); - session.setDisplayType(data.at(1).split(":").at(1)); + ySession->setDeviceType(data.at(0).split(":").at(1)); + ySession->setDisplayType(data.at(1).split(":").at(1)); QList comics = data.at(2).split(":").at(1).split("\t"); - session.clearComics(); + ySession->clearComics(); foreach(QString hash,comics) { - session.setComicOnDevice(hash); + ySession->setComicOnDevice(hash); } } else { if(data.length()>1) { - session.setDeviceType(data.at(0).split(":").at(1)); - session.setDisplayType(data.at(1).split(":").at(1)); + ySession->setDeviceType(data.at(0).split(":").at(1)); + ySession->setDisplayType(data.at(1).split(":").at(1)); } } } } else { + YACReaderHttpSession *ySession = new YACReaderHttpSession(this); + + Static::yacreaderSessionStore->addYACReaderHttpSession(session.getId(), ySession); + session.set("ySession","ok"); QString postData = QString::fromUtf8(request.getBody()); @@ -74,18 +88,18 @@ void RequestMapper::loadSession(HttpRequest & request, HttpResponse& response) if(data.length() > 2) { - session.setDeviceType(data.at(0).split(":").at(1)); - session.setDisplayType(data.at(1).split(":").at(1)); + ySession->setDeviceType(data.at(0).split(":").at(1)); + ySession->setDisplayType(data.at(1).split(":").at(1)); QList comics = data.at(2).split(":").at(1).split("\t"); foreach(QString hash,comics) { - session.setComicOnDevice(hash); + ySession->setComicOnDevice(hash); } } else //values by default, only for debug purposes. { - session.setDeviceType("ipad"); - session.setDisplayType("@2x"); + ySession->setDeviceType("ipad"); + ySession->setDisplayType("@2x"); } } @@ -105,6 +119,14 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { QRegExp cover("/library/.+/cover/[0-9a-f]+.jpg"); //get comic cover (navigation) QRegExp comicPage("/library/.+/comic/[0-9]+/page/[0-9]+/?"); //get comic page QRegExp comicPageRemote("/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); //get comic page (remote reading) + QRegExp serverVersion("/version/?"); + QRegExp folderContent("/library/.+/folder/[0-9]+/content/?"); + QRegExp favs("/library/.+/favs/?"); + QRegExp reading("/library/.+/reading/?"); + QRegExp tags("/library/.+/tags/?"); + QRegExp tagContent("/library/.+/tag/[0-9]+/content/?"); + QRegExp readingLists("/library/.+/reading_lists/?"); + QRegExp readingListContent("/library/.+/reading_list/[0-9]+/content/?"); QRegExp sync("/sync"); @@ -122,8 +144,14 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { } else { - if(sync.exactMatch(path)) + if(serverVersion.exactMatch(path)) + { + VersionController().service(request, response); + } + else if(sync.exactMatch(path)) + { SyncController().service(request, response); + } else { //se comprueba que la sesión sea la correcta con el fin de evitar accesos no autorizados @@ -161,6 +189,34 @@ void RequestMapper::service(HttpRequest& request, HttpResponse& response) { { UpdateComicController().service(request, response); } + else if(folderContent.exactMatch(path)) + { + FolderContentController().service(request, response); + } + else if(tags.exactMatch(path)) + { + TagsController().service(request, response); + } + else if(tagContent.exactMatch(path)) + { + TagContentController().service(request, response); + } + else if(favs.exactMatch(path)) + { + FavoritesController().service(request, response); + } + else if(reading.exactMatch(path)) + { + ReadingComicsController().service(request, response); + } + else if(readingLists.exactMatch(path)) + { + //ReadingListsController().service(request, response); + } + else if(readingListContent.exactMatch(path)) + { + //ReadingListContentController().service(request, response); + } } else { diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index 4be20612..535989cb 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -15,7 +15,18 @@ HEADERS += \ $$PWD/controllers/covercontroller.h \ $$PWD/controllers/updatecomiccontroller.h \ $$PWD/controllers/comicdownloadinfocontroller.h \ - $$PWD/controllers/synccontroller.h + $$PWD/controllers/synccontroller.h \ + #v2 + $$PWD/controllers/versioncontroller.h \ + $$PWD/controllers/foldercontentcontroller.h \ + $$PWD/controllers/tagscontroller.h \ + $$PWD/yacreader_http_session.h \ + $$PWD/yacreader_http_session_store.h \ + $$PWD/controllers/tagcontentcontroller.h \ + $$PWD/yacreader_server_data_helper.h \ + $$PWD/controllers/favoritescontroller.h \ + $$PWD/controllers/readingcomicscontroller.h \ + $$PWD/controllers/readinglistscontroller.h SOURCES += \ $$PWD/static.cpp \ @@ -31,8 +42,21 @@ SOURCES += \ $$PWD/controllers/covercontroller.cpp \ $$PWD/controllers/updatecomiccontroller.cpp \ $$PWD/controllers/comicdownloadinfocontroller.cpp \ - $$PWD/controllers/synccontroller.cpp + $$PWD/controllers/synccontroller.cpp \ + #v2 + $$PWD/controllers/versioncontroller.cpp \ + $$PWD/controllers/foldercontentcontroller.cpp \ + $$PWD/controllers/tagscontroller.cpp \ + $$PWD/yacreader_http_session.cpp \ + $$PWD/yacreader_http_session_store.cpp \ + $$PWD/controllers/tagcontentcontroller.cpp \ + $$PWD/yacreader_server_data_helper.cpp \ + $$PWD/controllers/favoritescontroller.cpp \ + $$PWD/controllers/readingcomicscontroller.cpp \ + $$PWD/controllers/readinglistscontroller.cpp -include(lib/bfLogging/bfLogging.pri) -include(lib/bfHttpServer/bfHttpServer.pri) -include(lib/bfTemplateEngine/bfTemplateEngine.pri) +include(lib/logging/logging.pri) +include(lib/httpserver/httpserver.pri) +include(lib/templateengine/templateengine.pri) + +DEFINES += SERVER_VERSION_NUMBER=\\\"2.0\\\" diff --git a/YACReaderLibrary/server/startup.cpp b/YACReaderLibrary/server/startup.cpp index fd1e982f..379e5137 100644 --- a/YACReaderLibrary/server/startup.cpp +++ b/YACReaderLibrary/server/startup.cpp @@ -35,28 +35,94 @@ void Startup::start() { mainLogSettings->beginGroup("mainLogFile"); //QSettings* debugLogSettings=new QSettings(configFileName,QSettings::IniFormat,app); //debugLogSettings->beginGroup("debugLogFile"); + + if(mainLogSettings->value("fileName").isNull()) + mainLogSettings->setValue("fileName", QFileInfo(YACReader::getSettingsPath(), "server_log.log").absoluteFilePath()); + + if(mainLogSettings->value("maxSize").isNull()) + mainLogSettings->setValue("maxSize",1048576); + + if(mainLogSettings->value("maxBackups").isNull()) + mainLogSettings->setValue("maxBackups",1); + + if(mainLogSettings->value("minLevel").isNull()) + mainLogSettings->setValue("minLevel",QtCriticalMsg); + Logger* logger=new FileLogger(mainLogSettings,10000,app); logger->installMsgHandler(); // Configure template loader and cache QSettings* templateSettings=new QSettings(configFileName,QSettings::IniFormat,app); templateSettings->beginGroup("templates"); + + if(templateSettings->value("cacheSize").isNull()) + templateSettings->setValue("cacheSize","160000"); + + QString baseTemplatePath = QString("./server/templates"); + QString templatePath; + + #if defined Q_OS_UNIX && !defined Q_OS_MAC + templatePath=QFileInfo(QString(DATADIR)+"/yacreader",baseTemplatePath).absoluteFilePath(); + #else + templatePath=QFileInfo(QCoreApplication::applicationDirPath(),baseTemplatePath).absoluteFilePath(); + #endif + + if(templateSettings->value("path").isNull()) + templateSettings->setValue("path",templatePath); + Static::templateLoader=new TemplateCache(templateSettings,app); // Configure session store QSettings* sessionSettings=new QSettings(configFileName,QSettings::IniFormat,app); sessionSettings->beginGroup("sessions"); + + if(sessionSettings->value("expirationTime").isNull()) + sessionSettings->setValue("expirationTime",864000000); + Static::sessionStore=new HttpSessionStore(sessionSettings,app); + Static::yacreaderSessionStore = new YACReaderHttpSessionStore(Static::sessionStore, app); + // Configure static file controller QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat,app); fileSettings->beginGroup("docroot"); + + QString basedocroot = "./server/docroot"; + QString docroot; + + #if defined Q_OS_UNIX && ! defined Q_OS_MAC + QFileInfo configFile(QString(DATADIR)+"/yacreader"); + docroot=QFileInfo(QString(DATADIR)+"/yacreader",basedocroot).absoluteFilePath(); + #else + QFileInfo configFile(QCoreApplication::applicationDirPath()); + docroot=QFileInfo(QCoreApplication::applicationDirPath(),basedocroot).absoluteFilePath(); + #endif + + if(fileSettings->value("path").isNull()) + fileSettings->setValue("path",docroot); + Static::staticFileController=new StaticFileController(fileSettings,app); // Configure and start the TCP listener qDebug("ServiceHelper: Starting service"); QSettings* listenerSettings=new QSettings(configFileName,QSettings::IniFormat,app); listenerSettings->beginGroup("listener"); + + if(listenerSettings->value("maxRequestSize").isNull()) + listenerSettings->setValue("maxRequestSize","32000000"); + + if(listenerSettings->value("maxMultiPartSize").isNull()) + listenerSettings->setValue("maxMultiPartSize","32000000"); + + if(listenerSettings->value("cleanupInterval").isNull()) + listenerSettings->setValue("cleanupInterval",10000); + + if(listenerSettings->value("minThreads").isNull()) + listenerSettings->setValue("maxThreads",1000); + + if(listenerSettings->value("minThreads").isNull()) + listenerSettings->setValue("minThreads",50); + listener = new HttpListener(listenerSettings,new RequestMapper(app),app); qDebug("ServiceHelper: Service has started"); diff --git a/YACReaderLibrary/server/static.cpp b/YACReaderLibrary/server/static.cpp index 49e0060e..0e8693f7 100644 --- a/YACReaderLibrary/server/static.cpp +++ b/YACReaderLibrary/server/static.cpp @@ -17,6 +17,8 @@ HttpSessionStore* Static::sessionStore=0; StaticFileController* Static::staticFileController=0; +YACReaderHttpSessionStore* Static::yacreaderSessionStore=0; + QString Static::getConfigFileName() { return QString("%1/%2.ini").arg(getConfigDir()).arg(QCoreApplication::applicationName()); } diff --git a/YACReaderLibrary/server/static.h b/YACReaderLibrary/server/static.h index 74abf55b..154f2ad9 100644 --- a/YACReaderLibrary/server/static.h +++ b/YACReaderLibrary/server/static.h @@ -11,6 +11,8 @@ #include "httpsessionstore.h" #include "staticfilecontroller.h" +#include "yacreader_http_session_store.h" + /** This class contains some static resources that are used by the application. */ @@ -51,6 +53,8 @@ public: /** Storage for session cookies */ static HttpSessionStore* sessionStore; + static YACReaderHttpSessionStore* yacreaderSessionStore; + /** Controller for static files */ static StaticFileController* staticFileController; diff --git a/YACReaderLibrary/server/yacreader_http_session.cpp b/YACReaderLibrary/server/yacreader_http_session.cpp new file mode 100644 index 00000000..24ad65d7 --- /dev/null +++ b/YACReaderLibrary/server/yacreader_http_session.cpp @@ -0,0 +1,174 @@ +#include "yacreader_http_session.h" + +YACReaderHttpSession::YACReaderHttpSession(QObject *parent) + : QObject(parent), comic(nullptr), remoteComic(nullptr), comicId(0), remoteComicId(0) +{ + +} + +YACReaderHttpSession::~YACReaderHttpSession() +{ + if(comic != nullptr) + delete comic; + + if(remoteComic != nullptr) + delete remoteComic; +} + +bool YACReaderHttpSession::isComicOnDevice(const QString & hash) +{ + return comicsOnDevice.contains(hash); +} + +bool YACReaderHttpSession::isComicDownloaded(const QString & hash) +{ + return downloadedComics.contains(hash); +} + +void YACReaderHttpSession::setComicOnDevice(const QString & hash) +{ + comicsOnDevice.insert(hash); +} + +void YACReaderHttpSession::setComicsOnDevice(const QSet & set) +{ + comicsOnDevice = set; +} + +void YACReaderHttpSession::setDownloadedComic(const QString & hash) +{ + downloadedComics.insert(hash); +} + +QSet YACReaderHttpSession::getComicsOnDevice() +{ + return comicsOnDevice ; +} + +QSet YACReaderHttpSession::getDownloadedComics() +{ + return downloadedComics ; +} + +void YACReaderHttpSession::clearComics() +{ + comicsOnDevice.clear(); + downloadedComics.clear(); +} +//current comic (import) +qulonglong YACReaderHttpSession::getCurrentComicId() +{ + return comicId; +} + +Comic* YACReaderHttpSession::getCurrentComic() +{ + return comic; +} + +void YACReaderHttpSession::dismissCurrentComic() +{ + if(comic != nullptr) + { + comic->deleteLater(); + comic = nullptr; + } +} + +void YACReaderHttpSession::setCurrentComic(qulonglong id, Comic * comic) +{ + dismissCurrentComic(); + comicId = id; + this->comic = comic; +} + +//current comic (read) +qulonglong YACReaderHttpSession::getCurrentRemoteComicId() +{ + return remoteComicId ; +} + +Comic* YACReaderHttpSession::getCurrentRemoteComic() +{ + return remoteComic ; +} + +void YACReaderHttpSession::dismissCurrentRemoteComic() +{ + if(remoteComic != nullptr) + { + remoteComic->deleteLater(); + remoteComic = nullptr; + } +} + +void YACReaderHttpSession::setCurrentRemoteComic(qulonglong id, Comic * comic) +{ + dismissCurrentRemoteComic(); + remoteComicId = id; + remoteComic = comic; +} + +QString YACReaderHttpSession::getDeviceType() +{ + return device; +} + +QString YACReaderHttpSession::getDisplayType() +{ + return display; +} + +void YACReaderHttpSession::setDeviceType(const QString & device) +{ + //comicsOnDevice.clear(); //TODO crear un m�todo clear que limpie la sesi�n completamente + //downloadedComics.clear(); + this->device = device; +} + +void YACReaderHttpSession::setDisplayType(const QString & display) +{ + this->display = display; +} + +void YACReaderHttpSession::clearNavigationPath() +{ + navigationPath.clear(); +} + +QPair YACReaderHttpSession::popNavigationItem() +{ + if(navigationPath.isEmpty() == false) + return navigationPath.pop(); + return QPair(); +} + +QPair YACReaderHttpSession::topNavigationItem() +{ + if(navigationPath.isEmpty() == false) + return navigationPath.top(); + return QPair(); +} + +void YACReaderHttpSession::pushNavigationItem(const QPair &item) +{ + navigationPath.push(item); +} + +void YACReaderHttpSession::updateTopItem(const QPair &item) +{ + if(navigationPath.isEmpty() == false) + { + navigationPath.pop(); + navigationPath.push(item); + } + else + { + navigationPath.push(item); + } +} + +QStack > YACReaderHttpSession::getNavigationPath() +{ + return navigationPath; +} diff --git a/YACReaderLibrary/server/yacreader_http_session.h b/YACReaderLibrary/server/yacreader_http_session.h new file mode 100644 index 00000000..7b1b677a --- /dev/null +++ b/YACReaderLibrary/server/yacreader_http_session.h @@ -0,0 +1,72 @@ +#ifndef YACREADERHTTPSESSION_H +#define YACREADERHTTPSESSION_H + +#include + +#include "comic.h" + + + +class YACReaderHttpSession : public QObject +{ + Q_OBJECT +public: + explicit YACReaderHttpSession(QObject *parent = 0); + ~YACReaderHttpSession(); + + void setComicsOnDevice(const QSet & set); + void setComicOnDevice(const QString & hash); + void setDownloadedComic(const QString & hash); + bool isComicOnDevice(const QString & hash); + bool isComicDownloaded(const QString & hash); + QSet getComicsOnDevice(); + QSet getDownloadedComics(); + void clearComics(); + + //current comic (import) + qulonglong getCurrentComicId(); + Comic * getCurrentComic(); + void dismissCurrentComic(); + void setCurrentComic(qulonglong id, Comic * comic); + + //current comic (read) + qulonglong getCurrentRemoteComicId(); + Comic * getCurrentRemoteComic(); + void dismissCurrentRemoteComic(); + void setCurrentRemoteComic(qulonglong id, Comic * comic); + + //device identification + QString getDeviceType(); + QString getDisplayType(); + void setDeviceType(const QString & device); + void setDisplayType(const QString & display); + + void clearNavigationPath(); + QPair popNavigationItem(); + QPair topNavigationItem(); + void pushNavigationItem(const QPair & item); + void updateTopItem(const QPair & item); + + //TODO replace QPair by a custom class for storing folderId, page and folderName(save some DB accesses) + QStack > getNavigationPath(); + +signals: + +public slots: + +private: + QSet comicsOnDevice; + QSet downloadedComics; + + QString device; + QString display; + + qulonglong comicId; + qulonglong remoteComicId; + Comic * comic; + Comic * remoteComic; + + QStack > navigationPath; /* folder_id, page_number */ +}; + +#endif // YACREADERHTTPSESSION_H diff --git a/YACReaderLibrary/server/yacreader_http_session_store.cpp b/YACReaderLibrary/server/yacreader_http_session_store.cpp new file mode 100644 index 00000000..52a55bd3 --- /dev/null +++ b/YACReaderLibrary/server/yacreader_http_session_store.cpp @@ -0,0 +1,44 @@ +#include "yacreader_http_session_store.h" + +#include "yacreader_http_session.h" + +#include "httpsessionstore.h" + + + +YACReaderHttpSessionStore::YACReaderHttpSessionStore(HttpSessionStore *sessionStore, QObject *parent) + : QObject(parent), sessionStore(sessionStore) +{ + connect(&cleanupTimer,SIGNAL(timeout()),this,SLOT(sessionTimerEvent())); + cleanupTimer.start(60000); +} + +void YACReaderHttpSessionStore::addYACReaderHttpSession(const QByteArray &httpSessionId, YACReaderHttpSession *yacreaderHttpSession) +{ + QMutexLocker locker(&mutex); + + sessions.insert(httpSessionId, yacreaderHttpSession); +} + +YACReaderHttpSession *YACReaderHttpSessionStore::getYACReaderSessionHttpSession(const QByteArray &httpSessionId) +{ + QMutexLocker locker(&mutex); + + return sessions.value(httpSessionId, nullptr); +} + +void YACReaderHttpSessionStore::sessionTimerEvent() +{ + QMutexLocker locker(&mutex); + for(const QByteArray &id : sessions.keys()) + { + if(sessionStore->getSession(id).isNull()) + { + YACReaderHttpSession *session = sessions.value(id, nullptr); + if(session != nullptr) + delete session; + + sessions.remove(id); + } + } +} diff --git a/YACReaderLibrary/server/yacreader_http_session_store.h b/YACReaderLibrary/server/yacreader_http_session_store.h new file mode 100644 index 00000000..6001c7ab --- /dev/null +++ b/YACReaderLibrary/server/yacreader_http_session_store.h @@ -0,0 +1,39 @@ +#ifndef YACREADERHTTPSESSIONSTORE_H +#define YACREADERHTTPSESSIONSTORE_H + +#include +#include + + + +class HttpSessionStore; +class YACReaderHttpSession; + + + +class YACReaderHttpSessionStore : public QObject +{ + Q_OBJECT +public: + explicit YACReaderHttpSessionStore(HttpSessionStore *sessionStore, QObject *parent = 0); + + void addYACReaderHttpSession(const QByteArray & httpSessionId, YACReaderHttpSession *yacreaderHttpSession); + YACReaderHttpSession *getYACReaderSessionHttpSession(const QByteArray & httpSessionId); + +signals: + +public slots: + +private: + QMap sessions; + HttpSessionStore *sessionStore; + QTimer cleanupTimer; + + QMutex mutex; + +private slots: + + void sessionTimerEvent(); +}; + +#endif // YACREADERHTTPSESSIONSTORE_H diff --git a/YACReaderLibrary/server/yacreader_server_data_helper.cpp b/YACReaderLibrary/server/yacreader_server_data_helper.cpp new file mode 100644 index 00000000..494f8a78 --- /dev/null +++ b/YACReaderLibrary/server/yacreader_server_data_helper.cpp @@ -0,0 +1,26 @@ +#include "yacreader_server_data_helper.h" + +QString YACReaderServerDataHelper::folderToYSFormat(const qulonglong libraryId, const Folder & folder) +{ + return QString("f\t%1\t%2\t%3\t%4\t%5\r\n") + .arg(libraryId) + .arg(folder.id) + .arg(folder.name) + .arg(folder.getNumChildren()) + .arg(folder.getFirstChildHash()); +} + +QString YACReaderServerDataHelper::comicToYSFormat(const qulonglong libraryId,const ComicDB & comic) +{ + return QString("c\t%1\t%2\t%3\t%4\t%5\t%6\t%7\t%8\r\n") + .arg(libraryId) + .arg(comic.id) + .arg(comic.getFileName()) + .arg(comic.getFileSize()) + .arg(comic.info.hash) + .arg(comic.info.currentPage) + .arg(comic.info.numPages.toInt()) + .arg(comic.info.read?1:0); +} + +YACReaderServerDataHelper::YACReaderServerDataHelper() {} diff --git a/YACReaderLibrary/server/yacreader_server_data_helper.h b/YACReaderLibrary/server/yacreader_server_data_helper.h new file mode 100644 index 00000000..b09da28d --- /dev/null +++ b/YACReaderLibrary/server/yacreader_server_data_helper.h @@ -0,0 +1,19 @@ +#ifndef YACREADERSERVERDATAHELPER_H +#define YACREADERSERVERDATAHELPER_H + +#include +#include "folder.h" +#include "comic_db.h" + +class YACReaderServerDataHelper +{ +public: + static QString folderToYSFormat(const qulonglong libraryId, const Folder & folder); + static QString comicToYSFormat(const qulonglong libraryId, const ComicDB & comic); + +private: + YACReaderServerDataHelper(); + +}; + +#endif // YACREADERSERVERDATAHELPER_H diff --git a/YACReaderLibraryServer/main.cpp b/YACReaderLibraryServer/main.cpp index 4f78d6a6..dd704f46 100644 --- a/YACReaderLibraryServer/main.cpp +++ b/YACReaderLibraryServer/main.cpp @@ -9,6 +9,8 @@ #include "console_ui_library_creator.h" +#include + #include "QsLog.h" #include "QsLogDest.h" @@ -118,7 +120,7 @@ int main( int argc, char ** argv ) QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::tr("\nYACReaderLibraryServer is the headless (no gui) version of YACReaderLibrary")); parser.addHelpOption(); - parser.addVersionOption(); + const QCommandLineOption versionOption = parser.addVersionOption(); parser.addPositionalArgument("command", "The command to execute. [start, create-library, update-library, add-library, remove-library, list-libraries]"); parser.parse(QCoreApplication::arguments()); @@ -126,6 +128,13 @@ int main( int argc, char ** argv ) const QStringList args = parser.positionalArguments(); const QString command = args.isEmpty() ? QString() : args.first(); + if(parser.isSet(versionOption)) + { + qout << "YACReaderLibraryServer" << " " << VERSION << endl; + + return 0; + } + if(command == "start") { QString destLog = YACReader::getSettingsPath()+"/yacreaderlibrary.log"; diff --git a/common/comic_db.cpp b/common/comic_db.cpp index 2f88ee58..1f136a58 100644 --- a/common/comic_db.cpp +++ b/common/comic_db.cpp @@ -16,7 +16,7 @@ ComicDB::ComicDB(const ComicDB &comicDB) operator=(comicDB); } -bool ComicDB::isDir() +bool ComicDB::isDir() const { return false; } @@ -235,6 +235,8 @@ ComicInfo & ComicInfo::operator=(const ComicInfo & comicInfo) notes = comicInfo.notes; comicVineID = comicInfo.comicVineID; + lastTimeOpened = comicInfo.lastTimeOpened; + return *this; } @@ -547,6 +549,8 @@ QDataStream &operator<<(QDataStream & stream, const ComicInfo & comicInfo) stream << comicInfo.comicVineID; + stream << comicInfo.lastTimeOpened; + return stream; } @@ -602,6 +606,8 @@ QDataStream &operator>>(QDataStream & stream, ComicInfo & comicInfo) stream >> comicInfo.notes; stream >> comicInfo.comicVineID; + + stream >> comicInfo.lastTimeOpened; return stream; } diff --git a/common/comic_db.h b/common/comic_db.h index 5850de65..51627243 100644 --- a/common/comic_db.h +++ b/common/comic_db.h @@ -83,6 +83,8 @@ public: QImage cover; + QVariant lastTimeOpened;//integer/date + /*void setTitle(QVariant value); void setCoverPage(QVariant value); @@ -186,6 +188,8 @@ public: Q_PROPERTY(QImage cover MEMBER cover CONSTANT) + Q_PROPERTY(QVariant lastTimeOpened MEMBER lastTimeOpened CONSTANT) + //-new properties, not loaded from the DB automatically bool isFavorite; Q_PROPERTY(bool isFavorite MEMBER isFavorite WRITE setFavorite NOTIFY favoriteChanged) @@ -210,7 +214,7 @@ public: ComicDB(); ComicDB(const ComicDB & comicDB); - bool isDir(); + bool isDir() const; bool _hasCover; diff --git a/common/folder.cpp b/common/folder.cpp index 4f08207e..4eb672dd 100644 --- a/common/folder.cpp +++ b/common/folder.cpp @@ -1,6 +1,23 @@ - #include "folder.h" +Folder::Folder() + :knownParent(false), + knownId(false), + numChildren(-1) +{} + +Folder::Folder(qulonglong folderId, qulonglong parentId, const QString &folderName, const QString &folderPath) + :knownParent(true), + knownId(true), + numChildren(-1) +{ + this->id = folderId; + this->parentId = parentId; + this->name = folderName; + this->path = folderPath; +} + + Folder::Folder(const Folder &folder) { operator=(folder); @@ -17,3 +34,11 @@ Folder &Folder::operator =(const Folder &other) return *this; } +Folder::Folder(const QString & folderName, const QString & folderPath) + :knownParent(false), + knownId(false), + numChildren(-1) +{ + this->name = folderName; + this->path = folderPath; +} diff --git a/common/folder.h b/common/folder.h index 2dc7002a..c16936ef 100644 --- a/common/folder.h +++ b/common/folder.h @@ -11,22 +11,86 @@ public: bool knownParent; bool knownId; - Folder():knownParent(false), knownId(false){} - Folder(qulonglong sid, qulonglong pid,QString fn, QString fp):knownParent(true), knownId(true){id = sid; parentId = pid;name = fn; path = fp;} - Folder(QString fn, QString fp):knownParent(false), knownId(false){name = fn; path = fp;} + Folder(); + Folder(qulonglong folderId, qulonglong parentId,const QString & folderName, const QString & folderPath); + Folder(const QString & folderName, const QString & folderPath); Folder(const Folder &folder); Folder &operator =(const Folder & other); - void setId(qulonglong sid){id = sid;knownId = true;} - void setFather(qulonglong pid){parentId = pid;knownParent = true;} - bool isDir() {return true;} - bool isFinished() const {return finished;} - bool isCompleted() const {return completed;} - void setFinished(bool b) {finished = b;} - void setCompleted(bool b) {completed = b;} + + inline void setId(qulonglong sid) + { + id = sid; + knownId = true; + } + inline void setFather(qulonglong pid) + { + parentId = pid; + knownParent = true; + } + + inline bool isDir() const + { + return true; + } + + inline bool isFinished() const + { + return finished; + } + + inline bool isCompleted() const + { + return completed; + } + + inline void setFinished(bool b) + { + finished = b; + } + + inline void setCompleted(bool b) + { + completed = b; + } + + inline qint32 getNumChildren() const + { + return numChildren; + } + + inline void setNumChildren(const qint32 v) + { + numChildren = v; + } + + inline QString getFirstChildHash() const + { + return firstChildHash; + } + + inline void setFirstChildHash(const QString & v) + { + firstChildHash = v; + } + + inline QString getCustomImage() const + { + return customImage; + } + + inline void setCustomImage(const QString & s) + { + customImage = s; + } private: bool finished; bool completed; + + qint32 numChildren; //-1 for unknown number of children + QString firstChildHash; //empty for unknown first child + QString customImage; //empty for none custom image + }; #endif diff --git a/common/library_item.h b/common/library_item.h index 5ca1958e..dcb08a6c 100644 --- a/common/library_item.h +++ b/common/library_item.h @@ -7,7 +7,7 @@ class LibraryItem : public QObject { Q_OBJECT public: - virtual bool isDir() = 0; + virtual bool isDir() const = 0; LibraryItem & operator=(const LibraryItem & other); QString name; QString path; From 1e74234dbf42164ffb75ce0ef787b5f5f6710839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sat, 26 Aug 2017 17:03:18 +0200 Subject: [PATCH 092/118] Fixed data base info retrieving through DBHelper - values(QString) and QSqlRecord. Methods implemented before the fixes in the develop branch. --- YACReaderLibrary/db_helper.cpp | 72 ++++++++++++++++------------------ 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 19b6ad33..510123bf 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -140,8 +140,7 @@ QString DBHelper::getFolderName(qulonglong libraryId, qulonglong id) if(selectQuery.next()) { - QSqlRecord record = selectQuery.record(); - name = record.value(0).toString(); + name = selectQuery.value(0).toString(); } } @@ -181,19 +180,14 @@ QList DBHelper::getLabelComics(qulonglong libraryId, qulonglong labelId { ComicDB comic; - QList data; - QSqlRecord record = selectQuery.record(); - for(int i=0;i DBHelper::getFavorites(qulonglong libraryId) { ComicDB comic; - QSqlRecord record = selectQuery.record(); - - comic.id = record.value(0).toULongLong(); + comic.id = selectQuery.value(0).toULongLong(); comic.parentId = FAV_ID; - comic.name = record.value(1).toString(); - comic.info.title = record.value(2).toString(); - comic.info.currentPage = record.value(3).toInt(); - comic.info.numPages = record.value(4).toInt(); - comic.info.hash = record.value(5).toString(); - comic.info.read = record.value(6).toBool(); + comic.name = selectQuery.value(1).toString(); + comic.info.title = selectQuery.value(2).toString(); + comic.info.currentPage = selectQuery.value(3).toInt(); + comic.info.numPages = selectQuery.value(4).toInt(); + comic.info.hash = selectQuery.value(5).toString(); + comic.info.read = selectQuery.value(6).toBool(); list.append(comic); } @@ -266,16 +258,14 @@ QList DBHelper::getReading(qulonglong libraryId) { ComicDB comic; - QSqlRecord record = selectQuery.record(); - - comic.id = record.value(0).toULongLong(); - comic.parentId = record.value(1).toULongLong(); - comic.name = record.value(2).toString(); - comic.info.title = record.value(3).toString(); - comic.info.currentPage = record.value(4).toInt(); - comic.info.numPages = record.value(5).toInt(); - comic.info.hash = record.value(6).toString(); - comic.info.read = record.value(7).toBool(); + comic.id = selectQuery.value(0).toULongLong(); + comic.parentId = selectQuery.value(1).toULongLong(); + comic.name = selectQuery.value(2).toString(); + comic.info.title = selectQuery.value(3).toString(); + comic.info.currentPage = selectQuery.value(4).toInt(); + comic.info.numPages = selectQuery.value(5).toInt(); + comic.info.hash = selectQuery.value(6).toString(); + comic.info.read = selectQuery.value(7).toBool(); list.append(comic); } @@ -1156,14 +1146,20 @@ QList DBHelper::getLabelItems(qulonglong libraryId) QSqlQuery selectQuery("SELECT * FROM label ORDER BY ordering,name",db); //TODO add some kind of QList labels; + QSqlRecord record = selectQuery.record(); + + int name = record.indexOf("name"); + int color = record.indexOf("color"); + int id = record.indexOf("id"); + int ordering = record.indexOf("ordering"); + while(selectQuery.next()) { - QSqlRecord record = selectQuery.record(); LabelItem *item = new LabelItem(QList() - << record.value("name") - << record.value("color") - << record.value("id") - << record.value("ordering")); + << selectQuery.value(name) + << selectQuery.value(color) + << selectQuery.value(id) + << selectQuery.value(ordering)); if(labels.isEmpty()) { From 4d4556f7e2698734772d638d83f00b6815da0692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Mon, 28 Aug 2017 19:02:18 +0200 Subject: [PATCH 093/118] Removed usage of QSqlRecoerd unless is totally necessary. --- YACReaderLibrary/db_helper.cpp | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index 9a9d10f3..86f89720 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -287,11 +287,15 @@ QList DBHelper::getReadingLists(qulonglong libraryId) selectQuery.exec(); + QSqlRecord record = selectQuery.record(); + + int name = record.indexOf("name"); + int id = record.indexOf("id"); + int ordering = record.indexOf("ordering"); + while (selectQuery.next()) { - QSqlRecord record = selectQuery.record(); - - ReadingList item(record.value("name").toString(), record.value("id").toLongLong(),record.value("ordering").toInt()); + ReadingList item(selectQuery.value(name).toString(), selectQuery.value(id).toLongLong(),selectQuery.value(ordering).toInt()); if(list.isEmpty()) { @@ -328,7 +332,7 @@ QList DBHelper::getReadingListFullContent(qulonglong libraryId, qulongl subfolders.bindValue(":parentId", readingListId); subfolders.exec(); while(subfolders.next()) - ids << subfolders.record().value(0).toULongLong(); + ids << subfolders.value(0).toULongLong(); foreach(qulonglong id, ids) { @@ -345,16 +349,14 @@ QList DBHelper::getReadingListFullContent(qulonglong libraryId, qulongl { ComicDB comic; - QSqlRecord record = selectQuery.record(); - - comic.id = record.value(0).toULongLong(); - comic.parentId = record.value(1).toULongLong(); - comic.name = record.value(2).toString(); - comic.info.title = record.value(3).toString(); - comic.info.currentPage = record.value(4).toInt(); - comic.info.numPages = record.value(5).toInt(); - comic.info.hash = record.value(6).toString(); - comic.info.read = record.value(7).toBool(); + comic.id = selectQuery.value(0).toULongLong(); + comic.parentId = selectQuery.value(1).toULongLong(); + comic.name = selectQuery.value(2).toString(); + comic.info.title = selectQuery.value(3).toString(); + comic.info.currentPage = selectQuery.value(4).toInt(); + comic.info.numPages = selectQuery.value(5).toInt(); + comic.info.hash = selectQuery.value(6).toString(); + comic.info.read = selectQuery.value(7).toBool(); list.append(comic); } @@ -660,7 +662,7 @@ void DBHelper::updateChildrenInfo(QSqlDatabase & db) while (selectQuery.next()) { - DBHelper::updateChildrenInfo(selectQuery.record().value(0).toULongLong(), db); + DBHelper::updateChildrenInfo(selectQuery.value(0).toULongLong(), db); } } From 68db72078f7bb6da7dcd13554d8ee72c17b202f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 4 Mar 2018 13:05:00 +0100 Subject: [PATCH 094/118] Separate v1 and v2 server api classes and execution path. --- .../server/controllers/dumpcontroller.cpp | 62 ----- .../server/controllers/dumpcontroller.h | 29 --- .../controllers/fileuploadcontroller.cpp | 38 --- .../server/controllers/fileuploadcontroller.h | 30 --- .../server/controllers/formcontroller.cpp | 64 ----- .../server/controllers/sessioncontroller.h | 29 --- .../server/controllers/sessionmanager.cpp | 0 .../server/controllers/templatecontroller.cpp | 31 --- .../server/controllers/templatecontroller.h | 30 --- .../server/controllers/v1/comiccontroller.cpp | 124 ++++++++++ .../controllers/{ => v1}/comiccontroller.h | 0 .../{ => v1}/comicdownloadinfocontroller.cpp | 0 .../{ => v1}/comicdownloadinfocontroller.h | 0 .../server/controllers/v1/covercontroller.cpp | 89 +++++++ .../controllers/{ => v1}/covercontroller.h | 0 .../controllers/{ => v1}/errorcontroller.cpp | 0 .../controllers/{ => v1}/errorcontroller.h | 0 .../controllers/{ => v1}/foldercontroller.cpp | 28 +-- .../controllers/{ => v1}/foldercontroller.h | 0 .../{ => v1}/folderinfocontroller.cpp | 8 +- .../{ => v1}/folderinfocontroller.h | 0 .../controllers/v1/librariescontroller.cpp | 42 ++++ .../{ => v1}/librariescontroller.h | 0 .../server/controllers/v1/pagecontroller.cpp | 99 ++++++++ .../controllers/{ => v1}/pagecontroller.h | 0 .../{ => v1}/sessioncontroller.cpp | 0 .../controllers/{ => v1}/sessionmanager.h | 0 .../controllers/{ => v1}/synccontroller.cpp | 0 .../controllers/{ => v1}/synccontroller.h | 2 +- .../{ => v1}/updatecomiccontroller.cpp | 0 .../{ => v1}/updatecomiccontroller.h | 0 .../comiccontroller_v2.cpp} | 10 +- .../controllers/v2/comiccontroller_v2.h | 23 ++ .../v2/comicdownloadinfocontroller_v2.cpp | 26 ++ .../v2/comicdownloadinfocontroller_v2.h | 19 ++ .../covercontroller_v2.cpp} | 10 +- .../controllers/v2/covercontroller_v2.h | 20 ++ .../controllers/v2/errorcontroller_v2.cpp | 26 ++ .../controllers/v2/errorcontroller_v2.h | 22 ++ .../favoritescontroller_v2.cpp} | 10 +- .../favoritescontroller_v2.h} | 10 +- .../foldercontentcontroller_v2.cpp} | 12 +- .../foldercontentcontroller_v2.h} | 10 +- .../v2/folderinfocontroller_v2.cpp | 48 ++++ .../controllers/v2/folderinfocontroller_v2.h | 23 ++ .../librariescontroller_v2.cpp} | 6 +- .../librariescontroller_v2.h} | 17 +- .../pagecontroller_v2.cpp} | 10 +- .../server/controllers/v2/pagecontroller_v2.h | 20 ++ .../readingcomicscontroller_v2.cpp} | 10 +- .../readingcomicscontroller_v2.h} | 10 +- .../readinglistcontentcontroller_v2.cpp} | 12 +- .../readinglistcontentcontroller_v2.h} | 10 +- .../readinglistscontroller_v2.cpp} | 10 +- .../readinglistscontroller_v2.h} | 10 +- .../controllers/v2/synccontroller_v2.cpp | 64 +++++ .../server/controllers/v2/synccontroller_v2.h | 21 ++ .../tagcontentcontroller_v2.cpp} | 12 +- .../tagcontentcontroller_v2.h} | 10 +- .../tagscontroller_v2.cpp} | 8 +- .../tagscontroller_v2.h} | 10 +- .../v2/updatecomiccontroller_v2.cpp | 46 ++++ .../controllers/v2/updatecomiccontroller_v2.h | 22 ++ YACReaderLibrary/server/requestmapper.cpp | 229 +++++++++++++----- YACReaderLibrary/server/requestmapper.h | 18 +- YACReaderLibrary/server/server.pri | 156 +++++++----- 66 files changed, 1105 insertions(+), 580 deletions(-) delete mode 100644 YACReaderLibrary/server/controllers/dumpcontroller.cpp delete mode 100644 YACReaderLibrary/server/controllers/dumpcontroller.h delete mode 100644 YACReaderLibrary/server/controllers/fileuploadcontroller.cpp delete mode 100644 YACReaderLibrary/server/controllers/fileuploadcontroller.h delete mode 100644 YACReaderLibrary/server/controllers/formcontroller.cpp delete mode 100644 YACReaderLibrary/server/controllers/sessioncontroller.h delete mode 100644 YACReaderLibrary/server/controllers/sessionmanager.cpp delete mode 100644 YACReaderLibrary/server/controllers/templatecontroller.cpp delete mode 100644 YACReaderLibrary/server/controllers/templatecontroller.h create mode 100644 YACReaderLibrary/server/controllers/v1/comiccontroller.cpp rename YACReaderLibrary/server/controllers/{ => v1}/comiccontroller.h (100%) rename YACReaderLibrary/server/controllers/{ => v1}/comicdownloadinfocontroller.cpp (100%) rename YACReaderLibrary/server/controllers/{ => v1}/comicdownloadinfocontroller.h (100%) create mode 100644 YACReaderLibrary/server/controllers/v1/covercontroller.cpp rename YACReaderLibrary/server/controllers/{ => v1}/covercontroller.h (100%) rename YACReaderLibrary/server/controllers/{ => v1}/errorcontroller.cpp (100%) rename YACReaderLibrary/server/controllers/{ => v1}/errorcontroller.h (100%) rename YACReaderLibrary/server/controllers/{ => v1}/foldercontroller.cpp (91%) rename YACReaderLibrary/server/controllers/{ => v1}/foldercontroller.h (100%) rename YACReaderLibrary/server/controllers/{ => v1}/folderinfocontroller.cpp (85%) rename YACReaderLibrary/server/controllers/{ => v1}/folderinfocontroller.h (100%) create mode 100644 YACReaderLibrary/server/controllers/v1/librariescontroller.cpp rename YACReaderLibrary/server/controllers/{ => v1}/librariescontroller.h (100%) create mode 100644 YACReaderLibrary/server/controllers/v1/pagecontroller.cpp rename YACReaderLibrary/server/controllers/{ => v1}/pagecontroller.h (100%) rename YACReaderLibrary/server/controllers/{ => v1}/sessioncontroller.cpp (100%) rename YACReaderLibrary/server/controllers/{ => v1}/sessionmanager.h (100%) rename YACReaderLibrary/server/controllers/{ => v1}/synccontroller.cpp (100%) rename YACReaderLibrary/server/controllers/{ => v1}/synccontroller.h (87%) rename YACReaderLibrary/server/controllers/{ => v1}/updatecomiccontroller.cpp (100%) rename YACReaderLibrary/server/controllers/{ => v1}/updatecomiccontroller.h (100%) rename YACReaderLibrary/server/controllers/{comiccontroller.cpp => v2/comiccontroller_v2.cpp} (89%) create mode 100644 YACReaderLibrary/server/controllers/v2/comiccontroller_v2.h create mode 100644 YACReaderLibrary/server/controllers/v2/comicdownloadinfocontroller_v2.cpp create mode 100644 YACReaderLibrary/server/controllers/v2/comicdownloadinfocontroller_v2.h rename YACReaderLibrary/server/controllers/{covercontroller.cpp => v2/covercontroller_v2.cpp} (87%) create mode 100644 YACReaderLibrary/server/controllers/v2/covercontroller_v2.h create mode 100644 YACReaderLibrary/server/controllers/v2/errorcontroller_v2.cpp create mode 100644 YACReaderLibrary/server/controllers/v2/errorcontroller_v2.h rename YACReaderLibrary/server/controllers/{favoritescontroller.cpp => v2/favoritescontroller_v2.cpp} (63%) rename YACReaderLibrary/server/controllers/{favoritescontroller.h => v2/favoritescontroller_v2.h} (56%) rename YACReaderLibrary/server/controllers/{foldercontentcontroller.cpp => v2/foldercontentcontroller_v2.cpp} (78%) rename YACReaderLibrary/server/controllers/{foldercontentcontroller.h => v2/foldercontentcontroller_v2.h} (60%) create mode 100644 YACReaderLibrary/server/controllers/v2/folderinfocontroller_v2.cpp create mode 100644 YACReaderLibrary/server/controllers/v2/folderinfocontroller_v2.h rename YACReaderLibrary/server/controllers/{librariescontroller.cpp => v2/librariescontroller_v2.cpp} (84%) rename YACReaderLibrary/server/controllers/{formcontroller.h => v2/librariescontroller_v2.h} (54%) rename YACReaderLibrary/server/controllers/{pagecontroller.cpp => v2/pagecontroller_v2.cpp} (88%) create mode 100644 YACReaderLibrary/server/controllers/v2/pagecontroller_v2.h rename YACReaderLibrary/server/controllers/{readingcomicscontroller.cpp => v2/readingcomicscontroller_v2.cpp} (62%) rename YACReaderLibrary/server/controllers/{readingcomicscontroller.h => v2/readingcomicscontroller_v2.h} (55%) rename YACReaderLibrary/server/controllers/{readinglistcontentcontroller.cpp => v2/readinglistcontentcontroller_v2.cpp} (58%) rename YACReaderLibrary/server/controllers/{readinglistcontentcontroller.h => v2/readinglistcontentcontroller_v2.h} (57%) rename YACReaderLibrary/server/controllers/{readinglistscontroller.cpp => v2/readinglistscontroller_v2.cpp} (62%) rename YACReaderLibrary/server/controllers/{readinglistscontroller.h => v2/readinglistscontroller_v2.h} (55%) create mode 100644 YACReaderLibrary/server/controllers/v2/synccontroller_v2.cpp create mode 100644 YACReaderLibrary/server/controllers/v2/synccontroller_v2.h rename YACReaderLibrary/server/controllers/{tagcontentcontroller.cpp => v2/tagcontentcontroller_v2.cpp} (59%) rename YACReaderLibrary/server/controllers/{tagcontentcontroller.h => v2/tagcontentcontroller_v2.h} (62%) rename YACReaderLibrary/server/controllers/{tagscontroller.cpp => v2/tagscontroller_v2.cpp} (74%) rename YACReaderLibrary/server/controllers/{tagscontroller.h => v2/tagscontroller_v2.h} (57%) create mode 100644 YACReaderLibrary/server/controllers/v2/updatecomiccontroller_v2.cpp create mode 100644 YACReaderLibrary/server/controllers/v2/updatecomiccontroller_v2.h diff --git a/YACReaderLibrary/server/controllers/dumpcontroller.cpp b/YACReaderLibrary/server/controllers/dumpcontroller.cpp deleted file mode 100644 index 2b67e536..00000000 --- a/YACReaderLibrary/server/controllers/dumpcontroller.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/** - @file - @author Stefan Frings -*/ - -#include "dumpcontroller.h" -#include -#include - -DumpController::DumpController(){} - -void DumpController::service(HttpRequest& request, HttpResponse& response) { - - response.setHeader("Content-Type", "text/html; charset=ISO-8859-1"); - response.setCookie(HttpCookie("firstCookie","hello",600)); - response.setCookie(HttpCookie("secondCookie","world",600)); - - QByteArray body(""); - body.append("Request:"); - body.append("
Method: "); - body.append(request.getMethod()); - body.append("
Path: "); - body.append(request.getPath()); - body.append("
Version: "); - body.append(request.getVersion()); - - body.append("

Headers:"); - QMapIterator i(request.getHeaderMap()); - while (i.hasNext()) { - i.next(); - body.append("
"); - body.append(i.key()); - body.append("="); - body.append(i.value()); - } - - body.append("

Parameters:"); - i=QMapIterator(request.getParameterMap()); - while (i.hasNext()) { - i.next(); - body.append("
"); - body.append(i.key()); - body.append("="); - body.append(i.value()); - } - - body.append("

Cookies:"); - i=QMapIterator(request.getCookieMap()); - while (i.hasNext()) { - i.next(); - body.append("
"); - body.append(i.key()); - body.append("="); - body.append(i.value()); - } - - body.append("

Body:
"); - body.append(request.getBody()); - - body.append(""); - response.write(body,true); -} diff --git a/YACReaderLibrary/server/controllers/dumpcontroller.h b/YACReaderLibrary/server/controllers/dumpcontroller.h deleted file mode 100644 index a3787dbb..00000000 --- a/YACReaderLibrary/server/controllers/dumpcontroller.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - @file - @author Stefan Frings -*/ - -#ifndef DUMPCONTROLLER_H -#define DUMPCONTROLLER_H - -#include "httprequest.h" -#include "httpresponse.h" -#include "httprequesthandler.h" - -/** - This controller dumps the received HTTP request in the response. -*/ - -class DumpController : public HttpRequestHandler { - Q_OBJECT - Q_DISABLE_COPY(DumpController); -public: - - /** Constructor */ - DumpController(); - - /** Generates the response */ - void service(HttpRequest& request, HttpResponse& response); -}; - -#endif // DUMPCONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/fileuploadcontroller.cpp b/YACReaderLibrary/server/controllers/fileuploadcontroller.cpp deleted file mode 100644 index 30d76035..00000000 --- a/YACReaderLibrary/server/controllers/fileuploadcontroller.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/** - @file - @author Stefan Frings -*/ - -#include "fileuploadcontroller.h" - -FileUploadController::FileUploadController() {} - -void FileUploadController::service(HttpRequest& request, HttpResponse& response) { - - if (request.getParameter("action")=="show") { - response.setHeader("Content-Type", "image/jpeg"); - QTemporaryFile* file=request.getUploadedFile("file1"); - if (file) { - while (!file->atEnd() && !file->error()) { - QByteArray buffer=file->read(65536); - response.write(buffer); - } - } - else { - response.write("upload failed"); - } - } - - else { - response.setHeader("Content-Type", "text/html; charset=ISO-8859-1"); - response.write(""); - response.write("Upload a JPEG image file

"); - response.write("

"); - response.write(" "); - response.write(" File:
"); - response.write(" "); - response.write("
"); - response.write("",true); - } -} - diff --git a/YACReaderLibrary/server/controllers/fileuploadcontroller.h b/YACReaderLibrary/server/controllers/fileuploadcontroller.h deleted file mode 100644 index 01865ea6..00000000 --- a/YACReaderLibrary/server/controllers/fileuploadcontroller.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - @file - @author Stefan Frings -*/ - -#ifndef FILEUPLOADCONTROLLER_H -#define FILEUPLOADCONTROLLER_H - -#include "httprequest.h" -#include "httpresponse.h" -#include "httprequesthandler.h" - -/** - This controller displays a HTML form for file upload and recieved the file. -*/ - - -class FileUploadController : public HttpRequestHandler { - Q_OBJECT - Q_DISABLE_COPY(FileUploadController); -public: - - /** Constructor */ - FileUploadController(); - - /** Generates the response */ - void service(HttpRequest& request, HttpResponse& response); -}; - -#endif // FILEUPLOADCONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/formcontroller.cpp b/YACReaderLibrary/server/controllers/formcontroller.cpp deleted file mode 100644 index 7a0f2b27..00000000 --- a/YACReaderLibrary/server/controllers/formcontroller.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/** - @file - @author Stefan Frings -*/ - -#include "formcontroller.h" -#include - -FormController::FormController() {} - -void FormController::service(HttpRequest& request, HttpResponse& response) { - - response.setHeader("Content-Type", "text/html; charset=utf-8"); - - QString data(request.getBody()); - - QStringList list = data.split("\n"); - - response.write(""); - response.writeText("á é í ó ú ñ -> \\ /Device type: "+list.first()); - - //test background proccesing - /*int i=0; - int j=0; - while(i<1000000000) - { - if(request.getBody().length()>1) - j++; - else - i++; - if(i%1000000 == 0) - response.write("

lista

"); - }*/ - - response.write("

lista

"); - - response.write("
    "); - - for(int i=1;i"+list.at(i)+""); - } - response.write("
",true); - - /*if (request.getParameter("action")=="show") { - response.write(""); - response.write("Name = "); - response.write(request.getParameter("name")); - response.write("
City = "); - response.write(request.getParameter("city")); - response.write("",true); - } - else { - response.write(""); - response.write("
"); - response.write(" "); - response.write(" Name:
"); - response.write(" City:
"); - response.write(" "); - response.write("
"); - response.write("",true); - }*/ -} - diff --git a/YACReaderLibrary/server/controllers/sessioncontroller.h b/YACReaderLibrary/server/controllers/sessioncontroller.h deleted file mode 100644 index a13ee51f..00000000 --- a/YACReaderLibrary/server/controllers/sessioncontroller.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - @file - @author Stefan Frings -*/ - -#ifndef SESSIONCONTROLLER_H -#define SESSIONCONTROLLER_H - -#include "httprequest.h" -#include "httpresponse.h" -#include "httprequesthandler.h" - -/** - This controller demonstrates how to use sessions. -*/ - -class SessionController : public HttpRequestHandler { - Q_OBJECT - Q_DISABLE_COPY(SessionController); -public: - - /** Constructor */ - SessionController(); - - /** Generates the response */ - void service(HttpRequest& request, HttpResponse& response); -}; - -#endif // SESSIONCONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/sessionmanager.cpp b/YACReaderLibrary/server/controllers/sessionmanager.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/YACReaderLibrary/server/controllers/templatecontroller.cpp b/YACReaderLibrary/server/controllers/templatecontroller.cpp deleted file mode 100644 index d1816808..00000000 --- a/YACReaderLibrary/server/controllers/templatecontroller.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/** - @file - @author Stefan Frings -*/ - -#include "templatecontroller.h" -#include "template.h" -#include "../static.h" - -TemplateController::TemplateController(){} - -void TemplateController::service(HttpRequest& request, HttpResponse& response) { - - response.setHeader("Content-Type", "text/html; charset=ISO-8859-1"); - - Template t=Static::templateLoader->getTemplate("demo",request.getHeader("Accept-Language")); - t.enableWarnings(); - t.setVariable("path",request.getPath()); - QMap headers=request.getHeaderMap(); - QMapIterator iterator(headers); - t.loop("header",headers.size()); - int i=0; - while (iterator.hasNext()) { - iterator.next(); - t.setVariable(QString("header%1.name").arg(i),QString(iterator.key())); - t.setVariable(QString("header%1.value").arg(i),QString(iterator.value())); - ++i; - } - - response.write(t.toLatin1(),true); -} diff --git a/YACReaderLibrary/server/controllers/templatecontroller.h b/YACReaderLibrary/server/controllers/templatecontroller.h deleted file mode 100644 index c5b0077d..00000000 --- a/YACReaderLibrary/server/controllers/templatecontroller.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - @file - @author Stefan Frings -*/ - -#ifndef TEMPLATECONTROLLER_H -#define TEMPLATECONTROLLER_H - -#include "httprequest.h" -#include "httpresponse.h" -#include "httprequesthandler.h" - -/** - This controller generates a website using the template engine. - It generates a Latin1 (ISO-8859-1) encoded website from a UTF-8 encoded template file. -*/ - -class TemplateController : public HttpRequestHandler { - Q_OBJECT - Q_DISABLE_COPY(TemplateController); - public: - - /** Constructor */ - TemplateController(); - - /** Generates the response */ - void service(HttpRequest& request, HttpResponse& response); - }; - -#endif // TEMPLATECONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/v1/comiccontroller.cpp b/YACReaderLibrary/server/controllers/v1/comiccontroller.cpp new file mode 100644 index 00000000..a5a50327 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v1/comiccontroller.cpp @@ -0,0 +1,124 @@ +#include "comiccontroller.h" + +#include "db_helper.h" +#include "yacreader_libraries.h" +#include "yacreader_http_session.h" + +#include "template.h" +#include "../static.h" + +#include "comic_db.h" +#include "comic.h" + +#include "QsLog.h" + +#include + +ComicController::ComicController() {} + +void ComicController::service(HttpRequest& request, HttpResponse& response) +{ + HttpSession session=Static::sessionStore->getSession(request,response,false); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + QStringList pathElements = path.split('/'); + qulonglong libraryId = pathElements.at(2).toLongLong(); + QString libraryName = DBHelper::getLibraryName(libraryId); + qulonglong comicId = pathElements.at(4).toULongLong(); + + bool remoteComic = path.endsWith("remote"); + + //TODO + //if(pathElements.size() == 6) + //{ + // QString action = pathElements.at(5); + // if(!action.isEmpty() && (action == "close")) + // { + // session.dismissCurrentComic(); + // response.write("",true); + // return; + // } + //} + + YACReaderLibraries libraries = DBHelper::getLibraries(); + + ComicDB comic = DBHelper::getComicInfo(libraryId, comicId); + + if(!remoteComic) + ySession->setDownloadedComic(comic.info.hash); + + Comic * comicFile = FactoryComic::newComic(libraries.getPath(libraryId)+comic.path); + + if(comicFile != NULL) + { + QThread * thread = NULL; + + thread = new QThread(); + + comicFile->moveToThread(thread); + + connect(comicFile, SIGNAL(errorOpening()), thread, SLOT(quit())); + connect(comicFile, SIGNAL(errorOpening(QString)), thread, SLOT(quit())); + connect(comicFile, SIGNAL(imagesLoaded()), thread, SLOT(quit())); + connect(thread, SIGNAL(started()), comicFile, SLOT(process())); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + + comicFile->load(libraries.getPath(libraryId)+comic.path); + + if(thread != NULL) + thread->start(); + + if(remoteComic) + { + QLOG_TRACE() << "remote comic requested"; + ySession->setCurrentRemoteComic(comic.id, comicFile); + + } + else + { + QLOG_TRACE() << "comic requested"; + ySession->setCurrentComic(comic.id, comicFile); + } + + response.setHeader("Content-Type", "text/plain; charset=utf-8"); + //TODO this field is not used by the client! + response.write(QString("library:%1\r\n").arg(libraryName).toUtf8()); + response.write(QString("libraryId:%1\r\n").arg(libraryId).toUtf8()); + if(remoteComic) //send previous and next comics id + { + QList siblings = DBHelper::getFolderComicsFromLibrary(libraryId, comic.parentId, true); + bool found = false; + int i; + for(i = 0; i < siblings.length(); i++) + { + if (siblings.at(i)->id == comic.id) + { + found = true; + break; + } + } + if(found) + { + if(i>0) + response.write(QString("previousComic:%1\r\n").arg(siblings.at(i-1)->id).toUtf8()); + if(iid).toUtf8()); + } + else + { + //ERROR + } + qDeleteAll(siblings); + } + response.write(comic.toTXT().toUtf8(),true); + } + else + { + //delete comicFile; + response.setStatus(404,"not found"); + response.write("404 not found",true); + } + //response.write(t.toLatin1(),true); + +} diff --git a/YACReaderLibrary/server/controllers/comiccontroller.h b/YACReaderLibrary/server/controllers/v1/comiccontroller.h similarity index 100% rename from YACReaderLibrary/server/controllers/comiccontroller.h rename to YACReaderLibrary/server/controllers/v1/comiccontroller.h diff --git a/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp b/YACReaderLibrary/server/controllers/v1/comicdownloadinfocontroller.cpp similarity index 100% rename from YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp rename to YACReaderLibrary/server/controllers/v1/comicdownloadinfocontroller.cpp diff --git a/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.h b/YACReaderLibrary/server/controllers/v1/comicdownloadinfocontroller.h similarity index 100% rename from YACReaderLibrary/server/controllers/comicdownloadinfocontroller.h rename to YACReaderLibrary/server/controllers/v1/comicdownloadinfocontroller.h diff --git a/YACReaderLibrary/server/controllers/v1/covercontroller.cpp b/YACReaderLibrary/server/controllers/v1/covercontroller.cpp new file mode 100644 index 00000000..0c8dd31e --- /dev/null +++ b/YACReaderLibrary/server/controllers/v1/covercontroller.cpp @@ -0,0 +1,89 @@ +#include "covercontroller.h" +#include "db_helper.h" //get libraries +#include "yacreader_libraries.h" +#include "yacreader_http_session.h" + +#include "template.h" +#include "../static.h" + +CoverController::CoverController() {} + +void CoverController::service(HttpRequest& request, HttpResponse& response) +{ + HttpSession session=Static::sessionStore->getSession(request,response,false); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); + + response.setHeader("Content-Type", "image/jpeg"); + response.setHeader("Connection","close"); + //response.setHeader("Content-Type", "plain/text; charset=ISO-8859-1"); + + YACReaderLibraries libraries = DBHelper::getLibraries(); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + QStringList pathElements = path.split('/'); + QString libraryName = DBHelper::getLibraryName(pathElements.at(2).toInt()); + QString fileName = pathElements.at(4); + + bool folderCover = request.getParameter("folderCover").length()>0; + + //response.writeText(path+"
"); + //response.writeText(libraryName+"
"); + //response.writeText(libraries.value(libraryName)+"/.yacreaderlibrary/covers/"+fileName+"
"); + + //QFile file(libraries.value(libraryName)+"/.yacreaderlibrary/covers/"+fileName); + //if (file.exists()) { + // if (file.open(QIODevice::ReadOnly)) + // { + // qDebug("StaticFileController: Open file %s",qPrintable(file.fileName())); + // // Return the file content, do not store in cache + // while (!file.atEnd() && !file.error()) { + // response.write(file.read(131072)); + // } + // } + + // file.close(); + //} + + QImage img(libraries.getPath(libraryName)+"/.yacreaderlibrary/covers/"+fileName); + if (!img.isNull()) { + + int width = 80, height = 120; + if(ySession->getDisplayType()=="@2x") + { + width = 160; + height = 240; + } + + if(float(img.width())/img.height() < 0.66666) + img = img.scaledToWidth(width,Qt::SmoothTransformation); + else + img = img.scaledToHeight(height,Qt::SmoothTransformation); + + QImage destImg(width,height,QImage::Format_RGB32); + destImg.fill(Qt::black); + QPainter p(&destImg); + + p.drawImage((width-img.width())/2,(height-img.height())/2,img); + + if(folderCover) + { + if(ySession->getDisplayType()=="@2x") + p.drawImage(0,0,QImage(":/images/f_overlayed_retina.png")); + else + p.drawImage(0,0,QImage(":/images/f_overlayed.png")); + } + + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + destImg.save(&buffer, "JPG"); + response.write(ba,true); + } + //DONE else, hay que devolver un 404 + else + { + response.setStatus(404,"not found"); + response.write("404 not found",true); + } +} + diff --git a/YACReaderLibrary/server/controllers/covercontroller.h b/YACReaderLibrary/server/controllers/v1/covercontroller.h similarity index 100% rename from YACReaderLibrary/server/controllers/covercontroller.h rename to YACReaderLibrary/server/controllers/v1/covercontroller.h diff --git a/YACReaderLibrary/server/controllers/errorcontroller.cpp b/YACReaderLibrary/server/controllers/v1/errorcontroller.cpp similarity index 100% rename from YACReaderLibrary/server/controllers/errorcontroller.cpp rename to YACReaderLibrary/server/controllers/v1/errorcontroller.cpp diff --git a/YACReaderLibrary/server/controllers/errorcontroller.h b/YACReaderLibrary/server/controllers/v1/errorcontroller.h similarity index 100% rename from YACReaderLibrary/server/controllers/errorcontroller.h rename to YACReaderLibrary/server/controllers/v1/errorcontroller.h diff --git a/YACReaderLibrary/server/controllers/foldercontroller.cpp b/YACReaderLibrary/server/controllers/v1/foldercontroller.cpp similarity index 91% rename from YACReaderLibrary/server/controllers/foldercontroller.cpp rename to YACReaderLibrary/server/controllers/v1/foldercontroller.cpp index bfa90dfc..976a7f4c 100644 --- a/YACReaderLibrary/server/controllers/foldercontroller.cpp +++ b/YACReaderLibrary/server/controllers/v1/foldercontroller.cpp @@ -1,5 +1,7 @@ #include "foldercontroller.h" -#include "controllers/errorcontroller.h" +#include "controllers/v1/errorcontroller.h" + +#include "yacreader_http_session.h" #include "db_helper.h" //get libraries #include "comic_db.h" @@ -12,8 +14,6 @@ #include "qnaturalsorting.h" #include "yacreader_global.h" -#include "yacreader_http_session.h" - #include "QsLog.h" struct LibraryItemSorter @@ -42,7 +42,7 @@ void FolderController::service(HttpRequest& request, HttpResponse& response) //QString y = session.get("xxx").toString(); //response.writeText(QString("session xxx : %1
").arg(y)); - Template t = Static::templateLoader->getTemplate("folder_"+ySession->getDeviceType(),request.getHeader("Accept-Language")); + Template t=Static::templateLoader->getTemplate("folder_"+ySession->getDeviceType(),request.getHeader("Accept-Language")); t.enableWarnings(); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); @@ -182,23 +182,14 @@ void FolderController::service(HttpRequest& request, HttpResponse& response) } else { - const Folder * folder = static_cast(item); - - if(folder->getFirstChildHash().length()>0) + QList children = DBHelper::getFolderComicsFromLibrary(libraryId, item->id); + if(children.length()>0) { - t.setVariable(QString("element%1.image.url").arg(i),QString("/library/%1/cover/%2.jpg?folderCover=true").arg(libraryId).arg(folder->getFirstChildHash())); + const ComicDB * comic = static_cast(children.at(0)); + t.setVariable(QString("element%1.image.url").arg(i),QString("/library/%1/cover/%2.jpg?folderCover=true").arg(libraryId).arg(comic->info.hash)); } else - { - QList children = DBHelper::getFolderComicsFromLibrary(libraryId, item->id); - if(children.length()>0) - { - const ComicDB * comic = static_cast(children.at(0)); - t.setVariable(QString("element%1.image.url").arg(i),QString("/library/%1/cover/%2.jpg?folderCover=true").arg(libraryId).arg(comic->info.hash)); - } - else - t.setVariable(QString("element%1.image.url").arg(i),"/images/f.png"); - } + t.setVariable(QString("element%1.image.url").arg(i),"/images/f.png"); } t.setVariable(QString("element%1.browse").arg(i),QString("BROWSE").arg(QString("/library/%1/folder/%2").arg(libraryId).arg(item->id))); @@ -342,4 +333,5 @@ void FolderController::service(HttpRequest& request, HttpResponse& response) t.setVariable("pages",QString("%1").arg(numPages)); response.write(t.toUtf8(), true); + } diff --git a/YACReaderLibrary/server/controllers/foldercontroller.h b/YACReaderLibrary/server/controllers/v1/foldercontroller.h similarity index 100% rename from YACReaderLibrary/server/controllers/foldercontroller.h rename to YACReaderLibrary/server/controllers/v1/foldercontroller.h diff --git a/YACReaderLibrary/server/controllers/folderinfocontroller.cpp b/YACReaderLibrary/server/controllers/v1/folderinfocontroller.cpp similarity index 85% rename from YACReaderLibrary/server/controllers/folderinfocontroller.cpp rename to YACReaderLibrary/server/controllers/v1/folderinfocontroller.cpp index b88ed0c2..595a8ca6 100644 --- a/YACReaderLibrary/server/controllers/folderinfocontroller.cpp +++ b/YACReaderLibrary/server/controllers/v1/folderinfocontroller.cpp @@ -15,10 +15,10 @@ void FolderInfoController::service(HttpRequest& request, HttpResponse& response) response.setHeader("Content-Type", "text/plain; charset=utf-8"); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); - QStringList pathElements = path.split('/'); - int libraryId = pathElements.at(2).toInt(); - QString libraryName = DBHelper::getLibraryName(libraryId); - qulonglong parentId = pathElements.at(4).toULongLong(); + QStringList pathElements = path.split('/'); + int libraryId = pathElements.at(2).toInt(); + QString libraryName = DBHelper::getLibraryName(libraryId); + qulonglong parentId = pathElements.at(4).toULongLong(); serviceComics(libraryId, parentId, response); diff --git a/YACReaderLibrary/server/controllers/folderinfocontroller.h b/YACReaderLibrary/server/controllers/v1/folderinfocontroller.h similarity index 100% rename from YACReaderLibrary/server/controllers/folderinfocontroller.h rename to YACReaderLibrary/server/controllers/v1/folderinfocontroller.h diff --git a/YACReaderLibrary/server/controllers/v1/librariescontroller.cpp b/YACReaderLibrary/server/controllers/v1/librariescontroller.cpp new file mode 100644 index 00000000..d35fbec1 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v1/librariescontroller.cpp @@ -0,0 +1,42 @@ +#include "librariescontroller.h" +#include "db_helper.h" //get libraries +#include "yacreader_libraries.h" +#include "yacreader_http_session.h" + +#include "template.h" +#include "../static.h" + +#include "QsLog.h" + +LibrariesController::LibrariesController() {} + +void LibrariesController::service(HttpRequest& request, HttpResponse& response) +{ + HttpSession session=Static::sessionStore->getSession(request,response,false); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); + + response.setHeader("Content-Type", "text/html; charset=utf-8"); + response.setHeader("Connection","close"); + + ySession->clearNavigationPath(); + + Template t=Static::templateLoader->getTemplate("libraries_"+ySession->getDeviceType(),request.getHeader("Accept-Language")); + t.enableWarnings(); + + YACReaderLibraries libraries = DBHelper::getLibraries(); + QList names = DBHelper::getLibrariesNames(); + + t.loop("library",names.length()); + + int currentId = 0; + int i = 0; + foreach (QString name,names) { + currentId = libraries.getId(name); + t.setVariable(QString("library%1.name").arg(i),QString::number(currentId)); + t.setVariable(QString("library%1.label").arg(i),name); + i++; + } + + response.setStatus(200,"OK"); + response.write(t.toUtf8(),true); +} diff --git a/YACReaderLibrary/server/controllers/librariescontroller.h b/YACReaderLibrary/server/controllers/v1/librariescontroller.h similarity index 100% rename from YACReaderLibrary/server/controllers/librariescontroller.h rename to YACReaderLibrary/server/controllers/v1/librariescontroller.h diff --git a/YACReaderLibrary/server/controllers/v1/pagecontroller.cpp b/YACReaderLibrary/server/controllers/v1/pagecontroller.cpp new file mode 100644 index 00000000..a7cf13b6 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v1/pagecontroller.cpp @@ -0,0 +1,99 @@ +#include "pagecontroller.h" + +#include "../static.h" + +#include "comic.h" +#include "comiccontroller.h" +#include "yacreader_http_session.h" + +#include +#include + +#include + +#include "db_helper.h" + +PageController::PageController() {} + +void PageController::service(HttpRequest& request, HttpResponse& response) +{ + HttpSession session=Static::sessionStore->getSession(request,response,false); + YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + bool remote = path.endsWith("remote"); + + //QByteArray path2=request.getPath(); + //qDebug("PageController: request to -> %s ",path2.data()); + + QStringList pathElements = path.split('/'); + QString libraryName = DBHelper::getLibraryName(pathElements.at(2).toInt()); + qulonglong comicId = pathElements.at(4).toULongLong(); + unsigned int page = pathElements.at(6).toUInt(); + + //qDebug("lib name : %s",pathElements.at(2).data()); + + Comic * comicFile; + qulonglong currentComicId; + if(remote) + { + QLOG_TRACE() << "se recupera comic remoto para servir páginas"; + comicFile = ySession->getCurrentRemoteComic(); + currentComicId = ySession->getCurrentRemoteComicId(); + } + else + { + QLOG_TRACE() << "se recupera comic para servir páginas"; + comicFile = ySession->getCurrentComic(); + currentComicId = ySession->getCurrentComicId(); + } + + if(currentComicId != 0 && !QPointer(comicFile).isNull()) + { + if(comicId == currentComicId && page < comicFile->numPages()) + { + if(comicFile->pageIsLoaded(page)) + { + //qDebug("PageController: La página estaba cargada -> %s ",path.data()); + response.setHeader("Content-Type", "image/jpeg"); + response.setHeader("Transfer-Encoding","chunked"); + QByteArray pageData = comicFile->getRawPage(page); + QDataStream data(pageData); + char buffer[4096]; + while (!data.atEnd()) { + int len = data.readRawData(buffer,4096); + response.write(QByteArray(buffer,len)); + } + //response.write(pageData,true); + response.write(QByteArray(),true); + } + else + { + //qDebug("PageController: La página NO estaba cargada 404 -> %s ",path.data()); + response.setStatus(404,"not found"); //TODO qué mensaje enviar + response.write("404 not found",true); + } + } + else + { + if(comicId != currentComicId) + { + //delete comicFile; + if(remote) + ySession->dismissCurrentRemoteComic(); + else + ySession->dismissCurrentComic(); + } + response.setStatus(404,"not found"); //TODO qué mensaje enviar + response.write("404 not found",true); + } + } + else + { + response.setStatus(404,"not found"); + response.write("404 not found",true); + } + + //response.write(t.toLatin1(),true); + +} diff --git a/YACReaderLibrary/server/controllers/pagecontroller.h b/YACReaderLibrary/server/controllers/v1/pagecontroller.h similarity index 100% rename from YACReaderLibrary/server/controllers/pagecontroller.h rename to YACReaderLibrary/server/controllers/v1/pagecontroller.h diff --git a/YACReaderLibrary/server/controllers/sessioncontroller.cpp b/YACReaderLibrary/server/controllers/v1/sessioncontroller.cpp similarity index 100% rename from YACReaderLibrary/server/controllers/sessioncontroller.cpp rename to YACReaderLibrary/server/controllers/v1/sessioncontroller.cpp diff --git a/YACReaderLibrary/server/controllers/sessionmanager.h b/YACReaderLibrary/server/controllers/v1/sessionmanager.h similarity index 100% rename from YACReaderLibrary/server/controllers/sessionmanager.h rename to YACReaderLibrary/server/controllers/v1/sessionmanager.h diff --git a/YACReaderLibrary/server/controllers/synccontroller.cpp b/YACReaderLibrary/server/controllers/v1/synccontroller.cpp similarity index 100% rename from YACReaderLibrary/server/controllers/synccontroller.cpp rename to YACReaderLibrary/server/controllers/v1/synccontroller.cpp diff --git a/YACReaderLibrary/server/controllers/synccontroller.h b/YACReaderLibrary/server/controllers/v1/synccontroller.h similarity index 87% rename from YACReaderLibrary/server/controllers/synccontroller.h rename to YACReaderLibrary/server/controllers/v1/synccontroller.h index 6f6a5d76..b4d3cbe1 100644 --- a/YACReaderLibrary/server/controllers/synccontroller.h +++ b/YACReaderLibrary/server/controllers/v1/synccontroller.h @@ -9,7 +9,7 @@ class SyncController : public HttpRequestHandler { Q_OBJECT - Q_DISABLE_COPY(SyncController); + Q_DISABLE_COPY(SyncController) public: /** Constructor */ SyncController(); diff --git a/YACReaderLibrary/server/controllers/updatecomiccontroller.cpp b/YACReaderLibrary/server/controllers/v1/updatecomiccontroller.cpp similarity index 100% rename from YACReaderLibrary/server/controllers/updatecomiccontroller.cpp rename to YACReaderLibrary/server/controllers/v1/updatecomiccontroller.cpp diff --git a/YACReaderLibrary/server/controllers/updatecomiccontroller.h b/YACReaderLibrary/server/controllers/v1/updatecomiccontroller.h similarity index 100% rename from YACReaderLibrary/server/controllers/updatecomiccontroller.h rename to YACReaderLibrary/server/controllers/v1/updatecomiccontroller.h diff --git a/YACReaderLibrary/server/controllers/comiccontroller.cpp b/YACReaderLibrary/server/controllers/v2/comiccontroller_v2.cpp similarity index 89% rename from YACReaderLibrary/server/controllers/comiccontroller.cpp rename to YACReaderLibrary/server/controllers/v2/comiccontroller_v2.cpp index 7538067d..02ecc249 100644 --- a/YACReaderLibrary/server/controllers/comiccontroller.cpp +++ b/YACReaderLibrary/server/controllers/v2/comiccontroller_v2.cpp @@ -1,4 +1,4 @@ -#include "comiccontroller.h" +#include "comiccontroller_v2.h" #include "db_helper.h" #include "yacreader_libraries.h" @@ -14,18 +14,18 @@ #include -ComicController::ComicController() {} +ComicControllerV2::ComicControllerV2() {} -void ComicController::service(HttpRequest& request, HttpResponse& response) +void ComicControllerV2::service(HttpRequest& request, HttpResponse& response) { HttpSession session=Static::sessionStore->getSession(request,response,false); YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); - qulonglong libraryId = pathElements.at(2).toLongLong(); + qulonglong libraryId = pathElements.at(3).toLongLong(); QString libraryName = DBHelper::getLibraryName(libraryId); - qulonglong comicId = pathElements.at(4).toULongLong(); + qulonglong comicId = pathElements.at(5).toULongLong(); bool remoteComic = path.endsWith("remote"); diff --git a/YACReaderLibrary/server/controllers/v2/comiccontroller_v2.h b/YACReaderLibrary/server/controllers/v2/comiccontroller_v2.h new file mode 100644 index 00000000..9d896f8e --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/comiccontroller_v2.h @@ -0,0 +1,23 @@ +#ifndef COMICCONTROLLER_V2_H +#define COMICCONTROLLER_V2_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +#include +class Comic; +class QString; + +class ComicControllerV2 : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(ComicControllerV2) +public: + /** Constructor */ + ComicControllerV2(); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); +}; + +#endif // COMICCONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/v2/comicdownloadinfocontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/comicdownloadinfocontroller_v2.cpp new file mode 100644 index 00000000..8c484033 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/comicdownloadinfocontroller_v2.cpp @@ -0,0 +1,26 @@ +#include "comicdownloadinfocontroller_v2.h" + +#include "db_helper.h" +#include "yacreader_libraries.h" + +#include "comic_db.h" + +ComicDownloadInfoControllerV2::ComicDownloadInfoControllerV2() {} + + +void ComicDownloadInfoControllerV2::service(HttpRequest& request, HttpResponse& response) +{ + response.setHeader("Content-Type", "text/plain; charset=utf-8"); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + QStringList pathElements = path.split('/'); + + qulonglong libraryId = pathElements.at(3).toLongLong(); + qulonglong comicId = pathElements.at(5).toULongLong(); + + ComicDB comic = DBHelper::getComicInfo(libraryId, comicId); + + //TODO: check if the comic wasn't found; + response.write(QString("fileName:%1\r\n").arg(comic.getFileName()).toUtf8()); + response.write(QString("fileSize:%1\r\n").arg(comic.getFileSize()).toUtf8(),true); +} diff --git a/YACReaderLibrary/server/controllers/v2/comicdownloadinfocontroller_v2.h b/YACReaderLibrary/server/controllers/v2/comicdownloadinfocontroller_v2.h new file mode 100644 index 00000000..a85730c1 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/comicdownloadinfocontroller_v2.h @@ -0,0 +1,19 @@ +#ifndef COMICDOWNLOADINFOCONTROLLER_V2_H +#define COMICDOWNLOADINFOCONTROLLER_V2_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class ComicDownloadInfoControllerV2 : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(ComicDownloadInfoControllerV2) +public: + /** Constructor **/ + ComicDownloadInfoControllerV2(); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); +}; + +#endif // COMICDOWNLOADINFOCONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/covercontroller.cpp b/YACReaderLibrary/server/controllers/v2/covercontroller_v2.cpp similarity index 87% rename from YACReaderLibrary/server/controllers/covercontroller.cpp rename to YACReaderLibrary/server/controllers/v2/covercontroller_v2.cpp index 09251bef..2652ce13 100644 --- a/YACReaderLibrary/server/controllers/covercontroller.cpp +++ b/YACReaderLibrary/server/controllers/v2/covercontroller_v2.cpp @@ -1,4 +1,4 @@ -#include "covercontroller.h" +#include "covercontroller_v2.h" #include "db_helper.h" //get libraries #include "yacreader_libraries.h" #include "yacreader_http_session.h" @@ -6,9 +6,9 @@ #include "template.h" #include "../static.h" -CoverController::CoverController() {} +CoverControllerV2::CoverControllerV2() {} -void CoverController::service(HttpRequest& request, HttpResponse& response) +void CoverControllerV2::service(HttpRequest& request, HttpResponse& response) { HttpSession session=Static::sessionStore->getSession(request,response,false); YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); @@ -21,8 +21,8 @@ void CoverController::service(HttpRequest& request, HttpResponse& response) QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); - QString libraryName = DBHelper::getLibraryName(pathElements.at(2).toInt()); - QString fileName = pathElements.at(4); + QString libraryName = DBHelper::getLibraryName(pathElements.at(3).toInt()); + QString fileName = pathElements.at(5); bool folderCover = request.getParameter("folderCover").length()>0; diff --git a/YACReaderLibrary/server/controllers/v2/covercontroller_v2.h b/YACReaderLibrary/server/controllers/v2/covercontroller_v2.h new file mode 100644 index 00000000..f77128fd --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/covercontroller_v2.h @@ -0,0 +1,20 @@ +#ifndef COVERCONTROLLER_V2_H +#define COVERCONTROLLER_V2_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class CoverControllerV2 : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(CoverControllerV2) +public: + + /** Constructor */ + CoverControllerV2(); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); +}; + +#endif // COVERCONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/v2/errorcontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/errorcontroller_v2.cpp new file mode 100644 index 00000000..cf9ca36e --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/errorcontroller_v2.cpp @@ -0,0 +1,26 @@ +#include "errorcontroller_v2.h" + +#include "template.h" +#include "../static.h" + + +ErrorControllerV2::ErrorControllerV2(int errorCode) +:error(errorCode) +{} + +void ErrorControllerV2::service(HttpRequest& request, HttpResponse& response) +{ + Q_UNUSED(request) + switch(error) + { + case 300: + response.setStatus(300,"redirect"); + response.write(" ", true); + break; + case 404: + response.setStatus(404,"not found"); + response.write("404 not found",true); + break; + } + +} diff --git a/YACReaderLibrary/server/controllers/v2/errorcontroller_v2.h b/YACReaderLibrary/server/controllers/v2/errorcontroller_v2.h new file mode 100644 index 00000000..1f33a9e6 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/errorcontroller_v2.h @@ -0,0 +1,22 @@ +#ifndef ERRORCONTROLLER_V2_H +#define ERRORCONTROLLER_V2_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class ErrorControllerV2 : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(ErrorControllerV2) +public: + + /** Constructor */ + ErrorControllerV2(int errorCode); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); +private: + int error; +}; + +#endif // ERRORCONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/favoritescontroller.cpp b/YACReaderLibrary/server/controllers/v2/favoritescontroller_v2.cpp similarity index 63% rename from YACReaderLibrary/server/controllers/favoritescontroller.cpp rename to YACReaderLibrary/server/controllers/v2/favoritescontroller_v2.cpp index dbb44556..7c61f415 100644 --- a/YACReaderLibrary/server/controllers/favoritescontroller.cpp +++ b/YACReaderLibrary/server/controllers/v2/favoritescontroller_v2.cpp @@ -1,26 +1,26 @@ -#include "favoritescontroller.h" +#include "favoritescontroller_v2.h" #include "db_helper.h" #include "comic_db.h" #include "yacreader_server_data_helper.h" -FavoritesController::FavoritesController() {} +FavoritesControllerV2::FavoritesControllerV2() {} -void FavoritesController::service(HttpRequest &request, HttpResponse &response) +void FavoritesControllerV2::service(HttpRequest &request, HttpResponse &response) { response.setHeader("Content-Type", "text/plain; charset=utf-8"); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); - int libraryId = pathElements.at(2).toInt(); + int libraryId = pathElements.at(3).toInt(); serviceContent(libraryId, response); response.write("",true); } -void FavoritesController::serviceContent(const int library, HttpResponse &response) +void FavoritesControllerV2::serviceContent(const int library, HttpResponse &response) { QList tagComics = DBHelper::getFavorites(library); diff --git a/YACReaderLibrary/server/controllers/favoritescontroller.h b/YACReaderLibrary/server/controllers/v2/favoritescontroller_v2.h similarity index 56% rename from YACReaderLibrary/server/controllers/favoritescontroller.h rename to YACReaderLibrary/server/controllers/v2/favoritescontroller_v2.h index ca103950..8661495c 100644 --- a/YACReaderLibrary/server/controllers/favoritescontroller.h +++ b/YACReaderLibrary/server/controllers/v2/favoritescontroller_v2.h @@ -1,15 +1,15 @@ -#ifndef FAVORITESCONTROLLER_H -#define FAVORITESCONTROLLER_H +#ifndef FAVORITESCONTROLLER_V2_H +#define FAVORITESCONTROLLER_V2_H #include "httprequest.h" #include "httpresponse.h" #include "httprequesthandler.h" -class FavoritesController : public HttpRequestHandler { +class FavoritesControllerV2 : public HttpRequestHandler { Q_OBJECT - Q_DISABLE_COPY(FavoritesController) + Q_DISABLE_COPY(FavoritesControllerV2) public: - FavoritesController(); + FavoritesControllerV2(); void service(HttpRequest& request, HttpResponse& response); diff --git a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp b/YACReaderLibrary/server/controllers/v2/foldercontentcontroller_v2.cpp similarity index 78% rename from YACReaderLibrary/server/controllers/foldercontentcontroller.cpp rename to YACReaderLibrary/server/controllers/v2/foldercontentcontroller_v2.cpp index 3c81e7f0..23a18b5d 100644 --- a/YACReaderLibrary/server/controllers/foldercontentcontroller.cpp +++ b/YACReaderLibrary/server/controllers/v2/foldercontentcontroller_v2.cpp @@ -1,4 +1,4 @@ -#include "foldercontentcontroller.h" +#include "foldercontentcontroller_v2.h" #include @@ -21,23 +21,23 @@ struct LibraryItemSorter } }; -FolderContentController::FolderContentController() {} +FolderContentControllerV2::FolderContentControllerV2() {} -void FolderContentController::service(HttpRequest& request, HttpResponse& response) +void FolderContentControllerV2::service(HttpRequest& request, HttpResponse& response) { response.setHeader("Content-Type", "text/plain; charset=utf-8"); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); - int libraryId = pathElements.at(2).toInt(); - qulonglong parentId = pathElements.at(4).toULongLong(); + int libraryId = pathElements.at(3).toInt(); + qulonglong parentId = pathElements.at(5).toULongLong(); serviceContent(libraryId, parentId, response); response.write("",true); } -void FolderContentController::serviceContent(const int &library, const qulonglong &folderId, HttpResponse &response) +void FolderContentControllerV2::serviceContent(const int &library, const qulonglong &folderId, HttpResponse &response) { //clock_t begin = clock(); diff --git a/YACReaderLibrary/server/controllers/foldercontentcontroller.h b/YACReaderLibrary/server/controllers/v2/foldercontentcontroller_v2.h similarity index 60% rename from YACReaderLibrary/server/controllers/foldercontentcontroller.h rename to YACReaderLibrary/server/controllers/v2/foldercontentcontroller_v2.h index aa986042..23401b79 100644 --- a/YACReaderLibrary/server/controllers/foldercontentcontroller.h +++ b/YACReaderLibrary/server/controllers/v2/foldercontentcontroller_v2.h @@ -1,16 +1,16 @@ -#ifndef FOLDERCONTENTCONTROLLER_H -#define FOLDERCONTENTCONTROLLER_H +#ifndef FOLDERCONTENTCONTROLLER_V2_H +#define FOLDERCONTENTCONTROLLER_V2_H #include "httprequest.h" #include "httpresponse.h" #include "httprequesthandler.h" -class FolderContentController : public HttpRequestHandler { +class FolderContentControllerV2 : public HttpRequestHandler { Q_OBJECT - Q_DISABLE_COPY(FolderContentController); + Q_DISABLE_COPY(FolderContentControllerV2) public: /** Constructor */ - FolderContentController(); + FolderContentControllerV2(); /** Generates the response */ void service(HttpRequest& request, HttpResponse& response); diff --git a/YACReaderLibrary/server/controllers/v2/folderinfocontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/folderinfocontroller_v2.cpp new file mode 100644 index 00000000..2eb26348 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/folderinfocontroller_v2.cpp @@ -0,0 +1,48 @@ +#include "folderinfocontroller_v2.h" +#include "db_helper.h" //get libraries + +#include "folder.h" +#include "comic_db.h" + +#include "template.h" +#include "../static.h" + + +FolderInfoControllerV2::FolderInfoControllerV2() {} + +void FolderInfoControllerV2::service(HttpRequest& request, HttpResponse& response) +{ + response.setHeader("Content-Type", "text/plain; charset=utf-8"); + + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); + QStringList pathElements = path.split('/'); + int libraryId = pathElements.at(3).toInt(); + QString libraryName = DBHelper::getLibraryName(libraryId); + qulonglong parentId = pathElements.at(5).toULongLong(); + + serviceComics(libraryId, parentId, response); + + response.write("",true); +} + +void FolderInfoControllerV2::serviceComics(const int &library, const qulonglong &folderId, HttpResponse &response) +{ + QList folderContent = DBHelper::getFolderSubfoldersFromLibrary(library,folderId); + QList folderComics = DBHelper::getFolderComicsFromLibrary(library,folderId); + + ComicDB * currentComic; + for(QList::const_iterator itr = folderComics.constBegin();itr!=folderComics.constEnd();itr++) + { + currentComic = (ComicDB *)(*itr); + response.write(QString("/library/%1/comic/%2:%3:%4\r\n").arg(library).arg(currentComic->id).arg(currentComic->getFileName()).arg(currentComic->getFileSize()).toUtf8()); + delete currentComic; + } + + Folder * currentFolder; + for(QList::const_iterator itr = folderContent.constBegin();itr!=folderContent.constEnd();itr++) + { + currentFolder = (Folder *)(*itr); + serviceComics(library, currentFolder->id, response); + delete currentFolder; + } +} diff --git a/YACReaderLibrary/server/controllers/v2/folderinfocontroller_v2.h b/YACReaderLibrary/server/controllers/v2/folderinfocontroller_v2.h new file mode 100644 index 00000000..94907ca3 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/folderinfocontroller_v2.h @@ -0,0 +1,23 @@ +#ifndef FOLDERINFOCONTROLLER_V2_H +#define FOLDERINFOCONTROLLER_V2_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class FolderInfoControllerV2 : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(FolderInfoControllerV2) +public: + + /** Constructor */ + FolderInfoControllerV2(); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); + +private: + void serviceComics(const int &library, const qulonglong & folderId, HttpResponse& response); +}; + +#endif // FOLDERINFOCONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/librariescontroller.cpp b/YACReaderLibrary/server/controllers/v2/librariescontroller_v2.cpp similarity index 84% rename from YACReaderLibrary/server/controllers/librariescontroller.cpp rename to YACReaderLibrary/server/controllers/v2/librariescontroller_v2.cpp index 547fb578..87a71ec8 100644 --- a/YACReaderLibrary/server/controllers/librariescontroller.cpp +++ b/YACReaderLibrary/server/controllers/v2/librariescontroller_v2.cpp @@ -1,4 +1,4 @@ -#include "librariescontroller.h" +#include "librariescontroller_v2.h" #include "db_helper.h" //get libraries #include "yacreader_libraries.h" #include "yacreader_http_session.h" @@ -8,9 +8,9 @@ #include "QsLog.h" -LibrariesController::LibrariesController() {} +LibrariesControllerV2::LibrariesControllerV2() {} -void LibrariesController::service(HttpRequest& request, HttpResponse& response) +void LibrariesControllerV2::service(HttpRequest& request, HttpResponse& response) { HttpSession session=Static::sessionStore->getSession(request,response,false); YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); diff --git a/YACReaderLibrary/server/controllers/formcontroller.h b/YACReaderLibrary/server/controllers/v2/librariescontroller_v2.h similarity index 54% rename from YACReaderLibrary/server/controllers/formcontroller.h rename to YACReaderLibrary/server/controllers/v2/librariescontroller_v2.h index 5ae709a8..4c21f1c2 100644 --- a/YACReaderLibrary/server/controllers/formcontroller.h +++ b/YACReaderLibrary/server/controllers/v2/librariescontroller_v2.h @@ -1,10 +1,5 @@ -/** - @file - @author Stefan Frings -*/ - -#ifndef FORMCONTROLLER_H -#define FORMCONTROLLER_H +#ifndef LIBRARIESCONTROLLER_V2_H +#define LIBRARIESCONTROLLER_V2_H #include "httprequest.h" #include "httpresponse.h" @@ -15,16 +10,16 @@ */ -class FormController : public HttpRequestHandler { +class LibrariesControllerV2 : public HttpRequestHandler { Q_OBJECT - Q_DISABLE_COPY(FormController); + Q_DISABLE_COPY(LibrariesControllerV2) public: /** Constructor */ - FormController(); + LibrariesControllerV2(); /** Generates the response */ void service(HttpRequest& request, HttpResponse& response); }; -#endif // FORMCONTROLLER_H +#endif // LIBRARIESCONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/pagecontroller.cpp b/YACReaderLibrary/server/controllers/v2/pagecontroller_v2.cpp similarity index 88% rename from YACReaderLibrary/server/controllers/pagecontroller.cpp rename to YACReaderLibrary/server/controllers/v2/pagecontroller_v2.cpp index b0af2613..668f352b 100644 --- a/YACReaderLibrary/server/controllers/pagecontroller.cpp +++ b/YACReaderLibrary/server/controllers/v2/pagecontroller_v2.cpp @@ -1,4 +1,4 @@ -#include "pagecontroller.h" +#include "pagecontroller_v2.h" #include "../static.h" @@ -13,9 +13,9 @@ #include "db_helper.h" -PageController::PageController() {} +PageControllerV2::PageControllerV2() {} -void PageController::service(HttpRequest& request, HttpResponse& response) +void PageControllerV2::service(HttpRequest& request, HttpResponse& response) { HttpSession session=Static::sessionStore->getSession(request,response,false); YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); @@ -28,8 +28,8 @@ void PageController::service(HttpRequest& request, HttpResponse& response) QStringList pathElements = path.split('/'); QString libraryName = DBHelper::getLibraryName(pathElements.at(2).toInt()); - qulonglong comicId = pathElements.at(4).toULongLong(); - unsigned int page = pathElements.at(6).toUInt(); + qulonglong comicId = pathElements.at(5).toULongLong(); + unsigned int page = pathElements.at(7).toUInt(); //qDebug("lib name : %s",pathElements.at(2).data()); diff --git a/YACReaderLibrary/server/controllers/v2/pagecontroller_v2.h b/YACReaderLibrary/server/controllers/v2/pagecontroller_v2.h new file mode 100644 index 00000000..9bbb9ed1 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/pagecontroller_v2.h @@ -0,0 +1,20 @@ +#ifndef PAGECONTROLLER_V2_H +#define PAGECONTROLLER_V2_H + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class PageControllerV2 : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(PageControllerV2) +public: + + /** Constructor */ + PageControllerV2(); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); +}; + +#endif // PAGECONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/readingcomicscontroller.cpp b/YACReaderLibrary/server/controllers/v2/readingcomicscontroller_v2.cpp similarity index 62% rename from YACReaderLibrary/server/controllers/readingcomicscontroller.cpp rename to YACReaderLibrary/server/controllers/v2/readingcomicscontroller_v2.cpp index 78e22073..3fc46ace 100644 --- a/YACReaderLibrary/server/controllers/readingcomicscontroller.cpp +++ b/YACReaderLibrary/server/controllers/v2/readingcomicscontroller_v2.cpp @@ -1,29 +1,29 @@ -#include "readingcomicscontroller.h" +#include "readingcomicscontroller_v2.h" #include "db_helper.h" #include "comic_db.h" #include "yacreader_server_data_helper.h" -ReadingComicsController::ReadingComicsController() +ReadingComicsControllerV2::ReadingComicsControllerV2() { } -void ReadingComicsController::service(HttpRequest &request, HttpResponse &response) +void ReadingComicsControllerV2::service(HttpRequest &request, HttpResponse &response) { response.setHeader("Content-Type", "text/plain; charset=utf-8"); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); - int libraryId = pathElements.at(2).toInt(); + int libraryId = pathElements.at(3).toInt(); serviceContent(libraryId, response); response.write("",true); } -void ReadingComicsController::serviceContent(const int &library, HttpResponse &response) +void ReadingComicsControllerV2::serviceContent(const int &library, HttpResponse &response) { QList readingComics = DBHelper::getReading(library); diff --git a/YACReaderLibrary/server/controllers/readingcomicscontroller.h b/YACReaderLibrary/server/controllers/v2/readingcomicscontroller_v2.h similarity index 55% rename from YACReaderLibrary/server/controllers/readingcomicscontroller.h rename to YACReaderLibrary/server/controllers/v2/readingcomicscontroller_v2.h index da20aab5..68998ad6 100644 --- a/YACReaderLibrary/server/controllers/readingcomicscontroller.h +++ b/YACReaderLibrary/server/controllers/v2/readingcomicscontroller_v2.h @@ -1,15 +1,15 @@ -#ifndef READINGCOMICSCONTROLLER_H -#define READINGCOMICSCONTROLLER_H +#ifndef READINGCOMICSCONTROLLER_V2_H +#define READINGCOMICSCONTROLLER_V2_H #include "httprequest.h" #include "httpresponse.h" #include "httprequesthandler.h" -class ReadingComicsController : public HttpRequestHandler { +class ReadingComicsControllerV2 : public HttpRequestHandler { Q_OBJECT - Q_DISABLE_COPY(ReadingComicsController) + Q_DISABLE_COPY(ReadingComicsControllerV2) public: - ReadingComicsController(); + ReadingComicsControllerV2(); void service(HttpRequest& request, HttpResponse& response); diff --git a/YACReaderLibrary/server/controllers/readinglistcontentcontroller.cpp b/YACReaderLibrary/server/controllers/v2/readinglistcontentcontroller_v2.cpp similarity index 58% rename from YACReaderLibrary/server/controllers/readinglistcontentcontroller.cpp rename to YACReaderLibrary/server/controllers/v2/readinglistcontentcontroller_v2.cpp index 55ee65c3..ee4f8245 100644 --- a/YACReaderLibrary/server/controllers/readinglistcontentcontroller.cpp +++ b/YACReaderLibrary/server/controllers/v2/readinglistcontentcontroller_v2.cpp @@ -1,30 +1,30 @@ -#include "readinglistcontentcontroller.h" +#include "readinglistcontentcontroller_v2.h" #include "db_helper.h" #include "comic_db.h" #include "yacreader_server_data_helper.h" -ReadingListContentController::ReadingListContentController() +ReadingListContentControllerV2::ReadingListContentControllerV2() { } -void ReadingListContentController::service(HttpRequest &request, HttpResponse &response) +void ReadingListContentControllerV2::service(HttpRequest &request, HttpResponse &response) { response.setHeader("Content-Type", "text/plain; charset=utf-8"); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); - int libraryId = pathElements.at(2).toInt(); - qulonglong readingListId = pathElements.at(4).toULongLong(); + int libraryId = pathElements.at(3).toInt(); + qulonglong readingListId = pathElements.at(5).toULongLong(); serviceContent(libraryId, readingListId, response); response.write("",true); } -void ReadingListContentController::serviceContent(const int &library, const qulonglong &readingListId, HttpResponse &response) +void ReadingListContentControllerV2::serviceContent(const int &library, const qulonglong &readingListId, HttpResponse &response) { QList comics = DBHelper::getReadingListFullContent(library, readingListId); diff --git a/YACReaderLibrary/server/controllers/readinglistcontentcontroller.h b/YACReaderLibrary/server/controllers/v2/readinglistcontentcontroller_v2.h similarity index 57% rename from YACReaderLibrary/server/controllers/readinglistcontentcontroller.h rename to YACReaderLibrary/server/controllers/v2/readinglistcontentcontroller_v2.h index 88ec4a28..e2fc4282 100644 --- a/YACReaderLibrary/server/controllers/readinglistcontentcontroller.h +++ b/YACReaderLibrary/server/controllers/v2/readinglistcontentcontroller_v2.h @@ -1,15 +1,15 @@ -#ifndef READINGLISTCONTENTCONTROLLER_H -#define READINGLISTCONTENTCONTROLLER_H +#ifndef READINGLISTCONTENTCONTROLLER_V2_H +#define READINGLISTCONTENTCONTROLLER_V2_H #include "httprequest.h" #include "httpresponse.h" #include "httprequesthandler.h" -class ReadingListContentController : public HttpRequestHandler { +class ReadingListContentControllerV2 : public HttpRequestHandler { Q_OBJECT - Q_DISABLE_COPY(ReadingListContentController) + Q_DISABLE_COPY(ReadingListContentControllerV2) public: - ReadingListContentController(); + ReadingListContentControllerV2(); void service(HttpRequest& request, HttpResponse& response); diff --git a/YACReaderLibrary/server/controllers/readinglistscontroller.cpp b/YACReaderLibrary/server/controllers/v2/readinglistscontroller_v2.cpp similarity index 62% rename from YACReaderLibrary/server/controllers/readinglistscontroller.cpp rename to YACReaderLibrary/server/controllers/v2/readinglistscontroller_v2.cpp index 0c76b314..24142ff7 100644 --- a/YACReaderLibrary/server/controllers/readinglistscontroller.cpp +++ b/YACReaderLibrary/server/controllers/v2/readinglistscontroller_v2.cpp @@ -1,27 +1,27 @@ -#include "readinglistscontroller.h" +#include "readinglistscontroller_v2.h" #include "db_helper.h" #include "reading_list.h" -ReadingListsController::ReadingListsController() +ReadingListsControllerV2::ReadingListsControllerV2() { } -void ReadingListsController::service(HttpRequest &request, HttpResponse &response) +void ReadingListsControllerV2::service(HttpRequest &request, HttpResponse &response) { response.setHeader("Content-Type", "text/plain; charset=utf-8"); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); - int libraryId = pathElements.at(2).toInt(); + int libraryId = pathElements.at(3).toInt(); serviceContent(libraryId, response); response.write("",true); } -void ReadingListsController::serviceContent(const int library, HttpResponse &response) +void ReadingListsControllerV2::serviceContent(const int library, HttpResponse &response) { QList readingLists = DBHelper::getReadingLists(library); diff --git a/YACReaderLibrary/server/controllers/readinglistscontroller.h b/YACReaderLibrary/server/controllers/v2/readinglistscontroller_v2.h similarity index 55% rename from YACReaderLibrary/server/controllers/readinglistscontroller.h rename to YACReaderLibrary/server/controllers/v2/readinglistscontroller_v2.h index 5bb3ba27..23443e07 100644 --- a/YACReaderLibrary/server/controllers/readinglistscontroller.h +++ b/YACReaderLibrary/server/controllers/v2/readinglistscontroller_v2.h @@ -1,15 +1,15 @@ -#ifndef READINGLISTSCONTROLLER_H -#define READINGLISTSCONTROLLER_H +#ifndef READINGLISTSCONTROLLER_V2_H +#define READINGLISTSCONTROLLER_V2_H #include "httprequest.h" #include "httpresponse.h" #include "httprequesthandler.h" -class ReadingListsController : public HttpRequestHandler { +class ReadingListsControllerV2 : public HttpRequestHandler { Q_OBJECT - Q_DISABLE_COPY(ReadingListsController) + Q_DISABLE_COPY(ReadingListsControllerV2) public: - ReadingListsController(); + ReadingListsControllerV2(); void service(HttpRequest& request, HttpResponse& response); diff --git a/YACReaderLibrary/server/controllers/v2/synccontroller_v2.cpp b/YACReaderLibrary/server/controllers/v2/synccontroller_v2.cpp new file mode 100644 index 00000000..1f38a785 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/synccontroller_v2.cpp @@ -0,0 +1,64 @@ +#include "synccontroller_v2.h" + +#include "QsLog.h" +#include + +#include "comic_db.h" +#include "db_helper.h" + +SyncControllerV2::SyncControllerV2() +{ + +} + +void SyncControllerV2::service(HttpRequest &request, HttpResponse &response) +{ + QString postData = QString::fromUtf8(request.getBody()); + + QLOG_TRACE() << "POST DATA: " << postData; + + if(postData.length()>0) { + QList data = postData.split("\n"); + + qulonglong libraryId; + qulonglong comicId; + int currentPage; + int currentRating; + QString hash; + foreach(QString comicInfo, data) + { + QList comicInfoProgress = comicInfo.split("\t"); + + if(comicInfoProgress.length() == 4 || comicInfoProgress.length() == 5) + { + libraryId = comicInfoProgress.at(0).toULongLong(); + comicId = comicInfoProgress.at(1).toULongLong(); + hash = comicInfoProgress.at(2); + currentPage = comicInfoProgress.at(3).toInt(); + + ComicInfo info; + info.currentPage = currentPage; + info.hash = hash; //TODO remove the hash check and add UUIDs for libraries + info.id = comicId; + + //Client 2.1+ version + if(comicInfoProgress.length() > 4) + { + currentRating = comicInfoProgress.at(4).toInt(); + info.rating = currentRating; + } + + DBHelper::updateFromRemoteClient(libraryId,info); + } + } + } + else + { + response.setStatus(412,"No comic info received"); + response.write("",true); + return; + } + + response.write("OK",true); +} + diff --git a/YACReaderLibrary/server/controllers/v2/synccontroller_v2.h b/YACReaderLibrary/server/controllers/v2/synccontroller_v2.h new file mode 100644 index 00000000..4a22e511 --- /dev/null +++ b/YACReaderLibrary/server/controllers/v2/synccontroller_v2.h @@ -0,0 +1,21 @@ +#ifndef SYNCCONTROLLER_V2_H +#define SYNCCONTROLLER_V2_H + +#include + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class SyncControllerV2 : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(SyncControllerV2) +public: + /** Constructor */ + SyncControllerV2(); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); +}; + +#endif // SYNCCONTROLLER_H diff --git a/YACReaderLibrary/server/controllers/tagcontentcontroller.cpp b/YACReaderLibrary/server/controllers/v2/tagcontentcontroller_v2.cpp similarity index 59% rename from YACReaderLibrary/server/controllers/tagcontentcontroller.cpp rename to YACReaderLibrary/server/controllers/v2/tagcontentcontroller_v2.cpp index 6f077b8f..029bbb40 100644 --- a/YACReaderLibrary/server/controllers/tagcontentcontroller.cpp +++ b/YACReaderLibrary/server/controllers/v2/tagcontentcontroller_v2.cpp @@ -1,4 +1,4 @@ -#include "tagcontentcontroller.h" +#include "tagcontentcontroller_v2.h" #include "db_helper.h" #include "comic_db.h" @@ -7,26 +7,26 @@ #include -TagContentController::TagContentController() +TagContentControllerV2::TagContentControllerV2() { } -void TagContentController::service(HttpRequest &request, HttpResponse &response) +void TagContentControllerV2::service(HttpRequest &request, HttpResponse &response) { response.setHeader("Content-Type", "text/plain; charset=utf-8"); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); - int libraryId = pathElements.at(2).toInt(); - qulonglong tagId = pathElements.at(4).toULongLong(); + int libraryId = pathElements.at(3).toInt(); + qulonglong tagId = pathElements.at(5).toULongLong(); serviceContent(libraryId, tagId, response); response.write("",true); } -void TagContentController::serviceContent(const int &library, const qulonglong &tagId, HttpResponse &response) +void TagContentControllerV2::serviceContent(const int &library, const qulonglong &tagId, HttpResponse &response) { QList tagComics = DBHelper::getLabelComics(library, tagId); diff --git a/YACReaderLibrary/server/controllers/tagcontentcontroller.h b/YACReaderLibrary/server/controllers/v2/tagcontentcontroller_v2.h similarity index 62% rename from YACReaderLibrary/server/controllers/tagcontentcontroller.h rename to YACReaderLibrary/server/controllers/v2/tagcontentcontroller_v2.h index ab43646a..c47b350a 100644 --- a/YACReaderLibrary/server/controllers/tagcontentcontroller.h +++ b/YACReaderLibrary/server/controllers/v2/tagcontentcontroller_v2.h @@ -1,16 +1,16 @@ -#ifndef TAGCONTENTCONTROLLER_H -#define TAGCONTENTCONTROLLER_H +#ifndef TAGCONTENTCONTROLLER_V2_H +#define TAGCONTENTCONTROLLER_V2_H #include "httprequest.h" #include "httpresponse.h" #include "httprequesthandler.h" -class TagContentController : public HttpRequestHandler { +class TagContentControllerV2 : public HttpRequestHandler { Q_OBJECT - Q_DISABLE_COPY(TagContentController); + Q_DISABLE_COPY(TagContentControllerV2) public: /** Constructor */ - TagContentController(); + TagContentControllerV2(); /** Generates the response */ void service(HttpRequest& request, HttpResponse& response); diff --git a/YACReaderLibrary/server/controllers/tagscontroller.cpp b/YACReaderLibrary/server/controllers/v2/tagscontroller_v2.cpp similarity index 74% rename from YACReaderLibrary/server/controllers/tagscontroller.cpp rename to YACReaderLibrary/server/controllers/v2/tagscontroller_v2.cpp index eecaa819..6584bbad 100644 --- a/YACReaderLibrary/server/controllers/tagscontroller.cpp +++ b/YACReaderLibrary/server/controllers/v2/tagscontroller_v2.cpp @@ -1,4 +1,4 @@ -#include "tagscontroller.h" +#include "tagscontroller_v2.h" #include "db_helper.h" #include "yacreader_libraries.h" @@ -9,15 +9,15 @@ #include "QsLog.h" -TagsController::TagsController() {} +TagsControllerV2::TagsControllerV2() {} -void TagsController::service(HttpRequest& request, HttpResponse& response) +void TagsControllerV2::service(HttpRequest& request, HttpResponse& response) { response.setHeader("Content-Type", "text/plain; charset=utf-8"); QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); - int libraryId = pathElements.at(2).toInt(); + int libraryId = pathElements.at(3).toInt(); QList