Merged luisangelsm/yacreader into default

This commit is contained in:
Felix Kauselmann
2015-08-20 12:28:47 +02:00
parent 3a6b2d9b98
commit 23c33cef61
64 changed files with 1758 additions and 877 deletions

View File

@ -56,6 +56,9 @@ void ComicController::service(HttpRequest& request, HttpResponse& response)
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()));

View File

@ -10,6 +10,8 @@ ComicDownloadInfoController::ComicDownloadInfoController() {}
void ComicDownloadInfoController::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('/');

View File

@ -51,7 +51,7 @@ void FolderController::service(HttpRequest& request, HttpResponse& response)
}
if(folderId!=1)
t.setVariable("folder.name",folderName);
t.setVariable("folder.name",folderName);
else
t.setVariable("folder.name",libraryName);
QList<LibraryItem *> folderContent = DBHelper::getFolderSubfoldersFromLibrary(libraryId,folderId);
@ -155,75 +155,81 @@ void FolderController::service(HttpRequest& request, HttpResponse& response)
t.setVariable(QString("path%1.name").arg(i-1),DBHelper::getFolderName(libraryId,foldersPath[i].first));
}
t.loop("element",numFoldersAtCurrentPage);
int i = 0;
while(i<numFoldersAtCurrentPage)
{
LibraryItem * item = folderContent.at(i + (page*elementsPerPage));
t.setVariable(QString("element%1.name").arg(i),folderContent.at(i + (page*elementsPerPage))->name);
if(item->isDir())
{
t.setVariable(QString("element%1.class").arg(i),"folder");
QList<LibraryItem *> children = DBHelper::getFolderComicsFromLibrary(libraryId, item->id);
if(children.length()>0)
if(folderContent.length() > 0)
{
t.loop("element",numFoldersAtCurrentPage);
int i = 0;
while(i<numFoldersAtCurrentPage)
{
LibraryItem * item = folderContent.at(i + (page*elementsPerPage));
t.setVariable(QString("element%1.name").arg(i),folderContent.at(i + (page*elementsPerPage))->name);
if(item->isDir())
{
const ComicDB * comic = static_cast<ComicDB*>(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.class").arg(i),"folder");
QList<LibraryItem *> children = DBHelper::getFolderComicsFromLibrary(libraryId, item->id);
if(children.length()>0)
{
const ComicDB * comic = static_cast<ComicDB*>(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.browse").arg(i),QString("<a class =\"browseButton\" href=\"%1\">BROWSE</a>").arg(QString("/library/%1/folder/%2").arg(libraryId).arg(item->id)));
t.setVariable(QString("element%1.cover.browse").arg(i),QString("<a href=\"%1\">").arg(QString("/library/%1/folder/%2").arg(libraryId).arg(item->id)));
t.setVariable(QString("element%1.cover.browse.end").arg(i),"</a>");
//t.setVariable(QString("element%1.url").arg(i),"/library/"+libraryName+"/folder/"+QString("%1").arg(folderContent.at(i + (page*10))->id));
//t.setVariable(QString("element%1.downloadurl").arg(i),"/library/"+libraryName+"/folder/"+QString("%1/info").arg(folderContent.at(i + (page*elementsPerPage))->id));
t.setVariable(QString("element%1.download").arg(i),QString("<a onclick=\"this.innerHTML='IMPORTING';this.className='importedButton';\" class =\"importButton\" href=\"%1\">IMPORT</a>").arg("/library/"+QString::number(libraryId)+"/folder/"+QString("%1/info").arg(folderContent.at(i + (page*elementsPerPage))->id)));
t.setVariable(QString("element%1.read").arg(i),"");
t.setVariable(QString("element%1.size").arg(i),"");
t.setVariable(QString("element%1.pages").arg(i),"");
t.setVariable(QString("element%1.status").arg(i),"");
}
else
t.setVariable(QString("element%1.image.url").arg(i),"/images/f.png");
t.setVariable(QString("element%1.browse").arg(i),QString("<a class =\"browseButton\" href=\"%1\">BROWSE</a>").arg(QString("/library/%1/folder/%2").arg(libraryId).arg(item->id)));
t.setVariable(QString("element%1.cover.browse").arg(i),QString("<a href=\"%1\">").arg(QString("/library/%1/folder/%2").arg(libraryId).arg(item->id)));
t.setVariable(QString("element%1.cover.browse.end").arg(i),"</a>");
//t.setVariable(QString("element%1.url").arg(i),"/library/"+libraryName+"/folder/"+QString("%1").arg(folderContent.at(i + (page*10))->id));
//t.setVariable(QString("element%1.downloadurl").arg(i),"/library/"+libraryName+"/folder/"+QString("%1/info").arg(folderContent.at(i + (page*elementsPerPage))->id));
t.setVariable(QString("element%1.download").arg(i),QString("<a onclick=\"this.innerHTML='IMPORTING';this.className='importedButton';\" class =\"importButton\" href=\"%1\">IMPORT</a>").arg("/library/"+QString::number(libraryId)+"/folder/"+QString("%1/info").arg(folderContent.at(i + (page*elementsPerPage))->id)));
t.setVariable(QString("element%1.read").arg(i),"");
t.setVariable(QString("element%1.size").arg(i),"");
t.setVariable(QString("element%1.pages").arg(i),"");
t.setVariable(QString("element%1.status").arg(i),"");
}
else
{
t.setVariable(QString("element%1.class").arg(i),"cover");
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))
t.setVariable(QString("element%1.download").arg(i),QString("<a onclick=\"this.innerHTML='IMPORTING';this.className='importedButton';\" class =\"importButton\" href=\"%1\">IMPORT</a>").arg("/library/"+QString::number(libraryId)+"/comic/"+QString("%1").arg(comic->id)));
else if (session.isComicOnDevice(comic->info.hash))
{
t.setVariable(QString("element%1.class").arg(i),"cover");
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))
t.setVariable(QString("element%1.download").arg(i),QString("<a onclick=\"this.innerHTML='IMPORTING';this.className='importedButton';\" class =\"importButton\" href=\"%1\">IMPORT</a>").arg("/library/"+QString::number(libraryId)+"/comic/"+QString("%1").arg(comic->id)));
else if (session.isComicOnDevice(comic->info.hash))
t.setVariable(QString("element%1.download").arg(i),QString("<div class=\"importedButton\">IMPORTED</div>"));
else
t.setVariable(QString("element%1.download").arg(i),QString("<div class=\"importedButton\">IMPORTING</div>"));
//t.setVariable(QString("element%1.image.url").arg(i),"/images/f.png");
else
t.setVariable(QString("element%1.download").arg(i),QString("<div class=\"importedButton\">IMPORTING</div>"));
t.setVariable(QString("element%1.read").arg(i),QString("<a class =\"readButton\" href=\"%1\">READ</a>").arg("/library/"+QString::number(libraryId)+"/comic/"+QString("%1").arg(comic->id)+"/remote"));
//t.setVariable(QString("element%1.image.url").arg(i),"/images/f.png");
t.setVariable(QString("element%1.image.url").arg(i),QString("/library/%1/cover/%2.jpg").arg(libraryId).arg(comic->info.hash));
t.setVariable(QString("element%1.read").arg(i),QString("<a class =\"readButton\" href=\"%1\">READ</a>").arg("/library/"+QString::number(libraryId)+"/comic/"+QString("%1").arg(comic->id)+"/remote"));
t.setVariable(QString("element%1.size").arg(i),"<span class=\"comicSize\">" + QString::number(comic->info.hash.right(comic->info.hash.length()-40).toInt()/1024.0/1024.0,'f',2)+"Mb</span>");
if(comic->info.hasBeenOpened)
t.setVariable(QString("element%1.pages").arg(i),QString("<span class=\"numPages\">%1/%2 pages</span>").arg(comic->info.currentPage).arg(comic->info.numPages.toInt()));
else
t.setVariable(QString("element%1.pages").arg(i),QString("<span class=\"numPages\">%1 pages</span>").arg(comic->info.numPages.toInt()));
t.setVariable(QString("element%1.image.url").arg(i),QString("/library/%1/cover/%2.jpg").arg(libraryId).arg(comic->info.hash));
if(comic->info.read)
t.setVariable(QString("element%1.status").arg(i), QString("<div class=\"mark\"><img src=\"/images/readMark.png\" style = \"width: 15px\"/> </div>"));
else if(comic->info.hasBeenOpened)
t.setVariable(QString("element%1.status").arg(i), QString("<div class=\"mark\"><img src=\"/images/readingMark.png\" style = \"width: 15px\"/> </div>"));
else
t.setVariable(QString("element%1.status").arg(i),"");
t.setVariable(QString("element%1.size").arg(i),"<span class=\"comicSize\">" + QString::number(comic->info.hash.right(comic->info.hash.length()-40).toInt()/1024.0/1024.0,'f',2)+"Mb</span>");
if(comic->info.hasBeenOpened)
t.setVariable(QString("element%1.pages").arg(i),QString("<span class=\"numPages\">%1/%2 pages</span>").arg(comic->info.currentPage).arg(comic->info.numPages.toInt()));
else
t.setVariable(QString("element%1.pages").arg(i),QString("<span class=\"numPages\">%1 pages</span>").arg(comic->info.numPages.toInt()));
t.setVariable(QString("element%1.cover.browse").arg(i),"");
t.setVariable(QString("element%1.cover.browse.end").arg(i),"");
}
i++;
}
if(comic->info.read)
t.setVariable(QString("element%1.status").arg(i), QString("<div class=\"mark\"><img src=\"/images/readMark.png\" style = \"width: 15px\"/> </div>"));
else if(comic->info.hasBeenOpened)
t.setVariable(QString("element%1.status").arg(i), QString("<div class=\"mark\"><img src=\"/images/readingMark.png\" style = \"width: 15px\"/> </div>"));
else
t.setVariable(QString("element%1.status").arg(i),"");
t.setVariable(QString("element%1.cover.browse").arg(i),"");
t.setVariable(QString("element%1.cover.browse.end").arg(i),"");
}
i++;
}
} else
{
t.loop("element",0);
}
if(numPages > 1)
{

View File

@ -0,0 +1,55 @@
#include "synccontroller.h"
#include "QsLog.h"
#include <QUrl>
#include "comic_db.h"
#include "db_helper.h"
SyncController::SyncController()
{
}
void SyncController::service(HttpRequest &request, HttpResponse &response)
{
QString postData = QString::fromUtf8(request.getBody());
QLOG_INFO() << "POST DATA: " << postData;
if(postData.length()>0) {
QList<QString> data = postData.split("\n");
qulonglong libraryId;
qulonglong comicId;
int currentPage;
QString hash;
foreach(QString comicInfo, data)
{
QList<QString> comicInfoProgress = comicInfo.split("\t");
if(comicInfoProgress.length() == 4)
{
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;
DBHelper::updateFromRemoteClient(libraryId,info);
}
}
}
else
{
response.setStatus(412,"No comic info received");
response.writeText("",true);
return;
}
response.write("OK",true);
}

View File

@ -0,0 +1,21 @@
#ifndef SYNCCONTROLLER_H
#define SYNCCONTROLLER_H
#include <QObject>
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
class SyncController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(SyncController);
public:
/** Constructor */
SyncController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // SYNCCONTROLLER_H

View File

@ -99,66 +99,72 @@ void HttpConnectionHandler::disconnected() {
}
void HttpConnectionHandler::read() {
while (socket.bytesAvailable()) {
#ifdef SUPERVERBOSE
qDebug("HttpConnectionHandler (%p): read input",this);
qDebug("HttpConnectionHandler (%p): read input",this);
#endif
// Create new HttpRequest object if necessary
if (!currentRequest) {
currentRequest=new HttpRequest(settings);
}
// Collect data for the request object
while (socket.bytesAvailable() && currentRequest->getStatus()!=HttpRequest::complete && currentRequest->getStatus()!=HttpRequest::abort) {
currentRequest->readFromSocket(socket);
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();
readTimer.start(readTimeout);
}
}
// 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();
delete currentRequest;
currentRequest=0;
return;
}
// If the request is complete, let the request mapper dispatch it
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 {
requestHandler->service(*currentRequest, response);
}
catch (...) {
qCritical("HttpConnectionHandler (%p): An uncatched exception occured in the request handler",this);
// Create new HttpRequest object if necessary
if (!currentRequest) {
currentRequest=new HttpRequest(settings);
}
// Finalize sending the response if not already done
if (!response.hasSentLastPart()) {
response.write(QByteArray(),true);
// Collect data for the request object
while (socket.bytesAvailable() && currentRequest->getStatus()!=HttpRequest::complete && currentRequest->getStatus()!=HttpRequest::abort) {
currentRequest->readFromSocket(socket);
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();
readTimer.start(readTimeout);
}
}
socket.disconnectFromHost(); //CAMBIADO s<>lo se van a soportar conexiones NO persistentes
// 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();
delete currentRequest;
currentRequest=0;
return;
}
// Close the connection after delivering the response, if requested
//if (QString::compare(currentRequest->getHeader("Connection"),"close",Qt::CaseInsensitive)==0) {
// 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;
// If the request is complete, let the request mapper dispatch it
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 {
requestHandler->service(*currentRequest, response);
}
catch (...) {
qCritical("HttpConnectionHandler (%p): An uncatched exception occured in the request handler",this);
}
// Finalize sending the response if not already done
if (!response.hasSentLastPart()) {
response.write(QByteArray(),true);
}
//socket.disconnectFromHost(); //CAMBIADO s<>lo se van a soportar conexiones NO persistentes
// Close the connection after delivering the response, if requested
if (QString::compare(currentRequest->getHeader("Connection"),"close",Qt::CaseInsensitive)==0) {
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);
}
}
}

View File

@ -14,7 +14,7 @@ HttpSessionStore::HttpSessionStore(QSettings* settings, QObject* parent)
connect(&cleanupTimer,SIGNAL(timeout()),this,SLOT(timerEvent()));
cleanupTimer.start(60000);
cookieName=settings->value("cookieName","sessionid").toByteArray();
expirationTime=settings->value("expirationTime",86400000).toInt();
expirationTime=settings->value("expirationTime",864000000).toInt();
qDebug("HttpSessionStore: Sessions expire after %i milliseconds",expirationTime);
}
@ -90,7 +90,9 @@ void HttpSessionStore::timerEvent() {
++i;
HttpSession session=prev.value();
qint64 lastAccess=session.getLastAccess();
if (now-lastAccess>expirationTime) {
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
qDebug("HttpSessionStore: session %s expired",session.getId().data());
sessions.erase(prev);
}

View File

@ -6,6 +6,8 @@
#ifndef LOGGER_H
#define LOGGER_H
#include <QObject>
#include <QtGlobal>
#include <QThreadStorage>
#include <QHash>

View File

@ -21,6 +21,7 @@
#include "controllers/updatecomiccontroller.h"
#include "controllers/errorcontroller.h"
#include "controllers/comicdownloadinfocontroller.h"
#include "controllers/synccontroller.h"
#include "db_helper.h"
#include "yacreader_libraries.h"
@ -91,81 +92,86 @@ void RequestMapper::loadSession(HttpRequest & request, HttpResponse& response)
}
void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
QByteArray path=request.getPath();
qDebug("RequestMapper: path=%s",path.data());
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 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 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 sync("/sync");
QRegExp library("/library/([0-9]+)/.+"); //permite verificar que la biblioteca solicitada existe
path = QUrl::fromPercentEncoding(path).toUtf8();
loadSession(request, response);
if(!sync.exactMatch(path)) //no session is needed for syncback info, until security will be added
loadSession(request, response);
//primera petición, se ha hecho un post, se sirven las bibliotecas si la seguridad mediante login no está habilitada
//primera petición, se ha hecho un post, se sirven las bibliotecas si la seguridad mediante login no está habilitada
if(path == "/") //Don't send data to the server using '/' !!!!
{
LibrariesController().service(request, response);
}
else
{
//se comprueba que la sesión sea la correcta con el fin de evitar accesos no autorizados
HttpSession session=Static::sessionStore->getSession(request,response,false);
if(!session.isNull() && session.contains("ySession"))
LibrariesController().service(request, response);
}
else
{
if(sync.exactMatch(path))
SyncController().service(request, response);
else
{
if(library.indexIn(path)!=-1 && DBHelper::getLibraries().contains(library.cap(1).toInt()) )
{
//listar el contenido del folder
if(folder.exactMatch(path))
{
FolderController().service(request, response);
}
else if (folderInfo.exactMatch(path))
{
FolderInfoController().service(request, response);
}
else if(cover.exactMatch(path))
{
CoverController().service(request, response);
}
else if(comicDownloadInfo.exactMatch(path))
//se comprueba que la sesión sea la correcta con el fin de evitar accesos no autorizados
HttpSession session=Static::sessionStore->getSession(request,response,false);
if(!session.isNull() && session.contains("ySession"))
{
if(library.indexIn(path)!=-1 && DBHelper::getLibraries().contains(library.cap(1).toInt()) )
{
ComicDownloadInfoController().service(request, response);
//listar el contenido del folder
if(folder.exactMatch(path))
{
FolderController().service(request, response);
}
else if (folderInfo.exactMatch(path))
{
FolderInfoController().service(request, response);
}
else if(cover.exactMatch(path))
{
CoverController().service(request, response);
}
else if(comicDownloadInfo.exactMatch(path))
{
ComicDownloadInfoController().service(request, response);
}
else if(comicFullInfo.exactMatch(path) || comicOpen.exactMatch(path))//start download or start remote reading
{
ComicController().service(request, response);
}
else if(comicPage.exactMatch(path) || comicPageRemote.exactMatch(path))
{
PageController().service(request,response);
}
else if(comicUpdate.exactMatch(path))
{
UpdateComicController().service(request, response);
}
}
else if(comicFullInfo.exactMatch(path) || comicOpen.exactMatch(path))//start download or start remote reading
{
ComicController().service(request, response);
}
else if(comicPage.exactMatch(path) || comicPageRemote.exactMatch(path))
{
PageController().service(request,response);
}
else if(comicUpdate.exactMatch(path))
else
{
UpdateComicController().service(request, response);
//response.writeText(library.cap(1));
Static::staticFileController->service(request, response);
}
}
else
{
//response.writeText(library.cap(1));
Static::staticFileController->service(request, response);
}
}
else //acceso no autorizado, redirección
{
ErrorController(300).service(request,response);
}
}
else //acceso no autorizado, redirección
{
ErrorController(300).service(request,response);
}
}
}
}

View File

@ -5,31 +5,33 @@ HEADERS += \
$$PWD/static.h \
$$PWD/startup.h \
$$PWD/requestmapper.h \
$$PWD/controllers/comiccontroller.h \
$$PWD/controllers/errorcontroller.h \
$$PWD/controllers/foldercontroller.h \
$$PWD/controllers/folderinfocontroller.h \
$$PWD/controllers/librariescontroller.h \
$$PWD/controllers/pagecontroller.h \
$$PWD/controllers/sessionmanager.h \
$$PWD/controllers/covercontroller.h \
server/controllers/updatecomiccontroller.h \
server/controllers/comicdownloadinfocontroller.h
$$PWD/controllers/comiccontroller.h \
$$PWD/controllers/errorcontroller.h \
$$PWD/controllers/foldercontroller.h \
$$PWD/controllers/folderinfocontroller.h \
$$PWD/controllers/librariescontroller.h \
$$PWD/controllers/pagecontroller.h \
$$PWD/controllers/sessionmanager.h \
$$PWD/controllers/covercontroller.h \
$$PWD/controllers/updatecomiccontroller.h \
$$PWD/controllers/comicdownloadinfocontroller.h \
$$PWD/controllers/synccontroller.h
SOURCES += \
$$PWD/static.cpp \
$$PWD/startup.cpp \
$$PWD/requestmapper.cpp \
$$PWD/controllers/comiccontroller.cpp \
$$PWD/controllers/errorcontroller.cpp \
$$PWD/controllers/foldercontroller.cpp \
$$PWD/controllers/folderinfocontroller.cpp \
$$PWD/controllers/librariescontroller.cpp \
$$PWD/controllers/pagecontroller.cpp \
$$PWD/controllers/sessionmanager.cpp \
$$PWD/controllers/covercontroller.cpp \
server/controllers/updatecomiccontroller.cpp \
server/controllers/comicdownloadinfocontroller.cpp
$$PWD/controllers/comiccontroller.cpp \
$$PWD/controllers/errorcontroller.cpp \
$$PWD/controllers/foldercontroller.cpp \
$$PWD/controllers/folderinfocontroller.cpp \
$$PWD/controllers/librariescontroller.cpp \
$$PWD/controllers/pagecontroller.cpp \
$$PWD/controllers/sessionmanager.cpp \
$$PWD/controllers/covercontroller.cpp \
$$PWD/controllers/updatecomiccontroller.cpp \
$$PWD/controllers/comicdownloadinfocontroller.cpp \
$$PWD/controllers/synccontroller.cpp
include(lib/bfLogging/bfLogging.pri)
include(lib/bfHttpServer/bfHttpServer.pri)