瀏覽代碼

初始化

zhuizhu 1 年之前
當前提交
c7c1dfa699
共有 100 個文件被更改,包括 8498 次插入0 次删除
  1. 4 0
      .gitignore
  2. 54 0
      CPPWebFramework/CMakeLists.txt
  3. 132 0
      CPPWebFramework/CPPWebFramework.pri
  4. 147 0
      CPPWebFramework/CPPWebFramework.pro
  5. 142 0
      CPPWebFramework/UnitTests.pro
  6. 136 0
      CPPWebFramework/cwf/configuration.cpp
  7. 181 0
      CPPWebFramework/cwf/configuration.h
  8. 268 0
      CPPWebFramework/cwf/constants.h
  9. 53 0
      CPPWebFramework/cwf/controller.cpp
  10. 80 0
      CPPWebFramework/cwf/controller.h
  11. 125 0
      CPPWebFramework/cwf/cppwebapplication.cpp
  12. 99 0
      CPPWebFramework/cwf/cppwebapplication.h
  13. 32 0
      CPPWebFramework/cwf/cppwebcontroller.cpp
  14. 33 0
      CPPWebFramework/cwf/cppwebcontroller.h
  15. 23 0
      CPPWebFramework/cwf/cppwebframework_global.h
  16. 22 0
      CPPWebFramework/cwf/cppwebframework_global.h.2
  17. 73 0
      CPPWebFramework/cwf/cppwebserver.cpp
  18. 81 0
      CPPWebFramework/cwf/cppwebserver.h
  19. 335 0
      CPPWebFramework/cwf/cstlcompiler.cpp
  20. 91 0
      CPPWebFramework/cwf/cstlcompiler.h
  21. 234 0
      CPPWebFramework/cwf/cstlcompilerattributes.cpp
  22. 34 0
      CPPWebFramework/cwf/cstlcompilerattributes.h
  23. 43 0
      CPPWebFramework/cwf/cstlcompilerfor.cpp
  24. 32 0
      CPPWebFramework/cwf/cstlcompilerfor.h
  25. 59 0
      CPPWebFramework/cwf/cstlcompilerif.cpp
  26. 44 0
      CPPWebFramework/cwf/cstlcompilerif.h
  27. 42 0
      CPPWebFramework/cwf/cstlcompilerimport.cpp
  28. 34 0
      CPPWebFramework/cwf/cstlcompilerimport.h
  29. 57 0
      CPPWebFramework/cwf/cstlcompilerobject.h
  30. 86 0
      CPPWebFramework/cwf/filemanager.cpp
  31. 36 0
      CPPWebFramework/cwf/filemanager.h
  32. 10 0
      CPPWebFramework/cwf/filter.cpp
  33. 96 0
      CPPWebFramework/cwf/filter.h
  34. 148 0
      CPPWebFramework/cwf/filterchain.cpp
  35. 62 0
      CPPWebFramework/cwf/filterchain.h
  36. 242 0
      CPPWebFramework/cwf/httpparser.cpp
  37. 168 0
      CPPWebFramework/cwf/httpparser.h
  38. 172 0
      CPPWebFramework/cwf/httpreadrequest.cpp
  39. 77 0
      CPPWebFramework/cwf/httpreadrequest.h
  40. 92 0
      CPPWebFramework/cwf/metaclassparser.cpp
  41. 67 0
      CPPWebFramework/cwf/metaclassparser.h
  42. 207 0
      CPPWebFramework/cwf/model.cpp
  43. 264 0
      CPPWebFramework/cwf/model.h
  44. 446 0
      CPPWebFramework/cwf/modelbasicoperation.cpp
  45. 175 0
      CPPWebFramework/cwf/modelbasicoperation.h
  46. 27 0
      CPPWebFramework/cwf/properties.cpp
  47. 33 0
      CPPWebFramework/cwf/properties.h
  48. 25 0
      CPPWebFramework/cwf/qlistobject.cpp
  49. 85 0
      CPPWebFramework/cwf/qlistobject.h
  50. 347 0
      CPPWebFramework/cwf/qmapthreadsafety.h
  51. 163 0
      CPPWebFramework/cwf/request.cpp
  52. 346 0
      CPPWebFramework/cwf/request.h
  53. 20 0
      CPPWebFramework/cwf/requestdispatcher.cpp
  54. 41 0
      CPPWebFramework/cwf/requestdispatcher.h
  55. 186 0
      CPPWebFramework/cwf/response.cpp
  56. 115 0
      CPPWebFramework/cwf/response.h
  57. 76 0
      CPPWebFramework/cwf/session.cpp
  58. 116 0
      CPPWebFramework/cwf/session.h
  59. 212 0
      CPPWebFramework/cwf/sqldatabasestorage.h
  60. 152 0
      CPPWebFramework/cwf/sqlquery.cpp
  61. 86 0
      CPPWebFramework/cwf/sqlquery.h
  62. 208 0
      CPPWebFramework/cwf/sqlquerymanager.cpp
  63. 92 0
      CPPWebFramework/cwf/sqlquerymanager.h
  64. 83 0
      CPPWebFramework/cwf/sslloader.cpp
  65. 30 0
      CPPWebFramework/cwf/sslloader.h
  66. 44 0
      CPPWebFramework/cwf/urlencoder.cpp
  67. 51 0
      CPPWebFramework/cwf/urlencoder.h
  68. 104 0
      CPPWebFramework/cwf/variant.h
  69. 61 0
      CPPWebFramework/main.cpp
  70. 20 0
      CPPWebFramework/server/config/CPPWeb.ini
  71. 17 0
      CPPWebFramework/server/config/cppwebserverpages/403.view
  72. 17 0
      CPPWebFramework/server/config/cppwebserverpages/404.view
  73. 31 0
      CPPWebFramework/server/config/cppwebserverpages/index.view
  74. 119 0
      CPPWebFramework/server/config/cppwebserverpages/resources/css/cppweb.css
  75. 二進制
      CPPWebFramework/server/config/cppwebserverpages/resources/images/favicon.ico
  76. 二進制
      CPPWebFramework/server/config/cppwebserverpages/resources/images/logo.png
  77. 0 0
      CPPWebFramework/server/config/log/CPPWebServer.log
  78. 34 0
      CPPWebFramework/server/config/ssl/cert.pem
  79. 54 0
      CPPWebFramework/server/config/ssl/key.pem
  80. 51 0
      CPPWebFramework/tests/tst_configuration.cpp
  81. 16 0
      CPPWebFramework/tests/tst_configuration.h
  82. 7 0
      CPPWebFramework/tests/tst_cppwebapplication.cpp
  83. 14 0
      CPPWebFramework/tests/tst_cppwebapplication.h
  84. 6 0
      CPPWebFramework/tests/tst_cppwebcontroller.cpp
  85. 14 0
      CPPWebFramework/tests/tst_cppwebcontroller.h
  86. 6 0
      CPPWebFramework/tests/tst_cppwebserver.cpp
  87. 14 0
      CPPWebFramework/tests/tst_cppwebserver.h
  88. 135 0
      CPPWebFramework/tests/tst_cstlcompiler.cpp
  89. 33 0
      CPPWebFramework/tests/tst_cstlcompiler.h
  90. 6 0
      CPPWebFramework/tests/tst_cstlcompilerattributes.cpp
  91. 14 0
      CPPWebFramework/tests/tst_cstlcompilerattributes.h
  92. 6 0
      CPPWebFramework/tests/tst_cstlcompilerfor.cpp
  93. 14 0
      CPPWebFramework/tests/tst_cstlcompilerfor.h
  94. 47 0
      CPPWebFramework/tests/tst_cstlcompilerif.cpp
  95. 16 0
      CPPWebFramework/tests/tst_cstlcompilerif.h
  96. 6 0
      CPPWebFramework/tests/tst_cstlcompilerimport.cpp
  97. 14 0
      CPPWebFramework/tests/tst_cstlcompilerimport.h
  98. 6 0
      CPPWebFramework/tests/tst_cstlcompilerobject.cpp
  99. 14 0
      CPPWebFramework/tests/tst_cstlcompilerobject.h
  100. 26 0
      CPPWebFramework/tests/tst_cstlcompilerout.cpp

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+/bin64
+/bin64out
+/*.user
+/build

+ 54 - 0
CPPWebFramework/CMakeLists.txt

@@ -0,0 +1,54 @@
+
+find_package(Qt5Core REQUIRED)
+find_package(Qt5Network REQUIRED)
+find_package(Qt5Sql REQUIRED)
+find_package(Qt5Xml REQUIRED)
+
+include_directories("cwf/")
+
+set(CPP_WebFrameWork_SRCS
+    cwf/configuration.cpp
+    cwf/cppwebapplication.cpp
+    cwf/cppwebserver.cpp
+    cwf/cstlcompiler.cpp
+    cwf/cstlcompilerattributes.cpp
+    cwf/cstlcompilerfor.cpp
+    cwf/cstlcompilerif.cpp
+    cwf/cstlcompilerimport.cpp
+    cwf/filemanager.cpp
+    cwf/filterchain.cpp
+    cwf/filter.cpp
+    cwf/httpparser.cpp
+    cwf/httpreadrequest.cpp
+    cwf/metaclassparser.cpp
+    cwf/properties.cpp
+    cwf/qlistobject.cpp
+    cwf/requestdispatcher.cpp
+    cwf/urlencoder.cpp
+    cwf/sqlquery.cpp
+    cwf/response.cpp
+    cwf/request.cpp
+    cwf/controller.cpp
+    cwf/session.cpp
+    cwf/cppwebcontroller.cpp
+    cwf/sslloader.cpp
+    cwf/model.cpp
+    cwf/modelbasicoperation.cpp
+    cwf/sqlquerymanager.cpp
+    cwf/cstlcompilerobject.h
+    cwf/variant.h
+
+    )
+
+add_library(CPPWebFramework SHARED ${CPP_WebFrameWork_SRCS} )
+
+target_link_libraries(
+    CPPWebFramework
+    Qt5::Core
+    Qt5::Network
+    Qt5::Xml
+    Qt5::Sql
+
+)
+INSTALL(TARGETS CPPWebFramework LIBRARY DESTINATION lib COMPONENT CPPWebFramework)
+add_definitions(-DCPPWEBFRAMEWORK_LIBRARY)

+ 132 - 0
CPPWebFramework/CPPWebFramework.pri

@@ -0,0 +1,132 @@
+QT       += network xml sql
+
+DEFINES += CPPWEBFRAMEWORK_STATIC
+
+DEFINES += QT_DEPRECATED_WARNINGS
+
+SOURCES += \
+    $$PWD/cwf/configuration.cpp \
+    $$PWD/cwf/cppwebapplication.cpp \
+    $$PWD/cwf/cppwebserver.cpp \
+    $$PWD/cwf/cstlcompiler.cpp \
+    $$PWD/cwf/cstlcompilerattributes.cpp \
+    $$PWD/cwf/cstlcompilerfor.cpp \
+    $$PWD/cwf/cstlcompilerif.cpp \
+    $$PWD/cwf/cstlcompilerimport.cpp \
+    $$PWD/cwf/filemanager.cpp \
+    $$PWD/cwf/filterchain.cpp \
+    $$PWD/cwf/httpparser.cpp \
+    $$PWD/cwf/httpreadrequest.cpp \
+    $$PWD/cwf/metaclassparser.cpp \
+    $$PWD/cwf/properties.cpp \
+    $$PWD/cwf/qlistobject.cpp \
+    $$PWD/cwf/requestdispatcher.cpp \
+    $$PWD/cwf/urlencoder.cpp \
+    $$PWD/cwf/sqlquery.cpp \
+    $$PWD/cwf/response.cpp \
+    $$PWD/cwf/request.cpp \
+    $$PWD/cwf/controller.cpp \
+    $$PWD/cwf/session.cpp \
+    $$PWD/cwf/cppwebcontroller.cpp \
+    $$PWD/cwf/sslloader.cpp \
+    $$PWD/cwf/filter.cpp \
+    $$PWD/cwf/model.cpp \
+    $$PWD/cwf/modelbasicoperation.cpp \
+    $$PWD/cwf/sqlquerymanager.cpp
+
+HEADERS += \
+     $$PWD/cwf/cppwebframework_global.h \
+     $$PWD/cwf/configuration.h \
+     $$PWD/cwf/constants.h \
+     $$PWD/cwf/cppwebapplication.h \
+     $$PWD/cwf/cppwebframework_global.h \
+     $$PWD/cwf/cppwebserver.h \
+     $$PWD/cwf/cstlcompiler.h \
+     $$PWD/cwf/cstlcompilerattributes.h \
+     $$PWD/cwf/cstlcompilerfor.h \
+     $$PWD/cwf/cstlcompilerif.h \
+     $$PWD/cwf/cstlcompilerimport.h \
+     $$PWD/cwf/cstlcompilerobject.h \
+     $$PWD/cwf/filemanager.h \
+     $$PWD/cwf/filter.h \
+     $$PWD/cwf/filterchain.h \
+     $$PWD/cwf/httpparser.h \
+     $$PWD/cwf/httpreadrequest.h \
+     $$PWD/cwf/metaclassparser.h \
+     $$PWD/cwf/properties.h \
+     $$PWD/cwf/qlistobject.h \
+     $$PWD/cwf/qmapthreadsafety.h \
+     $$PWD/cwf/requestdispatcher.h \
+     $$PWD/cwf/urlencoder.h \
+     $$PWD/cwf/variant.h \
+     $$PWD/cwf/sqldatabasestorage.h \
+     $$PWD/cwf/sqlquery.h \
+     $$PWD/cwf/controller.h \
+     $$PWD/cwf/request.h \
+     $$PWD/cwf/response.h \
+     $$PWD/cwf/session.h \
+     $$PWD/cwf/cppwebcontroller.h \
+     $$PWD/cwf/sslloader.h \
+     $$PWD/cwf/model.h \
+     $$PWD/cwf/modelbasicoperation.h \
+     $$PWD/cwf/sqlquerymanager.h
+
+DISTFILES += \
+     $$PWD/server/config/ssl/my.key \
+     $$PWD/server/config/ssl/my.cert \
+     $$PWD/server/config/cppwebserverpages/403.view \
+     $$PWD/server/config/cppwebserverpages/404.view \
+     $$PWD/server/config/cppwebserverpages/index.view \
+     $$PWD/server/config/cppwebserverpages/resources/images/logo.jpg \
+     $$PWD/server/config/cppwebserverpages/resources/images/favicon.ico \
+     $$PWD/server/config/cppwebserverpages/resources/css/cppweb.css \
+     $$PWD/server/config/CPPWeb.ini \
+     $$PWD/server/config/log/CPPWebServer.log
+
+
+INCLUDEPATH += $$PWD
+
+# unix {
+#     headers.path   = /usr/lib/cwf
+#     headers.files += $$HEADERS
+#     target.path    = /usr/lib
+#     config.path    = /usr/lib/server
+#     config.files   = server/*
+# }
+
+# macx {
+#     headers.path   = /usr/local/lib/cwf
+#     headers.files += $$HEADERS
+#     target.path    = /usr/local/lib
+#     config.path    = /usr/local/lib/server
+#     config.files   = server/*
+# }
+
+# win32 {
+#     headers.path   = C:/cwf
+#     headers.files += $$HEADERS
+#     target.path    = C:/cwf
+#     config.path    = C:/cwf/server
+#     config.files   = server/*
+# }
+
+# CONFIG += debug_and_release
+# CONFIG += build_all
+
+# CONFIG(debug, debug|release) {
+#     TARGET = CPPWebFrameworkd
+# } else {
+#     TARGET = CPPWebFramework
+#     QMAKE_CXXFLAGS_RELEASE -= -O1
+#     QMAKE_CXXFLAGS_RELEASE -= -O2
+#     QMAKE_CXXFLAGS_RELEASE += -O3
+# }
+
+# INSTALLS += target
+# INSTALLS += headers
+# INSTALLS += config
+
+QMAKE_CXXFLAGS += -std=c++11
+
+#Strongly recommended
+#LIBS += -ljemalloc

+ 147 - 0
CPPWebFramework/CPPWebFramework.pro

@@ -0,0 +1,147 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2017-06-05T23:53:17
+#
+#-------------------------------------------------
+
+QT       += network xml sql
+QT       -= gui
+
+TEMPLATE = lib
+
+DEFINES += CPPWEBFRAMEWORK_LIBRARY
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which as been marked as deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += \
+    cwf/configuration.cpp \
+    cwf/cppwebapplication.cpp \
+    cwf/cppwebserver.cpp \
+    cwf/cstlcompiler.cpp \
+    cwf/cstlcompilerattributes.cpp \
+    cwf/cstlcompilerfor.cpp \
+    cwf/cstlcompilerif.cpp \
+    cwf/cstlcompilerimport.cpp \
+    cwf/filemanager.cpp \
+    cwf/filterchain.cpp \
+    cwf/httpparser.cpp \
+    cwf/httpreadrequest.cpp \
+    cwf/metaclassparser.cpp \
+    cwf/properties.cpp \
+    cwf/qlistobject.cpp \
+    cwf/requestdispatcher.cpp \
+    cwf/urlencoder.cpp \
+    cwf/sqlquery.cpp \
+    cwf/response.cpp \
+    cwf/request.cpp \
+    cwf/controller.cpp \
+    cwf/session.cpp \
+    cwf/cppwebcontroller.cpp \
+    cwf/sslloader.cpp \
+    cwf/filter.cpp \
+    cwf/model.cpp \
+    cwf/modelbasicoperation.cpp \
+    cwf/sqlquerymanager.cpp
+
+HEADERS += \
+    cwf/cppwebframework_global.h \
+    cwf/configuration.h \
+    cwf/constants.h \
+    cwf/cppwebapplication.h \
+    cwf/cppwebframework_global.h \
+    cwf/cppwebserver.h \
+    cwf/cstlcompiler.h \
+    cwf/cstlcompilerattributes.h \
+    cwf/cstlcompilerfor.h \
+    cwf/cstlcompilerif.h \
+    cwf/cstlcompilerimport.h \
+    cwf/cstlcompilerobject.h \
+    cwf/filemanager.h \
+    cwf/filter.h \
+    cwf/filterchain.h \
+    cwf/httpparser.h \
+    cwf/httpreadrequest.h \
+    cwf/metaclassparser.h \
+    cwf/properties.h \
+    cwf/qlistobject.h \
+    cwf/qmapthreadsafety.h \
+    cwf/requestdispatcher.h \
+    cwf/urlencoder.h \
+    cwf/variant.h \
+    cwf/sqldatabasestorage.h \
+    cwf/sqlquery.h \
+    cwf/controller.h \
+    cwf/request.h \
+    cwf/response.h \
+    cwf/session.h \
+    cwf/cppwebcontroller.h \
+    cwf/sslloader.h \
+    cwf/model.h \
+    cwf/modelbasicoperation.h \
+    cwf/sqlquerymanager.h
+
+DISTFILES += \
+    server/config/ssl/my.key \
+    server/config/ssl/my.cert \
+    server/config/cppwebserverpages/403.view \
+    server/config/cppwebserverpages/404.view \
+    server/config/cppwebserverpages/index.view \
+    server/config/cppwebserverpages/resources/images/logo.jpg \
+    server/config/cppwebserverpages/resources/images/favicon.ico \
+    server/config/cppwebserverpages/resources/css/cppweb.css \
+    server/config/CPPWeb.ini \
+    server/config/log/CPPWebServer.log
+
+unix {
+    headers.path   = /usr/lib/cwf
+    headers.files += $$HEADERS
+    target.path    = /usr/lib
+    config.path    = /usr/lib/server
+    config.files   = server/*
+}
+
+macx {
+    headers.path   = /usr/local/lib/cwf
+    headers.files += $$HEADERS
+    target.path    = /usr/local/lib
+    config.path    = /usr/local/lib/server
+    config.files   = server/*
+}
+
+win32 {
+    headers.path   = C:/cwf
+    headers.files += $$HEADERS
+    target.path    = C:/cwf
+    config.path    = C:/cwf/server
+    config.files   = server/*
+}
+
+CONFIG += debug_and_release
+CONFIG += build_all
+
+CONFIG(debug, debug|release) {
+    TARGET = CPPWebFrameworkd
+} else {
+    TARGET = CPPWebFramework
+    QMAKE_CXXFLAGS_RELEASE -= -O1
+    QMAKE_CXXFLAGS_RELEASE -= -O2
+    QMAKE_CXXFLAGS_RELEASE += -O3
+}
+
+INSTALLS += target
+INSTALLS += headers
+INSTALLS += config
+
+QMAKE_CXXFLAGS += -std=c++11
+
+#Strongly recommended
+#LIBS += -ljemalloc

+ 142 - 0
CPPWebFramework/UnitTests.pro

@@ -0,0 +1,142 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2017-01-28T22:39:30
+#
+#-------------------------------------------------
+
+QT       += network testlib xml
+
+QT       -= gui
+
+TARGET = UnitTests
+CONFIG   += console
+CONFIG   -= app_bundle
+
+TEMPLATE = app
+CONFIG  += c++11
+
+# The following define makes your compiler emit warnings if you use
+# any feature of Qt which as been marked as deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
+
+
+SOURCES += \
+    main.cpp \
+    tests/tst_configuration.cpp \
+    cwf/configuration.cpp \
+    cwf/cppwebapplication.cpp \
+    cwf/cppwebserver.cpp \
+    cwf/cppwebcontroller.cpp \
+    cwf/cstlcompiler.cpp \
+    cwf/cstlcompilerattributes.cpp \
+    cwf/cstlcompilerfor.cpp \
+    cwf/cstlcompilerif.cpp \
+    cwf/cstlcompilerimport.cpp \
+    cwf/filemanager.cpp \
+    cwf/filter.cpp \
+    cwf/filterchain.cpp \    
+    cwf/httpparser.cpp \
+    cwf/httpreadrequest.cpp \
+    cwf/controller.cpp \
+    cwf/request.cpp \
+    cwf/response.cpp \
+    cwf/session.cpp \
+    cwf/metaclassparser.cpp \
+    cwf/properties.cpp \
+    cwf/qlistobject.cpp \
+    cwf/requestdispatcher.cpp \    
+    cwf/urlencoder.cpp \
+    tests/tst_cppwebapplication.cpp \
+    tests/tst_cppwebserver.cpp \
+    tests/tst_cppwebcontroller.cpp \
+    tests/tst_cstlcompiler.cpp \
+    tests/tst_cstlcompilerattributes.cpp \
+    tests/tst_cstlcompilerfor.cpp \
+    tests/tst_cstlcompilerif.cpp \
+    tests/tst_cstlcompilerimport.cpp \
+    tests/tst_cstlcompilerobject.cpp \
+    tests/tst_filemanager.cpp \
+    tests/tst_filter.cpp \
+    tests/tst_filterchain.cpp \
+    tests/tst_httpparser.cpp \
+    tests/tst_httpreadrequest.cpp \
+    tests/tst_response.cpp \
+    tests/tst_session.cpp \
+    tests/tst_metaclassparser.cpp \
+    tests/tst_properties.cpp \
+    tests/tst_qlistobject.cpp \
+    tests/tst_qmapthreadsafety.cpp \
+    tests/tst_requestdispatcher.cpp \
+    tests/tst_urlencoder.cpp \
+    tests/tst_request.cpp \
+    tests/tst_cstlcompilerout.cpp \
+    cwf/sslloader.cpp
+
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
+
+HEADERS += \
+    tests/tst_configuration.h \
+    cwf/configuration.h \
+    cwf/cppwebapplication.h \
+    cwf/cppwebserver.h \
+    cwf/cppwebcontroller.h \
+    cwf/cstlcompiler.h \
+    cwf/cstlcompilerattributes.h \
+    cwf/cstlcompilerfor.h \
+    cwf/cstlcompilerif.h \
+    cwf/cstlcompilerimport.h \
+    cwf/cstlcompilerobject.h \
+    cwf/filemanager.h \
+    cwf/filter.h \
+    cwf/filterchain.h \
+    cwf/httpparser.h \
+    cwf/httpreadrequest.h \
+    cwf/controller.h \
+    cwf/request.h \
+    cwf/response.h \
+    cwf/session.h \
+    cwf/metaclassparser.h \
+    cwf/properties.h \
+    cwf/qlistobject.h \
+    cwf/qmapthreadsafety.h \
+    cwf/requestdispatcher.h \    
+    cwf/urlencoder.h \
+    tests/tst_cppwebapplication.h \
+    tests/tst_cppwebserver.h \
+    tests/tst_cstlcompiler.h \
+    tests/tst_cstlcompilerattributes.h \
+    tests/tst_cstlcompilerfor.h \
+    tests/tst_cstlcompilerif.h \
+    tests/tst_cstlcompilerimport.h \
+    tests/tst_cstlcompilerobject.h \
+    tests/tst_filemanager.h \
+    tests/tst_filter.h \
+    tests/tst_filterchain.h \
+    tests/tst_httpparser.h \
+    tests/tst_httpreadrequest.h \
+    tests/tst_response.h \
+    tests/tst_session.h \
+    tests/tst_metaclassparser.h \
+    tests/tst_properties.h \
+    tests/tst_qlistobject.h \
+    tests/tst_qmapthreadsafety.h \
+    tests/tst_requestdispatcher.h \
+    tests/tst_urlencoder.h \
+    tests/tst_request.h \
+    cwf/constants.h \
+    cwf/cppwebframework_global.h \
+    tests/tst_cppwebcontroller.h \
+    cwf/variant.h \
+    tests/tst_cstlcompilerout.h \
+    cwf/sslloader.h
+
+QMAKE_CXXFLAGS += -g -Wall -fprofile-arcs -ftest-coverage -O0
+QMAKE_LFLAGS   += -g -Wall -fprofile-arcs -ftest-coverage -O0
+LIBS           += -lgcov

+ 136 - 0
CPPWebFramework/cwf/configuration.cpp

@@ -0,0 +1,136 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "configuration.h"
+#include "filemanager.h"
+
+#include <QDir>
+
+CWF_BEGIN_NAMESPACE
+
+Configuration::Configuration(const QString &serverFilesPath)
+    : path(serverFilesPath)
+{
+    FileManager::removeLastBar(path);
+    const QString iniFile("/config/CPPWeb.ini");
+    if (QFile(path + iniFile).exists()) {
+        valid = true;
+        configure();
+    } else {
+        path = QDir::currentPath() + "/server";
+        if (QFile(path + iniFile).exists()) {
+            valid = true;
+            configure();
+        } else {
+            valid = false;
+        }
+    }
+}
+
+QSsl::KeyAlgorithm extractSslKeyAlgorithm(QString value)
+{
+    value = value.trimmed().toLower();
+    if (value.startsWith("opaque"))
+        return QSsl::Opaque;
+    if (value.startsWith("dsa"))
+        return QSsl::Dsa;
+    if (value.startsWith("ec"))
+        return QSsl::Dsa;
+    return QSsl::Rsa;
+}
+
+QSsl::EncodingFormat extractSslEncodingFormat(QString value)
+{
+    value = value.trimmed().toLower();
+    if (value.startsWith("der"))
+        return QSsl::Der;
+    return QSsl::Pem;
+}
+
+QSsl::KeyType extractSslKeyType(QString value)
+{
+    value = value.trimmed().toLower();
+    if (value.startsWith("publickey"))
+        return QSsl::PublicKey;
+    return QSsl::PrivateKey;
+}
+
+QSslSocket::PeerVerifyMode extractSslPeerVerifyMode(QString value)
+{
+    value = value.trimmed().toLower();
+    if (value.startsWith("querypeer"))
+        return QSslSocket::QueryPeer;
+    if (value.startsWith("verifypeer"))
+        return QSslSocket::QueryPeer;
+    if (value.startsWith("autoverifypeer"))
+        return QSslSocket::QueryPeer;
+    return QSslSocket::VerifyNone;
+}
+
+QSsl::SslProtocol extractSslProtocol(QString value)
+{
+    value = value.trimmed().toLower();
+    if (value.startsWith("sslv3"))
+        return QSsl::SslV3;
+    if (value.startsWith("sslv2"))
+        return QSsl::SslV2;
+    if (value.startsWith("tlsv1_0"))
+        return QSsl::TlsV1_0;
+    if (value.startsWith("tlsv1_1"))
+        return QSsl::TlsV1_1;
+    if (value.startsWith("tlsv1_2"))
+        return QSsl::TlsV1_2;
+    if (value.startsWith("anyprotocol"))
+        return QSsl::AnyProtocol;
+    if (value.startsWith("secureprotocols"))
+        return QSsl::SecureProtocols;
+    if (value.startsWith("tlsv1_0Orlater"))
+        return QSsl::TlsV1_1OrLater;
+    if (value.startsWith("tlsv1_2Orlater"))
+        return QSsl::TlsV1_2OrLater;
+    if (value.startsWith("unknownprotocol"))
+        return QSsl::UnknownProtocol;
+    return QSsl::TlsV1SslV3;
+}
+
+void Configuration::configure()
+{
+    const QString iniFile(path + "/config/CPPWeb.ini");
+    if (valid) {
+        QSettings settings(iniFile, QSettings::Format::IniFormat);
+        settings.beginGroup("config");
+        host.setAddress(settings.value("host").toString());
+        port = static_cast<quint16>(settings.value("port").toInt());
+        maxThread = settings.value("maxThread").toInt();
+        cleanupInterval = settings.value("cleanupInterval").toInt();
+        timeOut = settings.value("timeOut").toInt();
+        sslKeyFile = settings.value("sslKeyFile").toString();
+        sslCertFile = settings.value("sslCertFile").toString();
+        sessionExpirationTime = settings.value("sessionExpirationTime").toInt();
+        maxUploadFile = settings.value("maxUploadFile").toLongLong();
+        indexPage = settings.value("indexPage").toString();
+        accessCPPWebIni = settings.value("accessCPPWebIni").toBool();
+        accessServerPages = settings.value("accessServerPages").toBool();
+        maxLogFile = settings.value("maxLogFile").toLongLong();
+        sslPassPhrase = settings.value("sslPassPhrase").toByteArray();
+        sslKeyAlgorithm = extractSslKeyAlgorithm(settings.value("sslKeyAlgorithm").toString());
+        sslEncodingFormat = extractSslEncodingFormat(settings.value("sslEncodingFormat").toString());
+        sslKeyType = extractSslKeyType(settings.value("sslKeyType").toString());
+        sslPeerVerifyMode = extractSslPeerVerifyMode(settings.value("sslPeerVerifyMode").toString());
+        sslProtocol = extractSslProtocol(settings.value("sslProtocol").toString());
+        logFilePath = path + "/config/log/CPPWebServer.log";
+
+        FileManager::removeFirstBar(sslCertFile);
+        FileManager::removeFirstBar(sslKeyFile);
+        if (!sslKeyFile.isEmpty())
+            sslKeyFile = path + "/" + sslKeyFile;
+        if (!sslCertFile.isEmpty())
+            sslCertFile = path + "/" + sslCertFile;
+    }
+}
+
+CWF_END_NAMESPACE

+ 181 - 0
CPPWebFramework/cwf/configuration.h

@@ -0,0 +1,181 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CONFIGURATION_H
+#define CONFIGURATION_H
+
+#include <QHostAddress>
+#include <QSettings>
+#include <QSslSocket>
+#include <QString>
+
+#include "cppwebframework_global.h"
+/**
+ * @brief All classes of C++ Web Framework are contained within the namespace CWF.
+ */
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief This class is responsable to read a ini file and extract its information.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT Configuration
+{    
+    bool valid                = false;
+    bool accessCPPWebIni      = false;
+    bool accessServerPages    = false;
+    quint16 port              = 8080;
+    int timeOut               = 30000;
+    int sessionExpirationTime = 1800000;
+    int cleanupInterval       = 86400000;    
+    int maxThread             = 100;
+    QString sslKeyFile;
+    QString sslCertFile;
+    QString path;
+    QString logFilePath;
+    QString indexPage;
+    QByteArray sslPassPhrase;
+    QHostAddress host;
+    qint64 maxUploadFile = 2097152;
+    qint64 maxLogFile    = 20000000;
+    QSsl::KeyAlgorithm sslKeyAlgorithm = QSsl::Rsa;
+    QSsl::KeyType sslKeyType = QSsl::PrivateKey;
+    QSsl::EncodingFormat sslEncodingFormat = QSsl::Pem;
+    QSsl::SslProtocol sslProtocol = QSsl::TlsV1SslV3;
+    QSslSocket::PeerVerifyMode sslPeerVerifyMode = QSslSocket::VerifyNone;
+    void configure();
+public:
+    /**
+     * @brief Will make reading the CPPWeb.ini file and extract all of its properties.
+     * @param QString serverFilesPath : You should always points to the directory server.
+     * @par Example
+     * @code
+     * #include <QCoreApplication>
+     * #include <cwf/cppwebapplication.h>
+     *
+     * int main(int argc, char *argv[])
+     * {     
+     *     CWF::CppWebApplication a(argc, argv, "PATH_TO_SERVER_FOLDER");
+     *     return a.start();
+     * }
+     * @endcode
+     */
+    explicit Configuration(const QString &serverFilesPath = "");   
+    /**
+     * @brief Returns the timeOut property that will be used by the server to expire threads that are not in use.
+     * Such threads will be restarted as needed. The default timeOut is 30000 milliseconds (30 seconds).
+     * If timeOut is negative, newly created threads will not expire, e.g., they will not exit until the thread pool is destroyed.
+     * @param int : Time in milliseconds.
+     */
+    inline int getTimeOut() const noexcept { return timeOut; }
+    /**
+     * @brief Returns the Session Expiration Time.
+     * @param int : Time in milliseconds.
+     */
+    inline int getSessionExpirationTime() const noexcept { return sessionExpirationTime; }
+    /**
+     * @brief Returns the clean up interval Time.
+     * @param int : Time in milliseconds.
+     */
+    inline int getCleanupInterval() const noexcept { return cleanupInterval; }
+    /**
+     * @brief Returns the port number.
+     * @param quint16 : port.
+     */
+    inline quint16 getPort() const noexcept { return port; }
+    /**
+     * @brief Returns the address.
+     * @param QHostAddress : host.
+     */
+    inline QHostAddress getHost() const noexcept { return host; }
+    /**
+     * @brief Returns the max thread number.
+     * @param int : max.
+     */
+    inline int getMaxThread() const noexcept { return maxThread; }
+    /**
+     * @brief Returns the ssl key file.
+     * @param QString : sslKeyFile name.
+     */
+    inline QString getSslKeyFile() const noexcept { return sslKeyFile; }
+    /**
+     * @brief Returns the ssl cert file.
+     * @param QString : sslCertFile name.
+     */
+    inline QString getSslCertFile() const noexcept { return sslCertFile; }
+    /**
+     * @brief Returns the path to the server's folder.
+     * @param QString : path.
+     */
+    inline QString getPath() const noexcept { return path; }
+    /**
+     * @brief Returns the log file path.
+     * @param QString : log file.
+     */
+    inline QString getLogFilePath() const noexcept { return logFilePath; }
+    /**
+     * @brief Returns max upload file size supported by the server.
+     * @param qint64: max file size.
+     */
+    inline qint64 getMaxUploadFile() const noexcept { return maxUploadFile; }
+    /**
+     * @brief getMaxLogFile the max file log
+     * @return qint64 : Max file log in bytes.
+     */
+    inline qint64 getMaxLogFile() const noexcept { return maxLogFile; }
+    /**
+     * @brief Returns the index page.
+     * @return QString : index page name.
+     */
+    inline QString getIndexPage() const noexcept { return indexPage; }
+    /**
+     * @brief Returns the access server page.
+     * @return bool : Access server pages.
+     */
+    inline bool getAccessServerPages() const noexcept { return accessServerPages; }
+    /**
+     * @brief Returns the ssl pass phrase
+     * @return QByteArray : sslPassPhrase.
+     */
+    inline QByteArray getSslPassPhrase() const noexcept { return sslPassPhrase; }
+    /**
+     * @brief Returns true if the Configuration is ok. Otherwise returns false.
+     * @param bool : is valid.
+     */
+    inline bool isValid() const noexcept { return valid; }
+    /**
+     * @brief Returns the SSL Key Algorithm. The RSA is defined by default.
+     * @param QSsl::KeyAlgorithm : SSL key Algorithm.
+     */
+    inline QSsl::KeyAlgorithm getSslKeyAlgorithm() const noexcept { return sslKeyAlgorithm; }
+    /**
+     * @brief Returns the SSL Key Type. The private is defined by default.
+     * @param QSsl::KeyType : SSL key Type.
+     */
+    inline QSsl::KeyType getSslKeyType() const noexcept { return sslKeyType; }
+    /**
+     * @brief Returns the SSL Encoding Format. The PEM is defined by default.
+     * @param QSsl::EncodingFormat : SSL key Encoding Format.
+     */
+    inline QSsl::EncodingFormat getSslEncodingFormat() const noexcept { return sslEncodingFormat; }
+    /**
+     * @brief Returns the SSL Peer Veryfy Mode. The VerifyNone is defined by default.
+     * @param QSslSocket::PeerVerifyMode : Peer Veryfy Mode;
+     */
+    inline QSslSocket::PeerVerifyMode getSslPeerVerifyMode() const noexcept { return sslPeerVerifyMode; }
+    /**
+     * @brief Returns the SSL Protocol. The TlsV1SslV3 is defined by default.
+     * @param QSsl::SslProtocol : SSL Protocol;
+     */
+    inline QSsl::SslProtocol getSslProtocol() const noexcept { return sslProtocol; }    
+    /**
+     * @brief Returns the sslOptionDisableEmptyFragments. It is defined false by default.
+     * @param bool : true if is enable otherwise returns false;
+     */
+};
+
+CWF_END_NAMESPACE
+
+#endif // CONFIGURATION_H

+ 268 - 0
CPPWebFramework/cwf/constants.h

@@ -0,0 +1,268 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+// clazy:excludeall=non-pod-global-static
+#include <QByteArray>
+#include <QString>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+//CONFIGURATION CONSTANTS
+// clang-format off
+namespace CONFIGURATION
+{
+    const QByteArray CPP_CONFIG          = "/config/";
+    const QByteArray CPP_WEB_INI         = "CPPWeb.ini";
+    const QByteArray CPP_LOG_FILE        = "CPPWebServer.log";
+    const QString    CPP_LOG_VAR         = "cwf_log_file";
+    const QString    CPP_LOG_MAX_VAR     = "cwf_log_max";
+}
+
+//XML CONSTANTS
+namespace XML
+{
+    const QByteArray HEADER = "<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?>\n";
+}
+
+//GETTER AND SETTER CONSTANTS
+namespace GET_SET
+{
+    const QString GET_LOWER = "get";
+    const QString SET_LOWER = "set";
+    const QString GET_UPPER = "GET";
+    const QString SET_UPPER = "SET";
+}
+
+//STATUS CONSTANTS
+namespace STATUS
+{
+    const QByteArray  STATUS_401  = "/config/cppwebserverpages/401.view";
+    const QByteArray  STATUS_403  = "/config/cppwebserverpages/403.view";
+    const QByteArray  STATUS_404  = "/config/cppwebserverpages/404.view";
+    const QByteArray  NOT_FOUND   = "Not Found";
+}
+
+//CSTL CONSTANTS
+namespace CSTL
+{
+    //CSTL TAGS CONSTANTS
+    namespace TAG
+    {
+        const QString OUT    = "out";
+        const QString FOR    = "for";
+        const QString IF     = "if";
+        const QString IMPORT = "import";
+        const QByteArray BEGIN  = "<cstl>";
+        const QByteArray END    = "</cstl>";
+
+        //PROPERTIES CONSTANTS
+        namespace PROPERTY
+        {
+            const QString VAR        = "var";
+            const QString ERROR      = "error";
+            const QString CONDITION  = "condition";
+
+            namespace IMPORT
+            {
+                const QString URL    = "url";
+            }
+
+            namespace FOR
+            {
+                const QString ITEMS      = "items";
+                const QString FROM       = "from";
+                const QString TO         = "to";
+                const QString INCREMENT  = "increment";
+            }
+
+            namespace IF
+            {
+                const QString EQUAL          = "equal";
+                const QString DIFFERENT      = "different";
+                const QString GREATER        = "greater";
+                const QString GREATER_EQUAL  = "greater_equal";
+                const QString LESS           = "less";
+                const QString LESS_EQUAL     = "less_equal";
+            }
+        }
+    }
+
+    //SUPORTED TYPES CONSTANTS
+    namespace SUPPORTED_TYPES
+    {
+        const QString QSTRING                = "QString";
+        const QString STD_STRING             = "std::string";
+        const QString BOOL                   = "bool";
+        const QString CHAR                   = "char";
+        const QString UNSIGNED_CHAR          = "uchar";
+        const QString SHORT                  = "short";
+        const QString UNSIGNED_SHORT         = "ushort";
+        const QString INT                    = "int";
+        const QString UNSIGNED_INT           = "uint";
+        const QString LONG                   = "long";
+        const QString UNSIGNED_LONG          = "ulong";
+        const QString LONG_LONG              = "qlonglong";
+        const QString UNSIGNED_LONG_LONG     = "qulonglong";
+        const QString FLOAT                  = "float";
+        const QString DOUBLE                 = "double";
+        const QString CWF_QLISTOBJECT        = "CWF::QListObject";
+    }
+}
+
+//CONTENT TYPE CONSTANTS
+namespace HTTP
+{
+    const QByteArray SERVER                           = "Server";
+    const QByteArray SERVER_VERSION                   = "C++-Web-Server/3.1";
+    const QByteArray DATA                             = "Data";
+    const QByteArray TRANSFER_ENCODING                = "Transfer-Encoding";
+    const QByteArray CHUNKED                          = "chunked";
+    const QByteArray END_LINE                         = "\r\n";
+    const QByteArray SEPARATOR                        = ": ";
+    const QByteArray HTTP_1_1                         = "HTTP/1.1 ";
+    const QByteArray END_OF_MESSAGE                   = "\r\n\r\n";
+    const QByteArray END_OF_MESSAGE_WITH_ZERO         = "0\r\n\r\n";
+    const QByteArray CONTENT_TYPE                     = "Content-Type";
+    const QByteArray CONTENT_LENGTH                   = "Content-Length";
+    const QByteArray CONTENT_DISPOSITION              = "Content-Disposition";
+    const QByteArray CONTENT_DISPOSITION_COLON        = "Content-Disposition:";
+    const QByteArray CONTENT_DISPOSITION_COLON_SPACE  = "Content-Disposition: ";
+    const QByteArray MULTIPART                        = "multipart";
+    const QByteArray URLENCODED                       = "urlencoded";
+    const QByteArray COOKIE                           = "Cookie";
+    const QByteArray SET_COOKIE                       = "Set-Cookie: ";
+    const QByteArray SESSION_ID                       = "sessionId";
+    const QByteArray FILENAME                         = "filename";
+    const QByteArray FORM_DATA_COLON_SPACE            = "form-data; ";
+    const QByteArray WEBKIT                           = "WebKit";
+    const QByteArray LOCATION                         = "Location";
+    const QByteArray REDIRECT                         = "Redirect";
+    const QByteArray SEE_OTHER                        = "See Other";
+    const QByteArray ACCEPT_ENCODING                  = "Accept-Encoding";
+    const QByteArray ACCEPT_LANGUAGE                  = "Accept-Language";
+    const QByteArray HOST                             = "Host";
+    const QByteArray USER_AGENT                       = "User-Agent";
+    const QByteArray OK                               = "OK";
+    /*
+    const QByteArray            = "";
+    const QByteArray            = "";
+    */
+
+
+    //TEXT
+    const QByteArray TEXT_HTML_UTF8       = "text/html; charset=UTF-8";
+
+    //IMAGE
+    const QByteArray IMAGE_MICROSOFT_ICO    = "image/vnd.microsoft.icon";
+    const QByteArray IMAGE_JPEG             = "image/jpeg";
+    const QByteArray IMAGE_TIFF             = "image/tiff";
+    const QByteArray IMAGE_SVG_XML          = "image/svg+xml";
+
+    //AUDIO
+    const QByteArray AUDIO_MP3              = "image/mpeg";
+    const QByteArray AUDIO_MP4              = "image/mp4";
+    const QByteArray APPLICATION_PHOTOSHOP  = "image/vnd.adobe.photoshop";
+
+    //VIDEO
+    const QByteArray VIDEO_FLV              = "video/x-flv";
+
+    //APPLICATION
+    const QByteArray APPLICATION_MSWORD                      = "application/msword";
+    const QByteArray APPLICATION_RTF                         = "application/rtf";
+    const QByteArray APPLICATION_EXCEL                       = "application/vnd.ms-excel";
+    const QByteArray APPLICATION_JSON                        = "application/json";
+    const QByteArray APPLICATION_POWER_POINT                 = "application/vnd.ms-powerpoint";
+    const QByteArray APPLICATION_JAVASCRIPT                  = "application/javascript";
+    const QByteArray APPLICATION_OPEN_DOCUMENT_TEXT          = "application/vnd.oasis.opendocument.text";
+    const QByteArray APPLICATION_OPEN_DOCUMENT_SPREADSHEET   = "application/vnd.oasis.opendocument.spreadsheet";
+    const QByteArray APPLICATION_SHOCKWAVE_FLASH             = "application/x-shockwave-flash";
+    const QByteArray APPLICATION_RAR_COMPRESSED              = "application/x-rar-compressed";
+    const QByteArray APPLICATION_MS_DOWNLOAD                 = "application/x-msdownload";
+    const QByteArray APPLICATION_CAB_COMPRESSED              = "application/vnd.ms-cab-compressed";
+    const QByteArray APPLICATION_POSTSCRIPT                  = "application/postscript";
+    const QByteArray APPLICATION_WWW_FORM_URLENCODED         = "application/x-www-form-urlencoded";
+    const QByteArray APPLICATION_FONT_WOFF                   = "application/x-font-woff";
+    const QByteArray APPLICATION_FONT_TTF                    = "application/octet-stream";
+
+    //HTTP METHODS CONSTANTS
+    namespace METHOD
+    {
+        const QByteArray GET     = "GET";
+        const QByteArray PUT     = "PUT";
+        const QByteArray POST    = "POST";
+        const QByteArray DELETE  = "DELETE";
+        const QByteArray OPTIONS = "OPTIONS";
+        const QByteArray HEAD    = "HEAD";
+        const QByteArray TRACE   = "TRACE";
+    }
+}
+
+namespace COOKIE
+{
+    const QByteArray COMMENT        = "Comment";
+    const QByteArray DOMAIN_COOKIE  = "Domain";
+    const QByteArray MAX_AGE        = "Max-Age";
+    const QByteArray PATH           = "Path";
+    const QByteArray SECURE         = "Secure";
+    const QByteArray VERSION        = "Version";
+}
+
+//FILE EXTENTIONS CONSTANTS
+namespace FILE_EXTENTION
+{
+    const QByteArray BAR  = "/";
+    const QByteArray HTML = "html";
+    const QByteArray HTM  = "htm";
+    const QByteArray CSS  = "css";
+    const QByteArray TXT  = "txt";
+    const QByteArray PHP  = "php";
+    const QByteArray ICO  = "ico";
+    const QByteArray PNG  = "png";
+    const QByteArray GIF  = "gif";
+    const QByteArray BMP  = "bmp";
+    const QByteArray JPE  = "jpe";
+    const QByteArray JPG  = "jpg";
+    const QByteArray TIF  = "TIF";
+    const QByteArray TIFF = "TIFF";
+    const QByteArray SVG  = "svg";
+    const QByteArray SVGZ = "svgz";
+    const QByteArray PDF  = "pdf";
+    const QByteArray XML  = "xml";
+    const QByteArray JSON = "json";
+    const QByteArray ZIP  = "zip";
+    const QByteArray MP3  = "mp3";
+    const QByteArray MP4  = "mp4";
+    const QByteArray FLV  = "flv";
+    const QByteArray DOC  = "doc";
+    const QByteArray RTF  = "rtf";
+    const QByteArray XLS  = "xls";
+    const QByteArray PPT  = "ppt";
+    const QByteArray JS   = "js";
+    const QByteArray ODT  = "odt";
+    const QByteArray ODS  = "ods";
+    const QByteArray SWF  = "swf";
+    const QByteArray RAR  = "rar";
+    const QByteArray EXE  = "exe";
+    const QByteArray MSI  = "msi";
+    const QByteArray CAB  = "cab";
+    const QByteArray PSD  = "psd";
+    const QByteArray AI   = "ai";
+    const QByteArray EPS  = "eps";
+    const QByteArray PS   = "ps";
+    const QByteArray INI  = "ini";
+    const QByteArray EOT  = "eot";
+    const QByteArray TTF  = "ttf";
+    const QByteArray WOFF = "woff";
+    const QByteArray WOFF2= "woff2";
+}
+// clang-format on
+CWF_END_NAMESPACE
+
+#endif // CONSTANTS_H

+ 53 - 0
CPPWebFramework/cwf/controller.cpp

@@ -0,0 +1,53 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "controller.h"
+#include "request.h"
+#include "response.h"
+
+CWF_BEGIN_NAMESPACE
+
+void Controller::doMessage(Request &req, Response &resp, const QString &method) const
+{
+    QString msg("http.method_" + method.toLower() + "_not_supported");
+    if (req.getProtocol().endsWith("1.1"))
+        resp.sendError(Response::SC_METHOD_NOT_ALLOWED, msg.toUtf8());
+    else
+        resp.sendError(Response::SC_BAD_REQUEST, msg.toUtf8());
+}
+
+void Controller::doDelete(Request &req, Response &resp) const
+{
+    doMessage(req, resp, HTTP::METHOD::DELETE);
+}
+
+void Controller::doGet(Request &req, Response &resp) const
+{
+    doMessage(req, resp, HTTP::METHOD::GET);
+}
+
+void Controller::doOptions(Request &req, Response &resp) const
+{
+    doMessage(req, resp, HTTP::METHOD::OPTIONS);
+}
+
+void Controller::doPost(Request &req, Response &resp) const
+{
+    doMessage(req, resp, HTTP::METHOD::POST);
+}
+
+void Controller::doPut(Request &req, Response &resp) const
+{
+    doMessage(req, resp, HTTP::METHOD::PUT);
+}
+
+void Controller::doTrace(Request &req, Response &resp) const
+{
+    doMessage(req, resp, HTTP::METHOD::TRACE);
+}
+
+CWF_END_NAMESPACE

+ 80 - 0
CPPWebFramework/cwf/controller.h

@@ -0,0 +1,80 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CONTROLLER_H
+#define CONTROLLER_H
+
+#include <QString>
+
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+class Request;
+class Response;
+
+/**
+ * @brief The Controller class is responsable to attend a request from a specific url.
+ * You will need to create a derived class from Controller and then, reconstruct the
+ * desired method to response a request, after this, you will need mapping the url to the
+ * new controller that you created, you need to do it into the ConfigureCppWebServer using
+ * the method addUrlController.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT Controller
+{
+    /**
+     * @brief This method is used to make a default response.
+     * @param req    : This is a reference to the Request.
+     * @param resp   : This is a reference to the Response.
+     * @param method : This parameter holds the method name.
+     */
+    void doMessage(Request &req, Response &resp, const QString &method) const;
+public:
+    /**
+     * @brief Destructor.
+     */
+    virtual ~Controller() = default;
+    /**
+     * @brief This is an virtual method that can be overloaded to attend the delete request method.
+     * @param req    : This is a reference to the Request.
+     * @param resp   : This is a reference to the Response.
+     */
+    virtual void doDelete(Request &req, Response &resp) const;
+    /**
+     * @brief This is an virtual method that can be overloaded to attend the get request method.
+     * @param req    : This is a reference to the Request.
+     * @param resp   : This is a reference to the Response.
+     */
+    virtual void doGet(Request &req, Response &resp) const;
+    /**
+     * @brief This is an virtual method that can be overloaded to attend the options request method.
+     * @param req    : This is a reference to the Request.
+     * @param resp   : This is a reference to the Response.
+     */
+    virtual void doOptions(Request &req, Response &resp) const;
+    /**
+     * @brief This is an virtual method that can be overloaded to attend the post request method.
+     * @param req    : This is a reference to the Request.
+     * @param resp   : This is a reference to the Response.
+     */
+    virtual void doPost(Request &req, Response &resp) const;
+    /**
+     * @brief This is an virtual method that can be overloaded to attend the put request method.
+     * @param req    : This is a reference to the Request.
+     * @param resp   : This is a reference to the Response.
+     */
+    virtual void doPut(Request &req, Response &resp) const;
+    /**
+     * @brief This is an virtual method that can be overloaded to attend the trace request method.
+     * @param req    : This is a reference to the Request.
+     * @param resp   : This is a reference to the Response.
+     */
+    virtual void doTrace(Request &req, Response &resp) const;
+};
+
+CWF_END_NAMESPACE
+
+#endif // CONTROLLER_H

+ 125 - 0
CPPWebFramework/cwf/cppwebapplication.cpp

@@ -0,0 +1,125 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "cppwebapplication.h"
+#include "cppwebcontroller.h"
+//  clazy:excludeall=qgetenv
+CWF_BEGIN_NAMESPACE
+
+QPair<QString, qint64> getFileAndMaxSize()
+{
+    QPair<QString, qlonglong> info;
+
+    info.first = qgetenv(CONFIGURATION::CPP_LOG_VAR.toStdString().data());
+    info.second = QByteArray(qgetenv(CONFIGURATION::CPP_LOG_MAX_VAR.toStdString().data())).toInt();
+
+    if (info.second <= 0) {
+        info.second = 20000000;
+    }
+    return info;
+}
+
+void writeLog(QtMsgType type, const QMessageLogContext &logContext, const QString &msg)
+{
+    QPair<QString, qint64> info(getFileAndMaxSize());
+    QFile file(info.first);
+
+    if (file.size() > info.second) {
+        file.resize(0);
+    }
+
+    if (file.open(QIODevice::Append)) {
+        QTextStream out(&file);
+        QString date = QDateTime::currentDateTime().toString("dd/MM/yyyy hh:mm:ss:zzz");
+        out << "Date: " << date;
+        out << "\nCategory: " << logContext.category;
+        out << "\nFile: " << logContext.file;
+        out << "\nFunction: " << logContext.function;
+        out << "\nVersion: " << logContext.version;
+        out << "\nMessage: " << msg << "\n\n";
+
+        if (type == QtFatalMsg) {
+            out << "\nFATAL: SERVER OFFLINE\n\n";
+            file.close();
+            abort();
+        }
+        file.close();
+    }
+}
+
+CppWebApplication::CppWebApplication(const QString &serverPath, Filter *filter)
+    : configuration(serverPath)
+{
+    if (configuration.isValid()) {
+        qunsetenv(CONFIGURATION::CPP_LOG_VAR.toStdString().data());
+        qunsetenv(CONFIGURATION::CPP_LOG_MAX_VAR.toStdString().data());
+
+        qputenv(CONFIGURATION::CPP_LOG_VAR.toStdString().data(),
+                configuration.getLogFilePath().toUtf8());
+        qputenv(CONFIGURATION::CPP_LOG_MAX_VAR.toStdString().data(),
+                QByteArray::number(configuration.getMaxLogFile()));
+
+        QPair<QString, qint64> info(getFileAndMaxSize());
+        if (!QFile(info.first).exists()) {
+            qDebug() << "Path not found to log file: " << configuration.getLogFilePath();
+            qDebug() << "Note: Use only US-ASCII characters for the serverPath.";
+        } else if (configuration.isValid()) {
+            valid = true;
+            //qInstallMessageHandler(writeLog);
+            server = new CppWebServer(configuration, filter);
+
+            if (configuration.getAccessServerPages()) {
+                server->addController<CppWebController>("/example");
+                server->addController<CppWebController>("/authors");
+                server->addController<CppWebController>("/documentation");
+                server->addController<CppWebController>("/ssl");
+                server->addController<CppWebController>("/index");
+            }
+        }
+    } else {
+        qDebug() << "CPPWeb.ini not found. Please copy the CWF server folder to your project.";
+    }
+}
+
+CppWebApplication::~CppWebApplication()
+{
+    if (!server)
+        delete server;
+}
+
+int CppWebApplication::start()
+{
+    return start(configuration.getHost(), configuration.getPort());
+}
+
+int CppWebApplication::start(const QHostAddress &host, const int &port)
+{
+    if (!server->listen(host, port)) {
+        qDebug() << "Error: " << server->errorString() << "\n";
+        qDebug() << "Host: " << host;
+        qDebug() << "Port: " << port;
+        qDebug() << "Server offline\n";
+        return -1;
+    }
+
+    qDebug() << "Server online\n";
+    qDebug() << "Host: " << host;
+    qDebug() << "Port: " << port;
+    qDebug() << "LogFilePath: " << configuration.getLogFilePath();
+    return 1;
+}
+
+bool CppWebApplication::close()
+{
+    if (server) {
+        server->close();
+        return true;
+    }
+    return false;
+}
+
+CWF_END_NAMESPACE

+ 99 - 0
CPPWebFramework/cwf/cppwebapplication.h

@@ -0,0 +1,99 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+/**
+  * @mainpage The C++ Web Framework
+  *
+  * ​​The C++ Web Framework (CWF) is a MVC web framework, Open Source, under MIT License, created by Herik Lima and Marcelo Eler,
+  * using C++ with Qt to be used in the development of web applications. The CWF was designed to consume few computational resources,
+  * such as memory and processing, and have a low response time for requests. With MVC (Model-View-Controller) architecture,
+  * you can create classes to take care of the business layer (Model), use CSTL (C++ Server Pages Standard Tag Library)
+  * within the Web Pages to take care of data presentation (View) and use the controllers as a between the two layers (Controller).
+  *
+  * Because it is created in Qt, the C++ Web Framework can run on the same platforms supported by Qt:
+  *
+  *
+  * Desktop: Linux, OS X, Windows
+  * Embedded and RTOS: Linux, QNX, VxWorks, Windows
+  * Mobile: Android, iOS, Windows
+  *
+  *
+  * The CWF has only one configuration file, called CPPWeb.ini and a policy of using only C++ and Qt in the development
+  * of its components in order to avoid the installation of numerous libraries to avoid conflicts, maintain multiplatform characteristics,
+  * facilitate installation and keep the learning curve low in order to make web development as simple as possible, even for beginners.
+  */
+
+#ifndef CPPWEBAPPLICATION_H
+#define CPPWEBAPPLICATION_H
+
+#include <QString>
+#include <QCoreApplication>
+#include <QMessageLogContext>
+#include "cppwebserver.h"
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief This class is responsible for encapsulating the QCoreApplication, the
+ * CppWebServer and configure the server logging mechanism.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT CppWebApplication
+{
+    Configuration    configuration;
+    CppWebServer     *server;
+    bool valid = false;
+    CppWebApplication(const CppWebApplication &other) = delete;
+    CppWebApplication &operator=(const CppWebApplication &other) = delete;        
+public:
+    /**
+     * @brief Constructs a QCoreApplication, a CppWebServer and install the message handler.
+     * if the server folder's path was not found in the serverPath, it will look into the executable's folder.
+     * @param int argc                    : Main function parameter used to build QCoreApplication.
+     * @param char *argv[]                : Main function parameter used to build QCoreApplication.
+     * @param const Configuration &config : Used to set the parameters of the server.
+     * @param Filter *filter              : Install a filter for requests on the server.
+     */
+    CppWebApplication(const QString &serverPath, Filter *filter = nullptr);
+    /**
+     * @brief Destroys the server dynamically allocated.
+     */
+    ~CppWebApplication();
+    /**
+     * @brief Hitches a url to a Controller.
+     * @param const QString &url   : Url name.
+     * @param Controller *controller : Controller that will answer requests made to url.
+     * @par Example
+     * @code
+     * #include <QCoreApplication>
+     * #include <controllers/helloworldcontroller.h>
+     * #include <cwf/cppwebapplication.h>
+     *
+     * int main(int argc, char *argv[])
+     * {
+     *      CWF::CppWebApplication server(argc, argv, "/PATH_TO_EXAMPLE/server"));
+     *      server.addUrlController<HelloWorldController>("/hello");
+     *      return server.start();
+     * }
+     * @endcode
+     */
+    template<typename CONTROLLER>
+    void addController(const QString &url) noexcept
+    {
+        server->addController<CONTROLLER>(url);
+    }
+    /**
+     * @brief Starts the server and QCoreApplication.
+     * @return int : Returns -1 if it fails.
+     */
+    int start();
+    int start(const QHostAddress &host, const int &port);
+    bool close();
+};
+
+CWF_END_NAMESPACE
+
+#endif // CPPWEBAPPLICATION_H

+ 32 - 0
CPPWebFramework/cwf/cppwebcontroller.cpp

@@ -0,0 +1,32 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "cppwebcontroller.h"
+
+CWF_BEGIN_NAMESPACE
+
+void CppWebController::doGet(CWF::Request &request, CWF::Response &response) const
+{
+    QString url(request.getRequestURL());
+    if (url.endsWith("/index")) {
+        request.getRequestDispatcher("/config/cppwebserverpages/index.view")
+            .forward(request, response);
+    } else if (url.endsWith("/examples")) {
+        request.getRequestDispatcher("/config/cppwebserverpages/examples.view")
+            .forward(request, response);
+    } else if (url.endsWith("/documentation")) {
+        request.getRequestDispatcher("/config/cppwebserverpages/documentation.view")
+            .forward(request, response);
+    } else if (url.endsWith("/ssl")) {
+        request.getRequestDispatcher("/config/cppwebserverpages/ssl.view").forward(request, response);
+    } else if (url.endsWith("/authors")) {
+        request.getRequestDispatcher("/config/cppwebserverpages/authors.view")
+            .forward(request, response);
+    }
+}
+
+CWF_END_NAMESPACE

+ 33 - 0
CPPWebFramework/cwf/cppwebcontroller.h

@@ -0,0 +1,33 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CPPWEBCONTROLLER_H
+#define CPPWEBCONTROLLER_H
+
+#include "controller.h"
+#include "request.h"
+#include "response.h"
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+* @brief This class is responsible for displaying the standard pages of C++ Web Framework: index, examples, documentation, ssl and authors.
+*/
+class CPPWEBFRAMEWORKSHARED_EXPORT CppWebController : public Controller
+{
+public:
+    /**
+     * @brief Method overload to answer the requests the system default pages.
+     * @param Request  &request  : Parameter generated by HttpReadRequest.
+     * @param Response &response : Parameter generated by HttpReadRequest.
+     */
+    void doGet(Request &request, Response &response) const override;
+};
+
+CWF_END_NAMESPACE
+
+#endif // CPPWEBCONTROLLER_H

+ 23 - 0
CPPWebFramework/cwf/cppwebframework_global.h

@@ -0,0 +1,23 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CPPWEBFRAMEWORK_GLOBAL_H
+#define CPPWEBFRAMEWORK_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+#if defined(CPPWEBFRAMEWORK_STATIC)
+#define CPPWEBFRAMEWORKSHARED_EXPORT
+#elif defined(CPPWEBFRAMEWORK_LIBRARY)
+#define CPPWEBFRAMEWORKSHARED_EXPORT Q_DECL_EXPORT
+#else
+#define CPPWEBFRAMEWORKSHARED_EXPORT Q_DECL_IMPORT
+#endif
+
+#define CWF_BEGIN_NAMESPACE namespace CWF {
+#define CWF_END_NAMESPACE }
+
+#endif // CPPWEBFRAMEWORK_GLOBAL_H

+ 22 - 0
CPPWebFramework/cwf/cppwebframework_global.h.2

@@ -0,0 +1,22 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CPPWEBFRAMEWORK_GLOBAL_H
+#define CPPWEBFRAMEWORK_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(CPPWEBFRAMEWORK_LIBRARY)
+#  define CPPWEBFRAMEWORKSHARED_EXPORT
+#else    
+#  define CPPWEBFRAMEWORKSHARED_EXPORT
+#endif
+
+#define CWF_BEGIN_NAMESPACE namespace CWF {
+#define CWF_END_NAMESPACE }
+
+#endif // CPPWEBFRAMEWORK_GLOBAL_H

+ 73 - 0
CPPWebFramework/cwf/cppwebserver.cpp

@@ -0,0 +1,73 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "cppwebserver.h"
+#include "sslloader.h"
+
+CWF_BEGIN_NAMESPACE
+
+CppWebServer::CppWebServer(const Configuration &configuration, Filter *filter)
+    : configuration(configuration)
+    , filter(filter)
+{
+    ssl = SslLoader(configuration).getSslConfiguration();
+    this->thread()->setPriority(QThread::TimeCriticalPriority);
+    pool.setMaxThreadCount(configuration.getMaxThread());
+    pool.setExpiryTimeout(configuration.getTimeOut());
+    if (!filter) {
+        this->filter = new Filter;
+    }
+    timer = new QTimer;
+    connect(timer, &QTimer::timeout, this, &CppWebServer::doClean);
+    timer->start(configuration.getCleanupInterval());
+}
+
+CppWebServer::~CppWebServer()
+{
+    while (!pool.waitForDone()) {
+    }
+
+    std::for_each(urlController.constBegin(), urlController.constEnd(), [](Controller *i) {
+        delete i;
+    });
+    std::for_each(sessions.constBegin(), sessions.constEnd(), [](Session *i) { delete i; });
+    delete filter;
+    delete ssl;
+}
+
+void CppWebServer::incomingConnection(qintptr socketfd)
+{
+    while (block) {
+        this->thread()->msleep(static_cast<unsigned long>(sleepTime));
+    }
+    pool.start(new HttpReadRequest(socketfd, urlController, sessions, configuration, ssl, filter),
+               QThread::LowPriority);
+}
+
+void CppWebServer::doClean()
+{
+    block = 1;
+    while (!pool.waitForDone(sleepTime)) {
+    }
+
+    Session *session = nullptr;
+    QStringList idSessionsToDelete;
+    for (auto it = sessions.begin(); it != sessions.end(); ++it) {
+        session = it.value();
+        if (session->isExpired()) {
+            idSessionsToDelete.push_back(session->getId());
+            delete session;
+        }
+    }
+    for (int i = 0; i < idSessionsToDelete.size(); ++i) {
+        sessions.remove(idSessionsToDelete[i]);
+    }
+
+    block = 0;
+}
+
+CWF_END_NAMESPACE

+ 81 - 0
CPPWebFramework/cwf/cppwebserver.h

@@ -0,0 +1,81 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CPPWEBSERVER_H
+#define CPPWEBSERVER_H
+
+#include <QTcpServer>
+#include <QSslKey>
+#include <QSslCertificate>
+#include <QHttpMultiPart>
+#include <QThreadPool>
+#include <QTimer>
+#include <atomic>
+#include "qmapthreadsafety.h"
+#include "httpreadrequest.h"
+#include "controller.h"
+#include "session.h"
+#include "filter.h"
+#include "controller.h"
+#include "configuration.h"
+#include "request.h"
+#include "response.h"
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief The CppWebServer class is a HTTP server, responsable to receive and dispatch the requisitions.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT CppWebServer : public QTcpServer
+{
+    Q_OBJECT
+private:
+    Configuration configuration;
+    Filter *filter;
+    QTimer *timer;
+    QThreadPool pool;
+    QMapThreadSafety<QString, Controller *> urlController;
+    QMapThreadSafety<QString, Session *> sessions;
+    QSslConfiguration *ssl = nullptr;
+    const int sleepTime = 10;
+    QAtomicInteger<qint8> block = 0;    
+public:
+    /**
+     * @brief Load SSL configuration, configure the thread pool and the filter.
+     * @param Filter *filter : Install a filter for requests on the server. Optional.
+     */
+    explicit CppWebServer(const Configuration &configuration, Filter *filter = nullptr);
+    /**
+     * @brief Destroys all controllers and sessions.
+    */
+    ~CppWebServer() override;
+    /**
+     * @brief Hitches a url to a Controller.
+     * @param const QString &url   : Url name.
+     * it will not be possible to map the controllers through the addController function, instead everything should be handled inside the Filter.
+     */
+    template<typename CONTROLLER>
+    void addController(const QString &url) noexcept
+    {
+        static_assert(std::is_base_of<Controller, CONTROLLER>::value, "CONTROLLER must be a descendant of Controller");
+        urlController.insert(url, new CONTROLLER);
+    }
+protected:
+    /**
+     * @brief incomingConnection
+     * @param qintptr socketfd : Socket descriptor.
+     */
+    void incomingConnection(qintptr socketfd) override;
+    /**
+     * @brief Clean expired sessions on Server.
+     */
+    void doClean();
+};
+
+CWF_END_NAMESPACE
+
+#endif // CPPWEBSERVER_H

+ 335 - 0
CPPWebFramework/cwf/cstlcompiler.cpp

@@ -0,0 +1,335 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "cstlcompiler.h"
+
+#include "constants.h"
+#include "cstlcompilerattributes.h"
+#include "cstlcompilerfor.h"
+#include "cstlcompilerif.h"
+#include "cstlcompilerimport.h"
+#include "cstlcompilerobject.h"
+#include <utility>
+
+CWF_BEGIN_NAMESPACE
+
+CSTLCompiler::CSTLCompiler(const QByteArray &str,
+                           QString path,
+                           QMap<QString, QObject *> &objects,
+                           bool isStrFileName)
+    : str(str)
+    , path(std::move(path))
+    , objects(objects)
+    , isStrFileName(isStrFileName)
+{
+    if (isStrFileName) {
+        isView = str.toLower().endsWith(".view");
+    }
+}
+
+QByteArray CSTLCompiler::openFile(QXmlStreamReader &xml)
+{
+    if (isStrFileName) {
+        QFile file(str);
+        if (!file.open(QIODevice::ReadOnly)) {
+            return "<html><body>" + file.errorString().toUtf8() + "</body></html>";
+        }
+
+        QByteArray content(file.readAll());
+        file.close();
+        if (isView) {
+            xml.addData(content);
+        } else {
+            return content;
+        }
+    } else {
+        xml.addData(str);
+    }
+
+    if (xml.hasError()) {
+        return "<html><body>XML ERROR: " + xml.errorString().toUtf8() + "</body></html>";
+    }
+    return "";
+}
+
+QByteArray CSTLCompiler::processOutTag(QMap<QString, QString> &attr)
+{
+    int size = attr.size();
+    if (size < 1) {
+        return "";
+    }
+    if (size > 1) {
+        return "***ERROR - OUT TAG DOES NOT HAS MORE THAN PROPERTY***";
+    }
+    return CSTLCompilerAttributes(objects).buildAttributes(attr, false).toUtf8();
+}
+
+QByteArray CSTLCompiler::getBody(QXmlStreamReader &xml, const QString &tagName)
+{
+    QByteArray name, text, content(CSTL::TAG::BEGIN);
+    QByteArray att;
+    CSTLCompilerAttributes compilerAttributes(objects);
+    QMap<QString, QString> attributes;
+    QMap<QString, QString>::iterator it;
+    int start = 0, end = 0;
+    xml.readNext();
+    while (!xml.atEnd()) {
+        name = xml.name().toUtf8().toLower();
+        text = xml.text().toUtf8();
+
+        if (xml.isStartElement()) {
+            if (name == tagName) {
+                ++start;
+            }
+            attributes = compilerAttributes.getAttributes(xml.attributes());
+            for (it = attributes.begin(); it != attributes.end(); ++it) {
+                att += " " + it.key().toUtf8() + "=\"" + it.value().toUtf8() + "\"";
+            }
+            content += "<" + name + att + ">" + text;
+            attributes.clear();
+            att.clear();
+        } else if (xml.isEndElement() && name == tagName) {
+            if (end < start) {
+                content += text + "</" + name + ">";
+            }
+            ++end;
+            if (end >= start) {
+                break;
+            }
+        } else if (xml.isEndElement()) {
+            content += text + "</" + name + ">";
+        } else {
+            content += text;
+        }
+        xml.readNext();
+    }
+    return content + CSTL::TAG::END;
+}
+
+void CSTLCompiler::processText(QString &text)
+{
+    QString copy(std::move(text));
+    CSTLCompilerAttributes(objects).compile(copy, text);
+}
+
+QByteArray CSTLCompiler::processForTag(QXmlStreamReader &xml)
+{
+    QByteArray htmlOut;
+    CSTLCompilerFor forAttributes(xml.attributes());
+    CSTLCompilerAttributes compilerAttributes(objects);
+
+    if (forAttributes.attributes.contains(CSTL::TAG::PROPERTY::ERROR)) {
+        getBody(xml, CSTL::TAG::FOR);
+        htmlOut = forAttributes.attributes[CSTL::TAG::PROPERTY::ERROR].toUtf8();
+    } else {
+        QString items(forAttributes.attributes[CSTL::TAG::PROPERTY::FOR::ITEMS]);
+        items.replace("#{", "").replace("}", "");
+        if (objects.contains(items)) {
+            auto *object = static_cast<QObject *>(objects[items]);
+            QString type(object->metaObject()->className());
+
+            if (!items.isEmpty()) {
+                if (type != CSTL::SUPPORTED_TYPES::CWF_QLISTOBJECT) {
+                    htmlOut = "***ERROR - " + type.toUtf8() + " ISN'T A CWF::QListObject***";
+                    getBody(xml, CSTL::TAG::FOR);
+                } else {
+                    auto *qListObject = dynamic_cast<QListObject *>(object);
+                    QString ret(getBody(xml, CSTL::TAG::FOR));
+                    QString var(forAttributes.attributes[CSTL::TAG::PROPERTY::VAR]);
+                    var.replace("#{", "").replace("}", "");
+                    for (int iL = 0; iL < qListObject->size(); ++iL) {
+                        objects.insert(var, qListObject->operator[](iL));
+                        QXmlStreamReader forBody(ret);
+                        htmlOut += processXml(forBody);
+                        objects.remove(var);
+                    }
+                }
+            }
+        } else {
+            int from = forAttributes.attributes[CSTL::TAG::PROPERTY::FOR::FROM].toInt();
+            int to = forAttributes.attributes[CSTL::TAG::PROPERTY::FOR::TO].toInt();
+            int increment = forAttributes.attributes[CSTL::TAG::PROPERTY::FOR::INCREMENT].toInt();
+            QString tagBody(getBody(xml, CSTL::TAG::FOR));
+            QString &var = forAttributes.attributes[CSTL::TAG::PROPERTY::VAR];
+            for (int i = from; i <= to; i += increment) {
+                QString copy(tagBody);
+                CSTLCompilerObject obj;
+                obj.setValue(QString::number(i));
+                objects.insert(var, &obj);
+
+                copy.replace(XML::HEADER, "");
+                QString outPutText;
+                compilerAttributes.compile(copy, outPutText);
+                copy = "<out>" + outPutText + "</out>";
+                copy = XML::HEADER + copy;
+
+                QXmlStreamReader forBody(copy);
+                htmlOut += processXml(forBody);
+                objects.remove(var);
+            }
+        }
+    }
+    return htmlOut;
+}
+
+QByteArray CSTLCompiler::processIfTag(QXmlStreamReader &xml)
+{
+    QByteArray htmlOut;
+    CSTLCompilerIf ifAttributes(xml.attributes());
+
+    if (ifAttributes.relationalOperator == RelationalOperator::ERROR) {
+        getBody(xml, CSTL::TAG::IF);
+        htmlOut = ifAttributes.attributes[CSTL::TAG::PROPERTY::ERROR].toUtf8();
+    } else {
+        QString var(ifAttributes.attributes[CSTL::TAG::PROPERTY::VAR]),
+            condition(ifAttributes.attributes[CSTL::TAG::PROPERTY::CONDITION]);
+        CSTLCompilerObject conditionObj, varObj;
+        bool removeVar = false, removeCondition = false;
+        if (!objects.contains(var)) {
+            removeVar = true;
+            varObj.setValue(var);
+            objects.insert(var, &varObj);
+        }
+        if (!objects.contains(condition)) {
+            removeCondition = true;
+            conditionObj.setValue(condition);
+            objects.insert(condition, &conditionObj);
+        }
+
+        CSTLCompilerAttributes compilerAttributes(objects);
+        QString tagBody(getBody(xml, CSTL::TAG::IF));
+        compilerAttributes.compileAttributes(ifAttributes.attributes);
+
+        bool isTrue = false;
+
+        if (ifAttributes.relationalOperator == RelationalOperator::EQUAL) {
+            isTrue = ifAttributes.attributes[CSTL::TAG::PROPERTY::VAR]
+                     == ifAttributes.attributes[CSTL::TAG::PROPERTY::CONDITION];
+        } else if (ifAttributes.relationalOperator == RelationalOperator::DIFFERENT) {
+            isTrue = ifAttributes.attributes[CSTL::TAG::PROPERTY::VAR]
+                     != ifAttributes.attributes[CSTL::TAG::PROPERTY::CONDITION];
+        } else if (ifAttributes.relationalOperator == RelationalOperator::GREATER) {
+            if (ifAttributes.isNumber) {
+                isTrue = ifAttributes.attributes[CSTL::TAG::PROPERTY::VAR].toDouble()
+                         > ifAttributes.attributes[CSTL::TAG::PROPERTY::CONDITION].toDouble();
+            } else {
+                isTrue = ifAttributes.attributes[CSTL::TAG::PROPERTY::VAR]
+                         > ifAttributes.attributes[CSTL::TAG::PROPERTY::CONDITION];
+            }
+        } else if (ifAttributes.relationalOperator == RelationalOperator::GREATER_EQUAL) {
+            if (ifAttributes.isNumber) {
+                isTrue = ifAttributes.attributes[CSTL::TAG::PROPERTY::VAR].toDouble()
+                         >= ifAttributes.attributes[CSTL::TAG::PROPERTY::CONDITION].toDouble();
+            } else {
+                isTrue = ifAttributes.attributes[CSTL::TAG::PROPERTY::VAR]
+                         >= ifAttributes.attributes[CSTL::TAG::PROPERTY::CONDITION];
+            }
+        } else if (ifAttributes.relationalOperator == RelationalOperator::LESS) {
+            if (ifAttributes.isNumber) {
+                isTrue = ifAttributes.attributes[CSTL::TAG::PROPERTY::VAR].toDouble()
+                         < ifAttributes.attributes[CSTL::TAG::PROPERTY::CONDITION].toDouble();
+            } else {
+                isTrue = ifAttributes.attributes[CSTL::TAG::PROPERTY::VAR]
+                         < ifAttributes.attributes[CSTL::TAG::PROPERTY::CONDITION];
+            }
+        } else if (ifAttributes.relationalOperator == RelationalOperator::LESS_EQUAL) {
+            if (ifAttributes.isNumber) {
+                isTrue = ifAttributes.attributes[CSTL::TAG::PROPERTY::VAR].toDouble()
+                         <= ifAttributes.attributes[CSTL::TAG::PROPERTY::CONDITION].toDouble();
+            } else {
+                isTrue = ifAttributes.attributes[CSTL::TAG::PROPERTY::VAR]
+                         <= ifAttributes.attributes[CSTL::TAG::PROPERTY::CONDITION];
+            }
+        }
+
+        if (isTrue) {
+            if (!tagBody.contains("<") || !tagBody.contains("</")) {
+                tagBody.replace(XML::HEADER, "");
+                QString outPutText;
+                compilerAttributes.compile(tagBody, outPutText);
+                tagBody = XML::HEADER + "<out>" + outPutText + "</out>";
+            }
+            QXmlStreamReader forBody(tagBody);
+            htmlOut += processXml(forBody);
+        }
+
+        if (removeVar) {
+            objects.remove(var);
+        }
+        if (removeCondition) {
+            objects.remove(condition);
+        }
+    }
+    return htmlOut;
+}
+
+QByteArray CSTLCompiler::processXml(QXmlStreamReader &xml)
+{
+    QByteArray htmlOut;
+    while (!xml.atEnd()) {
+        CSTLCompilerAttributes compilerAttributes(objects);
+        QString name(xml.name().toString().toLower());
+        QString text(xml.text().toString());
+        QString tagAttributes;
+        QMap<QString, QString> attr;
+
+        if (name == CSTL::TAG::OUT && xml.isStartElement()) {
+            attr = compilerAttributes.getAttributes(xml.attributes());
+            htmlOut += processOutTag(attr);
+            name.clear();
+        } else if (name == CSTL::TAG::FOR && xml.isStartElement()) {
+            htmlOut += processForTag(xml);
+            name.clear();
+        } else if (name == CSTL::TAG::IF && xml.isStartElement()) {
+            htmlOut += processIfTag(xml);
+            name.clear();
+        } else if (name == CSTL::TAG::IMPORT && xml.isStartElement()) {
+            CSTLCompilerImport importUrl(xml.attributes(), path);
+            if (!importUrl.attributes.contains(CSTL::TAG::PROPERTY::ERROR)) {
+                htmlOut += importUrl.attributes[CSTL::TAG::PROPERTY::IMPORT::URL].toUtf8();
+            } else {
+                htmlOut += importUrl.attributes[CSTL::TAG::PROPERTY::ERROR].toUtf8();
+            }
+            name.clear();
+
+        } else {
+            if (name != CSTL::TAG::OUT && name != CSTL::TAG::IF && name != CSTL::TAG::FOR) {
+                attr = compilerAttributes.getAttributes(xml.attributes());
+                tagAttributes = compilerAttributes.buildAttributes(attr);
+                if (xml.isStartElement()) {
+                    name = "<" + name + tagAttributes + ">";
+                } else if (xml.isEndElement()) {
+                    name = "</" + name + ">";
+                }
+            } else {
+                name.clear();
+            }
+        }
+
+        processText(text);
+        htmlOut += name.toUtf8() + text.toUtf8();
+        xml.readNext();
+        if (xml.hasError()) {
+            return "<html><body>XML ERROR: " + xml.errorString().toUtf8() + "</body></html>";
+        }
+    }
+    return htmlOut;
+}
+
+QByteArray CSTLCompiler::output()
+{
+    QXmlStreamReader xml;
+    QByteArray htmlOutput(openFile(xml));
+    if (isView) {
+        if (htmlOutput.isEmpty()) {
+            return processXml(xml).replace(CSTL::TAG::BEGIN, "").replace(CSTL::TAG::END, "");
+        }
+    }
+    return htmlOutput;
+}
+
+CWF_END_NAMESPACE

+ 91 - 0
CPPWebFramework/cwf/cstlcompiler.h

@@ -0,0 +1,91 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CSTLCOMPILER_H
+#define CSTLCOMPILER_H
+
+#include <QMap>
+#include <QFile>
+#include <QString>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+#include <QStringList>
+#include <QMetaObject>
+#include <QMetaMethod>
+#include "properties.h"
+#include "qlistobject.h"
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+* @brief This class compiles view pages with CSTL (C++ Server Pages Standard Tags Library).
+*/
+class CPPWEBFRAMEWORKSHARED_EXPORT CSTLCompiler
+{
+    QByteArray str;
+    QString path;
+    QMap<QString, QObject *>  &objects;
+    bool isStrFileName;    
+    bool isView = true;
+    /**
+     * @brief Try to open the view page, if it fails, it will return an error QByteArray, else, it will return an empty QByteArray.
+     * @param QXmlStreamReader &xml : The opened xml.
+     * @return QByteArray : Return an error QByteArray, else, it will return an empty QByteArray.
+     */
+    QByteArray openFile(QXmlStreamReader &xml);
+    /**
+     * @brief Process XML file to find special tags and expressions.
+     * @param QXmlStreamReader &xml : The opened xml.
+     * @return QByteArray : Returns the processed QByteArray or an error.
+     */
+    QByteArray processXml(QXmlStreamReader &xml);
+    /**
+     * @brief Process out tag from CSTL
+     * @param QXmlStreamReader &xml : The opened xml.
+     * @return QByteArray : Returns the processed QByteArray or an error.
+     */
+    QByteArray processOutTag(QMap<QString, QString> &attr);
+    /**
+     * @brief Process for tag from CSTL
+     * @param QXmlStreamReader &xml : The opened xml.
+     * @return QByteArray : Returns the processed QByteArray or an error.
+     */
+    QByteArray processForTag(QXmlStreamReader &xml);
+    /**
+     * @brief Process if tag from CSTL
+     * @param QXmlStreamReader &xml : The opened xml.
+     * @return QByteArray : Returns the processed QByteArray or an error.
+     */
+    QByteArray processIfTag(QXmlStreamReader &xml);
+    /**
+     * @brief Extract a tag body.
+     * @param QXmlStreamReader &xml : The opened xml.
+     * @param const QString &tagName : Tag name to extract body.
+     * @return QByteArray : Tag body.
+     */
+    QByteArray getBody(QXmlStreamReader &xml, const QString &tagName);
+
+
+    void processText(QString &text);
+public:
+    /**
+     * @brief Initialize the str, objects and isStrFileName properties.
+     * @param const QByteArray &str             : If isStrFileName is true, it should be a file name, else, it should be the file content.
+     * @param QMap<QString, QObject *> &objects : Container objects that can be compiled into the view page.
+     * @param bool isStrFileName                : It indicates whether str is the name of a file or its contents.
+     */
+    CSTLCompiler(const QByteArray &str, QString path, QMap<QString, QObject *> &objects, bool isStrFileName = true);
+    /**
+     * @brief Returns the compiled view page.
+     * @return QByteArray : Compiled page.
+     */
+    QByteArray output();
+};
+
+CWF_END_NAMESPACE
+
+#endif // CSTLCOMPILER_H

+ 234 - 0
CPPWebFramework/cwf/cstlcompilerattributes.cpp

@@ -0,0 +1,234 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "cstlcompilerattributes.h"
+#include <QVariant>
+
+#include "constants.h"
+#include "metaclassparser.h"
+#include "properties.h"
+//  clazy:excludeall=connect-not-normalized
+CWF_BEGIN_NAMESPACE
+
+CSTLCompilerAttributes::CSTLCompilerAttributes(QMap<QString, QObject *> &objects)
+    : objects(objects)
+{}
+
+QString CSTLCompilerAttributes::buildAttributes(QMap<QString, QString> &attr, bool keyValue)
+{
+    QString htmlOut;
+    for (QMap<QString, QString>::iterator it = attr.begin(); it != attr.end(); ++it) {
+        compileAttributes(attr);
+        if (keyValue)
+            htmlOut += " " + it.key() + "=\"" + it.value() + "\"";
+        else
+            htmlOut += it.value();
+    }
+    return htmlOut;
+}
+
+void CSTLCompilerAttributes::compileAttributes(QMap<QString, QString> &attr)
+{
+    for (QMap<QString, QString>::iterator it = attr.begin(); it != attr.end(); ++it) {
+        QString outPutText;
+        QString &value = it.value();
+        compile(value, outPutText);
+        value = outPutText;
+        /*
+        //Compile at runtime
+        if(value.startsWith("#{") && value.endsWith("}"))
+        {
+            value.replace("#{", "").replace("}", "");
+            Properties prop(value);
+            if(!objects.contains(prop.m_class))
+            {
+                value = "***ERROR - OBJECT " + prop.m_class + " DOES NOT EXIST.***";
+            }
+            else
+            {
+                QObject *object = objects[prop.m_class];
+                bool ok = QMetaObject::invokeMethod(object, prop.m_method.toStdString().data(), Qt::DirectConnection, Q_RETURN_ARG(QString, value));
+                if(!ok)
+                {
+                    value = "***ERROR - METHOD " + prop.m_method + " .***";
+                }
+            }
+        }
+        */
+    }
+}
+
+void CSTLCompilerAttributes::compile(QString &text, QString &outPutText)
+{
+    bool start = false;
+    int size = text.size();
+    QString expr;
+
+    for (int i = 0; i < size; ++i) {
+        QChar ch = text[i];
+        if (ch == '#') {
+            if ((i + 1) < size) {
+                QChar c = text[i + 1];
+                if (c == '{') {
+                    start = true;
+                }
+            }
+        } else if (ch == '}') {
+            if (start) {
+                start = false;
+                expr.replace("#{", "");
+                Properties prop(expr);
+                if (!objects.contains(prop.m_class)) {
+                    outPutText += "***ERROR - OBJECT " + prop.m_class + " DOES NOT EXIST.***";
+                } else {
+                    QString value;
+                    QObject *object = objects[prop.m_class];
+                    bool ok = false;
+                    MetaClassParser metaClassParser(object);
+                    QString methodReturnType(metaClassParser.getReturnType(prop.m_method + "()"));
+
+                    if (!methodReturnType.isEmpty()) {
+                        if (methodReturnType == CSTL::SUPPORTED_TYPES::QSTRING)
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(QString, value));
+                        else if (methodReturnType == CSTL::SUPPORTED_TYPES::STD_STRING) {
+                            std::string returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(std::string, returnValue));
+                            value = returnValue.data();
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::BOOL) {
+                            bool returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(bool, returnValue));
+                            value = QString::number(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::CHAR) {
+                            char returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(char, returnValue));
+                            value.push_back(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::UNSIGNED_CHAR) {
+                            unsigned char returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(unsigned char, returnValue));
+                            value.push_back(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::SHORT) {
+                            short returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(short, returnValue));
+                            value = QString::number(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::UNSIGNED_SHORT) {
+                            unsigned short returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(unsigned short,
+                                                                        returnValue));
+                            value = QString::number(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::INT) {
+                            int returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(int, returnValue));
+                            value = QString::number(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::UNSIGNED_INT) {
+                            unsigned int returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(unsigned int, returnValue));
+                            value = QString::number(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::LONG) {
+                            long returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(long, returnValue));
+                            value = QString::number(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::UNSIGNED_LONG) {
+                            unsigned long returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(unsigned long, returnValue));
+                            value = QString::number(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::LONG_LONG) {
+                            long long returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(long long, returnValue));
+                            value = QString::number(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::UNSIGNED_LONG_LONG) {
+                            unsigned long long returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(unsigned long long,
+                                                                        returnValue));
+                            value = QString::number(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::FLOAT) {
+                            float returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(float, returnValue));
+                            value = QString::number(returnValue);
+                        } else if (methodReturnType == CSTL::SUPPORTED_TYPES::DOUBLE) {
+                            double returnValue;
+                            ok = QMetaObject::invokeMethod(object,
+                                                           prop.m_method.toStdString().data(),
+                                                           Qt::DirectConnection,
+                                                           Q_RETURN_ARG(double, returnValue));
+                            value = QString::number(returnValue);
+                        }
+
+                        if (!ok) {
+                            outPutText += "***ERROR - METHOD " + prop.m_method + " .***";
+                        } else {
+                            outPutText += value;
+                        }
+                    } else {
+                        outPutText += "***ERROR - METHOD NOT FOUND " + prop.m_method + " .***";
+                    }
+                }
+                expr.clear();
+                continue;
+            }
+        }
+        if (start) {
+            expr.push_back(ch);
+        } else {
+            outPutText.push_back(ch);
+        }
+    }
+}
+
+QMap<QString, QString> CSTLCompilerAttributes::getAttributes(const QXmlStreamAttributes &attributes)
+{
+    QMap<QString, QString> attr;
+    for (int i = 0; i < attributes.size(); ++i) {
+        QString name(attributes[i].name().toString());
+        QString value(attributes[i].value().toString());
+        attr.insert(name, value);
+    }
+    return attr;
+}
+
+CWF_END_NAMESPACE

+ 34 - 0
CPPWebFramework/cwf/cstlcompilerattributes.h

@@ -0,0 +1,34 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CSTLCOMPILERATTRIBUTES_H
+#define CSTLCOMPILERATTRIBUTES_H
+
+#include <QMap>
+#include <QString>
+#include <QObject>
+#include <QXmlStreamAttributes>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+* @brief This class search for expressions #{obj.get} and compiles it.
+*/
+class CPPWEBFRAMEWORKSHARED_EXPORT CSTLCompilerAttributes
+{
+    QMap<QString, QObject *> &objects;
+public:
+    explicit CSTLCompilerAttributes(QMap<QString, QObject *> &objects);
+    QString buildAttributes(QMap<QString, QString> &attr, bool keyValue = true);
+    void compileAttributes(QMap<QString, QString> &attr);
+    void compile(QString &text, QString &outPutText);
+    QMap<QString, QString> getAttributes(const QXmlStreamAttributes &attributes);
+};
+
+CWF_END_NAMESPACE
+
+#endif // CSTLCOMPILERATTRIBUTES_H

+ 43 - 0
CPPWebFramework/cwf/cstlcompilerfor.cpp

@@ -0,0 +1,43 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "cstlcompilerfor.h"
+#include "constants.h"
+
+CWF_BEGIN_NAMESPACE
+
+CSTLCompilerFor::CSTLCompilerFor(const QXmlStreamAttributes &attr)
+{
+    for (int i = 0; i < attr.size(); ++i) {
+        QString name(attr[i].name().toString().toLower());
+        QString value(attr[i].value().toString());
+        if (name != CSTL::TAG::PROPERTY::FOR::ITEMS && name != CSTL::TAG::PROPERTY::VAR
+            && name != CSTL::TAG::PROPERTY::FOR::FROM && name != CSTL::TAG::PROPERTY::FOR::TO
+            && name != CSTL::TAG::PROPERTY::FOR::INCREMENT) {
+            attributes.insert(CSTL::TAG::PROPERTY::ERROR,
+                              "***ERROR FOR TAG - FOR TAG DOESN'T PERMITS AN ATTRIBUTE CALLED "
+                                  + name + "***");
+            return;
+        }
+        attributes.insert(name, value);
+    }
+    if (!attributes.contains(CSTL::TAG::PROPERTY::FOR::ITEMS)
+        || !attributes.contains(CSTL::TAG::PROPERTY::VAR)) {
+        bool from, to, increment;
+        attributes[CSTL::TAG::PROPERTY::FOR::FROM].toInt(&from);
+        attributes[CSTL::TAG::PROPERTY::FOR::TO].toInt(&to);
+        attributes[CSTL::TAG::PROPERTY::FOR::INCREMENT].toInt(&increment);
+
+        if (!(from && to && increment) || !attributes.contains(CSTL::TAG::PROPERTY::VAR)) {
+            attributes.insert(CSTL::TAG::PROPERTY::ERROR,
+                              "***ERROR FOR TAG - USE THE CORRECT ATTRIBUTES (FROM, TO, INCREMENT, "
+                              "VAR OR ITEMS, VAR)***");
+        }
+    }
+}
+
+CWF_END_NAMESPACE

+ 32 - 0
CPPWebFramework/cwf/cstlcompilerfor.h

@@ -0,0 +1,32 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CSTLCOMPILERFOR_H
+#define CSTLCOMPILERFOR_H
+
+#include <QMap>
+#include <QXmlStreamAttributes>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief Extracts and valites all attibutes from a "for" tag.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT CSTLCompilerFor
+{
+public:
+    QMap<QString, QString> attributes;
+    /**
+     * @brief This constructor processes and validates the attributes of "for" tags.
+     * @param const QXmlStreamAttributes &attr : XML tag attributes.
+     */
+    explicit CSTLCompilerFor(const QXmlStreamAttributes &attr);
+};
+
+CWF_END_NAMESPACE
+
+#endif // CSTLCOMPILERFOR_H

+ 59 - 0
CPPWebFramework/cwf/cstlcompilerif.cpp

@@ -0,0 +1,59 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "cstlcompilerif.h"
+#include "constants.h"
+
+CWF_BEGIN_NAMESPACE
+
+CSTLCompilerIf::CSTLCompilerIf(const QXmlStreamAttributes &attr)
+{
+    int size = attr.size();
+    if (size == 0 || size == 1) {
+        relationalOperator = RelationalOperator::ERROR;
+        attributes.insert(CSTL::TAG::PROPERTY::ERROR,
+                          "***ERROR IF TAG - IF TAG NEEDS TWO ATTRIBUTES ***");
+    } else if (size == 2) {
+        for (int i = 0; i < size; ++i) {
+            QString name(attr[i].name().toString().toLower());
+            QString value(attr[i].value().toString());
+            if (name == CSTL::TAG::PROPERTY::VAR) {
+                attributes.insert(CSTL::TAG::PROPERTY::VAR, value);
+            } else {
+                if (name == CSTL::TAG::PROPERTY::IF::EQUAL) {
+                    relationalOperator = RelationalOperator::EQUAL;
+                } else if (name == CSTL::TAG::PROPERTY::IF::DIFFERENT) {
+                    relationalOperator = RelationalOperator::DIFFERENT;
+                } else if (name == CSTL::TAG::PROPERTY::IF::GREATER) {
+                    relationalOperator = RelationalOperator::GREATER;
+                } else if (name == CSTL::TAG::PROPERTY::IF::GREATER_EQUAL) {
+                    relationalOperator = RelationalOperator::GREATER_EQUAL;
+                } else if (name == CSTL::TAG::PROPERTY::IF::LESS) {
+                    relationalOperator = RelationalOperator::LESS;
+                } else if (name == CSTL::TAG::PROPERTY::IF::LESS_EQUAL) {
+                    relationalOperator = RelationalOperator::LESS_EQUAL;
+                } else {
+                    relationalOperator = RelationalOperator::ERROR;
+                    attributes
+                        .insert(CSTL::TAG::PROPERTY::ERROR,
+                                "***ERROR IF TAG - IF TAG DOESN'T PERMITS AN ATTRIBUTE CALLED \""
+                                    + name + "\"***");
+                    break;
+                }
+
+                value.toDouble(&isNumber);
+                attributes.insert(CSTL::TAG::PROPERTY::CONDITION, value);
+            }
+        }
+    } else if (size > 2) {
+        relationalOperator = RelationalOperator::ERROR;
+        attributes.insert(CSTL::TAG::PROPERTY::ERROR,
+                          "***ERROR IF TAG - IF TAG DOESN'T PERMITS MORE THAN ONE ATTRIBUTE ***");
+    }
+}
+
+CWF_END_NAMESPACE

+ 44 - 0
CPPWebFramework/cwf/cstlcompilerif.h

@@ -0,0 +1,44 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CSTLCOMPILERIF_H
+#define CSTLCOMPILERIF_H
+
+#include <QMap>
+#include <QXmlStreamAttributes>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+
+enum class RelationalOperator : char {
+    EQUAL,
+    DIFFERENT,
+    GREATER,
+    GREATER_EQUAL,
+    LESS,
+    LESS_EQUAL,
+    ERROR
+};
+/**
+ * @brief Extracts and valites all attibutes from a "if" tag.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT CSTLCompilerIf
+{
+public:
+    RelationalOperator relationalOperator;
+    bool isNumber = false;
+    QMap<QString, QString> attributes;
+    /**
+     * @brief This constructor processes and validates the attributes of "if" tag.
+     * @param const QXmlStreamAttributes &attr : XML tag attributes.
+     */
+    explicit CSTLCompilerIf(const QXmlStreamAttributes &attr);
+};
+
+CWF_END_NAMESPACE
+
+#endif // CSTLCOMPILERIF_H

+ 42 - 0
CPPWebFramework/cwf/cstlcompilerimport.cpp

@@ -0,0 +1,42 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "cstlcompilerimport.h"
+#include "constants.h"
+#include "filemanager.h"
+
+CWF_BEGIN_NAMESPACE
+
+CSTLCompilerImport::CSTLCompilerImport(const QXmlStreamAttributes &attr, QString path)
+{
+    int size = attr.size();
+    if (size == 0) {
+        attributes.insert(CSTL::TAG::PROPERTY::ERROR,
+                          "***ERROR IMPORT TAG - IMPORT TAG NEEDS THE URL ATTRIBUTE***");
+    } else if (size == 1) {
+        QString name(attr[0].name().toString().toLower());
+        QString value(attr[0].value().toString());
+
+        if (name != CSTL::TAG::PROPERTY::IMPORT::URL) {
+            attributes.insert(CSTL::TAG::PROPERTY::ERROR,
+                              "***ERROR IMPORT TAG - IMPORT TAG NEEDS THE URL ATTRIBUTE***");
+        } else {
+            CWF::FileManager::removeLastBar(path);
+
+            path += value;
+
+            QFile::FileError fileError;
+            attributes.insert(CSTL::TAG::PROPERTY::IMPORT::URL,
+                              CWF::FileManager::readAll(path, fileError));
+        }
+    } else {
+        attributes.insert(CSTL::TAG::PROPERTY::ERROR,
+                          "***ERROR IMPORT TAG - IMPORT TAG ONLY NEEDS THE URL ATTRIBUTE***");
+    }
+}
+
+CWF_END_NAMESPACE

+ 34 - 0
CPPWebFramework/cwf/cstlcompilerimport.h

@@ -0,0 +1,34 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CSTLCOMPILERIMPORT_H
+#define CSTLCOMPILERIMPORT_H
+
+#include <QFile>
+#include <QMap>
+#include <QString>
+#include <QXmlStreamAttributes>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+
+/**
+ * @brief Extracts and valites all attibutes from a "import" tag.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT CSTLCompilerImport
+{
+public:
+    QMap<QString, QString> attributes;
+    /**
+     * @brief This constructor processes and validates the attributes of "import" tag.
+     */
+    CSTLCompilerImport(const QXmlStreamAttributes &attr, QString path);
+};
+
+CWF_END_NAMESPACE
+
+#endif // CSTLCOMPILERIMPORT_H

+ 57 - 0
CPPWebFramework/cwf/cstlcompilerobject.h

@@ -0,0 +1,57 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef CSTLCOMPILEROBJECT_H
+#define CSTLCOMPILEROBJECT_H
+//  clazy:excludeall=const-signal-or-slot
+#include <QObject>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief The Properties class is an auxiliar class to the CSTLCompiler.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT CSTLCompilerObject : public QObject
+{
+    Q_OBJECT
+    QString value;
+    QString type;
+
+public:
+    /**
+     * @brief This constructor can set the CSTLCompilerObject's parent.
+     * @param QObject *parent : parent.
+     */
+    explicit CSTLCompilerObject(QObject *parent = nullptr)
+        : QObject(parent)
+    {}
+public slots:
+    /**
+     * @brief Returns the value.
+     * @return QString : Value.
+     */
+    inline QString getValue() const noexcept { return value; }
+    /**
+     * @brief Sets the value.
+     * @param const QString &value : Value.
+     */
+    inline void setValue(const QString &value) noexcept { this->value = value; }
+    /**
+     * @brief Returns the type.
+     * @return QString : Type.
+     */
+    inline QString getType() const noexcept { return type; }
+    /**
+     * @brief Sets the type.
+     * @param const QString &value : Type.
+     */
+    inline void setType(const QString &value) noexcept { type = value; }
+};
+
+CWF_END_NAMESPACE
+
+#endif // CSTLCOMPILEROBJECT_H

+ 86 - 0
CPPWebFramework/cwf/filemanager.cpp

@@ -0,0 +1,86 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "filemanager.h"
+#include <QDir>
+
+CWF_BEGIN_NAMESPACE
+
+QString FileManager::extract(QString &name, char ch)
+{
+    QString fName;
+    for (int i = (name.size() - 1); i >= 0; --i) {
+        if (name[i] == ch)
+            break;
+        fName.push_front(name[i]);
+    }
+    return fName;
+}
+
+void FileManager::removeLastBar(QString &path)
+{
+    if (path.endsWith("/"))
+        path.remove(path.length() - 1, 1);
+}
+
+void FileManager::removeFirstBar(QString &path)
+{
+    if (path.startsWith("/"))
+        path.remove(0, 1);
+}
+
+void FileManager::putFirstBar(QString &path)
+{
+    if (!path.startsWith("/"))
+        path.push_front("/");
+}
+
+QByteArray FileManager::readAll(const QString &fileName, QFile::FileError &fileErro)
+{
+    QFile file(fileName);
+    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+        fileErro = file.error();
+        return file.errorString().toUtf8();
+    }
+    return file.readAll();
+}
+
+bool FileManager::copyDirectoryFiles(const QString &fromDir,
+                                     const QString &toDir,
+                                     bool coverFileIfExist)
+{
+    QDir sourceDir(fromDir);
+    QDir targetDir(toDir);
+    if (!targetDir.exists()) {
+        if (!targetDir.mkdir(targetDir.absolutePath()))
+            return false;
+    }
+
+    QFileInfoList fileInfoList(sourceDir.entryInfoList());
+    for (const QFileInfo &fileInfo : fileInfoList) {
+        if (fileInfo.fileName() == "." || fileInfo.fileName() == "..") {
+            continue;
+        }
+        if (fileInfo.isDir()) {
+            if (!copyDirectoryFiles(fileInfo.filePath(),
+                                    targetDir.filePath(fileInfo.fileName()),
+                                    coverFileIfExist)) {
+                return false;
+            }
+        } else {
+            if (coverFileIfExist && targetDir.exists(fileInfo.fileName())) {
+                targetDir.remove(fileInfo.fileName());
+            }
+            if (!QFile::copy(fileInfo.filePath(), targetDir.filePath(fileInfo.fileName()))) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+CWF_END_NAMESPACE

+ 36 - 0
CPPWebFramework/cwf/filemanager.h

@@ -0,0 +1,36 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef FILEMANAGER_H
+#define FILEMANAGER_H
+
+#include <QFile>
+#include <QString>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief The FileManager class can manage file's name.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT FileManager
+{
+public:
+    static QString extract(QString &name, char ch);
+    inline static QString fileName(QString &name) { return extract(name, '/'); }
+    inline static QString fileExtention(QString &name) { return extract(name, '.'); }
+    static void removeLastBar(QString &path);
+    static void removeFirstBar(QString &path);
+    static void putFirstBar(QString &path);
+    static QByteArray readAll(const QString &fileName, QFileDevice::FileError &fileErro);
+    static bool copyDirectoryFiles(const QString &fromDir,
+                                   const QString &toDir,
+                                   bool coverFileIfExist);
+};
+
+CWF_END_NAMESPACE
+
+#endif // FILEMANAGER_H

+ 10 - 0
CPPWebFramework/cwf/filter.cpp

@@ -0,0 +1,10 @@
+#include "filter.h"
+
+CWF_BEGIN_NAMESPACE
+
+void Filter::doFilter(Request &request, Response &response, FilterChain &chain)
+{
+    chain.doFilter(request, response);
+}
+
+CWF_END_NAMESPACE

+ 96 - 0
CPPWebFramework/cwf/filter.h

@@ -0,0 +1,96 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef FILTER_H
+#define FILTER_H
+
+#include "cppwebframework_global.h"
+#include "filterchain.h"
+#include "request.h"
+#include "response.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief The Filter class works like a filter. You can use this class to validate sessions or
+ * measuring runtime of a specific method, for example.
+ * To use this class, you will need to create a derived class and reconstruct the doFilter method,
+ * after this, you will need to configure it into the ConfigureCppWebServer, using the setFilter method.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT Filter
+{
+public:
+    /**
+     * @brief Virtual destructor.
+     */
+    virtual ~Filter() = default;
+    /**
+     * @brief This method will be called always that the CppWebServer receives a requisition.
+     * @param request  : This is a reference to the Request.
+     * @param response : This is a reference to the Response.
+     * @param chain    : This is a reference to the FilterChain.
+     * @par Example
+     * @code
+     * //----loginfilter.h-----
+     *
+     * #ifndef LOGINFILTER_H
+     * #define LOGINFILTER_H
+     *
+     * #include "cwf/filter.h"
+     *
+     * class LoginFilter : public CWF::Filter
+     * {
+     * public:
+     *     void doFilter(CWF::Request &request, CWF::Response &response, CWF::FilterChain &chain)
+     *     {
+     *         QString url = request.getRequestURL();
+     *         if(url.endsWith(".css") || url.endsWith(".png") || url.endsWith(".jpg"))
+     *         {
+     *             chain.doFilter(request, response);
+     *         }
+     *         else if(url != "/login")
+     *         {
+     *             if(request.getSession().getAttribute("user") == nullptr || request.getSession().isExpired())
+     *             {
+     *                 request.getRequestDispatcher("/pages/login").forward(request, response);
+     *             }
+     *             else
+     *             {
+     *                 chain.doFilter(request, response);
+     *             }
+     *          }
+     *          else
+     *          {
+     *              chain.doFilter(request, response);
+     *          }
+     *     }
+     * };
+     *
+     * #endif // LOGINFILTER_H
+     *
+     * //----main.h-----
+     *
+     * #include <filter/loginfilter.h>
+     * #include "cwf/cppwebapplication.h"
+     *
+     * int main(int argc, char *argv[])
+     * {
+     *     CWF::CppWebApplication server(argc,
+     *                                   argv,
+     *                                   "PATH_TO_SERVER_FOLDER",
+     *                                   new LoginFilter);
+     *
+     *     return server.start();
+     * }
+     *
+     * @endcode
+     */
+    virtual void doFilter(CWF::Request &request, CWF::Response &response, FilterChain &chain);
+};
+
+CWF_END_NAMESPACE
+
+#endif // FILTER_H

+ 148 - 0
CPPWebFramework/cwf/filterchain.cpp

@@ -0,0 +1,148 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "filterchain.h"
+
+#include "configuration.h"
+#include "constants.h"
+#include "controller.h"
+#include "filemanager.h"
+
+CWF_BEGIN_NAMESPACE
+
+FilterChain::FilterChain(Controller *controller, const Configuration &configuration)
+    : controller(controller)
+    , configuration(configuration)
+{}
+
+void FilterChain::doFilter(CWF::Request &request, CWF::Response &response)
+{
+    if (controller != nullptr) {
+        const HttpParser &parser = request.getHttpParser();
+        if (parser.getMethod() == HTTP::METHOD::GET)
+            controller->doGet(request, response);
+        else if (parser.getMethod() == HTTP::METHOD::POST)
+            controller->doPost(request, response);
+        else if (parser.getMethod() == HTTP::METHOD::PUT)
+            controller->doPut(request, response);
+        else if (parser.getMethod() == HTTP::METHOD::DELETE)
+            controller->doDelete(request, response);
+        else if (parser.getMethod() == HTTP::METHOD::OPTIONS)
+            controller->doOptions(request, response);
+        else if (parser.getMethod() == HTTP::METHOD::TRACE)
+            controller->doTrace(request, response);
+    } else {
+        QString url = request.getRequestURL();
+        QString path = request.getPath();
+        const QString &extention = CWF::FileManager::fileExtention(url);
+
+        if (url == FILE_EXTENTION::BAR) {
+            request.getRequestDispatcher(configuration.getIndexPage()).forward(request, response);
+        } else if (extention == FILE_EXTENTION::HTML || extention == FILE_EXTENTION::HTM) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::TEXT_HTML_UTF8);
+        } else if (extention == FILE_EXTENTION::CSS || extention == FILE_EXTENTION::TXT
+                   || extention == FILE_EXTENTION::PHP) {
+            write(response,
+                  path,
+                  url,
+                  HTTP::CONTENT_TYPE,
+                  ("text/" + extention.toUtf8() + "; charset=UTF-8"));
+        } else if (extention == FILE_EXTENTION::ICO) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::IMAGE_MICROSOFT_ICO);
+        } else if (extention == FILE_EXTENTION::PNG || extention == FILE_EXTENTION::GIF
+                   || extention == FILE_EXTENTION::BMP) {
+            write(response, path, url, HTTP::CONTENT_TYPE, ("image/" + extention.toUtf8()));
+        } else if (extention == FILE_EXTENTION::JPE || extention == FILE_EXTENTION::JPG) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::IMAGE_JPEG);
+        } else if (extention == FILE_EXTENTION::TIFF || extention == FILE_EXTENTION::TIF) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::IMAGE_TIFF);
+        } else if (extention == FILE_EXTENTION::SVG || extention == FILE_EXTENTION::SVGZ) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::IMAGE_SVG_XML);
+        } else if (extention == FILE_EXTENTION::PDF || extention == FILE_EXTENTION::XML
+                   || extention == FILE_EXTENTION::JSON || extention == FILE_EXTENTION::ZIP) {
+            write(response, path, url, HTTP::CONTENT_TYPE, ("application/" + extention.toUtf8()));
+        } else if (extention == FILE_EXTENTION::MP3) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::AUDIO_MP3);
+        } else if (extention == FILE_EXTENTION::MP4) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::AUDIO_MP4);
+        } else if (extention == FILE_EXTENTION::FLV) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::VIDEO_FLV);
+        } else if (extention == FILE_EXTENTION::DOC) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_MSWORD);
+        } else if (extention == FILE_EXTENTION::RTF) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_RTF);
+        } else if (extention == FILE_EXTENTION::XLS) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_EXCEL);
+        } else if (extention == FILE_EXTENTION::PPT) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_POWER_POINT);
+        } else if (extention == FILE_EXTENTION::JS) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_JAVASCRIPT);
+        } else if (extention == FILE_EXTENTION::ODT) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_OPEN_DOCUMENT_TEXT);
+        } else if (extention == FILE_EXTENTION::ODS) {
+            write(response,
+                  path,
+                  url,
+                  HTTP::CONTENT_TYPE,
+                  HTTP::APPLICATION_OPEN_DOCUMENT_SPREADSHEET);
+        } else if (extention == FILE_EXTENTION::SWF) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_SHOCKWAVE_FLASH);
+        } else if (extention == FILE_EXTENTION::RAR) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_RAR_COMPRESSED);
+        } else if (extention == FILE_EXTENTION::EXE || extention == FILE_EXTENTION::MSI) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_MS_DOWNLOAD);
+        } else if (extention == FILE_EXTENTION::CAB) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_CAB_COMPRESSED);
+        } else if (extention == FILE_EXTENTION::PSD) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_PHOTOSHOP);
+        } else if (extention == FILE_EXTENTION::AI || extention == FILE_EXTENTION::EPS
+                   || extention == FILE_EXTENTION::PS) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_POSTSCRIPT);
+        } else if (extention == FILE_EXTENTION::WOFF || extention == FILE_EXTENTION::WOFF2) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_FONT_WOFF);
+        } else if (extention == FILE_EXTENTION::EOT || extention == FILE_EXTENTION::TTF) {
+            write(response, path, url, HTTP::CONTENT_TYPE, HTTP::APPLICATION_FONT_TTF);
+        } else if (extention == FILE_EXTENTION::INI) {
+            QString file(CWF::FileManager::fileName(url));
+            if (file != CONFIGURATION::CPP_WEB_INI)
+                write(response,
+                      path,
+                      url,
+                      HTTP::CONTENT_TYPE,
+                      ("text/" + extention.toUtf8() + "; charset=UTF-8"));
+            else if (configuration.getAccessServerPages())
+                write(response,
+                      path,
+                      url,
+                      HTTP::CONTENT_TYPE,
+                      ("text/" + extention.toUtf8() + "; charset=UTF-8"));
+            else
+                request.getRequestDispatcher(STATUS::STATUS_401).forward(request, response);
+        } else {
+            response.setStatus(Response::SC_NOT_FOUND, STATUS::NOT_FOUND);
+            response.addHeader("Content-Type; charset=UTF-8", "text/html");
+            request.getRequestDispatcher(STATUS::STATUS_404).forward(request, response);
+        }
+    }
+}
+
+void FilterChain::write(Response &response,
+                        const QString &path,
+                        const QString &url,
+                        const QByteArray &name,
+                        const QByteArray &value) const
+{
+    QFile file(path + url);
+    if (file.open(QIODevice::ReadOnly)) {
+        response.addHeader(name, value);
+        response.write(file.readAll());
+    } else {
+        response.sendError(0, file.errorString().toUtf8());
+    }
+}
+
+CWF_END_NAMESPACE

+ 62 - 0
CPPWebFramework/cwf/filterchain.h

@@ -0,0 +1,62 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef FILTERCHAIN_H
+#define FILTERCHAIN_H
+
+#include <QFile>
+
+#include "cppwebframework_global.h"
+
+#include "request.h"
+#include "requestdispatcher.h"
+#include "response.h"
+
+CWF_BEGIN_NAMESPACE
+class Controller;
+/**
+ * @brief The FilterChain class is responsable to dispatch a requisition.
+ * This class was built to work with Filter. Always when a Filter makes all the
+ * necessary validations, it can dispatches the requisition to the FilterChain.
+ * NOTE: It is a final class, you can't derive from it.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT FilterChain final
+{
+    Controller *controller = nullptr;
+    const Configuration &configuration;
+    /**
+     * @brief This method writes a response into the Response.
+     * @param response : This is a reference to the Response.
+     * @param path     : This is a reference to the QString.
+     * @param url      : This is a reference to the QString.
+     * @param name     : This is a reference to the QByteArray.
+     * @param value    : This is a reference to the QByteArray.
+     */
+    void write(Response &response,
+               const QString &path,
+               const QString &url,
+               const QByteArray &name,
+               const QByteArray &value) const;
+
+public:
+    /**
+     * @brief FilterChain
+     * @param controller
+     */
+    FilterChain(Controller *controller, const Configuration &configuration);
+    /**
+     * @brief This method dispaches a requisition to a Request or, if the requesition
+     * is for a file, it can reads and send the file through the Response.
+     * @param request  : This is a reference to the Request.
+     * @param response : This is a reference to the Response.
+     */
+    void doFilter(CWF::Request &request, CWF::Response &response);
+};
+
+CWF_END_NAMESPACE
+
+#endif

+ 242 - 0
CPPWebFramework/cwf/httpparser.cpp

@@ -0,0 +1,242 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "httpparser.h"
+#include <QDebug>
+#include <QMultiMap>
+#include <QNetworkRequest>
+#include "constants.h"
+#include "qglobal.h"
+
+CWF_BEGIN_NAMESPACE
+
+bool HttpParser::extractHeaderAndBody(QByteArray &httpMessage)
+{
+    int index = httpMessage.indexOf(HTTP::END_OF_MESSAGE);
+    if (index != -1) {
+        index += 4;
+        int size = httpMessage.size();
+        for (int it = index; it < size; ++it) {
+            body.push_back(httpMessage[it]);
+        }
+        httpMessage.remove(index, httpMessage.size());
+        valid = true;
+    } else {
+        valid = false;
+    }
+    return valid;
+}
+
+void HttpParser::doParseHttpHeader(QByteArray &httpMessage)
+{
+    httpMessage.replace("\r", "");
+    QByteArrayList lines(httpMessage.split('\n'));
+    QByteArrayList firstHeaderLine;
+    if (!lines.empty())
+        firstHeaderLine = lines[0].split(' ');
+
+    if (firstHeaderLine.size() < 3) {
+        valid = false;
+        return;
+    }
+
+    method = firstHeaderLine[0].toUpper();
+    url = std::move(firstHeaderLine[1]);
+    httpVersion = std::move(firstHeaderLine[2]);
+    doParseUrl();
+
+    int size = lines.size();
+    for (int i = 1, column = 0; i < size; ++i) {
+        QByteArray &line = lines[i];
+        if (line.isEmpty())
+            continue;
+        column = line.indexOf(':');
+        headerField.insert(line.left(column).trimmed(), line.mid(column + 1).trimmed());
+    }
+
+    contentLenght = headerField.value(HTTP::CONTENT_LENGTH).toLongLong();
+    contentType = headerField.value(HTTP::CONTENT_TYPE);
+    multiPart = contentType.contains(HTTP::MULTIPART);
+    if (contentType.contains(HTTP::URLENCODED))
+        doParseBody();
+
+    extractCookies();
+    if (multiPart) {
+        qint64 bodySize = body.size();
+        readFile = bodySize == contentLenght;
+        if (readFile)
+            doParseFiles();
+    }
+
+    valid = true;
+}
+
+void HttpParser::doParseUrl()
+{
+    if (url.contains("?") && url.contains("=")) {
+        QByteArrayList realUrl(url.split('?'));
+        url = std::move(realUrl[0]);
+
+        QByteArrayList tempParam;
+        if (realUrl.size() > 1)
+            tempParam = realUrl[1].split('&');
+
+        int size = tempParam.size();
+        for (int i = 0; i < size; ++i) {
+            QByteArrayList p(tempParam[i].split('='));
+            if (p.size() == 2)
+                parameters.insert(p[0], p[1]);
+            else if (p.size() == 1)
+                parameters.insert(p[0], "");
+        }
+    }
+}
+
+void HttpParser::doParseBody()
+{
+    if (body.contains("&")) {
+        QByteArrayList tempBody(body.split('&'));
+        int size = tempBody.size();
+        for (int i = 0; i < size; ++i) {
+            QByteArrayList p(tempBody[i].split('='));
+            if (p.size() == 2)
+                parameters.insert(p[0], p[1]);
+            else if (p.size() == 1)
+                parameters.insert(p[0], "");
+        }
+    } else {
+        QByteArrayList p(body.split('='));
+        if (p.size() == 2)
+            parameters.insert(p[0], p[1]);
+        else if (p.size() == 1)
+            parameters.insert(p[0], "");
+    }
+}
+
+void HttpParser::extractCookies()
+{
+    QByteArrayList temp(headerField.values(HTTP::COOKIE));
+    int size = temp.size();
+    for (int i = 0; i < size; ++i) {
+        const QByteArray &txt = temp[i].replace(";", ";\n");
+        ;
+        QList<QNetworkCookie> cookiesList = QNetworkCookie::parseCookies(txt);
+        for (QNetworkCookie &cookie : cookiesList) {
+            if (cookie.name() == HTTP::SESSION_ID)
+                sessionId = cookie.value();
+            cookies.push_back(std::move(cookie));
+        }
+    }
+}
+
+void HttpParser::doParseFiles()
+{
+    // --------------------------493060581858925222102364
+    /*
+----------------------------493060581858925222102364\r\n
+Content-Disposition: form-data; name=\"file\"; filename=\"a\"\r\n
+Content-Type: application/octet-stream\r\n
+\r\n
+n
+\r\n
+----------------------------493060581858925222102364\r\n
+Content-Disposition: form-data; name=\"file\"; filename=\"aa\"\r\n
+Content-Type: application/octet-stream\r\n
+\r\n
+1\r\n2\r\n
+\r\n
+----------------------------493060581858925222102364--\r\n
+*/
+    // 提取 boundary
+    const QString ContentType = headerField.value("Content-Type");
+    QByteArray boundary = "--------";
+    const QStringList ContentTypeList = ContentType.split(";");
+    for (const QString &type : ContentTypeList) {
+        if (type.indexOf("boundary") != -1) {
+            boundary = type.trimmed().toUtf8();
+            boundary = boundary.replace("\"", "");
+            boundary = boundary.replace("boundary=", "");
+        }
+    }
+    const QByteArray endBoundary = "--" + boundary + "--";
+
+    QByteArray body = this->body;
+
+    QByteArrayList cont(body.split('\n'));
+    body.clear();
+
+    int total = cont.size();
+    QByteArray fileName;
+
+    bool isContent = false;
+    for (int i = 0; i < total; ++i) {
+        QByteArray &temp = cont[i];
+        if (temp.contains(endBoundary)) {
+            break;
+        }
+        // 文件开始
+        if (temp.contains(boundary)) {
+            isContent = false;
+            if (!fileName.isEmpty()) {
+                body.chop(2);
+                files.insert(fileName, body);
+            }
+            fileName.clear();
+            body.clear();
+            continue;
+        }
+        if (temp.contains(HTTP::CONTENT_DISPOSITION_COLON_SPACE)) {
+            temp.replace(HTTP::CONTENT_DISPOSITION_COLON, "")
+                .replace(HTTP::FORM_DATA_COLON_SPACE, "")
+                .replace("\r", "")
+                .replace("\"", "");
+            if (temp.contains(HTTP::FILENAME)) {
+                const QByteArrayList tmp(temp.split(';'));
+                for (int j = 0; j < tmp.size(); ++j) {
+                    QByteArrayList keyValue(tmp[j].split('='));
+                    if (keyValue.size() >= 2) {
+                        const QByteArray &key = keyValue[0];
+                        const QByteArray &value = keyValue[1];
+                        if (key.contains(HTTP::FILENAME)) {
+                            fileName = std::move(value);
+                            break;
+                        }
+                    }
+                }
+            }
+            isContent = true;
+        } else if (temp.contains(HTTP::CONTENT_TYPE)) {
+            isContent = true;
+        } else {
+            if (temp.size() > 0) {
+                char ch = temp.back();
+                if (ch == '\r') {
+                    if (isContent) {
+                        isContent = false;
+                        // 开始 ?
+                        continue;
+                    }
+                }
+            }
+            body += temp + "\n";
+        }
+    }
+    if (!fileName.isEmpty()) {
+        body.chop(2);
+        files.insert(fileName, body);
+    }
+}
+
+void HttpParser::doParse(QByteArray &httpMessage)
+{
+    //qDebug() << httpMessage;
+    if (extractHeaderAndBody(httpMessage)) {
+        doParseHttpHeader(httpMessage);
+    }
+}
+
+CWF_END_NAMESPACE

+ 168 - 0
CPPWebFramework/cwf/httpparser.h

@@ -0,0 +1,168 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef HTTPPARSER_H
+#define HTTPPARSER_H
+
+#include <QByteArray>
+#include <QMap>
+#include <QNetworkCookie>
+#include <QVector>
+#include "cppwebframework_global.h"
+#include "urlencoder.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief The class parses a HTTP message.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT HttpParser
+{
+    friend class HttpReadRequest;
+    qint64 contentLenght = 0;
+    QByteArray contentType;
+    QByteArray httpVersion;
+    QByteArray method;
+    QByteArray body;
+    QByteArray sessionId;
+    QByteArray url;
+    QMultiMap<QByteArray, QByteArray> parameters;
+    QMultiMap<QByteArray, QByteArray> headerField;
+    QMultiMap<QByteArray, QByteArray> files;
+    QVector<QNetworkCookie> cookies;
+    bool valid = false;
+    bool multiPart = false;
+    bool readFile = false;
+    bool extractHeaderAndBody(QByteArray &httpMessage);
+    void doParse(QByteArray &httpMessage);
+    void doParseHttpHeader(QByteArray &httpMessage);
+    void doParseUrl();
+    void doParseBody();
+    void doParseFiles();
+    void extractCookies();
+
+public:
+    /**
+     * @brief This constructor receives a HTTP message and parses it.
+     * @param QByteArray &httpMessage : HTTP message.
+     */
+    explicit HttpParser(QByteArray &httpMessage) { doParse(httpMessage); }
+    /**
+     * @brief Returns the content lenght.
+     * @return qint64 : Content length.
+     */
+    inline qint64 getContentLenght() const noexcept { return contentLenght; }
+    /**
+     * @brief Returns the content type.
+     * @return QByteArray : Content type.
+     */
+    inline QByteArray getContentType() const noexcept { return contentType; }
+    /**
+     * @brief Returns the HTTP version.
+     * @return QByteArray : HTTP version.
+     */
+    inline QByteArray getHttpVersion() const noexcept { return httpVersion; }
+    /**
+     * @brief Returns HTTP method.
+     * @return QByteArray : HTTP method.
+     */
+    inline QByteArray getMethod() const noexcept { return method; }
+    /**
+     * @brief Returns HTTP body message.
+     * @return QByteArray : HTTP body message.
+     */
+    inline QByteArray getBody() const noexcept { return body; }
+    /**
+     * @brief Returns session id.
+     * @return QByteArray : session id.
+     */
+    inline QByteArray getSessionId() const noexcept { return sessionId; }
+    /**
+     * @brief Returns the url.
+     * @return QByteArray : url.
+     */
+    inline QByteArray getUrl() const noexcept { return url; }
+    /**
+     * @brief Returns a specific parameter given a name.
+     * If the parameter name does not exists, the function returns defaultValue.
+     * If no defaultValue is specified, the function returns a default-constructed value.
+     * If there are multiple parameters with a name, the value of the most recently inserted one is returned.
+     * @param const QByteArray &name : Parameter name.
+     * @return QByteArray : Parameter value.
+     */
+    inline QByteArray getParameter(const QByteArray &name,
+                                   bool urlDecode = true,
+                                   bool replacePlusForSpace = true) const noexcept
+    {
+        return urlDecode
+                   ? URLEncoder::paramDecode(parameters.value(name), replacePlusForSpace).toUtf8()
+                   : parameters.value(name);
+    }
+    /**
+     * @brief Returns all parameters with a specific name.
+     * @param const QByteArray &name : Parameter name.
+     * @return QByteArrayList : Parameters list.
+     */
+    inline QByteArrayList getParameters(const QByteArray &name) const noexcept
+    {
+        return parameters.values(name);
+    }
+    /**
+     * @brief Returns all parameters.
+     * @return QMap<QByteArray, QByteArray> : Parameters name and value.
+     */
+    inline QMultiMap<QByteArray, QByteArray> getParameters() const noexcept { return parameters; }
+    /**
+     * @brief Returns all uploaded files.
+     * @return QMultiMap<QByteArray, QByteArray> : Files name and content.
+     */
+    inline QMultiMap<QByteArray, QByteArray> getUploadedFiles() const noexcept { return files; }
+    /**
+     * @brief Returns all cookies.
+     * @return QVector<QNetworkCookie> : Cookies.
+     */
+    inline QVector<QNetworkCookie> getCookies() const noexcept { return cookies; }
+    /**
+     * @brief Returns all header fields given a specific name.
+     * @param const QByteArray &headerField : Header field name.
+     * @return QByteArrayList : Header fields.
+     */
+    inline QByteArrayList getHeaderFields(const QByteArray &name) const noexcept
+    {
+        return headerField.values(name);
+    }
+    /**
+     * @brief Returns a specific header field given a name.
+     * If the header field name does not exists, the function returns defaultValue.
+     * If no defaultValue is specified, the function returns a default-constructed value.
+     * If there are multiple header field with a name, the value of the most recently inserted one is returned.
+     * @param const QByteArray &name : Parameter name.
+     * @return QByteArray : Parameter value.
+     */
+    inline QByteArray getHeaderField(const QByteArray &name) const noexcept
+    {
+        return headerField.value(name);
+    }
+    /**
+     * @brief Returns true if HTTP is valid, else it returns false.
+     * @return bool : HTTP message valid.
+     */
+    inline bool isValid() const noexcept { return valid; }
+    /**
+     * @brief Returns the multi part.
+     * @return bool : Multi part.
+     */
+    inline bool isMultiPart() const noexcept { return multiPart; }
+    /**
+     * @brief Returns true if all message was read.
+     * @return bool : Read file.
+     */
+    inline bool getReadFile() const noexcept { return readFile; }
+};
+
+CWF_END_NAMESPACE
+
+#endif // HTTPPARSER_H

+ 172 - 0
CPPWebFramework/cwf/httpreadrequest.cpp

@@ -0,0 +1,172 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "httpreadrequest.h"
+#include "configuration.h"
+#include "constants.h"
+#include "cwf/filter.h"
+#include "cwf/filterchain.h"
+
+CWF_BEGIN_NAMESPACE
+
+HttpReadRequest::HttpReadRequest(qintptr socketDescriptor,
+                                 QMapThreadSafety<QString, Controller *> &urlController,
+                                 QMapThreadSafety<QString, Session *> &sessions,
+                                 const Configuration &configuration,
+                                 QSslConfiguration *ssl,
+                                 Filter *filter)
+    : socketDescriptor(socketDescriptor)
+    , urlController(urlController)
+    , sessions(sessions)
+    , configuration(configuration)
+    , ssl(ssl)
+    , filter(filter)
+{}
+
+HttpReadRequest::~HttpReadRequest()
+{
+    delete socket;
+}
+
+bool HttpReadRequest::buildSslSocket()
+{
+#ifndef QT_NO_SSL
+    if (ssl) {
+        auto *sslSocket = new QSslSocket;
+        socket = sslSocket;
+        sslSocket->setSslConfiguration(*ssl);
+        sslSocket->setSocketDescriptor(socketDescriptor);
+        sslSocket->startServerEncryption();
+        return true;
+    }
+#endif
+    return false;
+}
+
+void HttpReadRequest::buildSocket()
+{
+    if (!buildSslSocket()) {
+        socket = new QTcpSocket;
+        socket->setSocketDescriptor(socketDescriptor);
+    }
+}
+
+void HttpReadRequest::run()
+{
+    buildSocket();
+    maxUploadFile = configuration.getMaxUploadFile();
+    socket->setReadBufferSize(maxUploadFile);
+
+    const QAbstractSocket::SocketState &state = socket->state();
+    if (state == QAbstractSocket::ConnectedState) {
+        if (socket->waitForReadyRead()) {
+            QByteArray req;
+            try {
+                req = socket->readAll();
+            } catch (const std::bad_alloc &e) {
+                qDebug() << e.what() << "\n";
+            }
+            HttpParser parser(req);
+            if (parser.valid) {
+                QString url = parser.url;
+                Request request(*socket, sessions, configuration);
+                Response response(*socket, configuration);
+                request.httpParser = &parser;
+                request.response = &response;
+
+                if ((parser.contentLenght > parser.body.size())) {
+                    if (!readBody(parser, request, response)) {
+                        return;
+                    }
+                }
+                Controller *controller = urlController.value(url, nullptr);
+                if (!controller) {
+                    for (QMapThreadSafety<QString, Controller *>::iterator it = urlController.begin();
+                         it != urlController.end();
+                         ++it) {
+                        const QString &key = it.key();
+                        if (key.endsWith('*')) {
+                            QString trueUrl(key.mid(0, key.size() - 1));
+                            if (url.startsWith(trueUrl)) {
+                                url = trueUrl + "*";
+                                controller = it.value();
+                                break;
+                            }
+                        }
+                    }
+                }
+                FilterChain chain(controller, configuration);
+                try {
+                    if (filter) {
+                        filter->doFilter(request, response, chain);
+                    } else {
+                        chain.doFilter(request, response);
+                    }
+                } catch (std::exception &e) {
+                    qDebug() << e.what();
+                } catch (...) {
+                    qDebug() << "An unknown exception has occurred\n\n";
+                }
+            }
+        }
+    }
+}
+
+bool HttpReadRequest::readBody(HttpParser &parser, Request &request, Response &response)
+{
+    qint64 contentLength = parser.contentLenght;
+    QByteArray content(std::move(parser.body));
+    int maximumTime = configuration.getTimeOut() / 2;
+    std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
+
+    while (true) {
+        if (socket->waitForReadyRead(10)) {
+            if (content.size() > maxUploadFile) {
+                socket->readAll(); // what if the  internal buffer throws a
+            } else {
+                try {
+                    content += socket->readAll();
+                } catch (const std::bad_alloc &e) {
+                    qDebug() << e.what() << "\n";
+                    break;
+                }
+            }
+        }
+
+        int spendTime = std::chrono::duration_cast<std::chrono::milliseconds>(
+                            std::chrono::high_resolution_clock::now() - start)
+                            .count();
+
+        if (content.size() > contentLength) {
+            content.remove(contentLength, content.size());
+            break;
+        }
+        if (content.size() == contentLength) {
+            break;
+        }
+        if (spendTime >= maximumTime) {
+            break;
+        }
+    }
+
+    if (content.size() > maxUploadFile) {
+        request.getRequestDispatcher(STATUS::STATUS_403).forward(request, response);
+        return false;
+    }
+
+    parser.body = std::move(content);
+
+    if (parser.contentType.contains(HTTP::APPLICATION_WWW_FORM_URLENCODED)) {
+        parser.doParseBody();
+    } else if (parser.multiPart) {
+        parser.doParseFiles();
+    }
+
+    return true;
+}
+
+CWF_END_NAMESPACE

+ 77 - 0
CPPWebFramework/cwf/httpreadrequest.h

@@ -0,0 +1,77 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef HTTPREADREQUEST_H
+#define HTTPREADREQUEST_H
+
+#include <QFile>
+#include <QMap>
+#include <QRunnable>
+#include <QSslConfiguration>
+#include <QStringList>
+#include <QTcpSocket>
+
+#include "httpparser.h"
+#include "request.h"
+#include "response.h"
+
+#include "cppwebframework_global.h"
+#include "qmapthreadsafety.h"
+
+CWF_BEGIN_NAMESPACE
+class Configuration;
+class Controller;
+class Session;
+class Filter;
+
+/**
+ * @brief The HttpReadRequest class is created automatically by the CppWebServer and inserted <br>
+ * in a QThreadPool, always when the CppWebServer has a call by a client(Browser).
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT HttpReadRequest : public QRunnable
+{
+    qintptr socketDescriptor;
+    QMapThreadSafety<QString, Controller *> &urlController;
+    QMapThreadSafety<QString, Session *> &sessions;
+    const Configuration &configuration;
+    QSslConfiguration *ssl;
+    Filter *filter;
+    QTcpSocket *socket = nullptr;
+    qint64 maxUploadFile{};
+    bool readBody(HttpParser &parser, Request &request, Response &response);
+    bool buildSslSocket();
+    void buildSocket();
+
+public:
+    /**
+     * @brief This constructor provides the necessary information to create a HttpReadRequest
+     * @param qintptr socketDescriptor                             : Used to create a socket.
+     * @param QMapThreadSafety<QString, Controller *> &urlController : All mapped controllers
+     * @param QMapThreadSafety<QString, Session *> &sessions   : Sessions.
+     * @param QSslConfiguration *sslConfiguration                  : SSL configuration.
+     * @param Filter *filter                                       : Filter
+     */
+    HttpReadRequest(qintptr socketDescriptor,
+                    QMapThreadSafety<QString, Controller *> &urlController,
+                    QMapThreadSafety<QString, Session *> &sessions,
+                    const Configuration &configuration,
+                    QSslConfiguration *ssl,
+                    Filter *filter);
+
+    /**
+     * @brief Destroys dynamically allocated resources.
+     */
+    ~HttpReadRequest() override;
+    /**
+     * @brief Starts to read the requisition.
+     */
+    void run() override;
+};
+
+CWF_END_NAMESPACE
+
+#endif // HTTPREADREQUEST_H

+ 92 - 0
CPPWebFramework/cwf/metaclassparser.cpp

@@ -0,0 +1,92 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "metaclassparser.h"
+
+CWF_BEGIN_NAMESPACE
+
+MetaClassParser::MetaClassParser(QObject *object, bool removeObjectName)
+{
+    const QMetaObject *meta = object->metaObject();
+    int total = meta->propertyCount();
+    for (int i = 0; i < total; ++i) {
+        QMetaProperty prop(meta->property(i));
+        QString propertyType(prop.typeName());
+        QString propertyName(prop.name());
+        if (removeObjectName && propertyName == "objectName")
+            continue;
+        properties.insert(propertyType + " " + propertyName, prop);
+        props.insert(QPair<QString, QString>(propertyType, propertyName), prop);
+    }
+
+    total = meta->methodCount();
+    for (int i = 0; i < total; ++i) {
+        QMetaMethod method(meta->method(i));
+        QString returnType(method.typeName());
+        std::tuple<QString, QString> signature(returnType, method.methodSignature());
+        methods.insert(signature, method);
+    }
+}
+
+QString MetaClassParser::getReturnType(const QString &methodName)
+{
+    for (QMap<std::tuple<QString, QString>, QMetaMethod>::iterator it = methods.begin();
+         it != methods.end();
+         ++it) {
+        const QString &name = std::get<1>(it.key());
+        if (name == methodName) {
+            return std::get<0>(it.key());
+        }
+    }
+    return "";
+}
+
+QString MetaClassParser::getParameterType(const QString &methodName)
+{
+    for (QMap<std::tuple<QString, QString>, QMetaMethod>::iterator it = methods.begin();
+         it != methods.end();
+         ++it) {
+        const QString &name = std::get<1>(it.key());
+        if (name.startsWith(methodName + "(")) {
+            return it.value()
+                .methodSignature()
+                .replace(methodName.toUtf8(), "")
+                .replace("(", "")
+                .replace(")", "");
+        }
+    }
+    return "";
+}
+
+QStringList MetaClassParser::getAllPropertiesNames() const
+{
+    QStringList temp;
+    temp.reserve(props.size());
+    for (const auto &it : props.keys()) {
+        temp.push_back(it.second);
+    }
+    return temp;
+}
+
+QMetaProperty MetaClassParser::findProperty(const QString &propertyName)
+{
+    for (auto it = props.begin(); it != props.end(); ++it) {
+        if (it.key().second == propertyName)
+            return it.value();
+    }
+    return {};
+}
+
+void *MetaClassParser::instantiateClassByName(const QByteArray &name)
+{
+    int id = QMetaType::type(name);
+    if (id != 0)
+        return QMetaType::create(id);
+    return nullptr;
+}
+
+CWF_END_NAMESPACE

+ 67 - 0
CPPWebFramework/cwf/metaclassparser.h

@@ -0,0 +1,67 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef METACLASSPARSER_H
+#define METACLASSPARSER_H
+
+#include "cppwebframework_global.h"
+
+#include <QDebug>
+#include <QMap>
+#include <QMetaMethod>
+#include <QMetaProperty>
+#include <QMetaType>
+#include <QObject>
+#include <QString>
+#include <tuple>
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief This class extracts all information from a QObject.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT MetaClassParser
+{
+public:
+    QMap<std::tuple<QString, QString>, QMetaMethod> methods;
+    QMap<QString, QMetaProperty> properties;
+    QMap<QPair<QString, QString>, QMetaProperty> props;
+    /**
+     * @brief Extracts all properties and methods names from a QObject.
+     * @param QObject *object : Object.
+     */
+    explicit MetaClassParser(QObject *object, bool removeObjectName = false);
+    /**
+     * @brief Returns the method return type given a method name.
+     * @param const QString &methodName : Method name.
+     * @return QString : Return type.
+     */
+    QString getReturnType(const QString &methodName);
+    /**
+     * @brief Returns the method parameter type given a method name.
+     * @param const QString &methodName : Method name.
+     * @return QString : Parameter type.
+     */
+    QString getParameterType(const QString &methodName);
+    /**
+     * @brief Returns all properties names.
+     */
+    QStringList getAllPropertiesNames() const;
+    /**
+     * @brief Tries to find a property.
+     */
+    QMetaProperty findProperty(const QString &propertyName);
+    /**
+     * @brief Instantiate a class by name.
+     * @param const QByteArray &name : Class name.
+     * @return void * : Returns nullptr if fails.
+     */
+    static void *instantiateClassByName(const QByteArray &name);
+};
+
+CWF_END_NAMESPACE
+
+#endif // METACLASSPARSER_H

+ 207 - 0
CPPWebFramework/cwf/model.cpp

@@ -0,0 +1,207 @@
+#include "model.h"
+
+CWF_BEGIN_NAMESPACE
+
+void Model::updateDB()
+{
+    verifyDbTableExist();
+    verifyDbFields();
+}
+
+void Model::build(const QMap<QString, QVariant> &selectCondition)
+{
+    // Get the data from the db and populate the model with them
+
+    const auto &properties = MetaClassParser(this, true).getAllPropertiesNames();
+    const auto &propValueMap = basicOperation.build(name, selectCondition, properties);
+
+    // Loop on all the prop value to update the current object
+    for (const auto &it : propValueMap.toStdMap()) {
+        setProperty(it.first.toStdString().c_str(), it.second);
+    }
+
+    // Depending on the content of propValueMap, we set the built flag to true or false.
+    // If the db query failed then the list is empty here.
+    built = !propValueMap.empty();
+}
+
+void Model::buildFromJson(const QJsonObject &json, bool withTableNamePrefix)
+{
+    const QStringList &props = MetaClassParser(this, true).getAllPropertiesNames();
+    // Loop on all props
+    for (const auto &prop : props) {
+        QString propName;
+
+        if (withTableNamePrefix)
+            propName = getTableName() + "_" + prop;
+        else
+            propName = prop;
+
+        const QJsonValue &jsonValue = json.value(propName);
+
+        if (jsonValue.isUndefined()) {
+            built = false;
+            break;
+        }
+        if (!jsonValue.isNull()) {
+            if (!setProperty(prop.toStdString().c_str(), jsonValue.toVariant())) {
+                qDebug() << prop << jsonValue;
+                built = false;
+                break;
+            }
+        }
+    }
+}
+
+bool Model::save()
+{
+    if (!preSaveCheck()) {
+        return false;
+    }
+
+    const auto &currentDt = QDateTime::currentDateTime();
+    if (id == -1) {
+        this->setProperty("createdDateTime", currentDt.toString(dtFormat));
+        setCreatedDt(currentDt);
+    }
+
+    this->setProperty("lastModifiedDateTime", currentDt.toString(dtFormat));
+    setLastModifiedDt(currentDt);
+    int id = basicOperation.save(name, computePropsMap(*this));
+    setProperty("id", id);
+
+    return id != -1;
+}
+
+void Model::customizeField(const QString &fieldName,
+                           const QVariant::Type &type,
+                           const QString &tableName) const
+{
+    Q_UNUSED(fieldName)
+    Q_UNUSED(type)
+    Q_UNUSED(tableName)
+}
+
+void Model::verifyDbTableExist()
+{
+    if (!basicOperation.isTableInDb("table_version")) {
+        if (!basicOperation.createVersionTable()) {
+            return;
+        }
+    }
+    if (!basicOperation.isTableInDb(name)) {
+        if (basicOperation.createTable(name)) {
+            qInfo() << "Model::verifyDbTableExist:"
+                    << "Table " << name << " was successfuly created";
+        }
+    }
+}
+
+void Model::verifyDbFields()
+{
+    MetaClassParser parser(this, true);
+    auto dbChangedPerformed = false;
+    const auto &fieldsFromDb = basicOperation.fields(name);
+    const auto &fieldsFromObject = parser.getAllPropertiesNames();
+    auto fieldsFromObjectTmp(fieldsFromObject);
+    for (auto &f : fieldsFromObjectTmp)
+        f = f.toLower();
+
+    for (auto it = parser.props.begin(); it != parser.props.end(); ++it) {
+        const auto &fieldName = it.key().second;
+        auto existInDb = fieldsFromDb.contains(fieldName);
+
+        if (!existInDb) {
+            qInfo() << "Model::verifyDbFields:"
+                    << "In" << name << "the field" << fieldName
+                    << "is present in the object but was not found in database."
+                    << "We update the database.";
+
+            dbChangedPerformed = true;
+
+            const auto &propertyType = this->propertyType(fieldName);
+
+            basicOperation.addFieldToTable(fieldName, propertyType, name);
+
+            customizeField(fieldName, propertyType, name);
+        }
+    }
+
+    // Security loop to check no field is missing
+    for (const auto &fieldName : fieldsFromDb) {
+        if (!fieldsFromObjectTmp.contains(fieldName.toLower())) {
+            qDebug() << "Model::verifyDbFields: The field " << fieldName
+                     << "was present in the DB and not in the object."
+                     << "It was probably deleted from the object property recently. "
+                     << "This is forbidden: a property should never be deleted because of db "
+                        "consistency";
+            qFatal("A property saved in the database was deleted");
+        }
+    }
+
+    // If we performed a change in the db
+    if (dbChangedPerformed) {
+        qInfo() << "The database was modified to update the table" << name << "."
+                << "We will update the version for this table.";
+
+        auto tableVersion = basicOperation.tableVersion(name);
+        tableVersion += 1;
+        basicOperation.changeTableVersion(name, tableVersion);
+    }
+}
+
+QMetaProperty Model::findProperty(const QString &propertyName)
+{
+    QMetaProperty emptyProperty;
+    auto found = MetaClassParser(this, true).findProperty(propertyName);
+
+    if (QString(found.name()) == QString(emptyProperty.name())) {
+        QString msg(QString("Model::findProperty: The property ") + propertyName
+                    + " was not found in " + name);
+        qDebug() << msg;
+        qFatal("Property should always be found here");
+    }
+
+    return found;
+}
+
+QMap<QString, QVariant> Model::computePropsMap(Model &model)
+{
+    QMap<QString, QVariant> propsMap;
+    const auto &properties = MetaClassParser(this, true).getAllPropertiesNames();
+
+    for (const auto &propName : properties) {
+        const auto &prop = model.findProperty(propName);
+        const auto &value = prop.read(&model);
+
+        if (!value.isValid()) {
+            qDebug() << "**** Error *****";
+            qDebug() << "Model::computePropsMap: propName invalid: " << propName;
+            qDebug() << "***************";
+        }
+
+        propsMap[propName] = value;
+    }
+
+    return propsMap;
+}
+
+void Model::setCreatedDt(const QDateTime &dt)
+{
+    if (dt != getCreatedDt()) {
+        createdDateTime = dt.toString(dtFormat);
+
+        createdDateTimeChanged();
+    }
+}
+
+void Model::setLastModifiedDt(const QDateTime &dt)
+{
+    if (dt != getLastModifiedDt()) {
+        lastModifiedDateTime = dt.toString(dtFormat);
+
+        lastModifiedDateTimeChanged();
+    }
+}
+
+CWF_END_NAMESPACE

+ 264 - 0
CPPWebFramework/cwf/model.h

@@ -0,0 +1,264 @@
+#ifndef MODEL_HH
+#define MODEL_HH
+
+#include "metaclassparser.h"
+#include "modelbasicoperation.h"
+#include "sqlquery.h"
+
+#include <QDateTime>
+#include <QDebug>
+#include <QMetaObject>
+#include <QMetaProperty>
+#include <QObject>
+#include <memory>
+
+CWF_BEGIN_NAMESPACE
+
+/**
+ * @brief The Model class implements basic ORM features and should be used as base class for every model (or entity).
+ *
+ * The class allows any derived model to be saved, retrieved or deleted from the database in very direct fashion
+ * (see the example below). By default, the class contains three properties that should not be overriden:
+ * - id : The id of the model. Its value is -1 when it the model is created or when the model could not be loaded from the database.
+ * - createdDateTime : The date at which the model was first created.
+ * - lastModifiedDateTime : The latest date at which the model was modified.
+ * These properties will be added to every model.
+ *
+ * Each model is represented by a table in the database which is identical to the name of the model.
+ *
+ * A typical model is defined as:
+ *
+ * ```cpp
+ * class UserModel : public Model
+ * {
+ *    Q_OBJECT
+ *
+ *    Q_PROPERTY(QString email MEMBER email NOTIFY emailChanged)
+ *
+ *    public:
+ *      UserModel() : Model("user") // Name of the model and database table
+ *      {
+
+ *      }
+ *      ~UserModel() override;
+ *
+ *    signals:
+ *      void emailChanged();
+ *
+ *    private:
+ *      QString email;
+ * }
+ * ```
+ *
+ * And can be used in a controller with:
+ * ```cpp
+ * // Create an empty instance of your model
+ * MyModel myModel;
+ *
+ * // Set some values
+ * myModel.setEmail(myEmail);
+ *
+ * // Save it in the database
+ * myModel.save();
+ *
+ * // Get the database id of the model just persisted.
+ * int myModelId = myModel.getId();
+ *
+ * // Create another instance
+ * MyModel myModel2;
+ *
+ * // Load the first instance data from the database
+ * myModel2.build(myModelId); // We use the id of the first instance
+ *
+ * // Check the object was loaded
+ * if(!myModel2.getWasBuild() )
+ * {
+ *      // Error: it was not loaded for some reason...
+ * }
+ *
+ * // Delete the object from the database
+ * myModel2.remove();
+ * ```
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT Model : public QObject
+{
+    Q_OBJECT
+protected:
+    ModelBasicOperation basicOperation;
+
+private:
+    Q_PROPERTY(qint64 id MEMBER id NOTIFY idChanged)
+    Q_PROPERTY(QString createdDateTime MEMBER createdDateTime NOTIFY createdDateTimeChanged)
+    Q_PROPERTY(
+        QString lastModifiedDateTime MEMBER lastModifiedDateTime NOTIFY lastModifiedDateTimeChanged)
+    /**
+     * @brief id The database id of the model
+     * The id is set to -1 by default. It may change if the model was populated with data from the database (the id will become the one
+     * of the entity stored in the database) or if a user manually set the variable before.
+     */
+    QString name;                                 ///< @brief name The name of the model
+    QString dtFormat = "dd/MM/yyyy hh:mm:ss.zzz"; ///< @brief Format of the date used in this class
+    QString createdDateTime;                      ///< @brief First save in the database date
+    QString lastModifiedDateTime;                 ///< @brief Last modified and persisted date
+    qint64 id = -1;
+    qint64 version;     ///< @brief The version of the table (database side)
+    bool built = false; ///< @brief Was the model instance populated with data ?
+private:
+    void verifyDbTableExist(); ///< @brief Check the database contains table corresponding to this model. Also create or update the table if needed.
+    void verifyDbFields(); ///< @brief Check the database table contains all required fields (columns). If not, create them.
+public:
+    /**
+      * @brief Constructor
+     */
+    explicit Model(SqlDatabaseStorage &connection, const QString &name)
+        : basicOperation(connection)
+        , name(name)
+    {}
+    /**
+      * @brief Destructor
+      */
+    virtual ~Model() = default;
+    /**
+     * @brief updateDB Will create or update the database schema to match the model class.     
+     * This allows to create a working table from scratch or to update one. Note that a table update can add new
+     * fields (Qt properties in the model code) but cannot remove any. Therefore, a developer should only add new
+     * properties to a model and never remove one.
+     */
+    void updateDB();
+    /**
+     * @brief build Populate the model with data from the database.
+     * @param id The id of the data to be used to populate the model. id = -1 indicates an insertion. id != -1 indicates an update.
+     */
+    inline void build(const qint64 &id = -1)
+    {
+        build({{"id", QVariant::fromValue(this->id = id)}});
+    }
+    /**
+     * @brief build Populate the model with data from the database.
+     * @param selectCondition A map ([propertyName] = propertyValue) of property values
+     * used to choose which data to insert in the model.
+     *
+     * The first entry of database table that matchs all the property values will be used to populate the model.
+     */
+    void build(const QMap<QString, QVariant> &selectCondition);
+    /**
+     * @brief buildFromJson Populate a model from json data
+     * @param json The data used to populate the model instance
+     * @param withTableNamePrefix Are the date properties prefixed with the name of this model db table ? This is false by default.
+     */
+    void buildFromJson(const QJsonObject &json, bool withTableNamePrefix = false);
+    /**
+     * @brief save Persist the model in the database
+     * @return Bool: Was the model correctly persisted ?
+     */
+    bool save();
+    /**
+     * @brief remove Delete the model from the database
+     * @return Bool: Was the data removed without error ?
+     */
+    inline bool remove() { return basicOperation.remove(getTableName(), id); }
+    /**
+     * @brief setCreatedDt Manually set the creation date
+     * @param dt Date of the model first save into the database.
+     */
+    void setCreatedDt(const QDateTime &dt);
+    /**
+     * @brief setLastModifiedDt Set the last date at which the model was persisted in the database.
+     * @param dt The date to be used.
+     */
+    void setLastModifiedDt(const QDateTime &dt);
+    /**
+     * @brief setWasBuild Set the "was build" flag that indicate if a model instance was correctly populated (build) by data.
+     * @param b The flag.
+     */
+    inline void setWasBuild(bool b) { built = b; }
+    /**
+     * @brief getWasBuild Get the value of the "was build" flag.
+     * @return Bool: Was the model instance populated by data from the database ?
+     */
+    inline bool getWasBuild() const { return built; }
+    /**
+     * @brief getId Get the id of the model
+     * @return qint64: The id of the model
+     */
+    inline qint64 getId() const { return id; }
+    /**
+     * @brief getCreatedDt Get the creation date of the model
+     * @return QDateTime: Creation date
+     */
+    inline QDateTime getCreatedDt() const
+    {
+        return QDateTime::fromString(createdDateTime, dtFormat);
+    }
+    /**
+     * @brief getCreatedDtStr Get the creation date as a string
+     * @return QString: Creation date as a string
+     */
+    inline QString getCreatedDtStr() const { return createdDateTime; }
+    /**
+     * @brief getLastModifiedDt Get the last date at which the model was modified and persisted in the database
+     * @return QDateTime: Last modified and persisted date
+     */
+    inline QDateTime getLastModifiedDt() const
+    {
+        return QDateTime::fromString(lastModifiedDateTime, dtFormat);
+    }
+    /**
+     * @brief getLastModifiedDtStr Get the last date at which the model was modified and persisted in the database
+     * @return QString: Last modified and persisted date as a string
+     */
+    inline QString getLastModifiedDtStr() const { return lastModifiedDateTime; }
+    /**
+     * @brief getTableName The name of database table corresponding to the model
+     * @return QString: Name of database table and of the model
+     */
+    inline QString getTableName() const { return name; }
+    /**
+     * @brief findProperty Find a property with its name
+     * @param propertyName The name of the property
+     * @return QMetaProperty: The property object
+     */
+    QMetaProperty findProperty(const QString &propertyName);
+    /**
+     * @brief propertyType Find the type of a property value from the property name
+     * @param propertyName The name of the property
+     * @return QVariant::Type: Type of the property
+     */
+    inline QVariant::Type propertyType(const QString &propertyName)
+    {
+        return findProperty(propertyName).type();
+    }
+    /**
+     * @brief computePropsMap Iterate on all the model properties to insert them in a map
+     * @param m The model that should be used (it often is "*this")
+     * @return QMap<QString, QVariant>: A map of all the model properties
+     */
+    QMap<QString, QVariant> computePropsMap(Model &model);
+    /**
+     * @brief toJson Convert the model data (its property name and value pairs) in JSON format
+     * @return QJsonObject: A JSON version of the model
+     */
+    inline QJsonObject toJson() { return QJsonObject::fromVariantMap(computePropsMap(*this)); }
+
+protected:
+    /**
+     * @brief preSaveCheck Perform some checks before persisting (saving) the model instance.
+     * The persisting is done only if the checks where successful.
+     * @return Bool: Was the check successful ?
+     */
+    virtual bool preSaveCheck() const { return true; }
+    /**
+     * @brief customizeField Allow to give special instructions when a field is created in the database.
+     */
+    virtual void customizeField(const QString &fieldName,
+                                const QVariant::Type &type,
+                                const QString &tableName) const;
+signals:
+    void idChanged();
+    void createdDateTimeChanged();
+    void lastModifiedDateTimeChanged();
+};
+
+CWF_END_NAMESPACE
+
+#endif // MODEL_HH

+ 446 - 0
CPPWebFramework/cwf/modelbasicoperation.cpp

@@ -0,0 +1,446 @@
+#include "modelbasicoperation.h"
+
+CWF_BEGIN_NAMESPACE
+
+ModelBasicOperation::ModelBasicOperation(SqlDatabaseStorage &connection)
+    : connection(connection)
+{}
+
+bool ModelBasicOperation::createTable(const QString &name)
+{
+    SqlQueryManager queryManager(connection);
+    QJsonObject json = queryManager.exec(queryManager.createTable(name));
+    bool success = json["success"].toBool();
+    if (!success) {
+        qFatal("%s",
+               ("ModelBasicOperation::createTable: CREATE TABLE " + name
+                + json["message"].toString())
+                   .toStdString()
+                   .data());
+    }
+
+    queryManager.reset();
+
+    queryManager.insert("table_version", "table_name, version_number");
+    queryManager.prepare();
+    queryManager.addBindValue(name);
+    queryManager.addBindValue(0);
+    json = queryManager.exec();
+    success = json["success"].toBool();
+    if (!success) {
+        qFatal("%s",
+               ("ModelBasicOperation::createTable: INSERT INTO table_version "
+                + json["message"].toString())
+                   .toStdString()
+                   .data());
+    }
+
+    return success;
+}
+
+bool ModelBasicOperation::createVersionTable()
+{
+    const auto &json
+        = SqlQueryManager(connection)
+              .exec("CREATE TABLE table_version (table_name TEXT, version_number INTEGER);");
+    bool success = json["success"].toBool();
+    if (!success) {
+        qFatal("ModelBasicOperation::createTable: CREATE TABLE table_version " //
+               "(tableName TEXT, version_number INTEGER);");
+    }
+    return success;
+}
+
+bool ModelBasicOperation::addFieldToTable(const QString &fieldName,
+                                          const QVariant::Type type,
+                                          const QString &tableName) const
+{
+    QString fieldTypeStr = convertQVariantTypeToSQLType(type);
+
+    SqlQueryManager queryManager(connection);
+    QJsonObject json = queryManager.exec(
+        queryManager.alterTable(tableName).addColumn(fieldName, fieldTypeStr).getQueryText());
+    bool success = json.value("success").toBool();
+    if (!success) {
+        qDebug() << "ModelBasicOperation::addFieldToTable:"
+                 << "ALTER TABLE" << json.value("message").toString();
+    }
+
+    return success;
+}
+
+bool ModelBasicOperation::changeTableVersion(const QString &tableName, qint32 version) const
+{
+    SqlQueryManager queryManager(connection);
+    QJsonObject json;
+
+    queryManager.update("table_version", "version_number=?").where("table_name=?");
+    queryManager.prepare();
+    queryManager.addBindValue(version);
+    queryManager.addBindValue(tableName);
+
+    json = queryManager.exec();
+    bool success = json["success"].toBool();
+    if (!success) {
+        qFatal("%s",
+               ("ModelBasicOperation::changeTableVersion: Error in UPDATE version message:"
+                + json["message"].toString())
+                   .toStdString()
+                   .data());
+    }
+
+    return success;
+}
+
+QStringList ModelBasicOperation::fields(const QString &tableName) const
+{
+    QStringList output;
+    const auto &record = connection.getDatabase().record(tableName);
+    for (int i = 0, numOfFields = record.count(); i < numOfFields; i++) {
+        output.push_back(record.fieldName(i));
+    }
+    return output;
+}
+
+qint32 ModelBasicOperation::tableVersion(const QString &tableName) const
+{
+    SqlQueryManager queryManager(connection);
+    QJsonObject json;
+    QJsonArray jsonArray;
+
+    queryManager.select("version_number", "table_version").where("table_name=?");
+    queryManager.prepare();
+    queryManager.addBindValue(tableName);
+    json = queryManager.exec();
+
+    if (!json.value("success").toBool()) {
+        qFatal("%s",
+               ("ModelBasicOperation::tableVersion: SELECT " + json["message"].toString())
+                   .toStdString()
+                   .data());
+    }
+    auto array = queryManager.toJson();
+    if (array.isEmpty()) {
+        qFatal("%s",
+               ("ModelBasicOperation::tableVersion: Empty array " + json["message"].toString())
+                   .toStdString()
+                   .data());
+    }
+    int version = array.at(0).toObject()["version_number"].toInt();
+    return version;
+}
+
+QString ModelBasicOperation::convertQVariantTypeToSQLType(const QVariant::Type type) const
+{
+    QString output("");
+
+    switch (type) {
+    case QVariant::Invalid: {
+        qDebug() << "ModelBasicOperation::convertQVariantTypeToSQLType:"
+                 << "Type was invalid";
+        qFatal("Type was invalid");
+    }
+    case QVariant::Type::ByteArray: {
+        output = "BLOB";
+        break;
+    }
+    case QVariant::UInt:
+    case QVariant::Int: {
+        output = "INTEGER";
+        break;
+    }
+    case QVariant::Bool: {
+        output = "INTEGER";
+        break;
+    }
+    case QVariant::ULongLong:
+    case QVariant::LongLong: {
+        output = "INTEGER";
+        break;
+    }
+    case QVariant::String: {
+        output = "TEXT";
+        break;
+    }
+    case QVariant::Double: {
+        output = "REAL";
+        break;
+    }
+    default: {
+        qDebug() << "ModelBasicOperation::convertQVariantTypeToSQLType:"
+                 << "Type was not defined " << type;
+        qFatal("Type was not defined");
+    }
+    }
+
+    return output;
+}
+
+qint64 ModelBasicOperation::save(const QString &tableName, const QMap<QString, QVariant> &map)
+{
+    QVariant idValue = map["id"];
+    qint64 id = idValue.toInt();
+
+    if (id == -1) {
+        id = insertEntry(tableName, map);
+    } else {
+        id = updateEntry(tableName, map);
+    }
+
+    return id;
+}
+
+QVector<QMap<QString, QVariant> > ModelBasicOperation::buildVector(
+    const QString &tableName,
+    const QMap<QString, QVariant> &selectCondition,
+    const QStringList &props,
+    const QString &orderBy)
+{
+    /* Example of insert query
+        SELECT id, type, datetime, entry, comment, status, priority, observerId, observationId FROM data WHERE id = ?;
+    */
+
+    CWF::SqlQuery query(connection);
+    QJsonObject json;
+
+    // *****************************
+    // Query contruction
+    // *****************************
+
+    QString what("");
+
+    // Loop on all the object properties
+    for (int i = 0, ie = props.size(); i < ie; i++) {
+        what += props.at(i);
+
+        if (i >= 0 && i < ie - 1)
+            what += ", ";
+    }
+
+    QString cond("");
+
+    QVector<QVariant> selectValues;
+    qint32 count = 0;
+    qint32 selectCondNum = selectCondition.size();
+
+    // Loop on all the select conditions
+    for (const auto &it : selectCondition.toStdMap()) {
+        const QString &name = it.first;
+        const QVariant &value = it.second;
+
+        cond += name + " = ?";
+
+        if (count >= 0 && count < selectCondNum - 1)
+            cond += " AND ";
+
+        selectValues.push_back(value);
+
+        count++;
+    }
+
+    SqlQueryManager queryManager(connection);
+    queryManager.select(what, tableName).where(cond).orderBy(orderBy);
+
+    QString queryText = queryManager.textQuery(true);
+
+    // Prepare the query
+    query.prepare(queryText);
+
+    // *****************************
+    // Bindings with values
+    // *****************************
+
+    // Loop on all the prop inserted in the sql query
+    for (const auto &v : selectValues) {
+        query.addBindValue(v);
+    }
+
+    // *****************************
+    // Run the query
+    // *****************************
+
+    json = query.exec();
+
+    if (!json.value("success").toBool()) {
+        qDebug() << "ModelBasicOperation::build:"
+                 << "Error in insert for table" << tableName << json.value("message").toString()
+                 << query.executedQuery();
+    }
+
+    // *****************************
+    // Get the result
+    // *****************************
+
+    QVector<QMap<QString, QVariant> > output;
+
+    if (query.first()) {
+        do {
+            QMap<QString, QVariant> map;
+
+            for (const QString &propName : props) {
+                QVariant val = query.value(propName);
+
+                map.insert(propName, val);
+            }
+
+            output.push_back(map);
+        } while (query.next());
+    }
+
+    return output;
+}
+
+QMap<QString, QVariant> ModelBasicOperation::build(const QString &tableName,
+                                                   const QMap<QString, QVariant> &selectCondition,
+                                                   const QStringList &props)
+{
+    QVector<QMap<QString, QVariant> > output = buildVector(tableName, selectCondition, props);
+    if (!output.empty())
+        return output.at(0);
+    return QMap<QString, QVariant>();
+}
+
+bool ModelBasicOperation::remove(const QString &tableName, const qint64 &id)
+{
+    CWF::SqlQuery query(connection);
+    SqlQueryManager qm(connection);
+    qm.remove(tableName, "id=?");
+
+    QString textQuery = qm.textQuery(true);
+
+    query.prepare(textQuery);
+    query.bindValue(0, id);
+
+    QJsonObject json;
+    json = query.exec();
+
+    if (!json.value("success").toBool()) {
+        qDebug() << "ModelBasicOperation::remove:"
+                 << "Error in index creation for table" << tableName
+                 << json.value("message").toString() << query.executedQuery();
+
+        return false;
+    }
+
+    return true;
+}
+
+bool ModelBasicOperation::createIndex(const QString &tableName,
+                                      const QString &column,
+                                      bool unique) const
+{
+    QString indexName = QString("index_") + tableName + QString("_") + column;
+
+    CWF::SqlQuery query(connection);
+    QJsonObject json;
+    SqlQueryManager queryManager(connection);
+    queryManager.createIndex(indexName, tableName, column, unique);
+
+    QString queryText = queryManager.textQuery(true);
+    query.prepare(queryText);
+
+    json = query.exec();
+
+    if (!json.value("success").toBool()) {
+        qDebug() << "ModelBasicOperation::createIndex:"
+                 << "Error in index creation for table" << tableName
+                 << json.value("message").toString() << query.executedQuery();
+
+        return false;
+    }
+
+    return true;
+}
+
+QString ModelBasicOperation::constructInsertTextQuery(const QString &tableName,
+                                                      const QMap<QString, QVariant> &map,
+                                                      QVector<QVariant> &values)
+{
+    QString fields;
+
+    for (const auto &it : map.toStdMap()) {
+        const QString &name = it.first.toLower();
+        const QVariant &value = it.second;
+
+        if (name == "id")
+            continue;
+
+        fields += name;
+        fields += ",";
+
+        values.push_back(value);
+    }
+    fields = fields.remove(fields.size() - 1, 1);
+
+    SqlQueryManager queryManager(connection);
+    queryManager.insert(tableName, fields);
+
+    QString queryText = queryManager.textQuery(true);
+
+    return queryText;
+}
+
+QString ModelBasicOperation::constructUpdateTextQuery(const QString &tableName,
+                                                      const QMap<QString, QVariant> &map,
+                                                      QVector<QVariant> &values)
+{
+    QString fieldValues;
+    for (const auto &it : map.toStdMap()) {
+        const QString &name = it.first;
+        const QVariant &value = it.second;
+
+        if (name == "id")
+            continue;
+        fieldValues += name + " = ?,";
+        values.push_back(value);
+    }
+    fieldValues = fieldValues.remove(fieldValues.size() - 1, 1);
+    SqlQueryManager queryManager(connection);
+    queryManager.update(tableName, fieldValues);
+    queryManager.where("id=?");
+
+    return queryManager.getQueryText() + ";";
+}
+
+qint64 ModelBasicOperation::insertEntry(const QString &tableName, const QMap<QString, QVariant> &map)
+{
+    CWF::SqlQuery query(connection);
+    QJsonObject json;
+    QVector<QVariant> values;
+    query.prepare(constructInsertTextQuery(tableName, map, values));
+
+    for (const auto &it : values) {
+        query.addBindValue(it);
+    }
+
+    json = query.exec();
+    if (!json["success"].toBool()) {
+        qDebug() << "ModelBasicOperation::insertEntry:"
+                 << "Error in insert for table" << tableName << json["message"].toString()
+                 << query.executedQuery();
+        return -1;
+    }
+
+    return query.lastInsertId().toInt();
+}
+
+qint64 ModelBasicOperation::updateEntry(const QString &tableName, const QMap<QString, QVariant> &map)
+{
+    CWF::SqlQuery query(connection);
+    QVector<QVariant> values;
+    query.prepare(constructUpdateTextQuery(tableName, map, values));
+    for (const auto &it : values) {
+        query.addBindValue(it);
+    }
+    int id = map.value("id").toInt();
+    query.addBindValue(map.value("id"));
+    QJsonObject json = query.exec();
+    if (!json["success"].toBool()) {
+        id = -1;
+        qDebug() << "ModelBasicOperation::updateEntry: Error in insert for table" << tableName
+                 << json["message"].toString();
+    }
+    return id;
+}
+
+CWF_END_NAMESPACE

+ 175 - 0
CPPWebFramework/cwf/modelbasicoperation.h

@@ -0,0 +1,175 @@
+#ifndef MODELBASICOPERATION_HH
+#define MODELBASICOPERATION_HH
+
+#include "sqlquery.h"
+#include "sqlquerymanager.h"
+
+#include <QMap>
+#include <QSqlField>
+#include <QVector>
+
+CWF_BEGIN_NAMESPACE
+
+/**
+ * @brief The ModelBasicOperation class implements several utility functions that are used in the Model class.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT ModelBasicOperation
+{
+protected:
+    SqlDatabaseStorage& connection;
+
+public:
+    /**
+     * @brief Contructor
+     */
+    explicit ModelBasicOperation(SqlDatabaseStorage& connection);
+    /**
+      * @brief Destructor
+      */
+    ~ModelBasicOperation() = default;
+    /**
+     * @brief createTable Create a table in the database
+     * @param name The name of the table to create
+     * @return Bool
+     */
+    bool createTable(const QString& name);
+    /**
+     * @brief Creates a version table.
+     * @return  Bool
+     */
+    bool createVersionTable();
+    /**
+     * @brief addFieldToTable Add a column in a database table
+     * @param fieldName The name of the column to add
+     * @param type The type of data stored in the column
+     * @param tableName The name of the database table
+     * @return Bool
+     */
+    bool addFieldToTable(const QString& fieldName,
+                         const QVariant::Type type,
+                         const QString& tableName) const;
+    /**
+     * @brief changeTableVersion Change the version number of a database table
+     * @param tableName The name of the table
+     * @param version The version to be saved
+     * @return Bool
+     */
+    bool changeTableVersion(const QString& tableName, qint32 version) const;
+    /**
+     * @brief tables List all the tables of a database
+     * @return QStringList: a list of all tables
+     */
+    inline QStringList tables() const { return connection.getDatabase().tables(); }
+    /**
+     * @brief fields List all the fields of a database table
+     * @param tableName The name of the database table
+     * @return QStringList
+     */
+    QStringList fields(const QString& tableName) const;
+    /**
+     * @brief tableVersion Get the version of the database table
+     * @param tableName The name of the database table
+     * @return qint32
+     */
+    qint32 tableVersion(const QString& tableName) const;
+    /**
+     * @brief convertQVariantTypeToSQLType Convert a given type (enum) to a string representing an sql type
+     * @param type The type of the data to be converted
+     * @return QString
+     */
+    QString convertQVariantTypeToSQLType(const QVariant::Type type) const;
+    /**
+     * @brief save Save the data contained in a map (given as an argument) in the given database table.
+     * @param tableName The name of the database table to receive a new entry
+     * @param map The data to be added
+     * @return qint64: The id of the new entry
+     */
+    qint64 save(const QString& tableName, const QMap<QString, QVariant>& map);
+    /**
+     * @brief buildVector Build a vector of map where each map represents an entry that was retrieved from the database
+     * @param tableName The name of the database table on which the method should work
+     * @param selectCondition A set of conditions to  filter the entries of the database table
+     * @param props The properties to be retrieved for each selected entry
+     * @param orderBy What is the order by criteria ?
+     * @return QVector<QMap<QString, QVariant> >
+     */
+    QVector<QMap<QString, QVariant> > buildVector(const QString& tableName,
+                                                  const QMap<QString, QVariant>& selectCondition,
+                                                  const QStringList& props,
+                                                  const QString& orderBy = "id");
+
+    /**
+     * @brief build Construct a map of properties retrieved from a single database entry
+     * @param tableName The name of the table with the entry
+     * @param selectCondition The conditions used to select the entry
+     * @param props The properties to retrieve from the entry
+     * @return QMap<QString, QVariant>
+     */
+    QMap<QString, QVariant> build(const QString& tableName,
+                                  const QMap<QString, QVariant>& selectCondition,
+                                  const QStringList& props);
+
+    /**
+     * @brief remove Delete an entry from the database
+     * @param tableName The table with the entry
+     * @param id The id of the entry to be deleted
+     * @return Bool
+     */
+    bool remove(const QString& tableName, const qint64& id);
+    /**
+     * @brief constructInsertTextQuery Build an insert query and return its text version
+     * @param tableName The name of the table in which an insertion should be done
+     * @param map The data to be inserted
+     * @param values The data to be inserted but sorted to match the field order
+     * @return QString
+     */
+    QString constructInsertTextQuery(const QString& tableName,
+                                     const QMap<QString, QVariant>& map,
+                                     QVector<QVariant>& values);
+    /**
+     * @brief constructUpdateTextQuery
+     * @param tableName
+     * @param map The data to be inserted
+     * @param values The data to be inserted but sorted to match the field order
+     * @return QString
+     */
+    QString constructUpdateTextQuery(const QString& tableName,
+                                     const QMap<QString, QVariant>& map,
+                                     QVector<QVariant>& values);
+    /**
+     * @brief createIndex Create an index for a given table and column
+     * @param tableName The name of the table to have an index
+     * @param column The name of the column (or field) which should be indexed
+     * @param unique Should the index be unique and enforce this ?
+     * @return Bool
+     */
+    bool createIndex(const QString& tableName, const QString& column, bool unique = false) const;
+    /**
+     * @brief isTableInDb Check if a table is present within the database
+     * @param tableName The name of the table
+     * @toUtf8ool
+     */
+    inline bool isTableInDb(const QString& tableName) const
+    {
+        return connection.getDatabase().tables().contains(QLatin1String(tableName.toUtf8()));
+    }
+
+private:
+    /**
+     * @brief insertEntry Insert an entry in a database table.
+     * @param tableName The name of the table to receive the data
+     * @param map The data
+     * @return qint64: The id of the new entry
+     */
+    qint64 insertEntry(const QString& tableName, const QMap<QString, QVariant>& map);
+    /**
+     * @brief updateEntry Update a database entry
+     * @param tableName The name of the database table to be updated
+     * @param map The data to be inserted in the entry. An id should be present here to select the correct entry.
+     */
+    qint64 updateEntry(const QString& tableName, const QMap<QString, QVariant>& map);
+};
+
+CWF_END_NAMESPACE
+
+#endif // MODELBASICOPERATION_HH

+ 27 - 0
CPPWebFramework/cwf/properties.cpp

@@ -0,0 +1,27 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "properties.h"
+
+CWF_BEGIN_NAMESPACE
+
+Properties::Properties(const QString &classAndMethod)
+{
+    QStringList temp = classAndMethod.split('.');
+    if(temp.size() == 2)
+    {
+        m_class  = temp[0];
+        m_method = temp[1];
+    }
+    else if(temp.size() == 1)
+    {
+        m_class  = temp[0];
+        m_method = "getValue";
+    }
+}
+
+CWF_END_NAMESPACE

+ 33 - 0
CPPWebFramework/cwf/properties.h

@@ -0,0 +1,33 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef PROPERTIES_H
+#define PROPERTIES_H
+
+#include <QString>
+#include <QStringList>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief The Properties class is an auxiliar class to the CSTLCompiler.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT Properties
+{
+public:
+    QString m_class;
+    QString m_method;
+    /**
+     * @brief Extracts class and method name.
+     * @param const QString &classAndMethod : Class and method name.
+     */
+    explicit Properties(const QString &classAndMethod);
+};
+
+CWF_END_NAMESPACE
+
+#endif // PROPERTIES_H

+ 25 - 0
CPPWebFramework/cwf/qlistobject.cpp

@@ -0,0 +1,25 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "qlistobject.h"
+
+CWF_BEGIN_NAMESPACE
+
+QListObject::~QListObject()
+{
+    if(!autoDelete)
+    {
+        const QObjectList &childrenList = children();
+
+        for(QObject *obj : childrenList)
+        {
+            obj->setParent(nullptr);
+        }
+    }
+}
+
+CWF_END_NAMESPACE

+ 85 - 0
CPPWebFramework/cwf/qlistobject.h

@@ -0,0 +1,85 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef QLISTOBJECT_H
+#define QLISTOBJECT_H
+
+#include <QObject>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief The QListObject class is used to pass a list of object to a view page.
+ * NOTE: Always when you need to pass a list of object to a view page you will need to use this class,
+ * your class need to inherit from the QObject class and all the methods needs to be in the public slots session.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT QListObject : public QObject
+{
+    Q_OBJECT
+private:
+    bool autoDelete = false;
+public:
+    /**
+     * @brief This constructor can receive a parent.
+     * @param QObject *parent : Parent.
+     */
+    explicit QListObject(QObject *parent = nullptr) : QObject(parent) {}
+    /**
+     * @brief Contructs a QListObject with N elements.
+     */
+    explicit QListObject(QObject *parent, const std::initializer_list<QObject *> &objects) : QObject(parent) { add(objects); }
+    /**
+     * @brief Contructs a QListObject with N elements.
+     */
+    explicit QListObject(const std::initializer_list<QObject *> &objects) : QObject(nullptr) { add(objects); }
+    /**
+     * @brief Destructor.
+     */
+    ~QListObject();
+    /**
+     * @brief This is an operator overload and returns a QObject given an specific index.
+     * @param index : This is an integer value.
+     * @return QObject *
+     */
+    inline QObject *operator [](int index) const { return children()[index]; }
+    /**
+     * @brief This method returns the number of elements in this QListObject.
+     * @return int
+     */
+    inline int size() const { return children().count(); }
+    /**
+     * @brief This method add a new QObject to the list.
+     * @param QObject *object : Object.
+     */
+    inline void add(QObject *object) { object->setParent(this); }
+    /**
+     * @brief This method add a N new QObjects to the list.
+     */
+    inline void add(const std::initializer_list<QObject *> &objects)
+    {
+        std::for_each(objects.begin(), objects.end(), [&](QObject *o){ o->setParent(this); });
+    }
+    /**
+     * @brief This method remove and object from the list.
+     * @param o
+     */
+    inline void remove(QObject *object) { object->setParent(nullptr); }
+    /**
+     * @brief getAutoDelete
+     * @return
+     */
+    inline bool getAutoDelete() const { return autoDelete; }
+    /**
+     * @brief setAutoDelete
+     * @param value
+     */
+    inline void setAutoDelete(bool value) { autoDelete = value; }
+};
+
+CWF_END_NAMESPACE
+
+#endif // QLISTOBJECT_H

+ 347 - 0
CPPWebFramework/cwf/qmapthreadsafety.h

@@ -0,0 +1,347 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef QMAPTHREADSAFETY_H
+#define QMAPTHREADSAFETY_H
+
+#include <QMap>
+#include <QPair>
+#include <QMutex>
+#include <QMutexLocker>
+#include <atomic>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+template <typename Key, typename T>
+/**
+ * @brief The QMapThreadSafety class is a thread safe QMap.
+ */
+class QMapThreadSafety
+{
+    mutable QMap<Key, T> m_map;
+    mutable QMutex mutex;
+public:
+    typedef typename QMap<Key, T>::iterator iterator;
+
+    typedef typename QMap<Key, T>::const_iterator const_iterator;
+
+    QMapThreadSafety() = default;
+
+    explicit QMapThreadSafety(std::initializer_list<std::pair<Key, T>> &list) : m_map(list){}
+
+    explicit QMapThreadSafety(const QMap<Key, T> &other) : m_map(other){}
+
+    explicit QMapThreadSafety(const std::map<Key, T> &other) : m_map(other){}
+
+    explicit QMapThreadSafety(QMap<Key, T> &&other) : m_map(other){}
+
+    /**
+     * @brief This method retuns the begin iterator.
+     * @return iterator
+     */
+    iterator begin() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.begin();
+    }
+
+    const_iterator cbegin() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.cbegin();
+    }
+
+    const_iterator cend() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.cend();
+    }
+
+    const_iterator constBegin() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.constBegin();
+    }
+
+    const_iterator constEnd() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.constEnd();
+    }
+
+    const_iterator constFind(const Key &key) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.constFind(key);
+    }
+    /**
+     * @brief This method checks if the map contains and specific element given a specific key.
+     * @param key : This represents the key that you want to find.
+     * @return returns true if find the key and false if not find.
+     */
+    bool contains(const Key &key) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.contains(key);
+    }
+
+    int count(const Key &key) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.count(key);
+    }
+
+    int count() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.count();
+    }
+
+    bool empty() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.empty();
+    }
+    /**
+     * @brief This method retuns the end iterator.
+     * @return iterator
+     */
+    iterator end()
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.end();
+    }
+
+    QPair<iterator, iterator> equal_range(const Key &key)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.equal_range(key);
+    }
+
+    iterator erase(iterator pos)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.erase(pos);
+    }
+
+    iterator find(const Key &key)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.find(key);
+    }
+
+    const_iterator find(const Key &key) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.find(key);
+    }
+
+    T & first()
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.first();
+    }
+
+    const T & first() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.first();
+    }
+
+    const Key & firstKey() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.firstKey();
+    }
+
+    /**
+     * @brief This method inserts a new key and value in the map.
+     * @param key   : This represents the key that will be insert.
+     * @param value : This represents the value that will be insert.
+     */
+    iterator insert(const Key &key, const T &value)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.insert(key, value);
+    }
+
+    iterator insert(const_iterator pos, const Key &key, const T &value)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.insert(pos, key, value);
+    }
+
+    iterator insertMulti(const Key &key, const T &value)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.insertMulti(key, value);
+    }
+
+    bool isEmpty() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.isEmpty();
+    }
+
+    QList<Key> keys() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.keys();
+    }
+
+    QList<Key> keys(const T &value) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.keys(value);
+    }
+
+    T &last()
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.last();
+    }
+
+    const T &last() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.last();
+    }
+
+
+    iterator lowerBound(const Key &key)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.lowerBound(key);
+    }
+
+    const_iterator lowerBound(const Key &key) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.lowerBound(key);
+    }
+    /**
+     * @brief This method removes a specific element given a specific key.
+     * @param key   : This represents the key that will be insert.
+     * @return int
+     */
+    int remove(const Key &key)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.remove(key);
+    }
+
+    int size() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.size();
+    }
+
+    void swap(QMap<Key, T> & other)
+    {
+        QMutexLocker locker(&mutex);
+        m_map.swap(other);
+    }
+
+    T take(const Key &key)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.take(key);
+    }
+
+    std::map<Key, T> toStdMap() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.toStdMap();
+    }
+
+    QList<Key> uniqueKeys() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.uniqueKeys();
+    }
+
+    QMap<Key, T> &unite(const QMap<Key, T> & other)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.unite(other);
+    }
+
+    iterator upperBound(const Key & key)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.upperBound(key);
+    }
+
+    const_iterator upperBound(const Key & key) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.upperBound(key);
+    }
+
+    const T value(const Key & key, const T & defaultValue = T()) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.value(key, defaultValue);
+    }
+
+    QList<T> values() const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.values();
+    }
+
+    QList<T> values(const Key & key) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.values(key);
+    }
+
+    bool operator!=(const QMap<Key, T> & other) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.operator !=(other);
+    }
+
+    QMap<Key, T> & operator=(const QMap<Key, T> & other)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.operator =(other);
+    }
+
+    QMap<Key, T> & operator=(QMap<Key, T> && other)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.operator =(other);
+    }
+
+    bool operator==(const QMap<Key, T> & other) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.operator ==(other);
+    }
+
+    T &	operator[](const Key & key)
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.operator [](key);
+    }
+
+    /**
+     * @brief This method is an overload of the operator [] and returns a value given a specific key.
+     * @param key : This represents the key that you want to find.
+     * @return T
+     */
+    const T operator [](const Key &key) const
+    {
+        QMutexLocker locker(&mutex);
+        return m_map.operator [](key);
+    }
+};
+
+CWF_END_NAMESPACE
+
+#endif // QMAPTHREADSAFETY_H

+ 163 - 0
CPPWebFramework/cwf/request.cpp

@@ -0,0 +1,163 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "request.h"
+#include <QUuid>
+#include "constants.h"
+#include "metaclassparser.h"
+#include "response.h"
+#include "urlencoder.h"
+//  clazy:excludeall=connect-not-normalized
+CWF_BEGIN_NAMESPACE
+
+Request::Request(QTcpSocket &socket,
+                 QMapThreadSafety<QString, Session *> &sessions,
+                 const Configuration &configuration)
+    : socket(socket)
+    , sessions(sessions)
+    , configuration(configuration)
+{}
+
+Request::~Request() noexcept
+{
+    delete requestDispatcher;
+}
+
+void Request::fillQObject(QObject *object, bool urlDecode, bool replacePlusForSpace)
+{
+    fillQObject(object, httpParser->getParameters(), urlDecode, replacePlusForSpace);
+}
+
+void Request::fillQObject(QObject *object,
+                          const QMap<QByteArray, QByteArray> &parameters,
+                          bool urlDecode,
+                          bool replacePlusForSpace)
+{
+    MetaClassParser meta(object);
+
+    for (QMap<QByteArray, QByteArray>::const_iterator it = parameters.constBegin();
+         it != parameters.constEnd();
+         ++it) {
+        QString method = it.key();
+        if (method.size() > 0) {
+            QString value = it.value();
+
+            method[0] = method[0].toUpper();
+            method = GET_SET::SET_LOWER + method;
+
+            QString parameterType(meta.getParameterType(method));
+
+            if (parameterType == CSTL::SUPPORTED_TYPES::QSTRING) {
+                if (urlDecode)
+                    value = URLEncoder::paramDecode(value.toUtf8(), replacePlusForSpace);
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(QString, value));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::STD_STRING) {
+                if (urlDecode)
+                    value = URLEncoder::paramDecode(value.toUtf8(), replacePlusForSpace);
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(std::string, value.toStdString()));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::BOOL) {
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(bool, value.toInt() == 0));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::CHAR) {
+                if (value.isEmpty())
+                    value.push_back(' ');
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(char, value.toStdString()[0]));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::UNSIGNED_CHAR) {
+                if (value.isEmpty())
+                    value.push_back(' ');
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(unsigned char,
+                                                static_cast<unsigned char>(value.toStdString()[0])));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::SHORT) {
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(short, static_cast<short>(value.toInt())));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::UNSIGNED_SHORT) {
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(unsigned short,
+                                                static_cast<unsigned short>(value.toInt())));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::INT) {
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(int, value.toInt()));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::UNSIGNED_INT) {
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(unsigned int, value.toUInt()));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::LONG) {
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(long, value.toLong()));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::UNSIGNED_LONG) {
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(unsigned long, value.toULong()));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::LONG_LONG) {
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(long long, value.toLongLong()));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::UNSIGNED_LONG_LONG) {
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(unsigned long long, value.toULongLong()));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::FLOAT) {
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(float, value.toFloat()));
+            } else if (parameterType == CSTL::SUPPORTED_TYPES::DOUBLE) {
+                QMetaObject::invokeMethod(object,
+                                          method.toStdString().data(),
+                                          Q_ARG(double, value.toDouble()));
+            }
+        }
+    }
+}
+
+RequestDispatcher &Request::getRequestDispatcher(const QString &page)
+{
+    delete requestDispatcher;
+    requestDispatcher = new RequestDispatcher(configuration.getPath() + page);
+    return *requestDispatcher;
+}
+
+Session &Request::getSession()
+{
+    static QMutex mutex;
+    QMutexLocker locker(&mutex);
+    qint64 currentTimeInt = QDateTime::currentMSecsSinceEpoch();
+    qint64 expiration = configuration.getSessionExpirationTime();
+    if (!session) {
+        QByteArray sessionId = httpParser->getSessionId();
+        if (sessionId.isEmpty() || !sessions.contains(sessionId)) {
+            sessionId = QUuid::createUuid().toString().toLocal8Bit();
+            response->addCookie(QNetworkCookie(HTTP::SESSION_ID, sessionId));
+            session = new Session(sessionId, expiration);
+            session->creationTime = currentTimeInt;
+            session->sessionExpirationTime = (currentTimeInt + expiration);
+            sessions.insert(session->getId(), session);
+        } else {
+            session = sessions[sessionId];
+        }
+    }
+    session->expired = (currentTimeInt >= session->sessionExpirationTime);
+    session->lastAccessedTime = currentTimeInt;
+    if (!session->expired) {
+        session->sessionExpirationTime = (currentTimeInt + expiration);
+    }
+    return *session;
+}
+
+CWF_END_NAMESPACE

+ 346 - 0
CPPWebFramework/cwf/request.h

@@ -0,0 +1,346 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef Request_H
+#define Request_H
+
+#include <QThread>
+#include <QTcpSocket>
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonDocument>
+
+#include "httpparser.h"
+#include "session.h"
+#include "configuration.h"
+#include "requestdispatcher.h"
+#include "cppwebframework_global.h"
+
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief The Request class holds all information about a http request.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT Request
+{
+    friend class HttpReadRequest;
+    friend class RequestDispatcher;
+    QTcpSocket         &socket;
+    Session            *session            = nullptr;
+    HttpParser         *httpParser         = nullptr;
+    RequestDispatcher  *requestDispatcher  = nullptr;
+    Response           *response           = nullptr;
+    QMap<QString, QObject *> attributes;
+    QMapThreadSafety<QString, Session *> &sessions;
+    const Configuration &configuration;
+public:
+    /**
+     * @brief This constructor needs to receive a reference to a QTcpSocket and QByteArray.
+     * The parameter parent is optional.
+     * NOTE: The CppWebServer is responsable to create the QTcpSocket, and the HttpReadRequest is
+     * responsable to create a HttpReadRequest and a Response.
+     */
+    explicit Request(QTcpSocket &socket, QMapThreadSafety<QString, Session *> &sessions, const Configuration &configuration);
+    /**
+     * @brief Destroys dynamically allocated resources.
+     */
+    ~Request() noexcept;
+    /**
+     * @brief This method add attributes that will be passed to a view page.
+     * The object can be processed within a page using view CSTL. For this to be possible the object must
+     * inherit from QObject and methods and must be in session "public slots".
+     */
+    inline void addAttribute(const std::initializer_list<QPair<QString, QObject *>> &value) noexcept
+    {
+        std::for_each(value.begin(), value.end(), [&](const QPair<QString, QObject *> &v){attributes.insert(v.first, v.second);});
+    }
+    /**
+     * @brief This method add an attribute that will be passed to a view page.
+     * The object can be processed within a page using view CSTL. For this to be possible the object must
+     * inherit from QObject and methods and must be in session "public slots".
+     */
+    inline void addAttribute(const QString &name, QObject *value) noexcept { attributes.insert(name, value); }
+    /**
+     * @brief This method returns all the attributes of a HttpReadRequest.
+     */
+    inline QMap<QString, QObject *> getAttributes() const noexcept { return attributes; }
+    /**
+     * @brief This method returns a specific object given its name.     
+     */
+    inline const QObject *getAttribute(const QString &name) const noexcept { return attributes.contains(name) ? attributes[name] : nullptr; }
+    /**
+     * @brief Returns the request body.
+     */
+    inline const QByteArray getBody() const noexcept { return httpParser->getBody(); }
+    /**
+     * @brief Tries returns the body of the converted request to QJsonObject.
+     */
+    inline QJsonObject bodyToJsonObject() const noexcept { return QJsonDocument::fromJson(httpParser->getBody()).object(); }
+    /**
+     * @brief Tries returns the body of the converted request to QJsonArray.
+     */
+    inline QJsonArray bodyToJsonArray() const noexcept { return QJsonDocument::fromJson(httpParser->getBody()).array(); }
+    /**
+     * @brief This method returns a requestDispatcher given an specific page.
+     * @param page : This is a reference to a QByteArray.
+     * @return RequestDispatcher
+     */
+    RequestDispatcher &getRequestDispatcher(const QString &page);
+    /**
+     * @brief This method returns the http protocol.
+     * @return QByteArray
+     */
+    inline QByteArray getProtocol() const noexcept { return httpParser != nullptr ? httpParser->getHttpVersion() : ""; }
+    /**
+     * @brief This method will clear all the attributes.
+     */
+    inline void clearAttributes() noexcept { attributes.clear(); }
+    /**
+     * @brief This method set the HttpParser.
+     * @param httpParser
+     */
+    inline void setHttpParser(HttpParser &httpParser) noexcept { this->httpParser = &httpParser; }
+    /**
+     * @brief This method returns the HttpParser.
+     * @return HttpParser
+     */
+    inline HttpParser &getHttpParser() const noexcept { return *httpParser; }
+    /**
+     * @brief This method returns the requested url.
+     * @return QByteArray
+     */
+    inline QByteArray getRequestURL() const noexcept{ return httpParser != nullptr ? httpParser->getUrl() : ""; }
+    /**
+     * @brief This method returns the requested url.
+     * @return QByteArray
+     */
+    inline QByteArray getRequestURI() const noexcept { return httpParser != nullptr ? httpParser->getUrl() : ""; }
+    /**
+     * @brief This method returns the user's session.
+     */
+    Session &getSession();
+    /**
+     * @brief This method set the user's session.
+     * @return Session
+     */
+    inline void setSession(Session &session) noexcept { this->session = &session; }
+    /**
+     * @brief This method returns the most recent parameter from a request given an specific name.
+     * @param name : This is a reference to a QByteArray.
+     * @param decode : If true, decode the parameter.
+     * @return QByteArray
+     */
+    QByteArray getParameter(const QByteArray &name, bool urlDecode = true, bool replacePlusForSpace = true) const noexcept
+    {
+        return httpParser->getParameter(name, urlDecode, replacePlusForSpace);
+    }
+    /**
+     * @brief This method returns the parameters from a request given an specific name.
+     * @param name : This is a reference to a QByteArray.     
+     * @return QByteArray
+     */
+    inline QByteArrayList getParameters(const QByteArray &name) const noexcept { return httpParser->getParameters(name); }
+    /**
+     * @brief This method returns a reference to the current socket.
+     * @return QTcpSocket
+     */
+    inline QTcpSocket &getSocket() const noexcept { return socket; }
+    /**
+     * @brief This method returns the path.
+     * @return QString
+     */
+    inline QString getPath() const noexcept { return configuration.getPath(); }
+    /**
+     * @brief This method returns all the files that the user has sent.
+     * @return QMap<QByteArray, QByteArray>
+     */
+    inline QMultiMap<QByteArray, QByteArray> getUploadedFiles() const noexcept { return httpParser->getUploadedFiles(); }
+    /**
+     * @brief Fill a QObject using parameters of a HTTP message.
+     * @param QObject *object : Object to be filled.
+     * @par Example
+     * @code
+     * //----------------bmi.view----------------
+     *
+     * <?xml version="1.0" encoding="iso-8859-1" ?>
+     * <html>
+     *      <head>
+     *          <title>Body Mass Index (BMI)</title>
+     *      </head>
+     *      <body>
+     *          <form method="POST" action="/bmi">
+     *              Name<br/><input type="text" name="name"/><br/>
+     *              Mass(KG)<br/><input type="text" name="mass"/><br/>
+     *              Height(m)<br/><input type="text" name="height"/><br/><br/>
+     *              <input type="submit" name="submit" value="Calculate"/>
+     *          </form>
+     *      </body>
+     *  </html>
+     *
+     * //----------------bmiresults.view----------------
+     *
+     * <?xml version="1.0" encoding="iso-8859-1" ?>
+     * <html>
+     *      <head>
+     *          <title>Body Mass Index (BMI) - Results</title>
+     *      </head>
+     *      <body>
+     *          Name: <out value="#{user.getName}"/><br/>
+     *          Mass(KG): <out value="#{user.getMass}"/><br/>
+     *          Height(m): <out value="#{user.getHeight}"/><br/>
+     *          BMI: <out value="#{user.getBmi}"/><br/>
+     *          Category: <out value="#{user.getCategory}"/>
+     *      </body>
+     * </html>
+     *
+     * //----------------user.h----------------
+     *
+     * #ifndef USER_H
+     * #define USER_H
+     *
+     * #include <QObject>
+     * #include <QString>
+     *
+     * class User : public QObject
+     * {
+     *     Q_OBJECT
+     * private:
+     *     QString name;
+     *     QString category;
+     *     double mass = 0;
+     *     double height = 0;
+     *     double bmi = 0;
+     * public:
+     *     explicit User(QObject *parent = 0) : QObject(parent)
+     *     {
+     *     }
+     * public slots:
+     *     QString getName() const
+     *     {
+     *         return name;
+     *     }
+     *     void setName(const QString &value)
+     *     {
+     *         name = value;
+     *     }
+     *     QString getCategory() const
+     *     {
+     *         return category;
+     *     }
+     *     double getMass() const
+     *     {
+     *         return mass;
+     *     }
+     *     void setMass(double value)
+     *     {
+     *         mass = value;
+     *     }
+     *     double getHeight() const
+     *     {
+     *         return height;
+     *     }
+     *     void setHeight(double value)
+     *     {
+     *         height = value;
+     *     }
+     *     double getBmi()
+     *     {
+     *         bmi = height != 0 ? mass / (height * height) : 0;
+     *
+     *         if(bmi <= 15)
+     *         {
+     *             category = "Very severely underweight";
+     *         }
+     *         else if(bmi > 15 && bmi <= 16)
+     *         {
+     *             category = "Severely underweight";
+     *         }
+     *         else if(bmi > 16 && bmi <= 18.5)
+     *         {
+     *             category = "Underweight";
+     *         }
+     *         else if(bmi > 18.5 && bmi <= 25)
+     *         {
+     *             category = "Normal (healthy weight)";
+     *         }
+     *         else if(bmi > 25 && bmi <= 30)
+     *         {
+     *             category = "Overweight";
+     *         }
+     *         else if(bmi > 30 && bmi <= 35)
+     *         {
+     *             category = "Obese Class I (Moderately obese)";
+     *         }
+     *         else if(bmi > 35 && bmi <= 40)
+     *         {
+     *             category = "Obese Class II (Severely obese)";
+     *         }
+     *         else
+     *         {
+     *             category = "Obese Class III (Very severely obese)";
+     *         }
+     *
+     *         return bmi;
+     *     }
+     * };
+     *
+     * #endif // USER_H
+     *
+     * //----------------bmicontroller.h----------------
+     *
+     * #ifndef BMICONTROLLER_H
+     * #define BMICONTROLLER_H
+     *
+     * #include "cwf/controller.h"
+     * #include "cwf/request.h"
+     * #include "cwf/response.h"
+     * #include "entities/user.h"
+     *
+     * class BmiController : public CWF::Controller
+     * {
+     * public:
+     *     void doGet(CWF::Request &request, CWF::Response &response) override
+     *     {
+     *         request.getRequestDispatcher("/pages/bmi").forward(request, response);
+     *     }
+     *     void doPost(CWF::Request &request, CWF::Response &response) override
+     *     {
+     *         User user;
+     *         request.fillQObject(&user);
+     *         request.addAttribute("user", &user);
+     *         request.getRequestDispatcher("/pages/bmiresults.view").forward(request, response);
+     *     }
+     * };
+     *
+     * #endif // BMICONTROLLER_H
+     *
+     * //----------------main.cpp----------------
+     *
+     * #include <QCoreApplication>
+     * #include <cwf/cppwebapplication.h>
+     * #include <controllers/bmicontroller.h>
+     *
+     * int main(int argc, char *argv[])
+     * {
+     *     CWF::CppWebApplication server(argc, argv, "PATH_TO_SERVER_FOLDER");
+     *
+     *     server.addUrlController<BmiController>("/bmi");
+     *
+     *     return server.start();
+     * }
+     * @endcode
+     */
+    void fillQObject(QObject *object, bool urlDecode = true, bool replacePlusForSpace = true);
+
+    void fillQObject(QObject *object,
+                     const QMap<QByteArray, QByteArray> &parameters,
+                     bool urlDecode = true, bool replacePlusForSpace = true);
+};
+
+CWF_END_NAMESPACE
+
+#endif // Request_H

+ 20 - 0
CPPWebFramework/cwf/requestdispatcher.cpp

@@ -0,0 +1,20 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "requestdispatcher.h"
+#include "request.h"
+#include "response.h"
+#include "cstlcompiler.h"
+
+CWF_BEGIN_NAMESPACE
+
+void RequestDispatcher::forward(CWF::Request &request, CWF::Response &response)
+{    
+    response.write(CSTLCompiler(file.toUtf8(), request.getPath(), request.attributes).output());
+}
+
+CWF_END_NAMESPACE

+ 41 - 0
CPPWebFramework/cwf/requestdispatcher.h

@@ -0,0 +1,41 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef REQUESTDISPATCHER_H
+#define REQUESTDISPATCHER_H
+
+#include <QString>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+
+class Request;
+class Response;
+/**
+ * @brief The RequestDispatcher class can be used to dispatch a requisition to a page.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT RequestDispatcher
+{
+    QString file;
+public:
+    /**
+     * @brief This constructor receives a file name.
+     */
+    explicit RequestDispatcher(const QString &file) : file(file) {}
+    /**
+     * @brief This method will dispatch the page file specificated in path to the CSTLCompiler,
+     * the CSTLCompiler will compile the xhtml file and returns the result.
+     * After this, the RequestDispatcher will take the return and write it on the response.
+     * @param CWF::Request &request   : Used to process the response.
+     * @param CWF::Response &response : Used to response.
+     */
+    void forward(CWF::Request &request, CWF::Response &response);
+};
+
+CWF_END_NAMESPACE
+
+#endif // REQUESTDISPATCHER_H

+ 186 - 0
CPPWebFramework/cwf/response.cpp

@@ -0,0 +1,186 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "response.h"
+#include <QDateTime>
+#include "configuration.h"
+
+CWF_BEGIN_NAMESPACE
+
+Response::Response(QTcpSocket &socket, const Configuration &configuration)
+    : socket(socket)
+    , configuration(configuration)
+{}
+
+static void sendBytes(QTcpSocket &socket, const QByteArray &text, int timeOut)
+{
+    const QAbstractSocket::SocketState &state = socket.state();
+    if (state == QAbstractSocket::ConnectedState && text.size() > 0) {
+        socket.write(text, text.size());
+        socket.flush();
+
+        if (state == QAbstractSocket::ConnectedState) {
+            socket.waitForBytesWritten(timeOut);
+        }
+    }
+}
+
+static void buildHeadersString(QByteArray &temp, const QMap<QByteArray, QByteArray> &headers)
+{
+    QList<QByteArray> headersList(headers.keys());
+
+    for (const auto &i : headersList) {
+        temp.push_back(i);
+        temp.push_back(HTTP::SEPARATOR);
+        temp.push_back(headers.value(i));
+        temp.push_back(HTTP::END_LINE);
+    }
+}
+
+static void buildCookiesString(QByteArray &temp, const QVector<QNetworkCookie> &cookies)
+{
+    for (const auto &i : cookies) {
+        temp.push_back(HTTP::SET_COOKIE);
+        temp.push_back(i.toRawForm());
+        temp.push_back(HTTP::END_LINE);
+    }
+}
+
+static void sendHeaders(int statusCode,
+                        int timeOut,
+                        const QByteArray &statusText,
+                        QMap<QByteArray, QByteArray> &headers,
+                        QVector<QNetworkCookie> &cookies,
+                        QTcpSocket &socket)
+{
+    QByteArray temp(HTTP::HTTP_1_1);
+    temp.reserve(100);
+    temp.push_back(QByteArray::number(statusCode));
+    temp.push_back(' ');
+    temp.push_back(statusText);
+    temp.push_back(HTTP::END_LINE);
+
+    if (!headers.contains(HTTP::CONTENT_TYPE)) {
+        headers.insert(HTTP::CONTENT_TYPE, HTTP::TEXT_HTML_UTF8);
+    }
+
+    buildHeadersString(temp, headers);
+    buildCookiesString(temp, cookies);
+    temp.push_back(HTTP::END_LINE);
+    sendBytes(socket, temp, timeOut);
+}
+
+void Response::flushBuffer()
+{
+    const int max = 32768;
+    if (!content.isEmpty()) {
+        static QLocale englishLocale(QLocale::English);
+        int timeOut = configuration.getTimeOut();
+        bool biggerThanLimit = content.size() > max;
+        headers.insert(HTTP::CONTENT_LENGTH, QByteArray::number(content.size()));
+        headers.insert(HTTP::SERVER, HTTP::SERVER_VERSION);
+        headers.insert(HTTP::DATA,
+                       QByteArray(
+                           englishLocale
+                               .toString(QDateTime::currentDateTime(), "ddd, dd MMM yyyy hh:mm:ss")
+                               .toUtf8()
+                           + " GMT"));
+
+        if (!biggerThanLimit) {
+            sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket);
+            sendBytes(socket, content, timeOut);
+        } else {
+            headers.insert(HTTP::TRANSFER_ENCODING, HTTP::CHUNKED);
+            sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket);
+            int total = (content.size() / max) + 1, last = 0;
+
+            QVector<QByteArray> vetor;
+            for (int i = 0; i < total; ++i) {
+                vetor.push_back(content.mid(last, max));
+                last += max;
+            }
+
+            for (auto &i : vetor) {
+                QByteArray data(std::move(i));
+                if (!data.isEmpty()) {
+                    sendBytes(socket,
+                              (QByteArray::number(data.size(), 16) + HTTP::END_LINE),
+                              timeOut);
+                    sendBytes(socket, data, timeOut);
+                    sendBytes(socket, HTTP::END_LINE, timeOut);
+                }
+            }
+            sendBytes(socket, HTTP::END_OF_MESSAGE_WITH_ZERO, timeOut);
+        }
+        socket.disconnectFromHost();
+        // content.clear();
+    }
+}
+
+void Response::sendError(int sc, const QByteArray &msg)
+{
+    int timeOut = configuration.getTimeOut();
+    sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket);
+    sendBytes(socket,
+              "<html><body><h1>" + QByteArray::number(sc) + " " + msg + "</h1></body></html>",
+              timeOut);
+}
+
+void Response::sendJsonError(int sc, const QByteArray &msg)
+{
+    int timeOut = configuration.getTimeOut();
+    sendHeaders(statusCode, timeOut, statusText, headers, cookies, socket);
+
+    QString data = QString(R"__({"code": 500, "data": -1, "message": "%1", "status": false})__")
+                       .arg((QString::number(sc) + " " + msg));
+    sendBytes(socket, data.toUtf8(), timeOut);
+}
+
+void Response::write(const QJsonObject &json, bool writeContentType)
+{
+    if (writeContentType)
+        addHeader(CWF::HTTP::CONTENT_TYPE, CWF::HTTP::APPLICATION_JSON);
+    content = QJsonDocument(json).toJson();
+    flushBuffer();
+}
+
+void Response::write(const QJsonArray &array, bool writeContentType)
+{
+    if (writeContentType)
+        addHeader(CWF::HTTP::CONTENT_TYPE, CWF::HTTP::APPLICATION_JSON);
+    content = QJsonDocument(array).toJson();
+    flushBuffer();
+}
+
+void Response::write(QByteArray &&data)
+{
+    content = std::move(data);
+    flushBuffer();
+}
+
+void Response::write(const QByteArray &data, bool flush)
+{
+    content += data;
+    if (flush)
+        flushBuffer();
+}
+
+void Response::setStatus(int statusCode, const QByteArray &description)
+{
+    this->statusCode = statusCode;
+    statusText = description;
+}
+
+void Response::sendRedirect(const QByteArray &url)
+{
+    setStatus(Response::SC_SEE_OTHER, HTTP::SEE_OTHER);
+    addHeader(HTTP::LOCATION, url);
+    write(HTTP::REDIRECT, true);
+}
+
+
+CWF_END_NAMESPACE

+ 115 - 0
CPPWebFramework/cwf/response.h

@@ -0,0 +1,115 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef RESPONSE_H
+#define RESPONSE_H
+
+#include <QFile>
+#include <QJsonDocument>
+#include <QNetworkCookie>
+#include <QTcpSocket>
+#include <QTextStream>
+#include "constants.h"
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+
+class Configuration;
+/**
+ * @brief The Response class is responsable to response a Http request.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT Response
+{
+    QTcpSocket &socket;
+    const Configuration &configuration;
+    int statusCode = Response::SC_OK;
+    QByteArray content;
+    QByteArray statusText = HTTP::OK;
+    QMap<QByteArray, QByteArray> headers;
+    QVector<QNetworkCookie> cookies;
+
+public:
+    Response(QTcpSocket &socket, const Configuration &configuration);
+
+    ~Response() noexcept {}
+
+    void write(const QJsonObject &json, bool writeContentType = true);
+
+    void write(const QJsonArray &array, bool writeContentType = true);
+
+    void write(QByteArray &&data);
+
+    void write(const QByteArray &data, bool flush = true);
+
+    void sendError(int sc, const QByteArray &msg);
+    void sendJsonError(int sc, const QByteArray &msg);
+
+    void flushBuffer();
+
+    inline int getBufferSize() const noexcept { return content.size(); }
+    inline QByteArray getBuffer() const noexcept { return content; }
+
+    inline void addHeader(const QByteArray &name, const QByteArray &value) noexcept
+    {
+        headers.insert(name, value);
+    }
+
+    inline void addCookie(const QNetworkCookie &cookie) noexcept { cookies.push_back(cookie); }
+
+    void setStatus(int statusCode, const QByteArray &description);
+
+    void sendRedirect(const QByteArray &url);
+
+    enum {
+        SC_CONTINUE = 100,
+        SC_SWITCHING_PROTOCOLS = 101,
+
+        SC_OK = 200,
+        SC_CREATED = 201,
+        SC_ACCEPTED = 202,
+        SC_NON_AUTHORITATIVE_INFORMATION = 203,
+        SC_NO_CONTENT = 204,
+        SC_RESET_CONTENT = 205,
+        SC_PARTIAL_CONTENT = 206,
+        SC_MULTIPLE_CHOICES = 300,
+        SC_MOVED_PERMANENTLY = 301,
+        SC_MOVED_TEMPORARILY = 302,
+        SC_FOUND = 302,
+        SC_SEE_OTHER = 303,
+        SC_NOT_MODIFIED = 304,
+        SC_USE_PROXY = 305,
+        SC_TEMPORARY_REDIRECT = 307,
+        SC_BAD_REQUEST = 400,
+        SC_UNAUTHORIZED = 401,
+        SC_PAYMENT_REQUIRED = 402,
+        SC_FORBIDDEN = 403,
+        SC_NOT_FOUND = 404,
+        SC_METHOD_NOT_ALLOWED = 405,
+        SC_NOT_ACCEPTABLE = 406,
+        SC_PROXY_AUTHENTICATION_REQUIRED = 407,
+        SC_REQUEST_TIMEOUT = 408,
+        SC_CONFLICT = 409,
+        SC_GONE = 410,
+        SC_LENGTH_REQUIRED = 411,
+        SC_PRECONDITION_FAILED = 412,
+        SC_REQUEST_ENTITY_TOO_LARGE = 413,
+        SC_REQUEST_URI_TOO_LONG = 414,
+        SC_UNSUPPORTED_MEDIA_TYPE = 415,
+        SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416,
+        SC_EXPECTATION_FAILED = 417,
+        SC_INTERNAL_SERVER_ERROR = 500,
+        SC_NOT_IMPLEMENTED = 501,
+        SC_BAD_GATEWAY = 502,
+        SC_SERVICE_UNAVAILABLE = 503,
+        SC_GATEWAY_TIMEOUT = 504,
+        SC_HTTP_VERSION_NOT_SUPPORTED = 505
+    };
+};
+
+CWF_END_NAMESPACE
+
+#endif // RESPONSE_H

+ 76 - 0
CPPWebFramework/cwf/session.cpp

@@ -0,0 +1,76 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "session.h"
+
+#include <utility>
+#include "configuration.h"
+
+CWF_BEGIN_NAMESPACE
+
+Session::Session(QString id, qint64 sessionTimeOut) : id(std::move(id)),
+    sessionTimeOut(sessionTimeOut),
+    autoClearAttributes(false),
+    expired (false)
+{    
+}
+
+Session::~Session()
+{
+    QMutexLocker locker(&mutex);
+    if(autoClearAttributes)
+    {
+        for(QMapThreadSafety<QString, QObject*>::iterator it = attributes.begin(); it != attributes.end(); ++it)
+        {
+            delete it.value();
+        }
+    }
+}
+
+QStringList Session::getAttributeNames()
+{    
+    QStringList list;
+    for(QMapThreadSafety<QString, QObject*>::iterator it = attributes.begin(); it != attributes.end(); ++it)
+        list.push_back(it.key());
+    return list;
+}
+
+QString Session::getId() const
+{
+    QMutexLocker locker(&mutex);
+    return id;
+}
+
+void Session::validate()
+{
+    QMutexLocker locker(&mutex);
+    expired = 0;
+    qint64 currentTime     = QDateTime::currentMSecsSinceEpoch();
+    lastAccessedTime       = currentTime;
+    sessionExpirationTime  = currentTime + sessionTimeOut;
+}
+
+bool Session::isExpired()
+{
+    QMutexLocker locker(&mutex);
+    if(!expired)
+    {
+        qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
+        expired = currentTime >= sessionExpirationTime;
+    }
+    return expired;
+}
+
+void Session::setSessionTimeOut(qint64 value)
+{
+    if(value >= 0)
+    {
+        sessionTimeOut = value;
+    }
+}
+
+CWF_END_NAMESPACE

+ 116 - 0
CPPWebFramework/cwf/session.h

@@ -0,0 +1,116 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef SESSION_H
+#define SESSION_H
+
+#include <QMap>
+#include <atomic>
+#include <QMutex>
+#include <QObject>
+#include <QThread>
+#include <QDateTime>
+#include <QStringList>
+#include <QMutexLocker>
+#include "qmapthreadsafety.h"
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+
+class Configuration;
+class Request;
+
+/**
+ * @brief The Session class holds information about a client session.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT Session
+{
+    friend class HttpReadRequest;
+    friend class Request;
+    QString id;
+    QAtomicInteger<qint64> sessionTimeOut;
+    QAtomicInteger<qint64> creationTime;
+    QAtomicInteger<qint64> lastAccessedTime;
+    QAtomicInteger<qint64> sessionExpirationTime;
+    QAtomicInteger<qint64> timeOut;
+    QAtomicInteger<qint8>  autoClearAttributes = 0;
+    QAtomicInteger<qint8>  expired = 0;
+    QMapThreadSafety<QString, QObject*> attributes;
+    mutable QMutex mutex;
+public:
+    /**
+     * @brief Construct a session with a unique identifier
+     */
+    explicit Session(QString id, qint64 sessionTimeOut);
+
+
+    ~Session();
+    /**
+     * @brief Returns a session attribute given a name.
+     * @warning: If the parameter is not found, nullptr is returned.
+     */
+    QObject *getAttribute(const QString &name) const noexcept { return attributes.value(name, nullptr); }
+    /**
+     * @brief Returns a session attribute given a name.
+     */
+    QStringList getAttributeNames();
+    /**
+     * @brief getCreationTime
+     */
+    inline qint64 getCreationTime() const noexcept { return creationTime; }
+    /**
+     * @brief Returns the unique id
+     */
+    QString getId() const;
+    /**
+     * @brief Returns the time of the last session access.
+     */
+    inline qint64 getLastAccessedTime() const noexcept { return lastAccessedTime; }
+    /**
+     * @brief Make a valid session.
+     */
+    void validate();
+    /**
+     * @brief Make a invalid session.
+     */
+    inline void invalidate() noexcept { expired = 1; }
+    /**
+     * @brief Removes all the items that have the key key from the map.
+     * Returns the number of items removed which is usually 1 but will be 0
+     * if the key isn't in the map, or > 1 if insertMulti() has been used with the key.
+     */
+    inline int removeAttribute(const QString &name) noexcept { return attributes.remove(name); }
+    /**
+     * @brief This method add an attribute to the session.
+     */
+    inline void addAttribute(const QString &name, QObject *value) noexcept { attributes.insert(name, value); }
+    /**
+     * @brief getAutoClearAttributes
+     */
+    inline bool getAutoClearAttributes() const noexcept { return autoClearAttributes; }
+    /**
+     * @brief setAutoClearAttributes
+     */
+    inline void setAutoClearAttributes(bool value) noexcept { autoClearAttributes = value; }
+    /**
+     * @brief This method returns true if the session is expired otherwise returns false.
+     */
+    bool isExpired();
+    /**
+     * @brief Returns the session timeout.
+     */
+    inline qint64 getSessionTimeOut() const noexcept { return sessionTimeOut; }
+    /**
+     * @brief Sets the current session timeout.
+     * If value is negative then it will be configured according to sessionExpirationTime of the CPPWeb.ini file.
+     */
+    void setSessionTimeOut(qint64 value);
+};
+
+CWF_END_NAMESPACE
+
+#endif // SESSION_H

+ 212 - 0
CPPWebFramework/cwf/sqldatabasestorage.h

@@ -0,0 +1,212 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef SQLDATABASESTORAGE_H
+#define SQLDATABASESTORAGE_H
+#include <QDebug>
+#include <QSqlDatabase>
+#include <QSqlError>
+#include <QThreadStorage>
+#include <QUuid>
+#include "cppwebframework_global.h"
+#include <iostream>
+
+#if __cplusplus >= 201703L
+#include <optional>
+#endif
+#if __cplusplus == 201402L
+#include <experimental/optional>
+#endif
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief The SqlDatabaseStorage class allows you to reuse connections made to the database through the QSqlDatabase class within QThreadPool.
+ */
+
+class CPPWEBFRAMEWORKSHARED_EXPORT SqlDatabaseStorage
+{
+    class Database
+    {
+        friend class SqlDatabaseStorage;
+        //  Map database connection names the library user uses to database connection names
+        //  which are valid for this thread
+        std::map<QString, QSqlDatabase *>
+            DatabaseConnections;                          //Unique Name to Heap allocated Connection
+        std::map<QString, QString> trivialNameToUniqueID; //Names given by User to Unique Name
+                                                          //#if __cplusplus == 201402L
+        //        std::experimental::optional<QSqlDatabase> DBConnection; //C++ 14 Compatablity
+        //#endif
+        //#if __cplusplus >= 201703L
+        //        std::optional<QSqlDatabase> DBConnection;
+        //#endif
+    public:
+        Database() = default;
+        Database(Database &other)
+        {
+            DatabaseConnections = other.DatabaseConnections;
+            trivialNameToUniqueID = other.trivialNameToUniqueID;
+            //qDebug() << "Database Constructed";
+        }
+        ~Database()
+        {
+            // qDebug() << "Database Destructed";
+            if (!DatabaseConnections.empty()) {
+                //                const QString conName(DBConnection->connectionName());
+
+                // Remove all Database connnections this thread has and which will become invalid
+                for (auto const &ConnectionNamePair : DatabaseConnections) {
+                    auto DBConnection = ConnectionNamePair.second;
+                    DBConnection->close();
+                    delete DBConnection;
+                    qDebug() << "" << ConnectionNamePair.first << "" << ConnectionNamePair.second;
+                    QSqlDatabase::removeDatabase(ConnectionNamePair.first);
+                }
+            }
+        }
+    };
+    QString type;
+    QString hostName;
+    QString databaseName;
+    QString userName;
+    QString password;
+    int port;
+    QThreadStorage<Database> pool;
+
+public:
+    /**
+     * @brief This constructor receives informations to create a connection to the database.
+     * @param const QString &type : Driver type.
+     * @param const QString &hostName : Sets the connection's host name to host.
+     * @param const QString &databaseName : Sets the connection's database name to name.
+     * @param const QString &userName : Sets the connection's user name to name.
+     * @param const QString &password : Sets the connection's password to password.
+     * @param int port : Sets the connection's port number to port.
+     */
+    SqlDatabaseStorage(const QString &type = "",
+                       const QString &hostName = "",
+                       const QString &databaseName = "",
+                       const QString &userName = "",
+                       const QString &password = "",
+                       int port = 0)
+        : type(type)
+        , hostName(hostName)
+        , databaseName(databaseName)
+        , userName(userName)
+        , password(password)
+        , port(port)
+    {}
+
+    /**
+     * @brief Returns the type.
+     */
+    inline QString getType() const { return type; }
+
+    /**
+     * @brief Returns the password.
+     */
+    inline QString getPassword() const { return password; }
+
+    /**
+     * @brief Returns the host name.
+     */
+    inline QString getHostName() const { return hostName; }
+
+    /**
+     * @brief Returns the database's name.
+     */
+    inline QString getDatabaseName() const { return databaseName; }
+
+    /**
+     * @brief Returns the user's name.
+     */
+    inline QString getUserName() const { return userName; }
+
+    /**
+     * @brief Returns the port.
+     */
+    inline int getPort() const { return port; }
+
+    /**
+     * @brief Returns the existing connection to the current thread's database. If there is no open connection to the current thread, a new connection will be created and returned.
+     */
+    QSqlDatabase &getDatabase()
+    {
+        if (!pool.hasLocalData()) //Pool has no Local Data -> create DataBaseConnection
+        {
+            Database database;
+            QString UniqueID = QUuid::createUuid().toString();
+            auto DBConnection = new QSqlDatabase(QSqlDatabase::addDatabase(type, UniqueID));
+            DBConnection->setHostName(hostName);
+            DBConnection->setDatabaseName(databaseName);
+            DBConnection->setPort(port);
+            DBConnection->setUserName(userName);
+            DBConnection->setPassword(password);
+            if (!DBConnection->open()) {
+                qDebug() << DBConnection->lastError().text();
+                std::cout << "Database not openable \t" << databaseName.toStdString() << "\n";
+            }
+
+            pool.setLocalData(database);
+            pool.localData().DatabaseConnections.insert({UniqueID, DBConnection});
+            pool.localData().trivialNameToUniqueID.insert({databaseName, UniqueID});
+        } else { //Pool has Local Data
+            auto IteratorToUserDatabaseName = pool.localData().trivialNameToUniqueID.find(
+                databaseName);
+            QString NameOfDBConnectionToThread;
+            QString UniqueConnectionName;
+
+            if (IteratorToUserDatabaseName != pool.localData().trivialNameToUniqueID.end()) {
+                // this thread has a Connection to the Database
+                //                if (PublicDBName->second == pool.localData().DBConnection->connectionName()) {
+                //                    //already right connection
+                //                } else {
+                //                    //set the right connection
+                //                    NameOfDBConnectionToThread = IteratorToUserDatabaseName->first;
+                //                    UniqueConnectionName = IteratorToUserDatabaseName->second;
+                //                    //pool.localData().DBConnection->close();
+                //                    pool.localData().DBConnection = QSqlDatabase::database(UniqueConnectionName, false);
+                //                    pool.localData().DBConnection->setHostName(hostName);
+                //                    pool.localData().DBConnection->setDatabaseName(databaseName);
+                //                    pool.localData().DBConnection->setPort(port);
+                //                    pool.localData().DBConnection->setUserName(userName);
+                //                    pool.localData().DBConnection->setPassword(password);
+                //                    if(!pool.localData().DBConnection->open()) {
+                //                        qDebug() << pool.localData().DBConnection->lastError().text();
+                //                    }
+                //                }
+            } else {
+                //make new Database connection for this thread
+                QString UniqueID = QUuid::createUuid().toString();
+
+                auto DBConnection = new QSqlDatabase(QSqlDatabase::addDatabase(type, UniqueID));
+                DBConnection->setHostName(hostName);
+                DBConnection->setDatabaseName(databaseName);
+                DBConnection->setPort(port);
+                DBConnection->setUserName(userName);
+                DBConnection->setPassword(password);
+                if (!DBConnection->open()) {
+                    qDebug() << DBConnection->lastError().text();
+                    std::cout << "Database not openable \t" << databaseName.toStdString() << "\n";
+                }
+                pool.localData().DatabaseConnections.insert({UniqueID, DBConnection});
+                pool.localData().trivialNameToUniqueID.insert({databaseName, UniqueID});
+            }
+        }
+        //        std::cerr << "Name To ID Mapping";
+        //        for ( auto const & foo : pool.localData().trivialNameToUniqueID) {
+        //            std::cerr << "Name " << foo.first.toStdString()
+        //                      << "\tID " << foo.second.toStdString()
+        //                      << "\n";
+        //        }
+        auto UniqueName = pool.localData().trivialNameToUniqueID.at(databaseName);
+        return *pool.localData().DatabaseConnections.at(UniqueName);
+        //
+    }
+};
+
+CWF_END_NAMESPACE
+
+#endif // SQLDATABASESTORAGE_H

+ 152 - 0
CPPWebFramework/cwf/sqlquery.cpp

@@ -0,0 +1,152 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "sqlquery.h"
+#include <QSqlDriver>
+#include "qjsonvalue.h"
+
+CWF_BEGIN_NAMESPACE
+
+SqlQuery::SqlQuery(CWF::SqlDatabaseStorage &dbStorage)
+    : QSqlQuery(dbStorage.getDatabase())
+{}
+
+void SqlQuery::makeBinds(const QVector<QVariant> &col, const QString &sql)
+{
+    prepare(sql);
+    int total = col.size();
+    for (int i = 0; i < total; ++i) {
+        bindValue(i, col[i]);
+    }
+}
+
+QJsonObject SqlQuery::validate(const QJsonObject &json, const QString &table)
+{
+    if (json.isEmpty()) {
+        return QJsonObject{{"success", false}, {"message", "JSON is empty"}};
+    }
+    if (table.isEmpty()) {
+        return QJsonObject{{"success", false}, {"message", "Table name is empty"}};
+    }
+    return QJsonObject{{"success", true}};
+}
+
+void SqlQuery::loadColumns()
+{
+    int total = record().count();
+    columns.resize(total);
+    for (int i = 0; i < total; ++i) {
+        columns[i] = record().fieldName(i);
+    }
+}
+
+QJsonObject SqlQuery::exec()
+{
+    if (QSqlQuery::exec()) {
+        return QJsonObject{{"success", true}, {"message", "Success"}};
+    }
+    return QJsonObject{{"success", false}, {"message", lastError().text()}};
+}
+
+QJsonObject SqlQuery::exec(const QString &query)
+{
+    if (QSqlQuery::exec(query)) {
+        return QJsonObject{{"success", true}, {"message", "Success"}};
+    }
+    return QJsonObject{{"success", false}, {"message", lastError().text()}};
+}
+
+QJsonArray SqlQuery::toJson()
+{
+    QJsonArray array;
+    QString error(lastError().text());
+    if (!error.trimmed().isEmpty()) {
+        array.push_back(QJsonObject{{"success", false}, {"message", error}});
+    } else if (isSelect()) {
+        loadColumns();
+        first();
+        int total = columns.size();
+        while (isValid()) {
+            QJsonObject json;
+            for (int i = 0; i < total; ++i) {
+                bool isNull = record().isNull(i);
+                if (isNull) {
+                    json[columns[i]] = QJsonValue();
+                } else {
+                    const QVariant var = record().value(i);
+
+                    if (var.type() == QVariant::Bool)
+                        json[columns[i]] = var.toBool();
+                    else if (var.type() == QVariant::Int)
+                        json[columns[i]] = var.toInt();
+                    else if (var.type() == QVariant::LongLong)
+                        json[columns[i]] = var.toLongLong();
+                    else if (var.type() == QVariant::Double)
+                        json[columns[i]] = var.toDouble();
+                    else {
+                        json[columns[i]] = var.toString();
+                    }
+                }
+            }
+            array.push_back(json);
+            next();
+        }
+        first();
+    }
+    return array;
+}
+
+QJsonObject SqlQuery::deleteRecord(const QString &table, const QString &condition)
+{
+    return exec("delete from " + table + " where " + condition);
+}
+
+QJsonObject SqlQuery::insertFromJson(const QJsonObject &json, const QString &table)
+{
+    QJsonObject status(validate(json, table));
+    if (status["success"].toBool()) {
+        QString firstSql, secondSql;
+        const QString comma(","), questionMark("?");
+        QVector<QVariant> values;
+        values.reserve(json.count());
+        for (auto it = json.begin(); it != json.end(); ++it) {
+            const QString &key = it.key();
+            firstSql += key + comma;
+            secondSql += questionMark + comma;
+            values.push_back(it.value().toVariant());
+        }
+        firstSql.remove(firstSql.size() - 1, 1);
+        secondSql.remove(secondSql.size() - 1, 1);
+        makeBinds(values, "insert into " + table + "(" + firstSql + ") values(" + secondSql + ");");
+        return exec();
+    }
+    return status;
+}
+
+QJsonObject SqlQuery::updateFromJson(const QJsonObject &json,
+                                     const QString &table,
+                                     const QString &condition)
+{
+    QJsonObject status(validate(json, table));
+    if (status["success"].toBool()) {
+        QString sets;
+        const QString comma(","), questionMark("?"), equal("=");
+        QVector<QVariant> values;
+        values.reserve(json.count());
+        for (auto it = json.begin(); it != json.end(); ++it) {
+            const QString &key = it.key();
+            sets += key + equal + questionMark + comma;
+            values.push_back(it.value().toVariant());
+        }
+        sets.remove(sets.size() - 1, 1);
+        makeBinds(values, "update " + table + " set " + sets + " where " + condition);
+        return exec();
+    }
+    return status;
+}
+
+CWF_END_NAMESPACE

+ 86 - 0
CPPWebFramework/cwf/sqlquery.h

@@ -0,0 +1,86 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef SQLQUERY_H
+#define SQLQUERY_H
+
+#include <QVector>
+#include <QSqlQuery>
+#include <QSqlRecord>
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <QJsonDocument>
+#include "sqldatabasestorage.h"
+
+CWF_BEGIN_NAMESPACE
+
+/**
+ * @brief The SqlQuery class was created to facilitate integration with SqlDataBaseStorage and manipulation to the database through JSON.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT SqlQuery : public QSqlQuery
+{
+    QVector<QString> columns;
+
+    void loadColumns();
+
+    void makeBinds(const QVector<QVariant> &col, const QString &sql);
+
+    QJsonObject validate(const QJsonObject &json, const QString &table);
+public:
+    using QSqlQuery::QSqlQuery;
+    /**
+     * @brief Constructs a QSqlQuery object using the database dbStorage.
+     * @param dbStorage
+     */
+    explicit SqlQuery(SqlDatabaseStorage &dbStorage);
+    /**
+     * @brief Executes and returns a JSON indicating success, or failure through the key success.
+     * @return QJsonObject
+     */
+    QJsonObject exec();
+    /**
+     * @brief Executes a query and returns a JSON indicating success, or failure through the key "success" and "message".
+     * @return QJsonObject
+     */
+    QJsonObject exec(const QString &query);
+    /**
+     * @brief Returns the result of a select converted to a QJsonArray.
+     * If there is a failure, a JSON object with two fields will be returned: "success" and "message".
+     * If there are no results, QJsonArray will return a empty object.
+     * @return QJsonArray
+     */
+    QJsonArray toJson();
+    /**
+     * @brief Delete a record given a table and a condition.
+     * Returns a JSON indicating success, or failure through the key "success" and "message".
+     * @param table
+     * @param condition
+     * @return QJsonObject
+     */
+    QJsonObject deleteRecord(const QString &table, const QString &condition);
+    /**
+     * @brief Insert a record given a json and a table.
+     * Returns a JSON indicating success, or failure through the key "success" and "message".
+     * @param json
+     * @param table
+     * @return QJsonObject
+     */
+    QJsonObject insertFromJson(const QJsonObject &json, const QString &table);
+    /**
+     * @brief Update a record given a json, a table and a condition.
+     * Returns a JSON indicating success, or failure through the key "success" and "message".
+     * @param json
+     * @param table
+     * @return QJsonObject
+     */    
+    QJsonObject updateFromJson(const QJsonObject &json, const QString &table, const QString &condition);
+};
+
+CWF_END_NAMESPACE
+
+#endif // SQLQUERY_H

+ 208 - 0
CPPWebFramework/cwf/sqlquerymanager.cpp

@@ -0,0 +1,208 @@
+#include "sqlquerymanager.h"
+#include <QDebug>
+#include "metaclassparser.h"
+#include "model.h"
+#include "qsqldriver.h"
+
+CWF_BEGIN_NAMESPACE
+
+inline void addSpace(QString &value)
+{
+    if (!value.isEmpty() && !value.endsWith(" "))
+        value += " ";
+}
+
+inline void executeQry(const QJsonObject &json, const QString &queryText)
+{
+    if (!json["success"].toBool()) {
+        qDebug() << "SqlQueryManager::exec:"
+                 << "Error: " << json["message"].toString() << " query: " << queryText;
+    }
+}
+
+void SqlQueryManager::reset()
+{
+    queryText = "";
+    query.clear();
+    bindingDone = 0;
+}
+
+QString SqlQueryManager::createTable(const QString &tableName)
+{
+    const auto &type = connection.getType();
+    if (type == "QSQLITE") {
+        queryText = "CREATE TABLE " + tableName + " (id INTEGER PRIMARY KEY AUTOINCREMENT);";
+    } else if (type == "QPSQL") {
+        queryText = "CREATE TABLE " + tableName + " (id serial PRIMARY KEY);";
+    } else {
+        qFatal("%s", (type + " is not supported yet!").toStdString().data());
+    }
+    return queryText;
+}
+
+void SqlQueryManager::createIndex(const QString &indexName,
+                                  const QString &tableName,
+                                  const QString &field,
+                                  bool unique)
+{
+    queryText = "CREATE ";
+
+    if (unique)
+        queryText += "UNIQUE ";
+
+    queryText += "INDEX ";
+    queryText += indexName + " ON " + tableName + " ( " + field + " );";
+}
+
+SqlQueryManager &SqlQueryManager::alterTable(const QString &tableName)
+{
+    addSpace(queryText);
+    queryText += "ALTER TABLE " + tableName;
+    return *this;
+}
+
+SqlQueryManager &SqlQueryManager::addColumn(const QString &field, const QString &type)
+{
+    addSpace(queryText);
+    queryText += "ADD COLUMN " + field + " " + type;
+    return *this;
+}
+
+SqlQueryManager &SqlQueryManager::select(const QString &what, const QString &tableName)
+{
+    addSpace(queryText);
+    queryText += "SELECT " + what + " FROM " + tableName;
+    return *this;
+}
+
+SqlQueryManager &SqlQueryManager::count(const QString &what, const QString &tableName)
+{
+    addSpace(queryText);
+    queryText += "COUNT " + what + " FROM " + tableName;
+    return *this;
+}
+
+SqlQueryManager &SqlQueryManager::insert(const QString &tableName, const QString &fields)
+{
+    addSpace(queryText);
+    queryText += "INSERT INTO " + tableName + " (" + fields + ") ";
+    queryText += "VALUES(";
+
+    for (int i = 0, fNum = fields.count(","); i < fNum; ++i) {
+        queryText += "?,";
+    }
+    queryText += "?);";
+
+    return *this;
+}
+
+SqlQueryManager &SqlQueryManager::remove(const QString &tableName, const QString &cond)
+{
+    addSpace(queryText);
+    queryText += "DELETE FROM " + tableName + " WHERE " + cond;
+    return *this;
+}
+
+SqlQueryManager &SqlQueryManager::update(const QString &tableName, const QString &fieldValue)
+{
+    addSpace(queryText);
+    queryText += "UPDATE " + tableName + " SET " + fieldValue;
+    return *this;
+}
+
+SqlQueryManager &SqlQueryManager::where(const QString &c)
+{
+    addSpace(queryText);
+    queryText += " WHERE " + c;
+    return *this;
+}
+
+SqlQueryManager &SqlQueryManager::orderBy(const QString &c)
+{
+    addSpace(queryText);
+    queryText += "ORDER BY " + c;
+    return *this;
+}
+
+SqlQueryManager &SqlQueryManager::leftJoin(const QString &tableName, const QString &cond)
+{
+    addSpace(queryText);
+    queryText += "LEFT JOIN " + tableName + " ON " + cond;
+    return *this;
+}
+
+SqlQueryManager &SqlQueryManager::innerJoin(const QString &tableName, const QString &cond)
+{
+    addSpace(queryText);
+    queryText += "INNER JOIN " + tableName + " ON " + cond;
+    return *this;
+}
+
+SqlQueryManager &SqlQueryManager::addBindValue(const QVariant &v)
+{
+    query.bindValue(bindingDone, v);
+    ++bindingDone;
+    return *this;
+}
+
+bool SqlQueryManager::prepare()
+{
+    if (!queryText.endsWith(";"))
+        queryText += ";";
+
+    if (!query.prepare(queryText)) {
+        qDebug() << "****** SqlQueryManager::prepare *******";
+        qDebug() << "Error with: " << queryText;
+        qDebug() << "************************************";
+
+        return false;
+    }
+    return true;
+}
+
+QJsonObject SqlQueryManager::exec()
+{
+    const auto &json = query.exec();
+    executeQry(json, queryText);
+    return json;
+}
+
+QJsonObject SqlQueryManager::exec(const QString &sql)
+{
+    const auto &json = query.exec(sql);
+    executeQry(json, queryText);
+    return json;
+}
+
+QString SqlQueryManager::textQuery(bool addEndDot) const
+{
+    QString query = queryText;
+
+    if (addEndDot) {
+        if (!query.endsWith(";"))
+            query += ";";
+    }
+
+    return query;
+}
+
+QString SqlQueryManager::prefixPropNames(Model &model)
+{
+    const auto &propNames = MetaClassParser(&model, true).getAllPropertiesNames();
+    QString tableName = model.getTableName(), text;
+
+    for (const auto &propName : propNames) {
+        if (text != "")
+            text += ", ";
+
+        text += propName;
+        text += " AS ";
+        text += tableName;
+        text += "_";
+        text += propName;
+    }
+
+    return text;
+}
+
+CWF_END_NAMESPACE

+ 92 - 0
CPPWebFramework/cwf/sqlquerymanager.h

@@ -0,0 +1,92 @@
+#ifndef SQLQUERYMANAGER_HH
+#define SQLQUERYMANAGER_HH
+
+#include "cppwebframework_global.h"
+#include "sqlquery.h"
+
+#include <memory>
+
+CWF_BEGIN_NAMESPACE
+
+class Model;
+
+/**
+ * @brief The SqlQueryManager class allows to create query by chaining public methods instead of writing raw sql.
+ *
+ * The class is meant as an interface to create query without writing raw sql. A query can be created by chaining the public
+ * methods of the call that emulates sql statements. The sql is generated from within these methods and should be adaptated to
+ * suit the database type (MySQL, SQLite, Postgresql...) currently used.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT SqlQueryManager
+{
+    SqlDatabaseStorage& connection;
+    QString queryText;      ///< @brief The text of the query being contructed by the manager
+    CWF::SqlQuery query;    ///< @brief The query being constructed
+    qint32 bindingDone = 0; ///< @brief How many bindings were done with the current query ?
+public:
+    explicit SqlQueryManager(SqlDatabaseStorage& connection)
+        : connection(connection)
+        , query(connection)
+    {} ///< @brief Constructor
+    /**
+     * @brief reset Reset the query manager
+     */
+    void reset();
+    /**
+     * @brief createTable Construct a query to create a new table in a database
+     * @param tableName The name of the table to be added
+     */
+    QString createTable(const QString& tableName);
+    /**
+     * @brief createIndex Construct a query to create a new index in the database
+     * @param indexName The name of the new index
+     * @param tableName The name of the table targeted
+     * @param field The name of the column targeted
+     * @param unique Should the index be unique ?
+     */
+    void createIndex(const QString& indexName,
+                     const QString& tableName,
+                     const QString& field,
+                     bool unique = false);
+    // clang-format off
+    SqlQueryManager& alterTable(const QString& tableName); ///< @brief Alter a table (can be chained)
+    SqlQueryManager& addColumn(const QString& field, const QString& type); ///< @brief Add a column (can be chained)
+    SqlQueryManager& select(const QString& what, const QString& tableName); ///< @brief Select statement (can be chained)
+    SqlQueryManager& count(const QString& what, const QString& tableName); ///< @brief Count statement (can be chained)
+    SqlQueryManager& insert(const QString& tableName, const QString& fields); ///< @brief Insert statement (can be chained)
+    SqlQueryManager& remove(const QString& tableName, const QString& cond); ///< @brief Remove statement (can be chained)
+    SqlQueryManager& update(const QString& tableName, const QString& fieldValue); ///< @brief Update statement (can be chained)
+    SqlQueryManager& where(const QString& c); ///< @brief Where statement (can be chained)
+    SqlQueryManager& orderBy(const QString& c); ///< @brief OrderBy statement (can be chained)
+    SqlQueryManager& leftJoin(const QString& tableName, const QString& cond); ///< @brief leftJoin statement (can be chained)
+    SqlQueryManager& innerJoin(const QString& tableName, const QString& cond); ///< @brief innerJoin statement (can be chained)
+    SqlQueryManager& addBindValue(const QVariant& v); ///< @brief addBindValue statemnet (can be chained)
+    // clang-format on
+    bool prepare();                       ///< @brief Prepare the query
+    QJsonObject exec();                   ///< @brief // Executte the query
+    QJsonObject exec(const QString& sql); ///< @brief // Executte the query
+    inline QJsonArray toJson()
+    {
+        return query.toJson();
+    } ///< @brief // Get the result of the query in JSON format
+    /**
+     * @brief textQuery Get the text of the query (raw sql)
+     * @param addEndDot Should we add ";" at the end of the query if it is not present ?
+     * @return QString
+     */
+    QString textQuery(bool addEndDot = false) const;
+    /**
+     * @brief prefixPropNames Create a string made of all the properties of a given model. These are all prefixed by the name of the model.
+     * @param model The targeted model object
+     * @return QString
+     *
+     * This feature is useful when building complex leftJoin request: it allows to build aliases to avoid name conflicts.
+     */
+    QString prefixPropNames(Model& model);
+
+    inline QString getQueryText() const { return queryText; }
+};
+
+CWF_END_NAMESPACE
+
+#endif // SQLQUERYMANAGER_HH

+ 83 - 0
CPPWebFramework/cwf/sslloader.cpp

@@ -0,0 +1,83 @@
+#include "sslloader.h"
+#include <QFile>
+#include <QDebug>
+#include <QSslKey>
+#include <QSslCertificate>
+#include <QSslConfiguration>
+#include <utility>
+
+CWF_BEGIN_NAMESPACE
+
+SslLoader::SslLoader(Configuration configuration) : configuration(std::move(configuration))
+{
+}
+
+QByteArray getFileContent(const QString &fileName, bool &ok)
+{
+    QFile file(fileName);
+    if(file.open(QIODevice::ReadOnly))
+    {
+        ok = true;
+        return file.readAll();
+    }
+    ok = false;
+    qDebug() << "Can't open " << fileName << ": " << file.errorString();
+    return QByteArray();
+}
+
+QSslConfiguration *buildSslConfiguration(const QSslKey &keySsl,
+                                         const QSslCertificate &certificateSsl,
+                                         const Configuration &configuration)
+{
+    auto *temp = new QSslConfiguration;
+    temp->setProtocol(configuration.getSslProtocol());
+    temp->setPeerVerifyMode(configuration.getSslPeerVerifyMode());
+    temp->setPrivateKey(keySsl);
+    temp->setLocalCertificate(certificateSsl);
+    return temp;
+}
+
+QSslConfiguration *SslLoader::getSslConfiguration() const
+{
+#ifdef QT_NO_SSL
+    qDebug() << "Secure Sockets Layer (SSL) is not supported, please check your configuration.";
+    return nullptr;
+#else
+    if(!configuration.getSslKeyFile().isEmpty() && !configuration.getSslCertFile().isEmpty())
+    {
+        bool okKey, okCert;
+        QByteArray myKeyStr(getFileContent(configuration.getSslKeyFile(), okKey));
+        QByteArray myCertificateStr(getFileContent(configuration.getSslCertFile(), okCert));
+
+        if(!okKey || !okCert)
+        {
+            return nullptr;
+        }
+
+        QSslKey keySsl(myKeyStr,
+                       configuration.getSslKeyAlgorithm(),
+                       configuration.getSslEncodingFormat(),
+                       configuration.getSslKeyType(),
+                       configuration.getSslPassPhrase());
+
+        QSslCertificate certificateSsl(myCertificateStr,
+                                       configuration.getSslEncodingFormat());
+
+        if(keySsl.isNull())
+        {
+            qDebug() << "Invalid SLL key file, please check the CPPWeb.ini file.";
+            return nullptr;
+        }
+        if(certificateSsl.isNull())
+        {
+            qDebug() << "Invalid SLL cert file, please check the CPPWeb.ini file.";
+            return nullptr;
+        }
+
+        return buildSslConfiguration(keySsl, certificateSsl, configuration);
+    }
+#endif
+    return nullptr;
+}
+
+CWF_END_NAMESPACE

+ 30 - 0
CPPWebFramework/cwf/sslloader.h

@@ -0,0 +1,30 @@
+#ifndef SSLLOADER_H
+#define SSLLOADER_H
+
+#include <QSslConfiguration>
+#include "configuration.h"
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+
+/**
+ * @brief The SslLoader class is responsible for loading the SSL settings.
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT SslLoader
+{
+    Configuration configuration;
+
+public:
+    /**
+     * @brief SslLoader constructor requires a Configuration object.
+     */
+    SslLoader(Configuration configuration);
+    /**
+     * @brief Returns a QSslConfiguration. It will returns nullptr if it fails.
+     */
+    QSslConfiguration *getSslConfiguration() const;
+};
+
+CWF_END_NAMESPACE
+
+#endif // SSLLOADER_H

+ 44 - 0
CPPWebFramework/cwf/urlencoder.cpp

@@ -0,0 +1,44 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#include "urlencoder.h"
+
+CWF_BEGIN_NAMESPACE
+
+QString URLEncoder::decode(QByteArray url, bool replacePlusForSpace)
+{
+    if (replacePlusForSpace)
+        url = url.replace("+", " ");
+    QUrl copy(url);
+    copy.setQuery(copy.query(QUrl::FullyDecoded), QUrl::DecodedMode);
+
+    return copy.toString();
+}
+
+QString URLEncoder::encode(const QByteArray &url)
+{
+    QUrl copy(url);
+    return copy.toEncoded();
+}
+
+QString URLEncoder::paramEncode(const QByteArray &param)
+{
+    QUrl url("?p=" + param);
+    return url.toEncoded().remove(0, 3);
+}
+
+QString URLEncoder::paramDecode(QByteArray param, bool replacePlusForSpace)
+{
+    if (replacePlusForSpace)
+        param = param.replace("+", " ");
+    QUrl url("?p=" + param);
+    url.setQuery(url.query(QUrl::FullyDecoded), QUrl::DecodedMode);
+
+    return url.toString().remove(0, 3);
+}
+
+CWF_END_NAMESPACE

+ 51 - 0
CPPWebFramework/cwf/urlencoder.h

@@ -0,0 +1,51 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef URLENCODER_H
+#define URLENCODER_H
+
+#include "cppwebframework_global.h"
+
+#include <QByteArray>
+#include <QUrl>
+
+CWF_BEGIN_NAMESPACE
+/**
+ * @brief The URLEncoder class
+ */
+class CPPWEBFRAMEWORKSHARED_EXPORT URLEncoder
+{
+public:
+    /**
+     * @brief decode
+     * @param url
+     * @return
+     */
+    static QString decode(QByteArray url, bool replacePlusForSpace = true);
+    /**
+     * @brief encode
+     * @param url
+     * @return
+     */
+    static QString encode(const QByteArray &url);
+    /**
+     * @brief paramEncode
+     * @param param
+     * @return
+     */
+    static QString paramEncode(const QByteArray &param);
+    /**
+     * @brief paramDecode
+     * @param param
+     * @return
+     */
+    static QString paramDecode(QByteArray param, bool replacePlusForSpace = true);
+};
+
+CWF_END_NAMESPACE
+
+#endif // URLENCODER_H

+ 104 - 0
CPPWebFramework/cwf/variant.h

@@ -0,0 +1,104 @@
+/*
+ Copyright 2017 Herik Lima de Castro and Marcelo Medeiros Eler
+ Distributed under MIT license, or public domain if desired and
+ recognized in your jurisdiction.
+ See file LICENSE for detail.
+*/
+
+#ifndef VARIANT_H
+#define VARIANT_H
+// clazy:excludeall=const-signal-or-slot
+#include <QObject>
+#include <QVariant>
+#include "cppwebframework_global.h"
+
+CWF_BEGIN_NAMESPACE
+/**
+* @brief This class is designed to facilitate the passing of simple type parameters such as
+* qlonglong, double, int, and QString to the CSTL (C++ Server Pages Standard Tags Library).
+*/
+class CPPWEBFRAMEWORKSHARED_EXPORT Variant : public QObject
+{
+    Q_OBJECT
+    QVariant variant;
+
+public:
+    /**
+     * @brief Constructs an invalid variant.
+     */
+    Variant() = default;
+    /**
+     * @brief Constructs a new variant with an integer value.
+     */
+    explicit Variant(int value)
+        : variant(value)
+    {}
+    /**
+     * @brief Constructs a new variant with an double value.
+     */
+    explicit Variant(double value)
+        : variant(value)
+    {}
+    /**
+     * @brief Constructs a new variant with an qlonglong value.
+     */
+    explicit Variant(qlonglong value)
+        : variant(value)
+    {}
+    /**
+     * @brief Constructs a new variant with an QString value.
+     */
+    explicit Variant(const QString &value)
+        : variant(value)
+    {}
+    /**
+     * @brief Constructs a new variant and converts to QString.
+     */
+    explicit Variant(const QByteArray &value)
+        : variant(QString(value))
+    {}
+public slots:
+    /**
+     * @brief Returns the variant as an int if the variant has userType() QMetaType::Int, QMetaType::Bool, QMetaType::QByteArray, QMetaType::QChar, QMetaType::Double, QMetaType::LongLong, QMetaType::QString, QMetaType::UInt, or QMetaType::ULongLong; otherwise returns 0.
+     * @warning: If the value is convertible to a QMetaType::LongLong but is too large to be represented in an int, the resulting arithmetic overflow will not be reflected in ok. A simple workaround is to use QString::toInt()
+     * @param If ok is non-null: *ok is set to true if the value could be converted to an int; otherwise *ok is set to false.
+     */
+    inline int toInt(bool *ok = nullptr) const noexcept { return variant.toInt(ok); }
+    /**
+     * @brief Constructs a new variant with an int value.
+     */
+    inline void setInt(int value) noexcept { variant = value; }
+    /**
+     * @brief Returns the variant as a double if the variant has userType() QMetaType::Double, QMetaType::Float, QMetaType::Bool, QMetaType::QByteArray, QMetaType::Int, QMetaType::LongLong, QMetaType::QString, QMetaType::UInt, or QMetaType::ULongLong; otherwise returns 0.0.
+     * @param If ok is non-null: *ok is set to true if the value could be converted to a double; otherwise *ok is set to false.
+     */
+    inline double toDouble(bool *ok = nullptr) const noexcept { return variant.toDouble(ok); }
+    /**
+     * @brief Constructs a new variant with an double value.
+     */
+    inline void setDouble(double value) noexcept { variant = value; }
+    /**
+     * @brief Returns the variant as a long long int if the variant has userType() QMetaType::LongLong, QMetaType::Bool, QMetaType::QByteArray, QMetaType::QChar, QMetaType::Double, QMetaType::Int, QMetaType::QString, QMetaType::UInt, or QMetaType::ULongLong; otherwise returns 0.
+     * @param If ok is non-null: *ok is set to true if the value could be converted to an int; otherwise *ok is set to false.
+     */
+    inline qlonglong toLongLong(bool *ok = nullptr) const noexcept
+    {
+        return variant.toLongLong(ok);
+    }
+    /**
+     * @brief Constructs a new variant with an qlonglong value.
+     */
+    inline void setLongLong(qlonglong value) noexcept { variant = value; }
+    /**
+     * @brief Returns the variant as a QString if the variant has userType() QMetaType::QString, QMetaType::Bool, QMetaType::QByteArray, QMetaType::QChar, QMetaType::QDate, QMetaType::QDateTime, QMetaType::Double, QMetaType::Int, QMetaType::LongLong, QMetaType::QStringList, QMetaType::QTime, QMetaType::UInt, or QMetaType::ULongLong; otherwise returns an empty string.
+     */
+    inline QString toString() const noexcept { return variant.toString(); }
+    /**
+     * @brief Constructs a new variant with an QString value.
+     */
+    inline void setString(const QString &value) noexcept { variant = value; }
+};
+
+CWF_END_NAMESPACE
+
+#endif // VARIANT_H

+ 61 - 0
CPPWebFramework/main.cpp

@@ -0,0 +1,61 @@
+#include <QtTest>
+#include <tests/tst_configuration.h>
+#include <tests/tst_cppwebapplication.h>
+#include <tests/tst_cppwebserver.h>
+#include <tests/tst_cppwebcontroller.h>
+#include <tests/tst_cstlcompiler.h>
+#include <tests/tst_cstlcompilerattributes.h>
+#include <tests/tst_cstlcompilerfor.h>
+#include <tests/tst_cstlcompilerif.h>
+#include <tests/tst_cstlcompilerimport.h>
+#include <tests/tst_cstlcompilerobject.h>
+#include <tests/tst_filemanager.h>
+#include <tests/tst_filter.h>
+#include <tests/tst_filterchain.h>
+#include <tests/tst_httpparser.h>
+#include <tests/tst_httpreadrequest.h>
+#include <tests/tst_request.h>
+#include <tests/tst_response.h>
+#include <tests/tst_session.h>
+#include <tests/tst_metaclassparser.h>
+#include <tests/tst_properties.h>
+#include <tests/tst_qlistobject.h>
+#include <tests/tst_qmapthreadsafety.h>
+#include <tests/tst_requestdispatcher.h>
+#include <tests/tst_cstlcompilerout.h>
+#include <tests/tst_urlencoder.h>
+
+
+int main(int argc, char** argv)
+{
+   int status = 0;
+   auto ASSERT_TEST = [&status, argc, argv](QObject* obj) { status |= QTest::qExec(obj, argc, argv); delete obj; };
+
+   ASSERT_TEST(new TST_Configuration);
+   ASSERT_TEST(new TST_CppWebApplication);
+   ASSERT_TEST(new TST_CppWebServer);
+   ASSERT_TEST(new TST_CppWebController);
+   ASSERT_TEST(new TST_CSTLCompiler);
+   ASSERT_TEST(new TST_CSTLCompilerAttributes);
+   ASSERT_TEST(new TST_CSTLCompilerFor);
+   ASSERT_TEST(new TST_CSTLCompilerIf);
+   ASSERT_TEST(new TST_CSTLCompilerImport);
+   ASSERT_TEST(new TST_CSTLCompilerObject);
+   ASSERT_TEST(new TST_FileManager);
+   ASSERT_TEST(new TST_Filter);
+   ASSERT_TEST(new TST_FilterChain);
+   ASSERT_TEST(new TST_HttpParser);
+   ASSERT_TEST(new TST_HttpReadRequest);
+   ASSERT_TEST(new TST_Request);
+   ASSERT_TEST(new TST_Response);
+   ASSERT_TEST(new TST_Session);
+   ASSERT_TEST(new TST_MetaClassParser);
+   ASSERT_TEST(new TST_Properties);
+   ASSERT_TEST(new TST_QListObject);
+   ASSERT_TEST(new TST_QMapThreadSafety);
+   ASSERT_TEST(new TST_RequestDispatcher);
+   ASSERT_TEST(new TST_URLEncoder);
+   ASSERT_TEST(new TST_CSTLCompilerOut);
+
+   return status;
+}

+ 20 - 0
CPPWebFramework/server/config/CPPWeb.ini

@@ -0,0 +1,20 @@
+[config]
+host=Any
+port=8080
+maxThread=200
+cleanupInterval=60000
+timeOut=60000
+sessionExpirationTime=6000
+maxUploadFile=20971520
+maxLogFile=20000000
+;sslKeyFile=/config/ssl/key.pem
+;sslCertFile=/config/ssl/cert.pem
+;sslPassPhrase=cppwebframework
+;sslKeyAlgorithm=rsa
+;sslEncodingFormat=pem
+;sslKeyType=privatekey
+;sslPeerVerifyMode=verifynone
+;sslProtocol=tlsv1sslv3
+indexPage=/config/cppwebserverpages/index.view
+accessCPPWebIni=false
+accessServerPages=true

+ 17 - 0
CPPWebFramework/server/config/cppwebserverpages/403.view

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8" />
+        <title>C++ Web Server - 403 - Forbidden</title>
+        <link href="/config/cppwebserverpages/resources/images/favicon.ico" rel="icon" type="image/x-icon" />
+        <link href="/config/cppwebserverpages/resources/images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
+        <link href="/config/cppwebserverpages/resources/css/cppweb.css" rel="stylesheet" type="text/css" />
+    </head>
+    <body>
+        <div id="menu">
+            <h1>403 - Forbidden</h1>
+            <br class="separator" />
+            <p class="footer">© 2018 By Herik Lima and Marcelo Eler</p>
+        </div>
+    </body>
+</html>

+ 17 - 0
CPPWebFramework/server/config/cppwebserverpages/404.view

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="UTF-8" />
+        <title>C++ Web Server - 404 - Page Not Found</title>
+        <link href="/config/cppwebserverpages/resources/images/favicon.ico" rel="icon" type="image/x-icon" />
+        <link href="/config/cppwebserverpages/resources/images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
+        <link href="/config/cppwebserverpages/resources/css/cppweb.css" rel="stylesheet" type="text/css" />
+    </head>
+    <body>
+        <div id="menu">
+            <h1>404 - Page Not Found</h1>
+            <br class="separator" />
+            <p class="footer">© 2018 By Herik Lima and Marcelo Eler</p>
+        </div>
+    </body>
+</html>

+ 31 - 0
CPPWebFramework/server/config/cppwebserverpages/index.view

@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8" />
+        <title>C++ Web Server</title>
+        <link href="/config/cppwebserverpages/resources/images/favicon.ico" rel="icon" type="image/x-icon" />
+        <link href="/config/cppwebserverpages/resources/images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
+        <link href="/config/cppwebserverpages/resources/css/cppweb.css" rel="stylesheet" type="text/css" />
+    </head>
+    <body>
+        <div id="menu">
+            <div id="nav" class="curved container">
+                <span id="home"><a href="/index">Home</a></span>
+                <span id="configuration"><a href="/config/CPPWeb.ini">Configuration</a></span>
+                <br class="separator" />
+            </div><br/>
+            <div id="upper" class="curved container">
+                <div id="notice">
+                    <img src="/config/cppwebserverpages/resources/images/logo.png"/><br/>
+                    <h1>C++ Web Framework</h1>
+                </div><br/><br/>
+                <br class="separator" />
+                <div id="server" class="curved container">
+                    <h2>C++ Web Server Online</h2>
+                </div>
+                <br class="separator" />
+            </div>
+            <p class="footer">© 2018 By Herik Lima and Marcelo Eler</p>
+        </div>
+    </body>
+</html>

+ 119 - 0
CPPWebFramework/server/config/cppwebserverpages/resources/css/cppweb.css

@@ -0,0 +1,119 @@
+body 
+{
+    margin: 10px 20px;
+    text-align: center;
+    font-family: Arial, sans-serif;
+}
+
+h1, h2, h3, h4 p, ul, ol 
+{
+    margin: 0 0 0.5em;
+}
+
+h1 
+{
+    font-size: 50pt;
+    margin: 0.5em 0 0;
+}
+
+h2 
+{
+    font-size: 16pt;
+}
+
+h3 
+{
+    font-size: 13pt;
+}
+
+h4 
+{
+    font-size: 12pt;
+}
+
+br.separator 
+{
+    margin: 0;
+    padding: 0;
+    clear: both;
+}
+
+.container 
+{
+    padding: 10px;
+    margin: 0 0 10px;
+}
+
+.curved 
+{
+    border-radius: 10px;
+    -moz-border-radius: 10px;
+    -webkit-border-radius: 10px;
+    -khtml-border-radius: 10px;
+}
+
+.footer 
+{
+    font-size: 10pt;
+    color: #666;
+}
+
+#menu 
+{
+    display: block;
+    margin: 0 auto;
+    text-align: center;
+    min-width: 720px;
+    max-width: 1000px;
+}
+
+#nav 
+{
+    background-color: #DCDCDC;
+    margin: 0 0 10px;
+    padding: 0;
+}
+
+#nav span 
+{
+    float: left;
+}
+
+#nav span a 
+{
+    display: block;
+    padding: 10px;
+    font-weight: bold;
+    text-shadow: 1px 1px 1px #fff;
+}
+
+#nav span a:link,
+#nav span a:visited,
+#nav span a:hover,
+#nav span a:active 
+{
+    color: #666;
+    text-decoration: none;
+}
+
+#nav span#authors 
+{
+    float: right;
+    margin-right: 0;
+}
+
+#server 
+{
+    text-align: center;
+    padding: 10px;
+    margin: 0 40px 20px;
+    background-color: #00FF00;
+}
+
+#server h2 
+{
+    font-size: 14pt;
+    padding: 0;
+    margin: 0;
+    color: #fff;
+}

二進制
CPPWebFramework/server/config/cppwebserverpages/resources/images/favicon.ico


二進制
CPPWebFramework/server/config/cppwebserverpages/resources/images/logo.png


+ 0 - 0
CPPWebFramework/server/config/log/CPPWebServer.log


+ 34 - 0
CPPWebFramework/server/config/ssl/cert.pem

@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIIF9zCCA9+gAwIBAgIJAKp3colN2g9ZMA0GCSqGSIb3DQEBCwUAMIGRMQswCQYD
+VQQGEwJCUjEVMBMGA1UECAwMU8ODwqNvIFBhdWxvMRUwEwYDVQQHDAxTw4PCo28g
+UGF1bG8xDDAKBgNVBAoMA0NXRjEMMAoGA1UECwwDQ1dGMQ4wDAYDVQQDDAVIZXJp
+azEoMCYGCSqGSIb3DQEJARYZY3Bwd2ViZnJhbWV3b3JrQGdtYWlsLmNvbTAeFw0x
+ODA1MjQyMzQ4MDVaFw0xOTA1MjQyMzQ4MDVaMIGRMQswCQYDVQQGEwJCUjEVMBMG
+A1UECAwMU8ODwqNvIFBhdWxvMRUwEwYDVQQHDAxTw4PCo28gUGF1bG8xDDAKBgNV
+BAoMA0NXRjEMMAoGA1UECwwDQ1dGMQ4wDAYDVQQDDAVIZXJpazEoMCYGCSqGSIb3
+DQEJARYZY3Bwd2ViZnJhbWV3b3JrQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEB
+BQADggIPADCCAgoCggIBAMWBgbY+Iy/ZInvLQvf7DFGhbd+kaT9aFHwivTbpFctb
+ihihbM5/4tgZhOBe10EZhUk5Jl5bFzYyYDQprQdaZ+79n7bBlNc/UW/iQHhp1gFy
+12ziiXjWPXCMchUN5cG/HoJ7prARt7aGmxtxCpcugDGSep9K+camWdCb4i+0snwV
+d9OjKiGacy+exn54cJSJ8UiRRUFu6YRyBYLGpXo5NboNBeBH7Foc2VEpIb4xfW+a
+fgVohSCQjVgtA5JouLEUAmDrbPDxkm0lKCDM7uaaVCcRwPhXbQ9+cBR3t0clWKXX
+SfQLCf9G4+cg/NJUHV6KUInskcN4By4AjY9jI3qjPNbsJvIavqsfTWy828FYucq7
+5fhtWYDTrG4WUyToBTBDz0qQgY/8JshwBNtKRmxVVBg7JxanazbQftexh2VDem+k
+lrLAs8GIYEKPAV7s0h50tyjnHQML3J0WPsiTHUtQQt873zmpJnvTq0jEq+2TXj5M
+rLmAJYwbJZSJef3SBZpAHQ363SYFiz7QH/37zXjWxFHVG/dtvU+tXBp5ha6LuwyB
+byLQ09SU9XcIoUoQCkoh+fDkx3d83CZpPxPrzHPchuLu5rlNAk0QlTFHrJbc5w2B
+TIz2/EO53Xj5/NgdQ0Dtkzp1od5chkFrI+hz4wz/YGqqDT/Cej/ZzSSEJklg9xv5
+AgMBAAGjUDBOMB0GA1UdDgQWBBSbXXGsjGAdjQ8oc1bKGk7CUH5/EzAfBgNVHSME
+GDAWgBSbXXGsjGAdjQ8oc1bKGk7CUH5/EzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
+DQEBCwUAA4ICAQBgADpX/UD49HL2QRCfTg8SMCQPEgm1cTRv7r+Q0tD5rqLA7vOu
+KtDEiqyBVZqXAQxS8NlhKw5FKGGH1CAy7oYwA6xYkQb5HgbTDZWtZCsLot9Ke6jY
+ueSZxlqVWe6sQC6b6dxL6fzUDzU2eACxKbsbLSM3mflfRxg0plpQ/q7nDJD4UzZb
+GXn86jKGKbG5eu/6DJxd20UQvrlQ1eEbyeLtNfrJ+uKC2fpLwJd5FLslzhOqE02B
+ql3CEpl8+StkGvVyw5q4C9AAqe5aiOdPxlNd7mMRtmKH2Cc6SIw1gSMui8Z89ZqS
+MCSjDYl/Br6lAb0q+6YUC7nWEUUEVzZJsrxCT0YecklQF9UT+zwQ7EgDEb2rKt8H
+sZM43VKp55V15adz+MEbLHe2resLZwiBX0eJJ1wR0VmAb+B9ZT46SWN70aSz0SWX
+0RdJh7b4XByWRZDqbuQ6J7Q7plmFnrVZEd5C1Q6MKB+DNfXwMKDBGo9xTteBAMSL
+9rDe2RvUk928nz3VVTd6cEIQ2btAde9cK/e99NasM3+wOrqdn7FwSOCNpAOFW6m2
+S9jy/Fq71DYReiUfQj2QlokJyABab20uW+MOHp0mPL3M1izqytIc6wyUzWXECx1S
+WxhXSf1dN2auhxpBDDCQSFjjKG50QCjKL50Ip8q+Vi+k3fY4wr0ECPcStg==
+-----END CERTIFICATE-----

+ 54 - 0
CPPWebFramework/server/config/ssl/key.pem

@@ -0,0 +1,54 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIJljBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIx+ZIy80wpuMCAggA
+MBQGCCqGSIb3DQMHBAhkFPNu5pmceQSCCVDp/xg2Edd/nxn6gplwm+LhX21QFJZo
+eC191Q7AKrlkJVkuXufzsaEB+MfpqMF0jEMNyQ+a+iLBFS/i7LpmZRIOY7LHf2qu
+3NzeOcUBc+nPwSFE7vaOybccUoe21HxVmowmA7PSMd14ge32KdqFRW8ZmXk74LE8
+PZQD4ktB/PiA6rBtHba8SMzAFL/o2L7JeeFbijbyyfadek6tsT6WMb0EMopgWOWo
+/wMlAq8iTehTGcjFVT8jengMR89xbVF8aX5Z1JJmwt83m9GJC/1TOqrHvTifwYJW
+FrRsOGs1IhbOenB0Ol3Hi/OC7eSXwWnhlkeCpjeIcItsPzD+i/5gb9eTYAw0LjWB
+Sx2mmKDguMg2rRvv2MwGseS3Fk6D8gFKM5X7UkYTLCKuWOzVGT+lFwIQvcxbo3/j
+zfaJ9Fxu1ncSbET6m1saJ5LZHGixncwPdMebb5hsfyEJzdAoL/KKlAgZumhV3Y9B
+fROlkxEVGF8nSDuuS5hYXdToccN+4qd54pgetde1aKW997MfJCGGB/Xcq9UxnmKk
+w009YKS0wXq5fEeeHu5mJnvZ3rT9zM19n8pHsp3SoaVxvfu1RxZFDZc4Ev9aM4NG
+DqeHe8bKAA9gsUZpC1NsUmJ/crW/edhLlqXdlB6dxVgKWUUAb7cTqawamM7SHqhN
+avPtu1tUx5Whxu/c6YCtNmmQnsh3BNtPaF5J/py5Ptn5ev9GIjI6tc3OL6qYtNFv
+nwJnEf8NdlzfKHZeD10VxbXrZDashnAsncfzG1yWkU3OqIwIoWiKGxTT1e3yI2g2
++VmjihYvnZJTHzP5G4WkkGtGX9ur0USRnMi73t4bxeKdJO4Ypds6JKnY6uks1VIj
+uPfwy3YuyBPZHTs+1ZHjP/QVbEacWDViZU4VjwCuzKYZg0zifVfI9XLPZJiI1P04
+6HmXMdqahPEjR9PmIlH55jeEbLAHg+14EvCg+AYuyMdMthOuPLKFCmwGufdmsKcW
+zZw2dmfJ53s6XVRFwb1gdLm9bVMb+7uq7x42Ve8ICoDg7b6Thk4/OJfcIOVkpGO5
+m3vUkQTTW/z/aPo+nzYV2Llet2WDF0e4sAGOiVdfXoqUds0OPCDpW3kJ2HlOmCRs
+A6TMQbsDVFd5xpmSqgZj1DAaH4edDCnxddc3+gI5Ay/MBK/4JtLmJuUqZSAy6qEP
+1+KfeM/nodAIlifW6p1LOMzVUEZHOL5Wgikkl+KaksE0Ry8p5e6yj8mkxYRU0QKc
+/6pPUi6A14PvXjDEeXW6aIIMabZWaWM9AyJ38KBQGBb+M0NRee3rr2+g4pYLpKaN
+Rc0p26KoNoNMCODP++HmSOJIIc0UfcNxRUEqSMfjAaCvxeU7LKxIlNYIOpHRitHJ
+C23+4YOSlP4rRsTt+ElzeNChXi0ldAkQIETDY9QAHfTOAKeQkSQwINSkIxsnrmo0
+Fzpn/bOHrK9NXJNOZcLQzdceW/Ie/rU9Q+I18J8rUGDD4rGKJ7u+vxZ2/OzzCiiI
+T3+AdLiV+nE5xJ1egYI2fSW6BemeH5Lqgq/gWfdILG2tUIVjC93wPswMhlsf47KV
+sKiAVdKmfXLyyTeOPRVIZI/FxN5r5arxlyFXor9pzFvIfOb0dKCra3Iw0ZUgpu3b
+O6bxG5ak386inDS6iTY/D1RpJl7W8m5pb5Qc/vHV+Nw3KWHDoOyz01uTaNTlYeYe
+A1Z3/RPSJ3v5VU6G0iYl9NSK2wFVFWaZK2YslnWUWN+QHXcJeGQ/LH3tFS0sUP/b
+T/bEGSuZSlSyC46ggtoxPsxB4WKvlXnjWVMwcRwmQMWhz56I4v1xshTSNJDsg2wM
+OE+MotNG3zGBkA9niCXKt9fF/66ZVpgqcyLeOgTKvEM9A4kfKKPMR/dBQk0ETXWV
+74Nt2rLxfi2KQBmRdkVT3U/a3JQmIsoO4LX5nl9Ru4vSfHW/BUn5R+ifcetty3Hn
+q7GbWuptwpG7V3ssU41yo80l9crbXvFDR3tKeRHgCxBNJdi90+u5VpVLXL9jtGvb
+J3vnNfg5wr6kkrjGyx3QVgAlPhL2f9PwtQ1RNT4I6/l27zQ8vvSWULJtOwYhxtWf
+DfuGp6YDP6sJVUlKwLA39UL4edDLoAsM6b0E+EbSb+5q49tAyB5GURO1b5344aKg
+cfoMylnojm6i4rZgL/S5537UHxxjy/tuCdvIIAQs44RJgNJmrGoTmJVAXFbqk8kD
+41h5yAaZRSfhIJG+rpGrQnWAQST8Fu6kfL0+s7+oEseOdE7Jn33RYhnZ2hVduhgR
+Vr1ASV7wASVZZDb0kZD6C4wy2w+2OLXkJDIMfklcgmnYNOeCKTmbvCDO+iKEG/gL
+DFS+f88taLVmFb28E7QLOyDM7p0oSLMTv6ItqFqRfe3tjEasnPe/Du4/GoustS0j
+z2EUUTBl+YVeToCPBf7qHVMuJBb432HU1dXAP9+uwEmg9TvG4BtcH7gy8GU4zbRI
+H0rsUqnOnZoxaRaeh8/s77wit9hGozkyxM7VaCqJKNUOXzbi/4KsOMhXmLiG6mRW
++tbjS7nG/kYI9b+wAhgtPOHzkHq1zfgrpgJNcs1teXowDdrEKZ3l+fUDJUHw9Y2O
+nmVkkPq63xkeXKePDsgjBbkj2xrn9bTza0migLcnf6rO0dQ7kIfR5523kfnaSQEC
+6kPkdCj1xXjbwsEf/p0vJe9oLrkDADOTtQulETFCWTJ1VpsluYOjoHEcE4XTx/WB
+EYWiWrMCRyyAGpU55QVFt20l8/LypRaeSOc3O/sucltMYJqfnEhO1hU7N/Y7xNbM
+MbB7hGcHkdqO1VY+Qn+N36Rnb/tQNZtdIu3Nl2wCQd+M6kCuHJsX964BV27owZ4p
+XSI7uiqRdIXvBgq5ZVw/dQ0hgL9pC9c3PMd6JD8PKdxyO2OLm3RJ4mR9ZRQ/nVJh
+xs7Qzx6VcwEEivpm2sWAUn6ral6JLH711pqvRkK8V0qJBr/mQQAvBL9B20bDak6d
+7N+aVWazdgTtyM1mOELL24+2kjKGnR4Rokxg8FW7jl5idA+EqvSYqxGlQxotEm9L
+gtb/rlpy9KOXo9h49YDHK/g8E6t2RIWF+n3jcLtB+70TsziTcHEBR/ZPCnaOxIkp
+ZX3jhB/M1Nb5btx7oWfc8c4z+rqw+VAq/nJUlvsC7izP/ZqxTDa+qeIKF8Sb9RCX
+afTrqwVFrAkCag==
+-----END ENCRYPTED PRIVATE KEY-----

+ 51 - 0
CPPWebFramework/tests/tst_configuration.cpp

@@ -0,0 +1,51 @@
+#include "tst_configuration.h"
+
+void TST_Configuration::test()
+{
+    QString path(QDir::currentPath() + "/server");
+    QFile f(path + "/config/CPPWeb.ini");
+    if(f.exists())
+        QVERIFY2(f.remove(), "Should return true");
+
+    QVERIFY2(CWF::Configuration("").isValid() == false, "Shoud return false.");
+
+
+    if(!QDir(path).exists())
+        QVERIFY2(QDir().mkdir(path), "Could not create server folder");
+    if(!QDir(path + "/config").exists())
+        QVERIFY2(QDir().mkdir(path + "/config"), "Could not create server config");
+    QFile file(path + "/config/CPPWeb.ini");
+    QVERIFY2(file.open(QIODevice::WriteOnly), "Could not create CPPWeb.ini file");
+
+    file.write("[config]\n");
+    file.write("host=127.0.0.1\n");
+    file.write("port=8080\n");
+    file.write("maxThread=200\n");
+    file.write("cleanupInterval=60000\n");
+    file.write("timeOut=60000\n");
+    file.write("sessionExpirationTime=6000\n");
+    file.write("maxUploadFile=20971520\n");
+    file.write("logFilePath=/config/log/\n");
+    file.write("sslKeyFile=/config/ssl/my.key\n");
+    file.write("sslCertFile=/config/ssl/my.cert\n");
+    file.write("indexPage=/config/cppwebserverpages/index\n");
+    file.write("accessCPPWebIni=false\n");
+    file.write("accessServerPages=true");
+
+    file.close();
+
+
+    CWF::Configuration configuration(path);
+    QVERIFY2(configuration.getHost().toString() == "127.0.0.1", "Should be Any");
+    QVERIFY2(configuration.getPort() == 8080, "Should be 8080");
+    QVERIFY2(configuration.getMaxThread() == 200, "Should be 200");
+    QVERIFY2(configuration.getCleanupInterval() == 60000, "Should return 60000");
+    QVERIFY2(configuration.getTimeOut() == 60000, "Should return 60000");
+    QVERIFY2(configuration.getSessionExpirationTime() == 6000, "Should return 6000");
+    QVERIFY2(configuration.getMaxUploadFile() == 20971520, "Should return 20971520");
+    QVERIFY2(configuration.getPath() == path, QString("Should be " + path).toStdString().data());
+    QVERIFY2(configuration.getLogFilePath().endsWith("log") == true, "Should be true");
+    QVERIFY2(configuration.getSslCertFile().endsWith("my.cert") == true, "Should be true");
+    QVERIFY2(configuration.getSslKeyFile().endsWith("my.key") == true, "Should be true");
+    QVERIFY2(CWF::Configuration("").isValid(), "Shoud return true.");
+}

+ 16 - 0
CPPWebFramework/tests/tst_configuration.h

@@ -0,0 +1,16 @@
+#ifndef TST_CONFIGURATION_H
+#define TST_CONFIGURATION_H
+
+#include <QDir>
+#include <QFile>
+#include <QtTest>
+#include <cwf/configuration.h>
+
+class TST_Configuration : public QObject
+{
+    Q_OBJECT
+private slots:
+    void test();
+};   
+
+#endif // TST_CONFIGURATION_H

+ 7 - 0
CPPWebFramework/tests/tst_cppwebapplication.cpp

@@ -0,0 +1,7 @@
+#include "tst_cppwebapplication.h"
+
+
+void TST_CppWebApplication::test()
+{
+
+}

+ 14 - 0
CPPWebFramework/tests/tst_cppwebapplication.h

@@ -0,0 +1,14 @@
+#ifndef TST_CPPWEBAPPLICATION_H
+#define TST_CPPWEBAPPLICATION_H
+
+#include <QtTest>
+#include <cwf/cppwebapplication.h>
+
+class TST_CppWebApplication : public QObject
+{
+    Q_OBJECT
+private slots:
+    void test();
+};
+
+#endif // TST_CPPWEBAPPLICATION_H

+ 6 - 0
CPPWebFramework/tests/tst_cppwebcontroller.cpp

@@ -0,0 +1,6 @@
+#include "tst_cppwebcontroller.h"
+
+void TST_CppWebController::test()
+{
+
+}

+ 14 - 0
CPPWebFramework/tests/tst_cppwebcontroller.h

@@ -0,0 +1,14 @@
+#ifndef TST_CPPWEBSERVLET_H
+#define TST_CPPWEBSERVLET_H
+
+#include <QtTest>
+#include <cwf/cppwebcontroller.h>
+
+class TST_CppWebController : public QObject
+{
+    Q_OBJECT
+private slots:
+    void test();
+};
+
+#endif // TST_CPPWEBSERVLET_H

+ 6 - 0
CPPWebFramework/tests/tst_cppwebserver.cpp

@@ -0,0 +1,6 @@
+#include "tst_cppwebserver.h"
+
+void TST_CppWebServer::test()
+{
+
+}

+ 14 - 0
CPPWebFramework/tests/tst_cppwebserver.h

@@ -0,0 +1,14 @@
+#ifndef TST_CPPWEBSERVER_H
+#define TST_CPPWEBSERVER_H
+
+#include <QtTest>
+#include <cwf/cppwebserver.h>
+
+class TST_CppWebServer : public QObject
+{
+    Q_OBJECT
+private slots:
+    void test();
+};
+
+#endif // TST_CPPWEBSERVER_H

+ 135 - 0
CPPWebFramework/tests/tst_cstlcompiler.cpp

@@ -0,0 +1,135 @@
+#include "tst_cstlcompiler.h"
+#include <QFile>
+
+void TST_CSTLCompiler::test()
+{
+    testImport();
+    testForOutIf();
+}
+
+void TST_CSTLCompiler::testImport()
+{
+    QMap<QString, QObject *> objects;
+    createFile("header.html", header);
+    QByteArray originalHtml;
+    originalHtml  = "<html>";
+    originalHtml += "<body>";
+    originalHtml += "xxx";
+    originalHtml += "</body>";
+    originalHtml += "</html>";
+
+    QByteArray copy1(originalHtml);
+    QByteArray copy2(originalHtml);
+    QByteArray copy3(originalHtml);
+    QByteArray copy4(originalHtml);
+
+    copy1.replace("xxx", "<import url=\"/header.html\"/>");
+    copy2.replace("xxx", "<import/>");
+    copy3.replace("xxx", "<import url=\"/header.html\" url2=\"/header.html\"/>");
+    copy4.replace("xxx", "<import x=\"/header.html\"/>");
+    createFile("home.html", copy1);
+
+    QVERIFY2(CWF::CSTLCompiler(copy1, QDir().currentPath(), objects, false).output().contains("TestHeader"), "Should contains 'TestHeader'");
+    QVERIFY2(!CWF::CSTLCompiler(copy1, "xxx", objects, false).output().contains("TestHeader"), "Should not contains 'TestHeader'");
+    QVERIFY2(CWF::CSTLCompiler((QDir().currentPath() + "/home.html").toLatin1(), QDir().currentPath(), objects).output().contains("import url"), "Should contains 'import url'");
+    QVERIFY2(CWF::CSTLCompiler(copy2, QDir().currentPath(), objects, false).output().contains("NEEDS THE URL"), "Should contains 'NEEDS THE URL'");
+    QVERIFY2(CWF::CSTLCompiler(copy3, QDir().currentPath(), objects, false).output().contains("ONLY NEEDS"), "Should contains 'ONLY NEEDS'");
+    QVERIFY2(CWF::CSTLCompiler(copy4, QDir().currentPath(), objects, false).output().contains("NEEDS THE URL"), "Should contains 'NEEDS THE URL'");
+}
+
+void TST_CSTLCompiler::testForOutIf()
+{
+    ClientTest a, b;
+    fillClient(a, 0);
+    fillClient(b, 1);
+
+    CWF::QListObject obj;
+    obj.add(&a);
+    obj.add(&b);
+
+    QMap<QString, QObject *> objects({{"clients", &obj}});
+
+    QByteArray equal(buildHtmlForIf("equal"));
+    QByteArray different(buildHtmlForIf("different"));
+    QByteArray greater(buildHtmlForIf("greater"));
+    QByteArray greaterEqual(buildHtmlForIf("greater_equal"));
+    QByteArray lessEqual(buildHtmlForIf("less_equal"));
+    QByteArray less(buildHtmlForIf("less"));
+    QByteArray dontExists(buildHtmlForIf("dont_exists"));
+    QByteArray zero(buildHtmlForIf("less", NUMBER_ATTR::ZERO));
+    QByteArray three(buildHtmlForIf("less", NUMBER_ATTR::THREE));
+
+    QByteArray xx = CWF::CSTLCompiler(equal, QDir().currentPath(), objects, false).output();
+
+    QVERIFY2(CWF::CSTLCompiler(equal, QDir().currentPath(), objects, false).output().contains("ab1DE111111111.51.5"), "Should contains 'ab1DE111111111.51.5'");
+    QVERIFY2(CWF::CSTLCompiler(different, QDir().currentPath(), objects, false).output().contains("ab1DE011111111.51.5"), "Should contains 'ab1DE011111111.51.5'");
+    QVERIFY2(!CWF::CSTLCompiler(greater, QDir().currentPath(), objects, false).output().contains("ab1DE"), "Should not contains 'ab1DE'");
+    QVERIFY2(CWF::CSTLCompiler(less, QDir().currentPath(), objects, false).output().contains("ab1DE011111111.51.5"), "Should contains 'ab1DE011111111.51.5'");
+    QVERIFY2(CWF::CSTLCompiler(greaterEqual, QDir().currentPath(), objects, false).output().contains("ab1DE111111111.51.5"), "Should contains 'ab1DE111111111.51.5'");
+    QVERIFY2(CWF::CSTLCompiler(lessEqual, QDir().currentPath(), objects, false).output().contains("ab1DE111111111.51.5"), "Should contains 'ab1DE111111111.51.5'");
+    QVERIFY2(CWF::CSTLCompiler(dontExists, QDir().currentPath(), objects, false).output().contains("IF TAG DOESN'T"), "Should contains 'IF TAG DOESN'T'");
+    QVERIFY2(CWF::CSTLCompiler(zero, QDir().currentPath(), objects, false).output().contains("IF TAG NEEDS"), "Should contains 'IF TAG NEEDS'");
+    QVERIFY2(CWF::CSTLCompiler(three, QDir().currentPath(), objects, false).output().contains("IF TAG DOESN'T"), "Should contains 'IF TAG DOESN'T'");
+}
+
+void TST_CSTLCompiler::fillClient(ClientTest &client, short h)
+{
+    client.setA("a");
+    client.setB("b");
+    client.setC(true);
+    client.setD('D');
+    client.setE('E');
+    client.setH(h);
+    client.setI(1);
+    client.setJ(1);
+    client.setK(1);
+    client.setL(1);
+    client.setM(1);
+    client.setN(1);
+    client.setO(1);
+    client.setP(1.5);
+    client.setQ(1.5);
+}
+
+QByteArray TST_CSTLCompiler::buildHtmlForIf(const QByteArray &condition, NUMBER_ATTR number)
+{
+    QByteArray html;
+    html += "<!DOCTYPE html>";
+    html += "<html>";
+    html +=     "<body>";
+    html +=         "<for items=\"clients\" var=\"client\">";
+    if(number == NUMBER_ATTR::ZERO)
+        html +=         "<if>";
+    else if(number == NUMBER_ATTR::TWO)
+        html +=         "<if var=\"#{client.getH}\" " + condition + "=\"1\">";
+    else if(number == NUMBER_ATTR::THREE)
+        html +=         "<if var=\"#{client.getH}\" " + condition + "=\"1\" test=\"0\">";
+    html +=                     "<out value=\"#{client.getA}\"/>";
+    html +=                     "<out value=\"#{client.getB}\"/>";
+    html +=                     "<out value=\"#{client.getC}\"/>";
+    html +=                     "<out value=\"#{client.getD}\"/>";
+    html +=                     "<out value=\"#{client.getE}\"/>";
+    html +=                     "<out value=\"#{client.getH}\"/>";
+    html +=                     "<out value=\"#{client.getI}\"/>";
+    html +=                     "<out value=\"#{client.getJ}\"/>";
+    html +=                     "<out value=\"#{client.getK}\"/>";
+    html +=                     "<out value=\"#{client.getL}\"/>";
+    html +=                     "<out value=\"#{client.getM}\"/>";
+    html +=                     "<out value=\"#{client.getN}\"/>";
+    html +=                     "<out value=\"#{client.getO}\"/>";
+    html +=                     "<out value=\"#{client.getP}\"/>";
+    html +=                     "<out value=\"#{client.getQ}\"/>";
+    html +=             "</if>";
+    html +=         "</for>";
+    html +=     "</body>";
+    html += "</html>";
+
+    return html;
+}
+
+void TST_CSTLCompiler::createFile(const QString &name, const QByteArray &content)
+{
+    QFile file(name);
+    QVERIFY2(file.open(QIODevice::WriteOnly), ("Could not create " + name).toStdString().data());
+    file.write(content);
+}

+ 33 - 0
CPPWebFramework/tests/tst_cstlcompiler.h

@@ -0,0 +1,33 @@
+#ifndef TST_CSTLCOMPILER_H
+#define TST_CSTLCOMPILER_H
+
+#include <QtTest>
+#include <tests/tst_request.h>
+#include <cwf/cstlcompiler.h>
+
+enum class NUMBER_ATTR : char
+{
+    ZERO,
+    TWO,
+    THREE
+};
+
+class TST_CSTLCompiler : public QObject
+{
+    Q_OBJECT
+    const QByteArray header = "<head><title>TestHeader</title></head>";
+private slots:
+    void test();
+
+    void testImport();
+
+    void testForOutIf();
+
+    void fillClient(ClientTest &client, short h);
+
+    QByteArray buildHtmlForIf(const QByteArray &condition, NUMBER_ATTR number = NUMBER_ATTR::TWO);
+
+    void createFile(const QString &name, const QByteArray &content);
+};
+
+#endif // TST_CSTLCOMPILER_H

+ 6 - 0
CPPWebFramework/tests/tst_cstlcompilerattributes.cpp

@@ -0,0 +1,6 @@
+#include "tst_cstlcompilerattributes.h"
+
+void TST_CSTLCompilerAttributes::test()
+{
+
+}

+ 14 - 0
CPPWebFramework/tests/tst_cstlcompilerattributes.h

@@ -0,0 +1,14 @@
+#ifndef TST_CSTLCOMPILERATTRIBUTES_H
+#define TST_CSTLCOMPILERATTRIBUTES_H
+
+#include <QtTest>
+#include <cwf/cstlcompilerattributes.h>
+
+class TST_CSTLCompilerAttributes : public QObject
+{
+    Q_OBJECT
+private slots:
+    void test();
+};
+
+#endif // TST_CSTLCOMPILERATTRIBUTES_H

+ 6 - 0
CPPWebFramework/tests/tst_cstlcompilerfor.cpp

@@ -0,0 +1,6 @@
+#include "tst_cstlcompilerfor.h"
+
+void TST_CSTLCompilerFor::test()
+{
+
+}

+ 14 - 0
CPPWebFramework/tests/tst_cstlcompilerfor.h

@@ -0,0 +1,14 @@
+#ifndef TST_CSTLCOMPILERFOR_H
+#define TST_CSTLCOMPILERFOR_H
+
+#include <QtTest>
+#include <cwf/cstlcompilerfor.h>
+
+class TST_CSTLCompilerFor : public QObject
+{
+    Q_OBJECT
+private slots:
+    void test();
+};
+
+#endif // TST_CSTLCOMPILERFOR_H

+ 47 - 0
CPPWebFramework/tests/tst_cstlcompilerif.cpp

@@ -0,0 +1,47 @@
+#include "tst_cstlcompilerif.h"
+#include <cwf/cstlcompiler.h>
+#include <cwf/variant.h>
+
+void TST_CSTLCompilerIf::test()
+{
+    {
+        CWF::Variant var(1);
+        QMap<QString, QObject *> objects({{"var", &var}});
+        QByteArray r = CWF::CSTLCompiler(buildHtml(), QDir().currentPath(), objects, false).output();
+        QVERIFY2(r.contains("One"), "Should contains 'One'");
+        QVERIFY2(r.contains("Greater than Zero"), "Should contains 'Greater than Zero'");
+        QVERIFY2(r.contains("Less Two"), "Should contains 'Less Two'");
+        QVERIFY2(r.contains("Less equal Two"), "Should contains 'Less equal Two'");
+        QVERIFY2(r.contains("Greater equal One"), "Should contains 'Greater equal One'");
+        QVERIFY2(!r.contains("Test"), "Should not contains 'Test'");
+    }
+}
+
+QByteArray TST_CSTLCompilerIf::buildHtml()
+{
+    QByteArray html;
+    html  = "<html>";
+    html +=     "<body>";
+    html +=         "<if var=\"#{var.toInt}\" equal=\"2\">";
+    html +=             "Test";
+    html +=         "</if>";
+    html +=         "<if var=\"#{var.toInt}\" equal=\"1\">";
+    html +=             "One";
+    html +=             "<if var=\"#{var.toInt}\" greater=\"0\">";
+    html +=                 "Greater than Zero";
+    html +=             "</if>";
+    html +=         "</if>";
+    html +=         "<if var=\"#{var.toInt}\" less=\"2\">";
+    html +=             "<if var=\"#{var.toInt}\" less_equal=\"2\">";
+    html +=                 "<if var=\"#{var.toInt}\" greater_equal=\"1\">";
+    html +=                     "Greater equal One";
+    html +=                 "</if>";
+    html +=                 "Less equal Two";
+    html +=             "</if>";
+    html +=             "Less Two";
+    html +=         "</if>";
+    html +=     "</body>";
+    html += "</html>";
+
+    return html;
+}

+ 16 - 0
CPPWebFramework/tests/tst_cstlcompilerif.h

@@ -0,0 +1,16 @@
+#ifndef TST_CSTLCOMPILERIF_H
+#define TST_CSTLCOMPILERIF_H
+
+#include <QtTest>
+#include <cwf/cstlcompilerif.h>
+
+class TST_CSTLCompilerIf : public QObject
+{
+    Q_OBJECT
+private slots:
+    void test();
+
+    QByteArray buildHtml();
+};
+
+#endif // TST_CSTLCOMPILERIF_H

+ 6 - 0
CPPWebFramework/tests/tst_cstlcompilerimport.cpp

@@ -0,0 +1,6 @@
+#include "tst_cstlcompilerimport.h"
+
+void TST_CSTLCompilerImport::test()
+{
+
+}

+ 14 - 0
CPPWebFramework/tests/tst_cstlcompilerimport.h

@@ -0,0 +1,14 @@
+#ifndef TST_CSTLCOMPILERIMPORT_H
+#define TST_CSTLCOMPILERIMPORT_H
+
+#include <QtTest>
+#include <cwf/cstlcompilerimport.h>
+
+class TST_CSTLCompilerImport : public QObject
+{
+    Q_OBJECT
+private slots:
+    void test();
+};
+
+#endif // TST_CSTLCOMPILERIMPORT_H

+ 6 - 0
CPPWebFramework/tests/tst_cstlcompilerobject.cpp

@@ -0,0 +1,6 @@
+#include "tst_cstlcompilerobject.h"
+
+void TST_CSTLCompilerObject::test()
+{
+
+}

+ 14 - 0
CPPWebFramework/tests/tst_cstlcompilerobject.h

@@ -0,0 +1,14 @@
+#ifndef TST_CSTLCOMPILEROBJECT_H
+#define TST_CSTLCOMPILEROBJECT_H
+
+#include <QtTest>
+#include <cwf/cstlcompilerobject.h>
+
+class TST_CSTLCompilerObject : public QObject
+{
+    Q_OBJECT
+private slots:
+    void test();
+};
+
+#endif // TST_CSTLCOMPILEROBJECT_H

+ 26 - 0
CPPWebFramework/tests/tst_cstlcompilerout.cpp

@@ -0,0 +1,26 @@
+#include "tst_cstlcompilerout.h"
+#include <cwf/cstlcompiler.h>
+#include <cwf/variant.h>
+
+void TST_CSTLCompilerOut::test()
+{
+    {
+        CWF::Variant var(1);
+        QMap<QString, QObject *> objects({{"var", &var}});
+        QByteArray r = CWF::CSTLCompiler(buildHtml(), QDir().currentPath(), objects, false).output();
+        QVERIFY2(r.contains("1"), "Should contains '1'");
+    }
+}
+
+QByteArray TST_CSTLCompilerOut::buildHtml()
+{
+    QByteArray html;
+    html  = "<html>";
+    html +=     "<body>";
+    html +=         "Um #{var.toInt} + #{var.toInt} = 2";
+    html +=         "<a link=\"#{var.toInt} #{var.toInt} \"/>";
+    html +=     "</body>";
+    html += "</html>";
+
+    return html;
+}

Some files were not shown because too many files changed in this diff