+
+
+
+
+
+
+LogDoctor: Member List
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LogDoctor 2.00
+
+ Parse Apache2/Nginx/IIS logs and create statistics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+
+
+
+
+
+
This is the complete list of members for BodyPart , including all inherited members.
+
+
+
+
+
diff --git a/docs/html/structBodyPart.html b/docs/html/structBodyPart.html
new file mode 100644
index 00000000..2af2036c
--- /dev/null
+++ b/docs/html/structBodyPart.html
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+LogDoctor: BodyPart Struct Reference
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LogDoctor 2.00
+
+ Parse Apache2/Nginx/IIS logs and create statistics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+
+
+
+
+
+
Instance of a part of the body of the snake.
+ More...
+
+
#include <snake.h>
+
+
+
+void update (const unsigned int &new_x, const unsigned int &new_y, const Direction &new_direction)
+ Updates the position and direction of the part.
+
+
+
+
+unsigned int x
+ The position on the X-axis.
+
+
+unsigned int y
+ The position on the Y-axis.
+
+
+Direction direction
+ The current direction of the part.
+
+
+Direction prev_direction
+ The previous direction of the part.
+
+QGraphicsPixmapItem * image
+
+
+
+
Instance of a part of the body of the snake.
+
+
+
◆ image
+
+
+
+
+
+ QGraphicsPixmapItem* BodyPart::image
+
+
+
+
The image which graphically represents the part
+
+
+
+
The documentation for this struct was generated from the following file:
+logdoctor/games/snake/snake.h
+
+
+
+
+
+
diff --git a/docs/html/structCraplog_1_1BWlist-members.html b/docs/html/structCraplog_1_1BWlist-members.html
new file mode 100644
index 00000000..7ccc85d3
--- /dev/null
+++ b/docs/html/structCraplog_1_1BWlist-members.html
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+LogDoctor: Member List
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LogDoctor 2.00
+
+ Parse Apache2/Nginx/IIS logs and create statistics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+
+
+
+
+
+
+
This is the complete list of members for Craplog::BWlist , including all inherited members.
+
+
+
+
+
diff --git a/docs/html/structCraplog_1_1BWlist.html b/docs/html/structCraplog_1_1BWlist.html
new file mode 100644
index 00000000..eddd9948
--- /dev/null
+++ b/docs/html/structCraplog_1_1BWlist.html
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+LogDoctor: Craplog::BWlist Struct Reference
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LogDoctor 2.00
+
+ Parse Apache2/Nginx/IIS logs and create statistics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+
+
+
+
+
+
+
Structure to hold the items of a blacklist/warnlist.
+ More...
+
+
#include <craplog.h>
+
+
+
+bool used
+ Whether the list is set to be used or not.
+
+
+std::vector< std::string > list
+ The list of items.
+
+
+
+
Structure to hold the items of a blacklist/warnlist.
+
The documentation for this struct was generated from the following file:
+logdoctor/modules/craplog/craplog.h
+
+
+
+
+
+
diff --git a/docs/html/structCraplog_1_1LogFile-members.html b/docs/html/structCraplog_1_1LogFile-members.html
new file mode 100644
index 00000000..aa8f6964
--- /dev/null
+++ b/docs/html/structCraplog_1_1LogFile-members.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+LogDoctor: Member List
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LogDoctor 2.00
+
+ Parse Apache2/Nginx/IIS logs and create statistics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+
+
+
+
+
+
+
This is the complete list of members for Craplog::LogFile , including all inherited members.
+
+
+
+
+
diff --git a/docs/html/structCraplog_1_1LogFile.html b/docs/html/structCraplog_1_1LogFile.html
new file mode 100644
index 00000000..43cd6399
--- /dev/null
+++ b/docs/html/structCraplog_1_1LogFile.html
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+LogDoctor: Craplog::LogFile Struct Reference
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LogDoctor 2.00
+
+ Parse Apache2/Nginx/IIS logs and create statistics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+
+
+
+
+
+
+
Structure which holds informations about a log file.
+ More...
+
+
#include <craplog.h>
+
+
+
+bool selected
+ Wheter the file has been selected to be use or not.
+
+
+bool used_already
+ Wheter the file has been used already or not.
+
+
+int size
+ The size of the file.
+
+
+QString name
+ The name of the file, to be displayed in the list.
+
+
+std::string hash
+ The sha256 hash of the content.
+
+
+std::string path
+ The path of the file, including the file name.
+
+
+
+
Structure which holds informations about a log file.
+
The documentation for this struct was generated from the following file:
+logdoctor/modules/craplog/craplog.h
+
+
+
+
+
+
diff --git a/docs/html/structCraplog_1_1LogName-members.html b/docs/html/structCraplog_1_1LogName-members.html
new file mode 100644
index 00000000..0a85de9d
--- /dev/null
+++ b/docs/html/structCraplog_1_1LogName-members.html
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+LogDoctor: Member List
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LogDoctor 2.00
+
+ Parse Apache2/Nginx/IIS logs and create statistics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+
+
+
+
+
+
+
This is the complete list of members for Craplog::LogName , including all inherited members.
+
+
+
+
+
diff --git a/docs/html/structCraplog_1_1LogName.html b/docs/html/structCraplog_1_1LogName.html
new file mode 100644
index 00000000..8939187e
--- /dev/null
+++ b/docs/html/structCraplog_1_1LogName.html
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+LogDoctor: Craplog::LogName Struct Reference
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LogDoctor 2.00
+
+ Parse Apache2/Nginx/IIS logs and create statistics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+
+
+
+
+
+
+
Web Server specific file names criterions.
+ More...
+
+
+
+std::string starts
+ What should be initial part of the name.
+
+
+std::string contains
+ What should be contained in the middle of the name.
+
+
+std::string ends
+ What should be final part of the name.
+
+
+
+
Web Server specific file names criterions.
+
The rules to be used to decide whether a file name is valid or not
See also isFileNameValid()
+
The documentation for this struct was generated from the following file:
+logdoctor/modules/craplog/craplog.h
+
+
+
+
+
+
diff --git a/docs/html/structFormatOps_1_1LogsFormat-members.html b/docs/html/structFormatOps_1_1LogsFormat-members.html
new file mode 100644
index 00000000..344b0d6d
--- /dev/null
+++ b/docs/html/structFormatOps_1_1LogsFormat-members.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+LogDoctor: Member List
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LogDoctor 2.00
+
+ Parse Apache2/Nginx/IIS logs and create statistics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+
+
+
+
+
+
+
This is the complete list of members for FormatOps::LogsFormat , including all inherited members.
+
+
+
+
+
diff --git a/docs/html/structFormatOps_1_1LogsFormat.html b/docs/html/structFormatOps_1_1LogsFormat.html
new file mode 100644
index 00000000..65f744ee
--- /dev/null
+++ b/docs/html/structFormatOps_1_1LogsFormat.html
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+LogDoctor: FormatOps::LogsFormat Struct Reference
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LogDoctor 2.00
+
+ Parse Apache2/Nginx/IIS logs and create statistics
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
Searching...
+
No Matches
+
+
+
+
+
+
+
+
+
+
+
Structure which holds informations about a log format.
+ More...
+
+
#include <formats.h>
+
+
+
+std::string string
+ The logs format string.
+
+
+std::string initial
+ The initial separator.
+
+
+std::string final
+ The final separator.
+
+
+std::vector< std::string > separators
+ The separators in the middle.
+
+
+std::vector< std::string > fields
+ The fields.
+
+
+int new_lines
+ The number of new lines.
+
+
+
+
Structure which holds informations about a log format.
+
The documentation for this struct was generated from the following file:
+logdoctor/modules/craplog/modules/formats.h
+
+
+
+
+
+
diff --git a/docs/html/sync_off.png b/docs/html/sync_off.png
new file mode 100644
index 00000000..3b443fc6
Binary files /dev/null and b/docs/html/sync_off.png differ
diff --git a/docs/html/sync_on.png b/docs/html/sync_on.png
new file mode 100644
index 00000000..e08320fb
Binary files /dev/null and b/docs/html/sync_on.png differ
diff --git a/docs/html/tab_a.png b/docs/html/tab_a.png
new file mode 100644
index 00000000..3b725c41
Binary files /dev/null and b/docs/html/tab_a.png differ
diff --git a/docs/html/tab_ad.png b/docs/html/tab_ad.png
new file mode 100644
index 00000000..e34850ac
Binary files /dev/null and b/docs/html/tab_ad.png differ
diff --git a/docs/html/tab_b.png b/docs/html/tab_b.png
new file mode 100644
index 00000000..e2b4a863
Binary files /dev/null and b/docs/html/tab_b.png differ
diff --git a/docs/html/tab_bd.png b/docs/html/tab_bd.png
new file mode 100644
index 00000000..91c25249
Binary files /dev/null and b/docs/html/tab_bd.png differ
diff --git a/docs/html/tab_h.png b/docs/html/tab_h.png
new file mode 100644
index 00000000..fd5cb705
Binary files /dev/null and b/docs/html/tab_h.png differ
diff --git a/docs/html/tab_hd.png b/docs/html/tab_hd.png
new file mode 100644
index 00000000..2489273d
Binary files /dev/null and b/docs/html/tab_hd.png differ
diff --git a/docs/html/tab_s.png b/docs/html/tab_s.png
new file mode 100644
index 00000000..ab478c95
Binary files /dev/null and b/docs/html/tab_s.png differ
diff --git a/docs/html/tab_sd.png b/docs/html/tab_sd.png
new file mode 100644
index 00000000..757a565c
Binary files /dev/null and b/docs/html/tab_sd.png differ
diff --git a/docs/html/tabs.css b/docs/html/tabs.css
new file mode 100644
index 00000000..71c8a470
--- /dev/null
+++ b/docs/html/tabs.css
@@ -0,0 +1 @@
+.sm{position:relative;z-index:9999}.sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0)}.sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right}.sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0}.sm ul{display:none}.sm li,.sm a{position:relative}.sm a{display:block}.sm a.disabled{cursor:not-allowed}.sm:after{content:"\00a0";display:block;height:0;font:0/0 serif;clear:both;visibility:hidden;overflow:hidden}.sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.main-menu-btn{position:relative;display:inline-block;width:36px;height:36px;text-indent:36px;margin-left:8px;white-space:nowrap;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.main-menu-btn-icon,.main-menu-btn-icon:before,.main-menu-btn-icon:after{position:absolute;top:50%;left:2px;height:2px;width:24px;background:var(--nav-menu-button-color);-webkit-transition:all .25s;transition:all .25s}.main-menu-btn-icon:before{content:'';top:-7px;left:0}.main-menu-btn-icon:after{content:'';top:7px;left:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon{height:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:before{top:0;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:after{top:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}#main-menu-state{position:absolute;width:1px;height:1px;margin:-1px;border:0;padding:0;overflow:hidden;clip:rect(1px,1px,1px,1px)}#main-menu-state:not(:checked) ~ #main-menu{display:none}#main-menu-state:checked ~ #main-menu{display:block}@media(min-width:768px){.main-menu-btn{position:absolute;top:-99999px}#main-menu-state:not(:checked) ~ #main-menu{display:block}}.sm-dox{background-image:var(--nav-gradient-image)}.sm-dox a,.sm-dox a:focus,.sm-dox a:hover,.sm-dox a:active{padding:0 12px;padding-right:43px;font-family:var(--font-family-nav);font-size:13px;font-weight:bold;line-height:36px;text-decoration:none;text-shadow:var(--nav-text-normal-shadow);color:var(--nav-text-normal-color);outline:0}.sm-dox a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox a.current{color:#d23600}.sm-dox a.disabled{color:#bbb}.sm-dox a span.sub-arrow{position:absolute;top:50%;margin-top:-14px;left:auto;right:3px;width:28px;height:28px;overflow:hidden;font:bold 12px/28px monospace !important;text-align:center;text-shadow:none;background:var(--nav-menu-toggle-color);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox a span.sub-arrow:before{display:block;content:'+'}.sm-dox a.highlighted span.sub-arrow:before{display:block;content:'-'}.sm-dox>li:first-child>a,.sm-dox>li:first-child>:not(ul) a{-moz-border-radius:5px 5px 0 0;-webkit-border-radius:5px;border-radius:5px 5px 0 0}.sm-dox>li:last-child>a,.sm-dox>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul{-moz-border-radius:0 0 5px 5px;-webkit-border-radius:0;border-radius:0 0 5px 5px}.sm-dox>li:last-child>a.highlighted,.sm-dox>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox ul{background:var(--nav-menu-background-color)}.sm-dox ul a,.sm-dox ul a:focus,.sm-dox ul a:hover,.sm-dox ul a:active{font-size:12px;border-left:8px solid transparent;line-height:36px;text-shadow:none;background-color:var(--nav-menu-background-color);background-image:none}.sm-dox ul a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:0 1px 1px black}.sm-dox ul ul a,.sm-dox ul ul a:hover,.sm-dox ul ul a:focus,.sm-dox ul ul a:active{border-left:16px solid transparent}.sm-dox ul ul ul a,.sm-dox ul ul ul a:hover,.sm-dox ul ul ul a:focus,.sm-dox ul ul ul a:active{border-left:24px solid transparent}.sm-dox ul ul ul ul a,.sm-dox ul ul ul ul a:hover,.sm-dox ul ul ul ul a:focus,.sm-dox ul ul ul ul a:active{border-left:32px solid transparent}.sm-dox ul ul ul ul ul a,.sm-dox ul ul ul ul ul a:hover,.sm-dox ul ul ul ul ul a:focus,.sm-dox ul ul ul ul ul a:active{border-left:40px solid transparent}@media(min-width:768px){.sm-dox ul{position:absolute;width:12em}.sm-dox li{float:left}.sm-dox.sm-rtl li{float:right}.sm-dox ul li,.sm-dox.sm-rtl ul li,.sm-dox.sm-vertical li{float:none}.sm-dox a{white-space:nowrap}.sm-dox ul a,.sm-dox.sm-vertical a{white-space:normal}.sm-dox .sm-nowrap>li>a,.sm-dox .sm-nowrap>li>:not(ul) a{white-space:nowrap}.sm-dox{padding:0 10px;background-image:var(--nav-gradient-image);line-height:36px}.sm-dox a span.sub-arrow{top:50%;margin-top:-2px;right:12px;width:0;height:0;border-width:4px;border-style:solid dashed dashed dashed;border-color:var(--nav-text-normal-color) transparent transparent transparent;background:transparent;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted{padding:0 12px;background-image:var(--nav-separator-image);background-repeat:no-repeat;background-position:right;-moz-border-radius:0 !important;-webkit-border-radius:0;border-radius:0 !important}.sm-dox a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox a:hover span.sub-arrow{border-color:var(--nav-text-hover-color) transparent transparent transparent}.sm-dox a.has-submenu{padding-right:24px}.sm-dox li{border-top:0}.sm-dox>li>ul:before,.sm-dox>li>ul:after{content:'';position:absolute;top:-18px;left:30px;width:0;height:0;overflow:hidden;border-width:9px;border-style:dashed dashed solid dashed;border-color:transparent transparent #bbb transparent}.sm-dox>li>ul:after{top:-16px;left:31px;border-width:8px;border-color:transparent transparent var(--nav-menu-background-color) transparent}.sm-dox ul{border:1px solid #bbb;padding:5px 0;background:var(--nav-menu-background-color);-moz-border-radius:5px !important;-webkit-border-radius:5px;border-radius:5px !important;-moz-box-shadow:0 5px 9px rgba(0,0,0,0.2);-webkit-box-shadow:0 5px 9px rgba(0,0,0,0.2);box-shadow:0 5px 9px rgba(0,0,0,0.2)}.sm-dox ul a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-color:transparent transparent transparent var(--nav-menu-foreground-color);border-style:dashed dashed dashed solid}.sm-dox ul a,.sm-dox ul a:hover,.sm-dox ul a:focus,.sm-dox ul a:active,.sm-dox ul a.highlighted{color:var(--nav-menu-foreground-color);background-image:none;border:0 !important;color:var(--nav-menu-foreground-color);background-image:none}.sm-dox ul a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox ul a:hover span.sub-arrow{border-color:transparent transparent transparent var(--nav-text-hover-color)}.sm-dox span.scroll-up,.sm-dox span.scroll-down{position:absolute;display:none;visibility:hidden;overflow:hidden;background:var(--nav-menu-background-color);height:36px}.sm-dox span.scroll-up:hover,.sm-dox span.scroll-down:hover{background:#eee}.sm-dox span.scroll-up:hover span.scroll-up-arrow,.sm-dox span.scroll-up:hover span.scroll-down-arrow{border-color:transparent transparent #d23600 transparent}.sm-dox span.scroll-down:hover span.scroll-down-arrow{border-color:#d23600 transparent transparent transparent}.sm-dox span.scroll-up-arrow,.sm-dox span.scroll-down-arrow{position:absolute;top:0;left:50%;margin-left:-6px;width:0;height:0;overflow:hidden;border-width:6px;border-style:dashed dashed solid dashed;border-color:transparent transparent var(--nav-menu-foreground-color) transparent}.sm-dox span.scroll-down-arrow{top:8px;border-style:solid dashed dashed dashed;border-color:var(--nav-menu-foreground-color) transparent transparent transparent}.sm-dox.sm-rtl a.has-submenu{padding-right:12px;padding-left:24px}.sm-dox.sm-rtl a span.sub-arrow{right:auto;left:12px}.sm-dox.sm-rtl.sm-vertical a.has-submenu{padding:10px 20px}.sm-dox.sm-rtl.sm-vertical a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-rtl>li>ul:before{left:auto;right:30px}.sm-dox.sm-rtl>li>ul:after{left:auto;right:31px}.sm-dox.sm-rtl ul a.has-submenu{padding:10px 20px !important}.sm-dox.sm-rtl ul a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-vertical{padding:10px 0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox.sm-vertical a{padding:10px 20px}.sm-dox.sm-vertical a:hover,.sm-dox.sm-vertical a:focus,.sm-dox.sm-vertical a:active,.sm-dox.sm-vertical a.highlighted{background:#fff}.sm-dox.sm-vertical a.disabled{background-image:var(--nav-gradient-image)}.sm-dox.sm-vertical a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-style:dashed dashed dashed solid;border-color:transparent transparent transparent #555}.sm-dox.sm-vertical>li>ul:before,.sm-dox.sm-vertical>li>ul:after{display:none}.sm-dox.sm-vertical ul a{padding:10px 20px}.sm-dox.sm-vertical ul a:hover,.sm-dox.sm-vertical ul a:focus,.sm-dox.sm-vertical ul a:active,.sm-dox.sm-vertical ul a.highlighted{background:#eee}.sm-dox.sm-vertical ul a.disabled{background:var(--nav-menu-background-color)}}
\ No newline at end of file
diff --git a/installation_stuff/LogDoctor.desktop b/installation_stuff/LogDoctor.desktop
new file mode 100644
index 00000000..39c3b780
--- /dev/null
+++ b/installation_stuff/LogDoctor.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Type=Application
+Name=LogDoctor
+GenericName=LogDoctor
+Comment=Apache2/Nginx/IIS access logs analyzer to view dynamically generated statistics
+Icon=/usr/share/LogDoctor/LogDoctor.svg
+Exec=logdoctor
+Actions=
+Categories=Utility;
+Keywords=logdoctor;apache2;nginx;iis;webservers;logs;analyzer;statistics;
diff --git a/installation_stuff/LogDoctor.svg b/installation_stuff/LogDoctor.svg
new file mode 100644
index 00000000..e03977a4
--- /dev/null
+++ b/installation_stuff/LogDoctor.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installation_stuff/logdocdata/help/en/apache_format.html b/installation_stuff/logdocdata/help/en/apache_format.html
new file mode 100644
index 00000000..78cec791
--- /dev/null
+++ b/installation_stuff/logdocdata/help/en/apache_format.html
@@ -0,0 +1,253 @@
+
+
+
+
+ LogDoctor - Help
+
+
+
+
+
+ Apache2
+
+
+
+ Access logs format string
+
+ Configuration file
+
+ The configuration file should be located at:
+ /etc/apache2/apache2.conf
+
+ The line to configure access logs is the one starting with "LogFormat " followed by the list of fields codes.
+
+
+
+ Common logs formats
+
+ Most commonly used format strings are:
+
+
+ Common log format (CLF)
+ LogFormat "%h %l %u %t \"%r\" %>s %O" common
+
+ Combined log format (NCSA standard)
+ LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-agent}i\"" combined
+
+
+
+
+ Suggested logs formats
+
+ A suggested format string, to allow using the complete set of functionalities of LogDoctor, is:
+ LogFormat "%{%F %T}t %H %m %U %q %>s %I %O %D \"%{Referer}i\" \"%{Cookie}i\" \"%{User-agent}i\" %{c}h" combined
+
+ The string above should be preferred, but alternatives can be used as well, like:
+ LogFormat "%{sec}t \"%r\" %q %<s %I %O %D \"%{Referer}i\" \"%{Cookie}i\" \"%{User-agent}i\" %h" combined
+
+
+
+ Note on custom format strings
+
+ If you're using your own custom string, please keep in mind that parsing is not magic. When you define your own string, think about which characters can be there in a field and use separators accordingly to not conflict with the field itself.
+ As an example: an URI (%U ) can't contain whitespaces, so it is safe to use a space to separe this field by the previous and next one. Instead, the User-Agent (*%{User-agent}i*) may contain spaces, as well as parenthesis, brackets, dashes, etc, so it's better to pick an appropriate separator (double-quotes are a good choice, since they get escaped while logging).
+
+
+
+ Note on control-characters
+
+ Although Apache2 does support some control-characters (aka escape sequences), it is reccomended to not use them inside format strings.
+ In particular, the carriage return will most-likely overwrite previous fields data, making it very difficult to understand where the current field ends (specially for fields like URIs, queries, user-agents, etc) and nearly impossible to retrieve the overwritten data, which will lead in having a wasted database, un-realistic statistics and/or crashes during execution.
+ About the new line character, it has no sense to use it, if not for testing purposes. The same is true for the horizontal tab , for which is better to use a simple whitespace instead.
+ The only control-characters supported by Apache2 are \n , \t and \r . Any other character will be ignored and treated as text.
+
+
+
+
+
+
+ Access logs format fields
+
+ Fields considered by LogDoctor
+
+ Only the following fields will be considered, meaning that only these fields' data will be stored and used for the statistics.
+
+
+ Code Informations
+
+ %%
+ The percent sign character, will result in a single percent sign and treated as normal text (from both Apache and LogDoctor).
+
+
+ %t
+ Time the request was received, in the format [DD/Mon/YYYY:hh:mm:ss ±TZ] . The last number (TZ) indicates the timezone offset from GMT.
+
+
+ %{FORMAT }t
+ Time the request was received, in the form given by FORMAT, which should be in an extended strftime format.
+ The following format tokens are supported (by LogDoctor, any other than the following will be discarded, even if valid):
+
+
+ Format Description
+ sec time since epoch, in seconds
+ msec time since epoch, in milliseconds
+ usec time since epoch, in microseconds
+ %b month name, abbreviated (same as %h )
+ %B month name
+ %c date and time representation
+ %d day number, zero padded
+ %D date, in the form of MM/DD/YY
+ %e day number, space padded
+ %F date, in the form of YYYY/MM/DD
+ %h month name, abbreviated (same as %b )
+ %H hour, in 24h format, zero padded
+ %m month number, zero padded
+ %M minute
+ %r time if the day, in 12h format, in the form of HH:MM:SS AM/PM
+ %R time of the day, in HH:MM format
+ %S second
+ %T ISO 8601 time, in the form of HH:MM:SS
+ %x date representation
+ %X time representation
+ %y year, last two digits (YY)
+ %Y year
+
+
+
+ Note: time formats sec , msec and usec can't be mixed together or with other formats.
+
+
+ %r
+ First line of request, equivalent to: %m %U?%q %H .
+
+
+ %H
+ The request protocol (HTTP/v, HTTPS/v) .
+
+
+ %m
+ The request method (GET, POST, HEAD, ...) .
+
+
+ %U
+ The URI path requested, not including any query string.
+
+
+ %q
+ Query string (if any).
+
+
+ %s
+ HTTP Status code at the beginning of the request (exclude redirections statuses).
+
+
+ %>s
+ Final HTTP Status code (in case requests have been internally redirected).
+
+
+ %I
+ Bytes received, including request and headers (you need to enable mod_logio to use this).
+
+
+ %O
+ Bytes sent, including headers (you need to enable mod_logio to use this).
+
+
+ %T
+ The time taken to serve the request, in seconds.
+
+
+ %{UNIT }T
+ The time taken to serve the request, in a time unit given by UNIT (only available in 2.4.13 and later).
+ Valid units are:
+
+
+ Unit Description
+ s seconds
+ ms milliseconds
+ us microseconds
+
+
+
+
+ %D
+ The time taken to serve the request, in milliseconds.
+
+
+ %h
+ IP Address of the client (remote hostname).
+
+
+ %{c}h
+ Like %h, but always reports on the hostname of the underlying TCP connection and not any modifications to the remote hostname by modules like mod_remoteip.
+
+
+ %{VARNAME }i
+ The contents of VARNAME: header line(s) in the request sent to the server.
+ Supported varnames (by LogDoctor) are:
+
+
+ VarName Description
+ Cookie cookie of the request
+ Referer referrer host
+ User-agent web-browser or bot identification string
+
+
+
+
+
+
+
+ Fields discarded by LogDoctor
+
+ Any field than the ones above won't be considered by LogDoctor.
+ When generating a log sample , these fields will appear as 'DISCARDED '.
+ If you aint using logs for any other purpose, please remove unnecessary fields to make the process faster and reduce the possibility of errors.
+
+
+
+
+
+ References
+
+
+
+
+
+
+
diff --git a/installation_stuff/logdocdata/help/en/iis_format.html b/installation_stuff/logdocdata/help/en/iis_format.html
new file mode 100644
index 00000000..9e9a887a
--- /dev/null
+++ b/installation_stuff/logdocdata/help/en/iis_format.html
@@ -0,0 +1,184 @@
+
+
+
+
+ LogDoctor - Help
+
+
+
+
+
+ IIS
+
+
+
+ Access logs format string
+
+ Configuration
+
+ The configuration file should be located at:
+ C:\inetpub\logs\LogFiles\<YourSiteName>
+
+ Access logs can be configured from the IIS Manager .
+ To open the IIS Manager, go to Control panel
→ Administrative tools
→
→ Internet Informations Service (IIS) Manager
.
+ Once inside, click on Logging
to edit the logs settings.
+
+
+
+ Logs format modules
+
+ Available logs formats (supported by LogDoctor) are:
+
+
+ IIS
+ Comma-separated values, can't be customized further. The file name is usually something like u_in<DATE>.log
+
+ NCSA
+ Whitespace-separated values, can't be customized further. The file name is usually something like u_nc<DATE>.log
+
+ W3C
+ Whitespace-separated values, can be customized by selecting which fields to log. The file name is usually something like u_ex<DATE>.log
+
+
+
+
+ Suggested logs format
+
+ The suggested logs module is the W3C format, which will allow using the complete set of functionalities of LogDoctor.
+ The suggested fields to use are the following:
+ date
, time
, cs-method
, cs-uri-stem
, cs-uri-query
, c-ip
, cs-version
, cs(User-Agent)
, cs(Cookie)
, cs(Referer)
, sc-status
, sc-bytes
, cs-bytes
, time-taken
+
+
+
+ Logs format strings
+
+ Both the IIS and NCSA modules are standard, and thus LogDoctor doesn't need the format string to be specified.
+ The W3C module can be customized and so a format string must be specified. It can be found insed any log file created using this format module, searching the line starting with #Fields:
.
+
+
+
+
+
+
+ W3C logs format fields
+
+ Fields considered by LogDoctor
+
+ Only the following fields will be considered, meaning that only these fields' data will be stored and used for the statistics.
+
+
+ Code Informations
+
+ date
+ The date on which the activity occurred (UTC) .
+
+
+ time
+ The time at which the activity occurred (UTC) .
+
+
+ cs-version
+ The request protocol (HTTP/v, HTTPS/v) .
+
+
+ cs-method
+ The request method (GET, POST, HEAD, ...) .
+
+
+ cs-uri-stem
+ The URI path requested, not including any query string.
+
+
+ cs-uri-query
+ Query string (if any).
+
+
+
+ sc-status
+ HTTP status code.
+
+
+ cs-bytes
+ The number of bytes received and processed by the server.
+
+
+ sc-bytes
+ The number of bytes sent back by the server.
+
+
+ time-taken
+ Time taken to serve the request, in milliseconds
+
+
+ cs(Referer)
+ The site which provided a link to the current site.
+
+
+ cs(Cookie)
+ The content of the cookie sent or received (if any).
+
+
+ cs(User-Agent)
+ Web-browser or bot identification string
+
+
+ c-ip
+ The IP address of the client that made the request.
+
+
+
+
+ Fields discarded by LogDoctor
+
+ Any field than the ones above won't be considered by LogDoctor.
+ When generating a log sample , these fields will appear as 'DISCARDED '.
+ If you aint using logs for any other purpose, please remove unnecessary fields to make the process faster and reduce the possibility of errors.
+
+
+
+
+ References
+
+
+
+
+
+
+
diff --git a/installation_stuff/logdocdata/help/en/nginx_format.html b/installation_stuff/logdocdata/help/en/nginx_format.html
new file mode 100644
index 00000000..61da06b2
--- /dev/null
+++ b/installation_stuff/logdocdata/help/en/nginx_format.html
@@ -0,0 +1,193 @@
+
+
+
+
+ LogDoctor - Help
+
+
+
+
+
+ Nginx
+
+
+
+ Access logs format string
+
+ Configuration file
+
+ The configuration file should be located at:
+ /usr/local/etc/nginx/nginx.conf
+
+ The line to configure access logs is the one starting with "log_format main " followed by the list of fields codes.
+
+
+
+ Default logs formats
+
+ The default logs format string is:
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $bytes_sent "$http_referer" "$http_user_agent"'
+
+
+
+ Suggested logs format
+
+ The suggested format string, to allow using the most of the functionalities of LogDoctor, is:
+ log_format main '$time_iso8601 "$request" $status $request_length $bytes_sent $request_time "$http_referer" "$http_user_agent" $remote_addr'
+
+
+
+ Note the format strings
+
+ When copy-pasting the format string, please remove any identation (if present) and just paste the resulting format string only.
+
+
+
+ Note on control-characters
+
+ Although Nginx does support some control-characters (aka escape sequences), it is reccomended to not use them inside format strings.
+ In particular, the carriage return will most-likely overwrite previous fields data, making it very difficult to understand where the current field ends (specially for fields like URIs, queries, user-agents, etc) and nearly impossible to retrieve the overwritten data, which will lead in having a wasted database, un-realistic statistics and/or crashes during execution.
+ About the new line character, it has no sense to use it, if not for testing purposes. The same is true for the horizontal tab , for which is better to use a simple whitespace instead.
+ The only control-characters supported by Nginx are \n , \t and \r . Any other character will be ignored and treated as text.
+
+
+
+
+
+
+ Access logs format fields
+
+ Fields considered by LogDoctor
+
+ Only the following fields will be considered, meaning that only these fields' data will be stored and used for the statistics.
+
+
+ Code Informations
+
+ $time_local
+ Date and time in CLF (NCSA) format.
+
+
+ $time_iso8601
+ Date and time in ISO 8601 format.
+
+
+ $msec
+ Time since epoch (in seconds, with a milliseconds resolution) at the time of the log write.
+
+
+ $request
+ First line of request, equivalent to: $request_method $request_uri $server_protocol .
+
+
+ $server_protocol
+ The request protocol (HTTP/v, HTTPS/v) .
+
+
+ $request_method
+ The request method (GET, POST, HEAD, ...) .
+
+
+ $request_uri
+ The requested URI, including the query string (if any).
+
+
+ $uri
+ The URI path requested, not including the query string.
+
+
+ $query_string
+ Query string (if any).
+
+
+ $status
+ HTTP status code.
+
+
+ $request_length
+ Number of bytes received (including request line, header, and request body).
+
+
+ $bytes_sent
+ The number of bytes sent to a client.
+
+
+ $request_time
+ Request processing time in seconds with a milliseconds resolution: time elapsed between the first bytes were read from the client and the log write after the last bytes were sent to the client.
+
+
+ $http_referer
+ Referrer hostname.
+
+
+ $cookie_NAME
+ Cookie of the request having the given NAME .
+
+
+ $http_user_agent
+ Web-browser or Bot identification string.
+
+
+ $remote_addr
+ IP Address of the visitor (client).
+
+
+ $realip_remote_addr
+ Original client IP address.
+
+
+
+
+
+ Fields discarded by LogDoctor
+
+ Any field than the ones above won't be considered by LogDoctor.
+ When generating a log sample , these fields will appear as 'DISCARDED '.
+ If you aint using logs for any other purpose, please remove unnecessary fields to make the process faster and reduce the possibility of errors.
+
+
+
+
+ References
+
+
+
+
+
+
+
diff --git a/installation_stuff/logdocdata/licenses/3270_LICENSE.md b/installation_stuff/logdocdata/licenses/3270_LICENSE.md
new file mode 100644
index 00000000..3ca71cba
--- /dev/null
+++ b/installation_stuff/logdocdata/licenses/3270_LICENSE.md
@@ -0,0 +1,49 @@
+Copyright (c) 2011-2017, Ricardo Banffy.
+Copyright (c) 1993-2011, Paul Mattes.
+Copyright (c) 2004-2005, Don Russell.
+Copyright (c) 2004, Dick Altenbern.
+Copyright (c) 1990, Jeff Sparkes.
+Copyright (c) 1989, Georgia Tech Research Corporation (GTRC), Atlanta, GA 30332.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of Ricardo Banffy, Paul Mattes, Don Russell,
+ Dick Altenbern, Jeff Sparkes, GTRC nor the names of their contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL RICARDO BANFFY, PAUL MATTES, DON RUSSELL, DICK ALTENBERN, JEFF
+SPARKES OR GTRC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The Debian Logo glyph is based on the Debian Open Use Logo and is
+Copyright (c) 1999 Software in the Public Interest, Inc., and it is
+incorporated here under the terms of the Creative Commons
+Attribution-ShareAlike 3.0 Unported License. The logo is released
+under the terms of the GNU Lesser General Public License, version 3 or
+any later version, or, at your option, of the Creative Commons
+Attribution-ShareAlike 3.0 Unported License.
+
+Ubuntu, the Ubuntu logo and the Circle of Friends symbol are
+registered trademarks of Canonical Ltd.
+
+The Fontforge SFD font description file is optionally licensed under
+the SIL Open Font License v1.1 with no Reserved Font Name. This
+license is available with a FAQ at http://scripts.sil.org/OFL.
diff --git a/installation_stuff/logdocdata/licenses/LogDoctor_LICENSE.md b/installation_stuff/logdocdata/licenses/LogDoctor_LICENSE.md
new file mode 100644
index 00000000..0ad25db4
--- /dev/null
+++ b/installation_stuff/logdocdata/licenses/LogDoctor_LICENSE.md
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+ .
diff --git a/installation_stuff/logdocdata/licenses/Metropolis_UNLICENSE.md b/installation_stuff/logdocdata/licenses/Metropolis_UNLICENSE.md
new file mode 100644
index 00000000..68a49daa
--- /dev/null
+++ b/installation_stuff/logdocdata/licenses/Metropolis_UNLICENSE.md
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
diff --git a/installation_stuff/logdocdata/licenses/sha256_LICENSE.md b/installation_stuff/logdocdata/licenses/sha256_LICENSE.md
new file mode 100644
index 00000000..9f47dce7
--- /dev/null
+++ b/installation_stuff/logdocdata/licenses/sha256_LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Jérémy LAMBERT (SystemGlitch)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/installation_stuff/logdoctor.conf b/installation_stuff/logdoctor.conf
new file mode 100644
index 00000000..17eb30b2
--- /dev/null
+++ b/installation_stuff/logdoctor.conf
@@ -0,0 +1,68 @@
+[UI]
+Language=en
+RememberGeometry=true
+Geometry=0,0,800,600,true
+WindowTheme=0
+ChartsTheme=0
+MainDialogLevel=1
+DefaultWebServer=11
+DatabaseDataPath=
+DatabaseHashesPath=
+DatabaseDoBackup=true
+DatabaseBackupsNumber=3
+
+[TextBrowser]
+Font=0
+WideLines=true
+ColorScheme=1
+
+[Craplog]
+CraplogDialogLevel=1
+HideUsedFiles=true
+WarningSize=52428801
+
+[Apache2]
+ApacheLogsPath=/var/log/apache2
+ApacheLogsFormat=%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"
+ApacheWarnlistMethod=DELETE HEAD OPTIONS PUT PATCH
+ApacheWarnlistMethodUsed=false
+ApacheWarnlistURI=/robots.txt /../ /./ /.env /.htaccess /phpmyadmin /wp-admin /wp-content /wp-config.php /config.py /views.py /routes.py /stepu.cgi /cgi-bin
+ApacheWarnlistURIUsed=true
+ApacheWarnlistClient=
+ApacheWarnlistClientUsed=false
+ApacheWarnlistUserAgent=
+ApacheWarnlistUserAgentUsed=false
+ApacheBlacklistClient=::1
+ApacheBlacklistClientUsed=false
+
+[Nginx]
+NginxLogsPath=/var/log/nginx
+NginxLogsFormat=$remote_addr - $remote_user [$time_local] "$request" $status $bytes_sent "$http_referer" "$http_user_agent"
+NginxWarnlistMethod=DELETE HEAD OPTIONS PUT PATCH
+NginxWarnlistMethodUsed=false
+NginxWarnlistURI=/robots.txt /../ /./ /.env /.htaccess /phpmyadmin /wp-admin /wp-content /wp-config.php /config.py /views.py /routes.py /stepu.cgi /cgi-bin
+NginxWarnlistURIUsed=true
+NginxWarnlistClient=
+NginxWarnlistClientUsed=false
+NginxWarnlistUserAgent=
+NginxWarnlistUserAgentUsed=false
+NginxBlacklistClient=::1
+NginxBlacklistClientUsed=false
+
+[IIS]
+IisLogsPath=C:/inetpub/logs/LogFiles/
+IisLogsModule=0
+IisLogsFormat=date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken
+IisWarnlistMethod=DELETE HEAD OPTIONS PUT PATCH
+IisWarnlistMethodUsed=false
+IisWarnlistURI=/robots.txt /../ /./ /.env /.htaccess /phpmyadmin /wp-admin /wp-content /wp-config.php /config.py /views.py /routes.py /stepu.cgi /cgi-bin
+IisWarnlistURIUsed=true
+IisWarnlistClient=
+IisWarnlistClientUsed=false
+IisWarnlistUserAgent=
+IisWarnlistUserAgentUsed=false
+IisBlacklistClient=::1
+IisBlacklistClientUsed=false
+
+[Crapview]
+CrapviewDialogLevel=1
diff --git a/installation_stuff/osx_bundle/Info.plist b/installation_stuff/osx_bundle/Info.plist
new file mode 100644
index 00000000..ea3aa3cd
--- /dev/null
+++ b/installation_stuff/osx_bundle/Info.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ LogDoctor
+ CFBundleDisplayName
+ LogDoctor
+ CFBundleIdentifier
+ com.logdoctor.LogDoctor
+ CFBundleShortVersionString
+ 2.01
+ CFBundleVersion
+ 2.01
+ CFBundleExecutable
+ LogDoctor
+ CFBundleIconFile
+ LogDoctor
+ CFBundlePackageType
+ APPL
+ LSApplicationCategoryType
+ public.app-category.utilities
+ CSResourcesFileMapped
+
+
+
diff --git a/installation_stuff/osx_bundle/Resources/LogDoctor.icns b/installation_stuff/osx_bundle/Resources/LogDoctor.icns
new file mode 100644
index 00000000..6675e385
Binary files /dev/null and b/installation_stuff/osx_bundle/Resources/LogDoctor.icns differ
diff --git a/installation_stuff/osx_bundle/bin/LogDoctor_uninstall b/installation_stuff/osx_bundle/bin/LogDoctor_uninstall
new file mode 100644
index 00000000..e69de29b
diff --git a/installation_stuff/osx_bundle/doc/licenses/LogDoctor/LogDoctor_LICENSE.md b/installation_stuff/osx_bundle/doc/licenses/LogDoctor/LogDoctor_LICENSE.md
new file mode 100644
index 00000000..0ad25db4
--- /dev/null
+++ b/installation_stuff/osx_bundle/doc/licenses/LogDoctor/LogDoctor_LICENSE.md
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+ .
diff --git a/installation_stuff/osx_bundle/doc/licenses/fonts/3270_LICENSE.md b/installation_stuff/osx_bundle/doc/licenses/fonts/3270_LICENSE.md
new file mode 100644
index 00000000..3ca71cba
--- /dev/null
+++ b/installation_stuff/osx_bundle/doc/licenses/fonts/3270_LICENSE.md
@@ -0,0 +1,49 @@
+Copyright (c) 2011-2017, Ricardo Banffy.
+Copyright (c) 1993-2011, Paul Mattes.
+Copyright (c) 2004-2005, Don Russell.
+Copyright (c) 2004, Dick Altenbern.
+Copyright (c) 1990, Jeff Sparkes.
+Copyright (c) 1989, Georgia Tech Research Corporation (GTRC), Atlanta, GA 30332.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of Ricardo Banffy, Paul Mattes, Don Russell,
+ Dick Altenbern, Jeff Sparkes, GTRC nor the names of their contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL RICARDO BANFFY, PAUL MATTES, DON RUSSELL, DICK ALTENBERN, JEFF
+SPARKES OR GTRC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The Debian Logo glyph is based on the Debian Open Use Logo and is
+Copyright (c) 1999 Software in the Public Interest, Inc., and it is
+incorporated here under the terms of the Creative Commons
+Attribution-ShareAlike 3.0 Unported License. The logo is released
+under the terms of the GNU Lesser General Public License, version 3 or
+any later version, or, at your option, of the Creative Commons
+Attribution-ShareAlike 3.0 Unported License.
+
+Ubuntu, the Ubuntu logo and the Circle of Friends symbol are
+registered trademarks of Canonical Ltd.
+
+The Fontforge SFD font description file is optionally licensed under
+the SIL Open Font License v1.1 with no Reserved Font Name. This
+license is available with a FAQ at http://scripts.sil.org/OFL.
diff --git a/installation_stuff/osx_bundle/doc/licenses/fonts/Metropolis_UNLICENSE.md b/installation_stuff/osx_bundle/doc/licenses/fonts/Metropolis_UNLICENSE.md
new file mode 100644
index 00000000..68a49daa
--- /dev/null
+++ b/installation_stuff/osx_bundle/doc/licenses/fonts/Metropolis_UNLICENSE.md
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
diff --git a/installation_stuff/osx_bundle/doc/licenses/sha256/sha256_LICENSE.md b/installation_stuff/osx_bundle/doc/licenses/sha256/sha256_LICENSE.md
new file mode 100644
index 00000000..9f47dce7
--- /dev/null
+++ b/installation_stuff/osx_bundle/doc/licenses/sha256/sha256_LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Jérémy LAMBERT (SystemGlitch)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/logdoctor/CMakeLists.txt b/logdoctor/CMakeLists.txt
new file mode 100644
index 00000000..387391c3
--- /dev/null
+++ b/logdoctor/CMakeLists.txt
@@ -0,0 +1,224 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(LogDoctor VERSION 2.01 LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Charts LinguistTools Sql Network)
+find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Charts LinguistTools Sql Network)
+
+
+set(TS_FILES
+ translations/LogDoctor_en.ts
+ translations/LogDoctor_es.ts
+ translations/LogDoctor_fr.ts
+ translations/LogDoctor_it.ts
+)
+
+
+set(PROJECT_SOURCES
+ main.cpp
+ mainwindow.ui
+ mainwindow.h
+ mainwindow.cpp
+
+ utilities/checks.h
+ utilities/checks.cpp
+ utilities/colors.h
+ utilities/colors.cpp
+ utilities/gzip.h
+ utilities/gzip.cpp
+ utilities/io.h
+ utilities/io.cpp
+ utilities/rtf.h
+ utilities/rtf.cpp
+ utilities/strings.h
+ utilities/strings.cpp
+ utilities/stylesheets.h
+ utilities/stylesheets.cpp
+ utilities/vectors.h
+ utilities/vectors.cpp
+
+ modules/shared.h
+ modules/shared.cpp
+
+ modules/exceptions.h
+ modules/exceptions.cpp
+ modules/tb.h
+ modules/tb.cpp
+
+ modules/dialogs.h
+ modules/dialogs.cpp
+ modules/dialogs/dialogmsg.ui
+ modules/dialogs/dialogmsg.h
+ modules/dialogs/dialogmsg.cpp
+ modules/dialogs/dialogbool.ui
+ modules/dialogs/dialogbool.h
+ modules/dialogs/dialogbool.cpp
+ modules/dialogs/dialogdia.ui
+ modules/dialogs/dialogdia.h
+ modules/dialogs/dialogdia.cpp
+
+ modules/craplog/craplog.h
+ modules/craplog/craplog.cpp
+ modules/craplog/modules/datetime.h
+ modules/craplog/modules/datetime.cpp
+ modules/craplog/modules/donuts.h
+ modules/craplog/modules/donuts.cpp
+ modules/craplog/modules/formats.h
+ modules/craplog/modules/formats.cpp
+ modules/craplog/modules/hash.h
+ modules/craplog/modules/hash.cpp
+ modules/craplog/modules/logs.h
+ modules/craplog/modules/logs.cpp
+ modules/craplog/modules/sha256.h
+ modules/craplog/modules/sha256.cpp
+ modules/craplog/modules/store.h
+ modules/craplog/modules/store.cpp
+
+ modules/crapview/crapview.h
+ modules/crapview/crapview.cpp
+ modules/crapview/modules/query.h
+ modules/crapview/modules/query.cpp
+
+ modules/craphelp/craphelp.ui
+ modules/craphelp/craphelp.h
+ modules/craphelp/craphelp.cpp
+
+ modules/crapup/crapup.ui
+ modules/crapup/crapup.h
+ modules/crapup/crapup.cpp
+
+ modules/crapinfo/crapinfo.ui
+ modules/crapinfo/crapinfo.h
+ modules/crapinfo/crapinfo.cpp
+
+ tools/crapnote/crapnote.ui
+ tools/crapnote/crapnote.h
+ tools/crapnote/crapnote.cpp
+
+ games/games.h
+ games/games.cpp
+
+ games/crisscross/crisscross.ui
+ games/crisscross/game.h
+ games/crisscross/game.cpp
+
+ games/snake/snake.ui
+ games/snake/game.h
+ games/snake/game.cpp
+ games/snake/snake.h
+ games/snake/snake.cpp
+ games/snake/food.h
+ games/snake/food.cpp
+
+ resources/resources.qrc
+ ${TS_FILES}
+)
+
+if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
+ qt_add_executable(LogDoctor
+ MANUAL_FINALIZATION
+ ${PROJECT_SOURCES}
+ )
+# Define target properties for Android with Qt 6 as:
+# set_property(TARGET LogDoctor APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
+# ${CMAKE_CURRENT_SOURCE_DIR}/android)
+# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
+
+ qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
+else()
+ if(ANDROID)
+ add_library(LogDoctor SHARED
+ ${PROJECT_SOURCES}
+ )
+# Define properties for Android with Qt 5 after find_package() calls as:
+# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
+ else()
+ add_executable(LogDoctor
+ ${PROJECT_SOURCES}
+ )
+ endif()
+
+ qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
+endif()
+
+if(WIN32)
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Bstatic -lgcc -lstdc++ -lwinpthread")
+ target_link_libraries(LogDoctor PRIVATE
+ -static Qt${QT_VERSION_MAJOR}::Widgets
+ -static Qt${QT_VERSION_MAJOR}::Charts
+ -static Qt${QT_VERSION_MAJOR}::Sql
+ -static Qt${QT_VERSION_MAJOR}::Network)
+else()
+ target_link_libraries(LogDoctor PRIVATE
+ Qt${QT_VERSION_MAJOR}::Widgets
+ Qt${QT_VERSION_MAJOR}::Charts
+ Qt${QT_VERSION_MAJOR}::Sql
+ Qt${QT_VERSION_MAJOR}::Network)
+ # Include zlib
+ find_package(ZLIB REQUIRED)
+ include_directories(${ZLIB_INCLUDE_DIRS})
+ target_link_libraries(LogDoctor PRIVATE ${ZLIB_LIBRARIES})
+endif()
+
+
+if(APPLE)
+ set_target_properties(LogDoctor PROPERTIES
+ MACOSX_BUNDLE TRUE
+ #MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
+ MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}
+ MACOSX_BUNDLE_ICON_FILE ${PROJECT_NAME}
+ MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
+ )
+elseif(WIN32)
+ set_target_properties(LogDoctor PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ )
+endif()
+
+
+if(QT_VERSION_MAJOR EQUAL 6)
+ qt_finalize_executable(LogDoctor)
+endif()
+
+
+# Deb/Flatpak
+if(UNIX AND NOT APPLE)
+ install(TARGETS
+ LogDoctor
+ RUNTIME DESTINATION /usr/bin
+ CONFIGURATIONS Release
+ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ
+ RENAME logdoctor
+ )
+
+ install(FILES
+ ${CMAKE_SOURCE_DIR}/installation_stuff/LogDoctor.desktop
+ DESTINATION /usr/share/applications
+ )
+
+ install(DIRECTORY
+ DESTINATION /usr/share/LogDoctor
+ )
+ install(FILES
+ ${CMAKE_SOURCE_DIR}/installation_stuff/LogDoctor.svg
+ DESTINATION /usr/share/LogDoctor
+ )
+ install(DIRECTORY
+ ${CMAKE_SOURCE_DIR}/installation_stuff/logdocdata/help
+ DESTINATION /usr/share/LogDoctor
+ )
+ install(DIRECTORY
+ ${CMAKE_SOURCE_DIR}/installation_stuff/logdocdata/licenses
+ DESTINATION /usr/share/LogDoctor
+ )
+endif()
diff --git a/logdoctor/games/crisscross/crisscross.ui b/logdoctor/games/crisscross/crisscross.ui
new file mode 100644
index 00000000..7d7f9eba
--- /dev/null
+++ b/logdoctor/games/crisscross/crisscross.ui
@@ -0,0 +1,535 @@
+
+
+ CrissCross
+
+
+
+ 0
+ 0
+ 512
+ 512
+
+
+
+
+ 512
+ 512
+
+
+
+ LogDoctor - CrissCross
+
+
+
+ :/logo/logo/logdoctor.svg :/logo/logo/logdoctor.svg
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 400
+ 400
+
+
+
+
+ 400
+ 400
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 8
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 120
+ 120
+
+
+
+
+ 120
+ 120
+
+
+
+ PointingHandCursor
+
+
+
+ 104
+ 104
+
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 120
+ 120
+
+
+
+
+ 120
+ 120
+
+
+
+ PointingHandCursor
+
+
+
+ 104
+ 104
+
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 120
+ 120
+
+
+
+
+ 120
+ 120
+
+
+
+ PointingHandCursor
+
+
+
+ 104
+ 104
+
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 120
+ 120
+
+
+
+
+ 120
+ 120
+
+
+
+ PointingHandCursor
+
+
+
+ 104
+ 104
+
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 120
+ 120
+
+
+
+
+ 120
+ 120
+
+
+
+ PointingHandCursor
+
+
+
+ 104
+ 104
+
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 120
+ 120
+
+
+
+
+ 120
+ 120
+
+
+
+ PointingHandCursor
+
+
+
+ 104
+ 104
+
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 120
+ 120
+
+
+
+
+ 120
+ 120
+
+
+
+ PointingHandCursor
+
+
+
+ 104
+ 104
+
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 120
+ 120
+
+
+
+
+ 120
+ 120
+
+
+
+ PointingHandCursor
+
+
+
+ 104
+ 104
+
+
+
+
+ -
+
+
+ QFrame::Plain
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 120
+ 120
+
+
+
+
+ 120
+ 120
+
+
+
+ PointingHandCursor
+
+
+
+ 104
+ 104
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::MinimumExpanding
+
+
+
+ 38
+ 397
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::MinimumExpanding
+
+
+
+ 4
+ 16
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::MinimumExpanding
+
+
+
+ 16
+ 4
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::MinimumExpanding
+
+
+
+ 16
+ 4
+
+
+
+
+
+
+
+
+
+
+
diff --git a/logdoctor/games/crisscross/game.cpp b/logdoctor/games/crisscross/game.cpp
new file mode 100644
index 00000000..fa677d44
--- /dev/null
+++ b/logdoctor/games/crisscross/game.cpp
@@ -0,0 +1,352 @@
+
+#include "game.h"
+#include "ui_crisscross.h"
+
+#include "games/games.h"
+
+#include
+
+
+CrissCross::CrissCross( const int& theme_id, QWidget* parent ) :
+ QWidget(parent),
+ ui(new Ui::CrissCross)
+{
+ this->ui->setupUi(this);
+
+ QString stylesheet = "";
+ GameSec::crisscrossStyleSheet( stylesheet, theme_id );
+ this->setStyleSheet( stylesheet );
+
+ // verify that one player is human and the other is not
+ if ( !(p1_human^p2_human) ) {
+ throw("Players identities error: "+std::to_string(p1_human)+" - "+std::to_string(p2_human));
+ }
+
+ this->victory_sequence.reserve( 3 );
+
+ this->board_buttons[0] = this->ui->button_NW;
+ this->board_buttons[1] = this->ui->button_N;
+ this->board_buttons[2] = this->ui->button_NE;
+ this->board_buttons[3] = this->ui->button_W;
+ this->board_buttons[4] = this->ui->button_C;
+ this->board_buttons[5] = this->ui->button_E;
+ this->board_buttons[6] = this->ui->button_SW;
+ this->board_buttons[7] = this->ui->button_S;
+ this->board_buttons[8] = this->ui->button_SE;
+
+ if ( ! p1_human ) {
+ // AI starts
+ this->AI_playTurn();
+ }
+
+}
+
+CrissCross::~CrissCross()
+{
+ delete ui;
+}
+
+
+/////////////////////
+//// BOARD TILES ////
+void CrissCross::on_button_NW_clicked()
+{
+ if ( ! this->ui->button_NW->isFlat() ) {
+ this->board[0] = this->p_turn;
+ this->ui->button_NW->setIcon( this->icons[ this->p_turn-1 ] );
+ this->ui->button_NW->setFlat( true );
+ this->endTurn();
+ }
+}
+
+void CrissCross::on_button_N_clicked()
+{
+ if ( ! this->ui->button_N->isFlat() ) {
+ this->board[1] = this->p_turn;
+ this->ui->button_N->setIcon( this->icons[ this->p_turn-1 ] );
+ this->ui->button_N->setFlat( true );
+ this->endTurn();
+ }
+}
+
+void CrissCross::on_button_NE_clicked()
+{
+ if ( ! this->ui->button_NE->isFlat() ) {
+ this->board[2] = this->p_turn;
+ this->ui->button_NE->setIcon( this->icons[ this->p_turn-1 ] );
+ this->ui->button_NE->setFlat( true );
+ this->endTurn();
+ }
+}
+
+void CrissCross::on_button_W_clicked()
+{
+ if ( ! this->ui->button_W->isFlat() ) {
+ this->board[3] = this->p_turn;
+ this->ui->button_W->setIcon( this->icons[ this->p_turn-1 ] );
+ this->ui->button_W->setFlat( true );
+ this->endTurn();
+ }
+}
+
+void CrissCross::on_button_C_clicked()
+{
+ if ( ! this->ui->button_C->isFlat() ) {
+ this->board[4] = this->p_turn;
+ this->ui->button_C->setIcon( this->icons[ this->p_turn-1 ] );
+ this->ui->button_C->setFlat( true );
+ this->endTurn();
+ }
+}
+
+void CrissCross::on_button_E_clicked()
+{
+ if ( ! this->ui->button_E->isFlat() ) {
+ this->board[5] = this->p_turn;
+ this->ui->button_E->setIcon( this->icons[ this->p_turn-1 ] );
+ this->ui->button_E->setFlat( true );
+ this->endTurn();
+ }
+}
+
+void CrissCross::on_button_SW_clicked()
+{
+ if ( ! this->ui->button_SW->isFlat() ) {
+ this->board[6] = this->p_turn;
+ this->ui->button_SW->setIcon( this->icons[ this->p_turn-1 ] );
+ this->ui->button_SW->setFlat( true );
+ this->endTurn();
+ }
+}
+
+void CrissCross::on_button_S_clicked()
+{
+ if ( ! this->ui->button_S->isFlat() ) {
+ this->board[7] = this->p_turn;
+ this->ui->button_S->setIcon( this->icons[ this->p_turn-1 ] );
+ this->ui->button_S->setFlat( true );
+ this->endTurn();
+ }
+}
+
+void CrissCross::on_button_SE_clicked()
+{
+ if ( ! this->ui->button_SE->isFlat() ) {
+ this->board[8] = this->p_turn;
+ this->ui->button_SE->setIcon( this->icons[ this->p_turn-1 ] );
+ this->ui->button_SE->setFlat( true );
+ this->endTurn();
+ }
+}
+
+
+//////////////////////
+//// TURN RELATED ////
+void CrissCross::endTurn()
+{
+ if ( this->checkVictory() ) {
+ // a player won!
+ this->victory();
+ } else {
+ // nobody won yet
+ if ( this->gameDraw() ) {
+ // game is draw
+ this->draw();
+ } else {
+ // change turn and keep playing
+ this->nextTurn();
+ }
+ }
+}
+
+void CrissCross::nextTurn()
+{
+ switch ( this->p_turn ) {
+ case 1:
+ this->p_turn += 1;
+ if ( ! this->p2_human ) {
+ this->AI_playTurn();
+ }
+ break;
+ case 2:
+ this->p_turn -= 1;
+ if ( ! this->p1_human ) {
+ this->AI_playTurn();
+ }
+ break;
+ default:
+ // wrong
+ throw("Wrong turn: "+std::to_string(this->p_turn));
+ break;
+ }
+}
+
+
+const bool CrissCross::checkVictory()
+{
+ bool result = false;
+ unsigned int streak;
+ for ( const auto& sequence : this->sequences ) {
+ streak = 0;
+ for ( const auto& index : sequence ) {
+ if ( this->board[ index ] == this->p_turn ) {
+ streak ++;
+ this->victory_sequence.push_back( index );
+ } else {
+ break;
+ }
+ }
+ if ( streak == 3 ) {
+ // victory
+ result = true;
+ break;
+ } else {
+ this->victory_sequence.clear();
+ }
+ }
+ return result;
+}
+
+void CrissCross::victory()
+{
+ // disable all buttons except the victory sequence ones
+ bool disable;
+ for ( unsigned int i=0; i<9; i++ ) {
+ disable = true;
+ for ( const auto& j : this->victory_sequence ) {
+ if ( i == j ) {
+ disable = false;
+ break;
+ } else if ( i < j ) {
+ break;
+ }
+ }
+ this->board_buttons[ i ]->setFlat( true );
+ if ( disable ) {
+ this->board_buttons[ i ]->setEnabled( false );
+ }
+ }
+
+ // display a dialog
+ QString message;
+ if ( (this->p_turn == 1 && this->p1_human)
+ || (this->p_turn == 2 && this->p2_human) ) {
+ // user won
+ message = CrissCross::tr("You beated me!");
+ } else {
+ // AI won
+ message = CrissCross::tr("This time you lost!");
+ }
+ QMessageBox::about(
+ this,
+ CrissCross::tr("Victory"),
+ message );
+}
+
+
+const bool CrissCross::gameDraw()
+{
+ bool result = false;
+ unsigned int empty_tiles = 9;
+ for ( const auto& tile : this->board ) {
+ if ( tile > 0 ) {
+ empty_tiles --;
+ }
+ }
+ if ( empty_tiles == 0 ) {
+ // no movement left
+ result = true;
+ }
+ return result;
+}
+
+void CrissCross::draw()
+{
+ // disable all buttons
+ for ( const auto& button : this->board_buttons ) {
+ button->setEnabled( false );
+ }
+
+ // display a dialog
+ QMessageBox::about(
+ this,
+ CrissCross::tr("Draw"),
+ CrissCross::tr("Nice match") );
+}
+
+
+////////////
+//// AI ////
+void CrissCross::AI_playTurn()
+{
+ this->AI_updateWeights();
+ emit this->board_buttons[ this->AI_makeChoice() ]->clicked();
+}
+
+void CrissCross::AI_updateWeights()
+{
+ // reset the weights
+ for ( int i=0; i<9; i++ ) {
+ this->board_weights[ i ] = 0;
+ }
+ // calculate the new weights
+ unsigned int win_streak, lose_streak;
+ std::vector empty_tiles;
+ for ( const auto& sequence : this->sequences ) {
+ // reset data
+ win_streak = lose_streak = 0;
+ empty_tiles.clear();
+ // check the tiles in the sequence
+ for ( const auto& index : sequence ) {
+ if ( this->board[ index ] == this->p_turn ) {
+ win_streak ++;
+ } else if ( this->board[ index ] > 0 ) {
+ lose_streak ++;
+ } else {
+ empty_tiles.push_back( index );
+ }
+ }
+ // set the new weight for the empty tiles
+ const unsigned int new_weight = (win_streak>=lose_streak)
+ ? (win_streak==2) ? win_streak+2 : win_streak+1
+ : lose_streak+1;
+ for ( const auto& index : empty_tiles ) {
+ if ( new_weight > this->board_weights[ index ] ) {
+ this->board_weights[ index ] = new_weight;
+ }
+ }
+ }
+
+}
+
+const unsigned int CrissCross::AI_makeChoice()
+{
+ // get a list of the heaviest tiles
+ std::vector moves;
+ unsigned int max_weight = 0;
+ unsigned int index = 0;
+ for ( const auto& weight : this->board_weights ) {
+ if ( weight > max_weight ) {
+ // heavier weight found
+ max_weight = weight;
+ moves.clear();
+ moves.push_back( index );
+ } else if ( weight == max_weight ) {
+ // same weight
+ moves.push_back( index );
+ }/* else {
+ // lighter weight
+ ;
+ }*/
+ index ++;
+ }
+ // decide the movement (or better, randomly pick one)
+ unsigned int next_move;
+ if ( max_weight == 0 ) {
+ // first turn
+ next_move = rand() % 9;
+ } else {
+ next_move = moves[ rand() % moves.size() ];
+ }
+ return next_move;
+}
diff --git a/logdoctor/games/crisscross/game.h b/logdoctor/games/crisscross/game.h
new file mode 100644
index 00000000..54cc691c
--- /dev/null
+++ b/logdoctor/games/crisscross/game.h
@@ -0,0 +1,134 @@
+#ifndef CRISSCROSS_H
+#define CRISSCROSS_H
+
+#include
+#include
+#include
+
+
+namespace Ui {
+ class CrissCross;
+}
+
+//! CrissCross
+/*!
+ Player vs AI criss-cross game
+*/
+class CrissCross : public QWidget
+{
+ Q_OBJECT
+
+public:
+ CrissCross( const int& theme_id, QWidget* parent=nullptr );
+ ~CrissCross();
+
+
+private slots:
+ void on_button_NW_clicked();
+
+ void on_button_N_clicked();
+
+ void on_button_NE_clicked();
+
+ void on_button_W_clicked();
+
+ void on_button_C_clicked();
+
+ void on_button_E_clicked();
+
+ void on_button_SW_clicked();
+
+ void on_button_S_clicked();
+
+ void on_button_SE_clicked();
+
+
+private:
+ Ui::CrissCross *ui;
+
+ // player turn
+ unsigned int p_turn = 1;
+
+ // players identity
+ const bool p1_human = rand() %2;
+ const bool p2_human = (p1_human) ? false : true;
+
+ // victory related
+ std::vector victory_sequence;
+
+ // game data
+ unsigned int board[9] = {
+ 0,0,0,
+ 0,0,0,
+ 0,0,0
+ };
+
+ QPushButton* board_buttons[9];
+
+ const QIcon icons[2] = {
+ QIcon(":/games/games/crisscross/o.png"),
+ QIcon(":/games/games/crisscross/x.png")
+ };
+
+ const unsigned int sequences[8][3] = {
+ {0,1,2},{3,4,5},{6,7,8}, // horizontal
+ {0,3,6},{1,4,7},{2,5,8}, // vertical
+ {0,4,8},{2,4,6} // diagonal
+ };
+
+ // AI data
+ unsigned int board_weights[9] = {
+ 1,1,1,
+ 1,1,1,
+ 1,1,1
+ };
+
+
+ //////////////
+ //// GAME ////
+
+ //! Ends the current turn
+ void endTurn();
+
+ //! Switches to the next turn
+ void nextTurn();
+
+ //! Checks whether it's the human player turn or not
+ const bool isPlayerTurn();
+
+
+ //! Checks whether somebody won or not
+ const bool checkVictory();
+
+ //! Checks whether the game is draw or not
+ const bool gameDraw();
+
+ //! Someone won, process the victory
+ void victory();
+
+ //! The match is over but nobody won, the game is draw
+ void draw();
+
+
+ ////////////
+ //// AI ////
+
+ //! Main function for the AI to play its turn
+ void AI_playTurn();
+
+ //! Updates the weights of the tiles
+ /*!
+ \see AI_playTurn();
+ */
+ void AI_updateWeights();
+
+ //! Makes the choice depending on the weights
+ /*!
+ \return The tile to select
+ \see AI_playTurn();
+ */
+ const unsigned int AI_makeChoice();
+
+};
+
+#endif // CRISSCROSS_H
diff --git a/logdoctor/games/games.cpp b/logdoctor/games/games.cpp
new file mode 100644
index 00000000..329d07be
--- /dev/null
+++ b/logdoctor/games/games.cpp
@@ -0,0 +1,328 @@
+
+#include "games.h"
+
+#include "modules/exceptions.h"
+
+#include
+
+
+GameSec::GameSec()
+{
+
+}
+
+
+void GameSec::crisscrossStyleSheet( QString& stylesheet, const int& theme_id )
+{
+ std::unordered_map style;
+ switch ( theme_id ) {
+ case 0:
+ break;
+ case 1:
+ style = {
+ {"window_base",
+ "rgb( 32, 32, 32 )"},
+ {"tiles_base_hover",
+ "rgb( 40, 40, 40 )"},
+ {"tiles_border_hover",
+ "rgb( 64, 64, 64 )"},
+ {"lines_base",
+ "rgb( 192, 192, 192 )"},
+ {"lines_border",
+ "rgb( 16, 16, 16 )"}
+ };
+ break;
+ case 2:
+ style = {
+ {"window_base",
+ "rgb( 255, 204, 143 )"},
+ {"tiles_base_hover",
+ "rgb( 245, 172, 142 )"},
+ {"tiles_border_hover",
+ "rgb( 255, 140, 141 )"},
+ {"lines_base",
+ "rgb( 195, 80, 81 )"},
+ {"lines_border",
+ "rgb( 255, 204, 143 )"}
+ };
+ break;
+ case 3:
+ style = {
+ {"window_base",
+ "rgb( 14, 28, 0 )"},
+ {"tiles_base_hover",
+ "rgb( 54, 78, 30 )"},
+ {"tiles_border_hover",
+ "rgb( 94, 118, 70 )"},
+ {"lines_base",
+ "rgb( 193, 175, 129 )"},
+ {"lines_border",
+ "rgb( 14, 28, 0 )"}
+ };
+ break;
+ case 4:
+ style = {
+ {"window_base",
+ "rgb( 170, 161, 137 )"},
+ {"tiles_base_hover",
+ "rgb( 130, 121, 97 )"},
+ {"tiles_border_hover",
+ "rgb( 140, 131, 107 )"},
+ {"lines_base",
+ "rgb( 60, 51, 27 )"},
+ {"lines_border",
+ "rgb( 170, 161, 137 )"}
+ };
+ break;
+ default:
+ throw GenericException( "Unexpected WindowTheme ID: "+std::to_string(theme_id), true );
+ break;
+ }
+ if ( theme_id != 0 ) {
+ stylesheet =
+ "QWidget#CrissCross {"
+ " background-color: "+style.at("window_base")+";"
+ "}"
+ "QPushButton#button_NE,"
+ "QPushButton#button_N,"
+ "QPushButton#button_NW,"
+ "QPushButton#button_E,"
+ "QPushButton#button_C,"
+ "QPushButton#button_W,"
+ "QPushButton#button_SE,"
+ "QPushButton#button_S,"
+ "QPushButton#button_SW {"
+ " border-radius: 4px;"
+ " border: 1px solid "+style.at("window_base")+";"
+ " background-color: "+style.at("window_base")+";"
+ "}"
+ "QPushButton#button_NE:hover,"
+ "QPushButton#button_N:hover,"
+ "QPushButton#button_NW:hover,"
+ "QPushButton#button_E:hover,"
+ "QPushButton#button_C:hover,"
+ "QPushButton#button_W:hover,"
+ "QPushButton#button_SE:hover,"
+ "QPushButton#button_S:hover,"
+ "QPushButton#button_SW:hover {"
+ " border-color: "+style.at("tiles_border_hover")+";"
+ " background-color: "+style.at("tiles_base_hover")+";"
+ "}"
+ "QPushButton#button_NE::flat,"
+ "QPushButton#button_N::flat,"
+ "QPushButton#button_NW::flat,"
+ "QPushButton#button_E::flat,"
+ "QPushButton#button_C::flat,"
+ "QPushButton#button_W::flat,"
+ "QPushButton#button_SE::flat,"
+ "QPushButton#button_S::flat,"
+ "QPushButton#button_SW::flat {"
+ " border-color: "+style.at("window_base")+";"
+ " background-color: "+style.at("window_base")+";"
+ "}"
+ "QFrame#line_1,"
+ "QFrame#line_2,"
+ "QFrame#line_3,"
+ "QFrame#line_4,"
+ "QFrame#line_5,"
+ "QFrame#line_6,"
+ "QFrame#line_7,"
+ "QFrame#line_8,"
+ "QFrame#line_9,"
+ "QFrame#line_10,"
+ "QFrame#line_11,"
+ "QFrame#line_12 {"
+ " border: 1px solid "+style.at("lines_border")+";"
+ " background-color: "+style.at("lines_base")+";"
+ "}";
+ }
+}
+
+
+void GameSec::snakeStyleSheet( QString& stylesheet, const int& theme_id )
+{
+ std::unordered_map style;
+ switch ( theme_id ) {
+ case 0:
+ break;
+ case 1:
+ style = {
+ {"text",
+ "rgb( 248, 248, 248 )"},
+ {"window_base_primary",
+ "rgb( 16, 16, 16 )"},
+ {"window_base_secondary",
+ "rgb( 32, 32, 32 )"},
+ {"window_border",
+ "rgb( 128, 128, 128 )"},
+ {"gamemode_box_base",
+ "rgb( 64, 64, 64 )"},
+ {"gamemode_box_base_selection",
+ "rgb( 96, 96, 96 )"},
+ {"gamemode_box_border",
+ "rgb( 82, 82, 82 )"},
+ {"play_button_base",
+ "rgb( 64, 64, 64 )"},
+ {"play_button_base_hover",
+ "rgb( 96, 96, 96 )"},
+ {"play_button_border",
+ "rgb( 82, 82, 82 )"},
+ {"play_button_border_hover",
+ "rgb( 128, 128, 128 )"},
+ {"score_frame_base",
+ "rgb( 64, 64, 64 )"},
+ {"score_frame_border",
+ "rgb( 82, 82, 82 )"},
+ {"score_text",
+ "rgb( 248, 248, 248 )"},
+ {"score_base",
+ "rgb( 0, 0, 0 )"}
+ };
+ break;
+ case 2:
+ style = {
+ {"text",
+ "rgb( 45, 0, 30 )"},
+ {"window_base_primary",
+ "rgb( 255, 140, 141 )"},
+ {"window_base_secondary",
+ "rgb( 255, 204, 143 )"},
+ {"window_border",
+ "rgb( 195, 80, 81 )"},
+ {"gamemode_box_base",
+ "rgb( 114, 235, 115 )"},
+ {"gamemode_box_base_selection",
+ "rgb( 154, 255, 155 )"},
+ {"gamemode_box_border",
+ "rgb( 245, 172, 142 )"},
+ {"play_button_base",
+ "rgb( 114, 235, 115 )"},
+ {"play_button_base_hover",
+ "rgb( 154, 255, 155 )"},
+ {"play_button_border",
+ "rgb( 245, 172, 142 )"},
+ {"play_button_border_hover",
+ "rgb( 195, 80, 81 )"},
+ {"score_frame_base",
+ "rgb( 245, 172, 142 )"},
+ {"score_frame_border",
+ "rgb( 195, 80, 81 )"},
+ {"score_text",
+ "rgb( 45, 0, 30 )"},
+ {"score_base",
+ "rgb( 255, 210, 221 )"}
+ };
+ break;
+ case 3:
+ style = {
+ {"text",
+ "rgb( 220, 211, 187 )"},
+ {"window_base_primary",
+ "rgb( 14, 28, 0 )"},
+ {"window_base_secondary",
+ "rgb( 24, 48, 0 )"},
+ {"window_border",
+ "rgb( 163, 145, 99 )"},
+ {"gamemode_box_base",
+ "rgb( 94, 118, 70 )"},
+ {"gamemode_box_base_selection",
+ "rgb( 124, 148, 100 )"},
+ {"gamemode_box_border",
+ "rgb( 163, 145, 99 )"},
+ {"play_button_base",
+ "rgb( 94, 118, 70 )"},
+ {"play_button_base_hover",
+ "rgb( 124, 148, 100 )"},
+ {"play_button_border",
+ "rgb( 163, 145, 99 )"},
+ {"play_button_border_hover",
+ "rgb( 193, 175, 129 )"},
+ {"score_frame_base",
+ "rgb( 54, 78, 30 )"},
+ {"score_frame_border",
+ "rgb( 163, 145, 99 )"},
+ {"score_text",
+ "rgb( 193, 175, 129 )"},
+ {"score_base",
+ "rgb( 14, 28, 0 )"}
+ };
+ break;
+ case 4:
+ style = {
+ {"text",
+ "rgb( 30, 21, 0 )"},
+ {"window_base_primary",
+ "rgb( 170, 161, 137 )"},
+ {"window_base_secondary",
+ "rgb( 230, 221, 197 )"},
+ {"window_border",
+ "rgb( 80, 71, 47 )"},
+ {"gamemode_box_base",
+ "rgb( 130, 121, 97 )"},
+ {"gamemode_box_base_selection",
+ "rgb( 140, 131, 107 )"},
+ {"gamemode_box_border",
+ "rgb( 90, 81, 57 )"},
+ {"play_button_base",
+ "rgb( 130, 121, 97 )"},
+ {"play_button_base_hover",
+ "rgb( 140, 131, 107 )"},
+ {"play_button_border",
+ "rgb( 90, 81, 57 )"},
+ {"play_button_border_hover",
+ "rgb( 80, 71, 47 )"},
+ {"score_frame_base",
+ "rgb( 210, 201, 177 )"},
+ {"score_frame_border",
+ "rgb( 170, 161, 137 )"},
+ {"score_text",
+ "rgb( 30, 21, 0 )"},
+ {"score_base",
+ "rgb( 230, 221, 197 )"}
+ };
+ break;
+ default:
+ throw GenericException( "Unexpected WindowTheme ID: "+std::to_string(theme_id), true );
+ break;
+ }
+ if ( theme_id != 0 ) {
+ stylesheet =
+ "QWidget#SnakeGame {"
+ " background-color: "+style.at("window_base_primary")+";"
+ "}"
+ "QWidget#stackedPage_GameMenu,"
+ "QWidget#stackedPage_GameBoard {"
+ " border-radius: 4px;"
+ " border: 1px solid "+style.at("window_border")+";"
+ " background-color: "+style.at("window_base_secondary")+";"
+ "}"
+ "QComboBox {"
+ " color: "+style.at("text")+";"
+ " background-color: "+style.at("gamemode_box_base")+";"
+ " selection-background-color: "+style.at("gamemode_box_base_selection")+";"
+ "}"
+ "QComboBox QAbstractItemView {"
+ " border: 1px solid "+style.at("gamemode_box_border")+";"
+ " background-color: "+style.at("gamemode_box_base")+";"
+ "}"
+ "QPushButton#button_Play {"
+ " border-radius: 4px;"
+ " border: 1px solid "+style.at("play_button_border")+";"
+ " color: "+style.at("text")+";"
+ " background-color: "+style.at("play_button_base")+";"
+ "}"
+ "QPushButton#button_Play:hover {"
+ " border-color: "+style.at("play_button_border_hover")+";"
+ " background-color: "+style.at("play_button_base_hover")+";"
+ "}"
+ "QFrame#frame_Score {"
+ " border-radius: 4px;"
+ " border: 1px solid "+style.at("score_frame_border")+";"
+ " background-color: "+style.at("score_frame_base")+";"
+ "}"
+ "QLCDNumber#lcd_Score {"
+ " color: "+style.at("score_text")+";"
+ " background-color: "+style.at("score_base")+";"
+ "}";
+ }
+}
diff --git a/logdoctor/games/games.h b/logdoctor/games/games.h
new file mode 100644
index 00000000..c0d7981a
--- /dev/null
+++ b/logdoctor/games/games.h
@@ -0,0 +1,20 @@
+#ifndef GAMES_H
+#define GAMES_H
+
+#include
+
+
+//! GameSec
+/*!
+ Utilities for the games
+*/
+class GameSec
+{
+public:
+ GameSec();
+
+ static void crisscrossStyleSheet( QString& stylesheet, const int& theme_id );
+ static void snakeStyleSheet( QString& stylesheet, const int& theme_id );
+};
+
+#endif // GAMES_H
diff --git a/logdoctor/games/snake/food.cpp b/logdoctor/games/snake/food.cpp
new file mode 100644
index 00000000..f21ccfec
--- /dev/null
+++ b/logdoctor/games/snake/food.cpp
@@ -0,0 +1,217 @@
+
+#include "food.h"
+
+
+Food::Food( const bool& can_move )
+{
+ this->movable = can_move;
+ this->image = new QGraphicsPixmapItem( (can_move) ? this->img_rat : this->img_egg );
+ this->x = 0;
+ this->y = 0;
+}
+
+
+const unsigned int& Food::X()
+{
+ return this->x;
+}
+const unsigned int& Food::Y()
+{
+ return this->y;
+}
+
+
+QGraphicsPixmapItem* Food::getImageItem()
+{
+ return this->image;
+}
+
+
+const bool Food::inTile( const unsigned int& x, const unsigned int& y )
+{
+ if ( this->x == x && this->y == y ) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+void Food::update( const unsigned int& new_x, const unsigned int& new_y ) {
+ this->x = new_x;
+ this->y = new_y;
+ this->image->setOffset( 16+(new_x*32), 16+(new_y*32) );
+}
+
+
+void Food::spawn( Snake& snake, Snake& snake_ )
+{
+ // pick a new random position
+ unsigned int x, y;
+ while (true) {
+ x = rand() % 16;
+ y = rand() % 16;
+ // check it's actually inside the field
+ if ( x < 16 && y < 16 ) {
+ // check the tile is empty
+ if ( x != this->x && y != this->y ) {
+ if ( !(snake.inTile( x, y, false ) || snake_.inTile( x, y, false )) ) {
+ break;
+ }
+ }
+ }
+ }
+
+ // update to new position
+ this->update( x, y );
+
+ // randomly rotate the image
+ int rand_ = rand()%4;
+ switch (rand_) {
+ case 1:
+ this->image->setPixmap(
+ this->image->pixmap().transformed(
+ QTransform().rotate( 90.0 ) ) );
+ break;
+ case 2:
+ this->image->setPixmap(
+ this->image->pixmap().transformed(
+ QTransform().rotate( 180.0 ) ) );
+ break;
+ case 3:
+ this->image->setPixmap(
+ this->image->pixmap().transformed(
+ QTransform().rotate( -90.0 ) ) );
+ break;
+ default:
+ // do not rotate
+ break;
+ }
+}
+
+
+void Food::move( Snake& snake )
+{
+ int move_up = 0,
+ move_down = 0,
+ move_left = 0,
+ move_right = 0;
+ const unsigned int
+ snake_x = snake.front().x,
+ snake_y = snake.front().y;
+
+
+ // check the field's limits
+ if ( this->y == 0 ) {
+ move_up -= 100;
+ } else if ( this->y == 15 ) {
+ move_down -= 100;
+ }
+ if ( this->x == 0 ) {
+ move_left -= 100;
+ } else if ( this->x == 15 ) {
+ move_right -= 100;
+ }
+
+ // check the snake
+ if ( ! snake.inTile( this->x, this->y-1, false ) ) {
+ move_up += 100;
+ } else {
+ move_up -= 100;
+ }
+ if ( ! snake.inTile( this->x, this->y+1, false ) ) {
+ move_down += 100;
+ } else {
+ move_down -= 100;
+ }
+ if ( ! snake.inTile( this->x-1, this->y, false ) ) {
+ move_left += 100;
+ } else {
+ move_left -= 100;
+ }
+ if ( ! snake.inTile( this->x+1, this->y, false ) ) {
+ move_right += 100;
+ } else {
+ move_right -= 100;
+ }
+
+ // check the snake position
+ if ( this->x == snake_x ) {
+ move_left += 30;
+ move_right += 30;
+ if ( this->y < snake_y ) {
+ move_up += 30;
+ } else if ( this->y > snake_y ) {
+ move_down += 30;
+ }
+ } else if ( this->x < snake_x ) {
+ move_left += 30;
+ if ( this->y < snake_y ) {
+ move_up += 30;
+ } else if ( this->y > snake_y ) {
+ move_down += 30;
+ } else if ( this->y == snake_y ) {
+ move_up += 30;
+ move_down += 30;
+ }
+ } else if ( this->x > snake_x ) {
+ move_right += 30;
+ if ( this->y < snake_y ) {
+ move_up += 30;
+ } else if ( this->y > snake_y ) {
+ move_down += 30;
+ } else if ( this->y == snake_y ) {
+ move_up += 30;
+ move_down += 30;
+ }
+ }
+
+ // decide
+ int max = -1000;
+ Direction choice;
+ if ( move_up > max || (move_up == max && rand()%2) ) {
+ max = move_up;
+ choice = Direction::UP;
+ }
+ if ( move_down > max || (move_down == max && rand()%2) ) {
+ max = move_down;
+ choice = Direction::DOWN;
+ }
+ if ( move_left > max || (move_left == max && rand()%2) ) {
+ max = move_left;
+ choice = Direction::LEFT;
+ }
+ if ( move_right > max || (move_right == max && rand()%2) ) {
+ choice = Direction::RIGHT;
+ }
+
+ // apply the move
+ switch ( choice ) {
+ case Direction::UP:
+ this->update( this->x, this->y-1 );
+ this->image->setPixmap(
+ this->img_rat );
+ break;
+ case Direction::DOWN:
+ this->update( this->x, this->y+1 );
+ this->image->setPixmap(
+ this->img_rat.transformed(
+ QTransform().rotate( 180.0 ) ) );
+ break;
+ case Direction::LEFT:
+ this->update( this->x-1, this->y );
+ this->image->setPixmap(
+ this->img_rat.transformed(
+ QTransform().rotate( -90.0 ) ) );
+ break;
+ case Direction::RIGHT:
+ this->update( this->x+1, this->y );
+ this->image->setPixmap(
+ this->img_rat.transformed(
+ QTransform().rotate( 90.0 ) ) );
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected choice direction: "+std::to_string(choice));
+ }
+}
diff --git a/logdoctor/games/snake/food.h b/logdoctor/games/snake/food.h
new file mode 100644
index 00000000..353127eb
--- /dev/null
+++ b/logdoctor/games/snake/food.h
@@ -0,0 +1,53 @@
+#ifndef FOOD_H
+#define FOOD_H
+
+#include "snake.h"
+
+#include
+
+class Food
+{
+public:
+ Food( const bool& can_move=false );
+
+ //! Returns the position on the X-axis
+ const unsigned int& X();
+ //! Returns the position on the Y-axis
+ const unsigned int& Y();
+
+ //!< Returns the image
+ QGraphicsPixmapItem* getImageItem();
+
+ //! Checks whether is there a part of the snake in the given position
+ const bool inTile( const unsigned int& x, const unsigned int& y );
+
+ //! Spawns the egg/rat in a new position
+ void spawn( Snake& snake, Snake& snake_ );
+
+ //! Moves the rat
+ void move( Snake& snake );
+
+ //! Updates the position and direction of the entity
+ void update( const unsigned int& new_x, const unsigned int& new_y );
+
+
+private:
+
+ QPixmap img_egg = QPixmap(":/games/games/snake/egg.png");
+
+ QPixmap img_rat = QPixmap(":/games/games/snake/rat.png");
+
+ //!< Whether the food is a rat or an egg
+ bool movable;
+
+ //!< The position on the X-axis
+ unsigned int x;
+
+ //!< The position on the Y-axis
+ unsigned int y;
+
+ //!< The image which graphically represents the food
+ QGraphicsPixmapItem* image;
+};
+
+#endif // FOOD_H
diff --git a/logdoctor/games/snake/game.cpp b/logdoctor/games/snake/game.cpp
new file mode 100644
index 00000000..4478b506
--- /dev/null
+++ b/logdoctor/games/snake/game.cpp
@@ -0,0 +1,417 @@
+
+#include "game.h"
+#include "ui_snake.h"
+
+#include "games/games.h"
+
+#include
+#include
+
+
+SnakeGame::SnakeGame( const int& theme_id, const QFont& term_font, QWidget* parent ) :
+ QWidget(parent),
+ ui(new Ui::SnakeGame)
+{
+ this->ui->setupUi(this);
+
+ QString stylesheet = "";
+ GameSec::snakeStyleSheet( stylesheet, theme_id );
+ this->setStyleSheet( stylesheet );
+
+ QFont font = QFont( term_font );
+ font.setPointSize( 64 );
+ this->ui->button_Play->setFont( font );
+ font.setPointSize( 12 );
+ this->ui->box_GameMode->setFont( font );
+
+ // create the field
+ this->field_scene = new QGraphicsScene( this );
+ this->field_scene->setSceneRect( 0,0, 544, 544 );
+ this->field_scene->setBackgroundBrush( Qt::black );
+ // put water limits
+ this->field_scene->addItem( new QGraphicsPixmapItem( this->img_water ) );
+ // add the scene to the view
+ this->ui->view_Field->setScene( this->field_scene );
+}
+
+SnakeGame::~SnakeGame()
+{
+ delete this->ui;
+ delete this->field_scene;
+ delete this->game_loop;
+}
+
+void SnakeGame::closeEvent( QCloseEvent* event )
+{
+ this->game_loop->stop();
+ this->playing = false;
+}
+
+
+void SnakeGame::keyPressEvent( QKeyEvent* event )
+{
+ // store the key pressed if needed
+ if ( this->playing ) {
+ switch ( event->key() ) {
+ case Qt::Key_Up:
+ case Qt::Key_W:
+ if ( this->key_events.empty() ) { // leave me here
+ this->key_events.push( 0 );
+ } else if ( this->key_events.back() != 0 ) {
+ this->key_events.push( 0 );
+ }
+ break;
+ case Qt::Key_Down:
+ case Qt::Key_S:
+ if ( this->key_events.empty() ) {
+ this->key_events.push( 1 );
+ } else if ( this->key_events.back() != 1 ) {
+ this->key_events.push( 1 );
+ }
+ break;
+ case Qt::Key_Left:
+ case Qt::Key_A:
+ if ( this->key_events.empty() ) {
+ this->key_events.push( 2 );
+ } else if ( this->key_events.back() != 2 ) {
+ this->key_events.push( 2 );
+ }
+ break;
+ case Qt::Key_Right:
+ case Qt::Key_D:
+ if ( this->key_events.empty() ) {
+ this->key_events.push( 3 );
+ } else if ( this->key_events.back() != 3 ) {
+ this->key_events.push( 3 );
+ }
+ break;
+ }
+ }
+}
+
+
+//////////////
+//// MENU ////
+void SnakeGame::on_button_Play_clicked()
+{
+ // set-up the game
+ this->newSnake();
+ bool food_movable = false;
+ switch ( this->ui->box_GameMode->currentIndex() ) {
+ case 0:
+ this->game_mode = GameMode::Classic;
+ break;
+ case 1:
+ this->game_mode = GameMode::Hunt;
+ food_movable = true;
+ break;
+ case 2:
+ this->game_mode = GameMode::Battle;
+ this->newSnake_();
+ break;
+ default:
+ throw("Unexpected GameMode: "+std::to_string(this->ui->box_GameMode->currentIndex()));
+ break;
+ }
+ this->newFood( food_movable );
+
+ // switch to game board
+ this->ui->stackedWidget_GameDisplay->setCurrentIndex( 1 );
+ // start playing
+ this->game_loop = new QTimer(this);
+ connect(this->game_loop, SIGNAL(timeout()), this, SLOT(processGameLogic()));
+ this->game_loop->start(175);
+ this->playing = true;
+}
+
+
+void SnakeGame::newSnake()
+{
+ // snake initial position
+ const unsigned int head_x = (rand()%4)+6;
+ const unsigned int head_y = (rand()%4)+6;
+ if ( head_x > 15 || head_y > 15 ) {
+ // should be unreachable
+ throw("Unexpected initial position: ("+std::to_string(head_x)+","+std::to_string(head_y)+")");
+ }
+
+ // snake initial direction
+ const int rand_d = rand()%4;
+ switch ( rand_d ) {
+ case 0:
+ this->snake.setDirection( Direction::UP );
+ break;
+ case 1:
+ this->snake.setDirection( Direction::DOWN );
+ break;
+ case 2:
+ this->snake.setDirection( Direction::LEFT );
+ break;
+ case 3:
+ this->snake.setDirection( Direction::RIGHT );
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected initial direction: "+std::to_string(rand_d));
+ }
+ this->key_events.push( rand_d );
+
+ // build the body with a head
+ this->snake.push_back(
+ { head_x, head_y,
+ this->snake.direction(), this->snake.direction(),
+ new QGraphicsPixmapItem( this->snake.getHeadImage() ) }
+ );
+ this->field_scene->addItem( this->snake.front().image );
+ this->snake.front().update( head_x, head_y, this->snake.direction() );
+ // a body part
+ this->snake.willGrow();
+ this->snake.update( this->field_scene, true, true );
+ // and a tail
+ this->snake.willGrow();
+ this->snake.update( this->field_scene, true, true );
+}
+
+void SnakeGame::newSnake_()
+{
+ // snake initial position
+ unsigned int head_x, head_y;
+ head_x = this->snake.front().x;
+ head_y = this->snake.front().y;
+
+ // snake initial direction
+ const unsigned int rnd = (rand()%2);
+ this->snake_.setDirection( this->snake.direction() );
+ switch ( this->snake_.direction() ) {
+ case Direction::UP:
+ case Direction::DOWN:
+ if ( rnd ) {
+ head_x ++;
+ } else {
+ head_x --;
+ }
+ break;
+ case Direction::LEFT:
+ case Direction::RIGHT:
+ if ( rnd ) {
+ head_y ++;
+ } else {
+ head_y --;
+ }
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected initial direction _: "+std::to_string(this->snake_.direction()));
+ }
+
+ // build the body with a head
+ this->snake_.push_back(
+ { head_x, head_y,
+ this->snake_.direction(), this->snake_.direction(),
+ new QGraphicsPixmapItem( this->snake_.getHeadImage() ) }
+ );
+ this->field_scene->addItem( this->snake_.front().image );
+ this->snake_.front().update( head_x, head_y, this->snake_.direction() );
+ // a body part
+ this->snake_.willGrow();
+ this->snake_.update( this->field_scene, true, true );
+ // and a tail
+ this->snake_.willGrow();
+ this->snake_.update( this->field_scene, true, true );
+}
+
+void SnakeGame::newFood( const bool& movable )
+{
+ // put some food on the field for it to eat
+ this->food = Food( movable );
+ this->food.spawn( this->snake, this->snake_ );
+ this->field_scene->addItem( this->food.getImageItem() );
+}
+
+
+//////////////
+//// GAME ////
+void SnakeGame::processGameLogic()
+{
+ if ( game_over ) {
+ this->game_loop->stop();
+ this->playing = false;
+ QMessageBox::about(
+ this,
+ SnakeGame::tr("Game Over"),
+ this->game_over_msg );
+ } else {
+
+
+ if ( !this->key_events.empty() ) {
+ this->processNextKeyEvent();
+ }
+ if ( this->game_mode == GameMode::Battle ) {
+ this->snake_.move( this->snake, this->food.X(), this->food.Y() );
+ }
+ // check for a possible collision of the head
+ this->checkCollision( this->snake, this->snake_, false );
+ if ( this->game_mode == GameMode::Battle ) {
+ this->checkCollision( this->snake_, this->snake, true );
+ }
+ // check for game over
+ if ( ! this->game_over ) {
+ // update snake position
+ this->snake.update( this->field_scene );
+ if ( this->game_mode == GameMode::Battle ) {
+ this->snake_.update( this->field_scene );
+ }
+ if ( this->spawn_food ) {
+ // updae the score and spawn food in a new position
+ this->updateGameScore();
+ this->food.spawn( this->snake, this->snake_ );
+ this->spawn_food = false;
+ if ( this->game_mode == GameMode::Hunt ) {
+ this->moving_rate = 6 - ((this->snake.size()/13)+1);
+ this->moving_countdown = this->moving_rate;
+ }
+ } else if ( this->game_mode == GameMode::Hunt ) {
+ this->moving_countdown --;
+ if ( this->moving_countdown == 0 ) {
+ this->moving_countdown = this->moving_rate;
+ this->food.move( this->snake );
+ }
+ }
+ }
+ }
+}
+
+
+void SnakeGame::processNextKeyEvent()
+{
+ // update direction if needed
+ switch ( this->key_events.front() ) {
+ case 0: // up
+ if ( this->snake.direction() != Direction::DOWN ) {
+ this->snake.setDirection( Direction::UP );
+ }
+ break;
+ case 1: // down
+ if ( this->snake.direction() != Direction::UP ) {
+ this->snake.setDirection( Direction::DOWN );
+ }
+ break;
+ case 2: // left
+ if ( this->snake.direction() != Direction::RIGHT ) {
+ this->snake.setDirection( Direction::LEFT );
+ }
+ break;
+ case 3: // right
+ if ( this->snake.direction() != Direction::LEFT ) {
+ this->snake.setDirection( Direction::RIGHT );
+ }
+ break;
+ }
+ this->key_events.pop();
+}
+
+
+void SnakeGame::updateGameScore()
+{
+ this->game_score += this->score_step;
+ this->ui->lcd_Score->setDigitCount( std::to_string(this->game_score).size() );
+ this->ui->lcd_Score->display( this->game_score );
+}
+
+
+void SnakeGame::checkCollision( Snake& snake, Snake& adv_snake, const bool& is_adv )
+{
+ unsigned int x, y, x_, y_;
+
+ x = snake.front().x,
+ y = snake.front().y;
+ switch ( snake.direction() ) {
+ case Direction::UP:
+ y--;
+ break;
+ case Direction::DOWN:
+ y++;
+ break;
+ case Direction::LEFT:
+ x--;
+ break;
+ case Direction::RIGHT:
+ x++;
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(snake.direction()));
+ }
+
+ if ( adv_snake.size() > 0 ) {
+ x_ = adv_snake.front().x,
+ y_ = adv_snake.front().y;
+ switch ( adv_snake.direction() ) {
+ case Direction::UP:
+ y_--;
+ break;
+ case Direction::DOWN:
+ y_++;
+ break;
+ case Direction::LEFT:
+ x_--;
+ break;
+ case Direction::RIGHT:
+ x_++;
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(adv_snake.direction()));
+ }
+ } else {
+ x_ = y_ = 16;
+ }
+
+ // check the upcoming movement
+ if ( x > 15 || y > 15 ) {
+ // collision with the field limits
+ this->game_over = true;
+ this->game_over_msg = (is_adv)
+ ? SnakeGame::tr("Your adversary fell in the water!")+"\n\n"+SnakeGame::tr("YOU WON!")
+ : SnakeGame::tr("You fell in the water!")+"\n\n"+SnakeGame::tr("YOU LOST!");
+
+ } else if ( snake.inTile( x, y ) ) {
+ // collision with another part of the snake
+ this->game_over = true;
+ this->game_over_msg = (is_adv)
+ ? SnakeGame::tr("Your adversary ate itself!")+"\n\n"+SnakeGame::tr("YOU WON!")
+ : SnakeGame::tr("You ate yourself!")+"\n\n"+SnakeGame::tr("YOU LOST!");
+
+ } else if ( adv_snake.inTile( x, y ) ) {
+ // collision with another part of the snake
+ if ( x_ != x || y_ != y ) {
+ // not the head
+ this->game_over = true;
+ this->game_over_msg = (is_adv)
+ ? SnakeGame::tr("Your adversary ate you!")+"\n\n"+SnakeGame::tr("YOU WON!")
+ : SnakeGame::tr("You ate your adversary!")+"\n\n"+SnakeGame::tr("YOU LOST!");
+ } else {
+ this->game_over = true;
+ this->game_over_msg = SnakeGame::tr("You ate each other!")+"\n\n"+SnakeGame::tr("MATCH IS DRAW!");
+ }
+
+ } else if ( this->food.inTile( x, y ) ) {
+ // will eat
+ if ( snake.size() < this->MAX_SNAKE_LENGTH ) {
+ // below max size, will grow
+ snake.willGrow();
+ } else {
+ // max size reached, increase speed
+ const int interval = this->game_loop->interval();
+ if ( interval > 50 ) {
+ this->game_loop->setInterval( interval - 5 );
+ }
+ }
+ if ( is_adv ) {
+ this->score_step = -1;
+ } else {
+ this->score_step = 1;
+ }
+ this->spawn_food = true;
+ }
+}
diff --git a/logdoctor/games/snake/game.h b/logdoctor/games/snake/game.h
new file mode 100644
index 00000000..2ef7af89
--- /dev/null
+++ b/logdoctor/games/snake/game.h
@@ -0,0 +1,140 @@
+#ifndef SNAKE_GAME_H
+#define SNAKE_GAME_H
+
+#include "snake.h"
+#include "food.h"
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+
+namespace Ui {
+ class SnakeGame;
+}
+
+//! Snake
+/*!
+ A reproduction of the timeless classic game
+*/
+class SnakeGame : public QWidget
+{
+ Q_OBJECT
+
+public:
+ SnakeGame( const int& theme_id, const QFont& term_font, QWidget* parent=nullptr );
+ ~SnakeGame();
+
+
+private slots:
+
+ // Override
+ void closeEvent( QCloseEvent* event ) override;
+
+ // Starts the game
+ void on_button_Play_clicked();
+
+ //! Processes the logic of the game
+ void processGameLogic();
+
+
+private:
+ Ui::SnakeGame *ui;
+
+ ////////////////////
+ //// KEY EVENTS ////
+
+ //! Stores the valid key events in a queue
+ /*!
+ Valid keys are UP/DOWN/LEFT/RIGHT arrows
+ and W/S/A/D letters
+ \see key_events, processNextKeyEvent()
+ */
+ void keyPressEvent( QKeyEvent* event ) override;
+
+ //! Stores the key events
+ std::queue key_events;
+
+ //! Processes the key events in the queue
+ /*!
+ \see key_events, keyPressEvent()
+ */
+ void processNextKeyEvent();
+
+
+ //////////////////
+ //// GRAPHICS ////
+
+ QGraphicsScene* field_scene;
+
+ QPixmap img_water = QPixmap(":/games/games/snake/water.png");
+
+
+ //////////////
+ //// GAME ////
+
+ //! Enumerates the available game modes
+ enum GameMode {
+ Classic, //!< Classic snake game
+ Hunt, //!< Game variant in which the food moves too
+ Battle //!< Game variant in which you play against another snake
+ };
+
+ GameMode game_mode;
+
+ bool playing = false;
+
+ QTimer* game_loop = new QTimer();
+
+ bool game_over = false;
+
+ QString game_over_msg;
+
+
+ ///////////////
+ //// SNAKE ////
+
+ //! The maximum length of the snake
+ const unsigned int MAX_SNAKE_LENGTH = 64;
+
+ //! The snake
+ Snake snake;
+ void newSnake();
+
+ // The adversary snake
+ Snake snake_ = Snake(true);
+ void newSnake_();
+
+ //! Checks if a snake will collide with another entity
+ void checkCollision( Snake& snake, Snake& adv_snake, const bool& is_adv );
+
+
+ //////////////
+ //// FOOD ////
+
+ //! Instance of the egg/rat which will be eat by the snake
+ Food food;
+ void newFood( const bool& movable );
+
+ bool spawn_food = false;
+
+ unsigned int moving_countdown = 5;
+ unsigned int moving_rate = 5;
+
+
+ ///////////////
+ //// SCORE ////
+
+ int game_score = 0;
+
+ //! Increases/Decreases the player's score by one
+ void updateGameScore();
+ int score_step = 1;
+
+};
+
+#endif // SNAKE_GAME_H
diff --git a/logdoctor/games/snake/snake.cpp b/logdoctor/games/snake/snake.cpp
new file mode 100644
index 00000000..d2d4e526
--- /dev/null
+++ b/logdoctor/games/snake/snake.cpp
@@ -0,0 +1,1141 @@
+
+#include "snake.h"
+
+
+Snake::Snake( const bool& is_adversary )
+{
+ this->will_grow = false;
+ this->adversary = is_adversary;
+ if ( is_adversary ) {
+ for ( size_t x=0; x<16; x++ ) {
+ this->field_map.push_back( std::vector{} );
+ std::vector& v = this->field_map.back();
+ for ( size_t y=0; y<16; y++ ) {
+ v.push_back({ Entity::N, 0 });
+ }
+ }
+ }
+}
+
+
+QPixmap& Snake::getHeadImage()
+{
+ if ( this->adversary ) {
+ return this->img_snakeHead_;
+ } else {
+ return this->img_snakeHead;
+ }
+}
+
+
+const bool Snake::inTile( const unsigned int& x, const unsigned int& y , const bool& avoid_tail )
+{
+ bool result = false;
+ if ( this->size() > 0 ) {
+ size_t i = 0,
+ max = this->size()-1;
+ if ( !avoid_tail ) {
+ max ++;
+ }
+ for ( std::vector::const_iterator bp = this->begin(); bp != this->end(); ++bp ) {
+ if ( bp->x == x && bp->y == y ) {
+ result = true;
+ break;
+ }
+ i++;
+ if ( i >= max ) {
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+
+void Snake::setDirection( const Direction new_direction )
+{
+ this->head_direction = new_direction;
+}
+const Direction& Snake::direction()
+{
+ return this->head_direction;
+}
+
+void Snake::willGrow()
+{
+ this->will_grow = true;
+}
+void Snake::grow( const bool& is_borning )
+{
+ // build from the tail
+ const BodyPart& tail = this->back();
+ unsigned int x = tail.x;
+ unsigned int y = tail.y;
+ const Direction d = tail.direction;
+ const Direction ld = tail.prev_direction;
+ if ( is_borning ) {
+ // one tile back
+ switch ( d ) {
+ case Direction::UP:
+ y ++;
+ break;
+ case Direction::DOWN:
+ y --;
+ break;
+ case Direction::LEFT:
+ x ++;
+ break;
+ case Direction::RIGHT:
+ x --;
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(d));
+ }
+ }
+ this->push_back(
+ { x, y,
+ d, ld,
+ new QGraphicsPixmapItem( (this->adversary) ? this->img_snakeTail_ : this->img_snakeTail ) }
+ );
+ this->update( nullptr, true );
+ this->back().update( x, y, d );
+}
+
+
+void Snake::update( QGraphicsScene* field_scene, const bool& dry , const bool& is_borning )
+{
+ // grow if planned
+ if ( this->will_grow ) {
+ this->will_grow = false;
+ this->grow( is_borning );
+ field_scene->addItem( this->back().image );
+ }
+ // anyway, update the whole body
+ size_t i = 0,
+ max_i = this->size()-1;
+ unsigned int new_x, prev_x, new_y, prev_y;
+ Direction new_direction, prev_direction, prev_body_d;
+ QPixmap& head_img = (this->adversary) ? this->img_snakeHead_ : this->img_snakeHead;
+ QPixmap& body_img = (this->adversary) ? this->img_snakeBody_ : this->img_snakeBody;
+ QPixmap& curve_img = (this->adversary) ? this->img_snakeCurve_ : this->img_snakeCurve;
+ QPixmap& tail_img = (this->adversary) ? this->img_snakeTail_ : this->img_snakeTail;
+ for ( std::vector::iterator bp = this->begin(); bp != this->end(); ++bp ) {
+ if ( ! dry ) {
+ // future position
+ if ( i == 0 ) {
+ // head doesn't follow any other part of the body
+ switch ( this->head_direction ) {
+ case Direction::UP:
+ new_y = bp->y - 1;
+ new_x = bp->x;
+ break;
+ case Direction::DOWN:
+ new_y = bp->y + 1;
+ new_x = bp->x;
+ break;
+ case Direction::LEFT:
+ new_x = bp->x - 1;
+ new_y = bp->y;
+ break;
+ case Direction::RIGHT:
+ new_x = bp->x + 1;
+ new_y = bp->y;
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(this->head_direction));
+ }
+ new_direction = this->head_direction;
+ } else {
+ // follow the previous part of the body
+ new_x = prev_x;
+ new_y = prev_y;
+ new_direction = prev_direction;
+ }
+ // store for the next part
+ prev_x = bp->x;
+ prev_y = bp->y;
+ prev_direction = bp->direction;
+
+ // update the body-part position
+ bp->update( new_x, new_y, new_direction );
+ }
+
+ // finally set the image to be shown
+ switch ( bp->direction ) {
+
+ case Direction::UP:
+ if ( i == 0 ) {
+ bp->image->setPixmap(
+ head_img );
+ } else if ( i == max_i ) {
+ switch ( prev_body_d ) {
+ case Direction::UP:
+ bp->image->setPixmap(
+ tail_img );
+ break;
+ case Direction::LEFT:
+ bp->image->setPixmap(
+ tail_img.transformed(
+ QTransform().rotate( -90.0 ) ) );
+ break;
+ case Direction::RIGHT:
+ bp->image->setPixmap(
+ tail_img.transformed(
+ QTransform().rotate( 90.0 ) ) );
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(prev_body_d));
+ }
+ } else {
+ switch ( prev_body_d ) {
+ case Direction::UP:
+ bp->image->setPixmap(
+ body_img );
+ break;
+ case Direction::LEFT:
+ bp->image->setPixmap(
+ curve_img.transformed(
+ QTransform().rotate( 90.0 ) ) );
+ break;
+ case Direction::RIGHT:
+ bp->image->setPixmap(
+ curve_img );
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(prev_body_d));
+ }
+ }
+ break;
+
+ case Direction::DOWN:
+ if ( i == 0 ) {
+ bp->image->setPixmap(
+ head_img.transformed(
+ QTransform().rotate( 180.0 ) ) );
+ } else if ( i == max_i ) {
+ switch ( prev_body_d ) {
+ case Direction::DOWN:
+ bp->image->setPixmap(
+ tail_img.transformed(
+ QTransform().rotate( 180.0 ) ) );
+ break;
+ case Direction::LEFT:
+ bp->image->setPixmap(
+ tail_img.transformed(
+ QTransform().rotate( -90.0 ) ) );
+ break;
+ case Direction::RIGHT:
+ bp->image->setPixmap(
+ tail_img.transformed(
+ QTransform().rotate( 90.0 ) ) );
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(prev_body_d));
+ }
+ } else {
+ switch ( prev_body_d ) {
+ case Direction::DOWN:
+ bp->image->setPixmap(
+ body_img );
+ break;
+ case Direction::LEFT:
+ bp->image->setPixmap(
+ curve_img.transformed(
+ QTransform().rotate( 180.0 ) ) );
+ break;
+ case Direction::RIGHT:
+ bp->image->setPixmap(
+ curve_img.transformed(
+ QTransform().rotate( -90.0 ) ) );
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(prev_body_d));
+ }
+ }
+ break;
+
+ case Direction::LEFT:
+ if ( i == 0 ) {
+ bp->image->setPixmap(
+ head_img.transformed(
+ QTransform().rotate( -90.0 ) ) );
+ } else if ( i == max_i ) {
+ switch ( prev_body_d ) {
+ case Direction::LEFT:
+ bp->image->setPixmap(
+ tail_img.transformed(
+ QTransform().rotate( -90.0 ) ) );
+ break;
+ case Direction::UP:
+ bp->image->setPixmap(
+ tail_img );
+ break;
+ case Direction::DOWN:
+ bp->image->setPixmap(
+ tail_img.transformed(
+ QTransform().rotate( 180.0 ) ) );
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(prev_body_d));
+ }
+ } else {
+ switch ( prev_body_d ) {
+ case Direction::LEFT:
+ bp->image->setPixmap(
+ body_img.transformed(
+ QTransform().rotate( -90.0 ) ) );
+ break;
+ case Direction::UP:
+ bp->image->setPixmap(
+ curve_img.transformed(
+ QTransform().rotate( -90.0 ) ) );
+ break;
+ case Direction::DOWN:
+ bp->image->setPixmap(
+ curve_img );
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(prev_body_d));
+ }
+ }
+ break;
+
+ case Direction::RIGHT:
+ if ( i == 0 ) {
+ bp->image->setPixmap(
+ head_img.transformed(
+ QTransform().rotate( 90.0 ) ) );
+ } else if ( i == max_i ) {
+ switch ( prev_body_d ) {
+ case Direction::RIGHT:
+ bp->image->setPixmap(
+ tail_img.transformed(
+ QTransform().rotate( 90.0 ) ) );
+ break;
+ case Direction::UP:
+ bp->image->setPixmap(
+ tail_img );
+ break;
+ case Direction::DOWN:
+ bp->image->setPixmap(
+ tail_img.transformed(
+ QTransform().rotate( 180.0 ) ) );
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(prev_body_d));
+ }
+ } else {
+ switch ( prev_body_d ) {
+ case Direction::RIGHT:
+ bp->image->setPixmap(
+ body_img.transformed(
+ QTransform().rotate( 90.0 ) ) );
+ break;
+ case Direction::UP:
+ bp->image->setPixmap(
+ curve_img.transformed(
+ QTransform().rotate( 180.0 ) ) );
+ break;
+ case Direction::DOWN:
+ bp->image->setPixmap(
+ curve_img.transformed(
+ QTransform().rotate( 90.0 ) ) );
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(prev_body_d));
+ }
+ }
+ break;
+
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(bp->direction));
+ }
+ prev_body_d = bp->direction;
+ i++;
+ }
+}
+
+
+void Snake::move( Snake& adv_snake, const unsigned int& food_x, const unsigned int& food_y )
+{
+ std::vector classes = {
+ Direction::UP,
+ Direction::DOWN,
+ Direction::LEFT,
+ Direction::RIGHT,
+ };
+
+ std::vector> dataset = {
+ {0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0}
+ };
+
+ std::vector weights = {
+ -1.0, // blocked way
+ -0.4, // dead way
+ 0.003, // dead way steps
+ 0.1, // food way
+ 0.4, // aggressive way
+ 0.01, // same direction
+ -1.0 // opposite direction
+ };
+
+ this->updateFieldMap( adv_snake, food_x, food_y );
+
+ for ( int i=0; i<4; i++ ) {
+ this->collectData( dataset.at(i), classes.at(i), adv_snake, food_x, food_y );
+ }
+
+ // decide
+ this->head_direction = this->predictDirection( dataset, weights, classes );
+}
+
+
+const Direction Snake::predictDirection( std::vector>& data, std::vector weights , std::vector classes )
+{
+ float results[] = { 1.0, 1.0, 1.0, 1.0 };
+ bool keep_current = false;
+ Direction class_label;
+
+ // process data
+ for ( int i=0; i<4; i++ ) {
+ std::vector& d = data.at(i);
+ float& r = results[i];
+ for ( int j=0; j<7; j++ ) {
+ r += d.at(j) * weights.at(j);
+ }
+ }
+
+ // normalize results (not really a need here...)
+ float min=10.0, max=-10.0;
+ for ( const float& r : results ) {
+ if ( r < min ) {
+ min = r;
+ }
+ if ( r > max ) {
+ max = r;
+ }
+ }
+ if ( max != min ) {
+ for ( int i=0; i<4; i++ ) {
+ results[i] = (results[i]-min) / (max-min);
+ }
+ } else {
+ keep_current = true;
+ for ( int i=0; i<4; i++ ) {
+ results[i] = 0.0;
+ }
+ }
+
+ // choose the best result
+ if ( keep_current ) {
+ class_label = this->head_direction;
+ } else {
+ max = 0;
+ for ( int i=0; i<4; i++ ) {
+ if ( results[i] > max ) {
+ class_label = classes.at(i);
+ max = results[i];
+ } else if ( results[i] == max ) {
+ if ( rand()%2 ) {
+ class_label = classes.at(i);
+ }
+ }
+ }
+ }
+
+ return class_label;
+}
+
+
+void Snake::collectData( std::vector& data, Direction& direction, Snake& adv_snake, const unsigned int& food_x, const unsigned int& food_y )
+{
+ unsigned int
+ blocked_way = 0,
+ dead_way = 0,
+ dead_way_steps = 0,
+ food_way = 0,
+ aggressive_way = 0,
+ same_direction = 0,
+ opposite_direction = 0;
+
+ const unsigned int head_x = this->front().x;
+ const unsigned int head_y = this->front().y;
+
+ unsigned int x, y;
+
+ // avoid choosing the opposite direction
+ switch ( direction ) {
+
+ case Direction::UP:
+ x = head_x;
+ y = head_y-1;
+ if ( this->head_direction == Direction::UP ) {
+ same_direction = 1;
+ } else if ( this->head_direction == Direction::DOWN ) {
+ opposite_direction = 1;
+ }
+ if ( head_y == 0 ) {
+ blocked_way = 1;
+ }
+ break;
+
+ case Direction::DOWN:
+ x = head_x;
+ y = head_y+1;
+ if ( this->head_direction == Direction::DOWN ) {
+ same_direction = 1;
+ } else if ( this->head_direction == Direction::UP ) {
+ opposite_direction = 1;
+ }
+ if ( head_y == 15 ) {
+ blocked_way = 1;
+ }
+ break;
+
+ case Direction::LEFT:
+ x = head_x-1;
+ y = head_y;
+ if ( this->head_direction == Direction::LEFT ) {
+ same_direction = 1;
+ } else if ( this->head_direction == Direction::RIGHT ) {
+ opposite_direction = 1;
+ }
+ if ( head_x == 0 ) {
+ blocked_way = 1;
+ }
+ break;
+
+ case Direction::RIGHT:
+ x = head_x+1;
+ y = head_y;
+ if ( this->head_direction == Direction::RIGHT ) {
+ same_direction = 1;
+ } else if ( this->head_direction == Direction::LEFT ) {
+ opposite_direction = 1;
+ }
+ if ( head_x == 15 ) {
+ blocked_way = 1;
+ }
+ break;
+
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(this->head_direction));
+ }
+
+
+ if ( !(blocked_way || opposite_direction) ) {
+
+ // check snakes
+ switch ( this->field_map.at( x ).at( y ).entity ) {
+ case Entity::S:
+ case Entity::A:
+ blocked_way = 1;
+ break;
+ }
+
+ if ( ! blocked_way ) {
+
+ // check for deadhole
+ dead_way_steps = this->isDeadHole( x, y, direction );
+ if ( dead_way_steps > 0 ) {
+ dead_way = 1;
+ }
+
+ // check for aggressivity purposes
+ switch ( direction ) {
+
+ case Direction::UP:
+ if ( adv_snake.direction() == Direction::LEFT && this->head_direction == Direction::RIGHT ) {
+ if ( this->inTileAdv( x+2, y+1 ) ) {
+ aggressive_way = 1;
+ }
+ } else if ( adv_snake.direction() == Direction::RIGHT && this->head_direction == Direction::LEFT ) {
+ if ( this->inTileAdv( x-2, y+1 ) ) {
+ aggressive_way = 1;
+ }
+ } else if ( adv_snake.direction() == this->head_direction ) {
+ if ( this->inTileAdv( x+2, y )
+ || this->inTileAdv( x-2, y ) ) {
+ if ( !rand()%this->aggressiveness ) {
+ aggressive_way = 1;
+ }
+ }
+ }
+ break;
+
+ case Direction::DOWN:
+ if ( adv_snake.direction() == Direction::LEFT && this->head_direction == Direction::RIGHT ) {
+ if ( this->inTileAdv( x+2, y-1 ) ) {
+ aggressive_way = 1;
+ }
+ } else if ( adv_snake.direction() == Direction::RIGHT && this->head_direction == Direction::LEFT ) {
+ if ( this->inTileAdv( x-2, y-1 ) ) {
+ aggressive_way = 1;
+ }
+ } else if ( adv_snake.direction() == this->head_direction ) {
+ if ( this->inTileAdv( x+2, y )
+ || this->inTileAdv( x-2, y ) ) {
+ if ( !rand()%this->aggressiveness ) {
+ aggressive_way = 1;
+ }
+ }
+ }
+ break;
+
+ case Direction::LEFT:
+ if ( adv_snake.direction() == Direction::UP && this->head_direction == Direction::DOWN ) {
+ if ( this->inTileAdv( x+1, y+2 ) ) {
+ aggressive_way = 1;
+ }
+ } else if ( adv_snake.direction() == Direction::DOWN && this->head_direction == Direction::UP ) {
+ if ( this->inTileAdv( x+1, y-2 ) ) {
+ aggressive_way = 1;
+ }
+ } else if ( adv_snake.direction() == this->head_direction ) {
+ if ( this->inTileAdv( x, y+2 )
+ || this->inTileAdv( x, y-2 ) ) {
+ if ( !rand()%this->aggressiveness ) {
+ aggressive_way = 1;
+ }
+ }
+ }
+ break;
+
+ case Direction::RIGHT:
+ if ( adv_snake.direction() == Direction::UP && this->head_direction == Direction::DOWN ) {
+ if ( this->inTileAdv( x-1, y+2 ) ) {
+ aggressive_way = 1;
+ }
+ } else if ( adv_snake.direction() == Direction::DOWN && this->head_direction == Direction::UP ) {
+ if ( this->inTileAdv( x-1, y-2 ) ) {
+ aggressive_way = 1;
+ }
+ } else if ( adv_snake.direction() == this->head_direction ) {
+ if ( this->inTileAdv( x, y+2 )
+ || this->inTileAdv( x, y-2 ) ) {
+ if ( !rand()%this->aggressiveness ) {
+ aggressive_way = 1;
+ }
+ }
+ }
+ break;
+
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(this->head_direction));
+ }
+ }
+ }
+
+
+ if ( !(blocked_way || dead_way) ) {
+
+ // check for food
+ switch ( direction ) {
+
+ case Direction::UP:
+ if ( food_y < head_y ) {
+ if ( ! opposite_direction ) {
+ food_way = 1;
+ }
+ } else if ( food_y == head_y ) {
+ if ( food_x > head_x && this->head_direction == Direction::LEFT ) {
+ food_way = 1;
+ } else if ( food_x < head_x && this->head_direction == Direction::RIGHT ) {
+ food_way = 1;
+ }
+ }
+ break;
+
+ case Direction::DOWN:
+ if ( food_y > head_y ) {
+ if ( ! opposite_direction ) {
+ food_way = 1;
+ }
+ } else if ( food_y == head_y ) {
+ if ( food_x > head_x && this->head_direction == Direction::LEFT ) {
+ food_way = 1;
+ } else if ( food_x < head_x && this->head_direction == Direction::RIGHT ) {
+ food_way = 1;
+ }
+ }
+ break;
+
+ case Direction::LEFT:
+ if ( food_x < head_x ) {
+ if ( ! opposite_direction ) {
+ food_way = 1;
+ }
+ } else if ( food_x == head_x ) {
+ if ( food_y > head_y && this->head_direction == Direction::UP ) {
+ food_way = 1;
+ } else if ( food_y < head_y && this->head_direction == Direction::DOWN ) {
+ food_way = 1;
+ }
+ }
+ break;
+
+ case Direction::RIGHT:
+ if ( food_x > head_x ) {
+ if ( ! opposite_direction ) {
+ food_way = 1;
+ }
+ } else if ( food_x == head_x ) {
+ if ( food_y > head_y && this->head_direction == Direction::UP ) {
+ food_way = 1;
+ } else if ( food_y < head_y && this->head_direction == Direction::DOWN ) {
+ food_way = 1;
+ }
+ }
+ break;
+
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(this->head_direction));
+ }
+ }
+
+
+ // update data
+ data.at(0) = blocked_way;
+ data.at(1) = dead_way;
+ data.at(2) = dead_way_steps;
+ data.at(3) = food_way;
+ data.at(4) = aggressive_way;
+ data.at(5) = same_direction;
+ data.at(6) = opposite_direction;
+}
+
+
+void Snake::updateFieldMap( Snake& adv_snake, const unsigned int& food_x, const unsigned int& food_y )
+{
+ // reset to default state
+ for ( size_t x=0; x<16; x++ ) {
+ for ( size_t y=0; y<16; y++ ) {
+ Tile& t = this->field_map.at(x).at(y);
+ t.entity = Entity::N;
+ t.s_index = 0;
+ }
+ }
+ // update food position
+ this->field_map.at(food_x).at(food_y).entity = Entity::F;
+ // update self
+ unsigned int i = this->size();
+ for ( std::vector::const_iterator bp = this->begin(); bp != this->end(); ++bp ) {
+ Tile& t = this->field_map.at(bp->x).at(bp->y);
+ t.entity = Entity::S;
+ t.s_index = i;
+ i--;
+ }
+ // update adversary
+ i = adv_snake.size();
+ for ( const auto& bp : adv_snake ) {
+ Tile& t = this->field_map.at(bp.x).at(bp.y);
+ t.entity = Entity::A;
+ t.s_index = i;
+ i--;
+ }
+}
+
+
+const bool Snake::inTileAdv(const unsigned int& x, const unsigned int& y )
+{
+ bool result = false;
+ if ( x < 16 && y < 16 ) {
+ switch ( this->field_map.at(x).at(y).entity ) {
+ case Entity::A:
+ result = true;
+ break;
+ }
+ }
+ return result;
+}
+
+const bool Snake::inTileMinusSteps(const unsigned int& x, const unsigned int& y, const unsigned int& steps )
+{
+ bool result = false;
+ switch ( this->field_map.at(x).at(y).entity ) {
+ case Entity::S:
+ case Entity::A:
+ if ( this->field_map.at(x).at(y).s_index > steps ) {
+ result = true;
+ }
+ break;
+ }
+ return result;
+}
+
+
+const std::vector Snake::checkAround( const Direction& direction, const unsigned int& x, const unsigned int& y )
+{
+ std::vector around = {
+ 0, 0, 0,
+ 0, 0,
+ 0, 0, 0,
+ };
+
+ std::vector x_pattern, y_pattern;
+
+ switch ( direction ) {
+ case Direction::UP:
+ x_pattern = { -1, 0, 1,
+ -1, 1,
+ -1, 0, 1 };
+ y_pattern = { -1, -1, -1,
+ 0, 0,
+ 1, 1, 1 };
+ break;
+ case Direction::DOWN:
+ x_pattern = { 1, 0, -1,
+ 1, -1,
+ 1, 0, -1 };
+ y_pattern = { 1, 1, 1,
+ 0, 0,
+ -1, -1, -1 };
+ break;
+ case Direction::LEFT:
+ x_pattern = { -1, -1, -1,
+ 0, 0,
+ 1, 1, 1 };
+ y_pattern = { 1, 0, -1,
+ 1, -1,
+ 1, 0, -1 };
+ break;
+ case Direction::RIGHT:
+ x_pattern = { 1, 1, 1,
+ 0, 0,
+ -1, -1, -1 };
+ y_pattern = { -1, 0, 1,
+ -1, 1,
+ -1, 0, 1 };
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(direction));
+ }
+
+ unsigned int x_, y_;
+ for ( int i=0; i<8; i++ ) {
+ x_ = x;
+ x_ += x_pattern.at(i);
+ y_ = y;
+ y_ += y_pattern.at(i);
+ if ( x_ > 15 || y_ > 15 ) {
+ around.at(i) = 1;
+ } else {
+ switch ( this->field_map.at( x_ ).at( y_ ).entity ) {
+ case Entity::S:
+ case Entity::A:
+ around.at(i) = 2;
+ break;
+ }
+ }
+ }
+
+ return around;
+}
+
+
+const unsigned int Snake::isDeadHole( const unsigned int& start_x, const unsigned int& start_y, Direction start_direction )
+{
+ bool result=false, check=false, check_clockwise=false;
+ int front_step, front_check, side_check;
+ unsigned int steps=1, side, front;
+ Direction direction = start_direction;
+
+ const auto blocked_around = this->checkAround( direction, start_x, start_y );
+ if ( (blocked_around.at(3)>0 && blocked_around.at(4)>0)
+ && (blocked_around.at(3)>1 || blocked_around.at(4)>1) ) {
+ check = true;
+ } else if ( (blocked_around.at(3)>0 && blocked_around.at(7)>0)
+ && (blocked_around.at(3)>1 || blocked_around.at(7)>1) ) {
+ check = true;
+ } else if ( (blocked_around.at(4)>0 && blocked_around.at(5)>0)
+ && (blocked_around.at(4)>1 || blocked_around.at(5)>1) ) {
+ check = true;
+ check_clockwise = true;
+ } else if ( (blocked_around.at(5)>0 && blocked_around.at(7)>0)
+ && (blocked_around.at(5)>1 || blocked_around.at(7)>1) ) {
+ check = true;
+ } else if ( blocked_around.at(0)>0 && blocked_around.at(1)>0 && blocked_around.at(2)>0 ) {
+ check = true;
+ }
+
+
+ if ( check ) {
+
+ unsigned int aux_side, aux_front;
+ const unsigned int max_steps = this->size()-1;
+ std::vector> tried_positions = {
+ std::make_tuple( start_x, start_y )
+ };
+
+ std::function change_direction;
+ std::function tried_already;
+ std::function tile_blocked;
+ std::function check_deadhole;
+
+
+ change_direction = [&](const bool& clockwise) {
+ switch ( direction ) {
+ case Direction::UP:
+ if ( clockwise ) {
+ direction = Direction::RIGHT;
+ front_check = +1;
+ side_check = (check_clockwise) ? +1 : -1;
+ } else {
+ direction = Direction::LEFT;
+ front_check = -1;
+ side_check = (check_clockwise) ? -1 : +1;
+ }
+ break;
+ case Direction::DOWN:
+ if ( clockwise ) {
+ direction = Direction::LEFT;
+ front_check = -1;
+ side_check = (check_clockwise) ? -1 : +1;
+ } else {
+ direction = Direction::RIGHT;
+ front_check = +1;
+ side_check = (check_clockwise) ? +1 : -1;
+ }
+ break;
+ case Direction::LEFT:
+ if ( clockwise ) {
+ direction = Direction::UP;
+ front_check = -1;
+ side_check = (check_clockwise) ? +1 : -1;
+ } else {
+ direction = Direction::DOWN;
+ front_check = +1;
+ side_check = (check_clockwise) ? -1 : +1;
+ }
+ break;
+ case Direction::RIGHT:
+ if ( clockwise ) {
+ direction = Direction::DOWN;
+ front_check = +1;
+ side_check = (check_clockwise) ? -1 : +1;
+ } else {
+ direction = Direction::UP;
+ front_check = -1;
+ side_check = (check_clockwise) ? +1 : -1;
+ }
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(direction));
+ }
+ // swap front/side
+ front = front ^ side;
+ side = front ^ side;
+ front = front ^ side;
+ front_step = front_check;
+ };
+
+
+ tried_already = [&](const unsigned int& side_, const unsigned int& front_, const bool& update) {
+ bool tried = false;
+ unsigned int x, y;
+ switch ( direction ) {
+ case Direction::UP:
+ case Direction::DOWN:
+ x = side_;
+ y = front_;
+ break;
+ case Direction::LEFT:
+ case Direction::RIGHT:
+ y = side_;
+ x = front_;
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(direction));
+ }
+ for ( const auto& position : tried_positions ) {
+ if ( std::get<0>(position) == x ) {
+ if ( std::get<1>(position) == y ) {
+ tried = true;
+ break;
+ }
+ }
+ }
+ if ( update ) {
+ tried_positions.push_back( std::make_tuple( x, y ) );
+ }
+ return tried;
+ };
+
+
+ tile_blocked = [&](const unsigned int& side_, const unsigned int& front_) {
+ bool blocked = false;
+ unsigned int x, y, x_, y_;
+ switch ( direction ) {
+ case Direction::UP:
+ case Direction::DOWN:
+ x = side_;
+ y = front_;
+ x_ = side;
+ y_ = front;
+ break;
+ case Direction::LEFT:
+ case Direction::RIGHT:
+ y = side_;
+ x = front_;
+ y_ = side;
+ x_ = front;
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(direction));
+ }
+ if ( x > 15 || y > 15 ) {
+ blocked = true;
+ } else if ( this->inTileMinusSteps( x, y, steps ) ) {
+ blocked = true;
+ } else if ( tried_already( side_, front_, false ) ) {
+ if ( this->field_map.at( x_ ).at( y_ ).entity == Entity::S ) {
+ blocked = true;
+ }
+ }
+ return blocked;
+ };
+
+
+ check_deadhole = [&]() {
+ switch ( start_direction ) {
+ case Direction::UP:
+ front = start_y;
+ side = start_x;
+ front_check = -1;
+ side_check = (check_clockwise) ? +1 : -1;
+ break;
+ case Direction::DOWN:
+ front = start_y;
+ side = start_x;
+ front_check = +1;
+ side_check = (check_clockwise) ? -1 : +1;
+ break;
+ case Direction::LEFT:
+ front = start_x;
+ side = start_y;
+ front_check = -1;
+ side_check = (check_clockwise) ? -1 : +1;
+ break;
+ case Direction::RIGHT:
+ front = start_x;
+ side = start_y;
+ front_check = +1;
+ side_check = (check_clockwise) ? +1 : -1;
+ break;
+ default:
+ // should be unreachable
+ throw("Unexpected direction: "+std::to_string(start_direction));
+ }
+ front_step = front_check;
+
+ while ( true ) {
+ // check the side
+ aux_side = side;
+ aux_side += side_check;
+ if ( !tile_blocked( aux_side, front ) ) {
+ // side is free, check for another deadhole
+ aux_front = front;
+ aux_front += front_check;
+ unsigned int aux_front_ = front;
+ aux_front_ += front_check*(-2);
+ if ( tile_blocked( side, aux_front ) && tile_blocked( aux_side, aux_front_ ) ) {
+ // may-be deadhole at side, check the opposite
+ aux_side = side;
+ aux_side += (side_check*(-1));
+ if ( !tile_blocked( aux_side, front ) ) {
+ // opposite side free
+ change_direction( !check_clockwise );
+ } else {
+ // opposite side blocked too
+ change_direction( check_clockwise );
+ }
+ } else {
+ // turn at side
+ change_direction( check_clockwise );
+ }
+ } else {
+ // side blocked, check front
+ aux_front = front;
+ aux_front += front_check;
+ if ( tile_blocked( side, aux_front ) ) {
+ // front blocked, check opposite side
+ aux_side = side;
+ aux_side += (side_check*(-1));
+ if ( !tile_blocked( aux_side, front ) ) {
+ // opposite side free
+ change_direction( !check_clockwise );
+ } else {
+ // opposite side blocked too
+ result = true;
+ break;
+ }
+ }
+ }
+
+ front += front_step;
+
+ if ( tried_already( side, front, true ) ) {
+ result = true;
+ break;
+ } else {
+ steps ++;
+ if ( steps >= max_steps ) {
+ break;
+ }
+ }
+ }
+ };
+
+
+ // start checking
+ bool aux_result=false;
+ unsigned int aux_steps=0, i=0;
+ while ( true ) {
+ check_deadhole();
+ if ( !result ) {
+ steps = 0;
+ }
+ i ++;
+ if ( i == 2 ) {
+ break;
+ }
+ aux_result = result;
+ result = false;
+ aux_steps = steps;
+ steps = 1;
+ check_clockwise = !check_clockwise;
+ direction = start_direction;
+ tried_positions.clear();
+ }
+
+ // mean result
+ result = result | aux_result;
+ steps = (steps>aux_steps) ? steps : aux_steps;
+ }
+
+ if ( !result ) {
+ steps = 0;
+ }
+ return steps;
+}
diff --git a/logdoctor/games/snake/snake.h b/logdoctor/games/snake/snake.h
new file mode 100644
index 00000000..051f608e
--- /dev/null
+++ b/logdoctor/games/snake/snake.h
@@ -0,0 +1,125 @@
+#ifndef SNAKE_H
+#define SNAKE_H
+
+#include
+
+#include
+#include
+#include
+
+
+//! Enumerates the possible directions
+enum Direction {
+ UP, //!< Up
+ DOWN, //!< Down
+ LEFT, //!< Left
+ RIGHT //!< Right
+};
+
+
+//! Instance of a part of the body of the snake
+struct BodyPart {
+ unsigned int x; //!< The position on the X-axis
+ unsigned int y; //!< The position on the Y-axis
+ Direction direction; //!< The current direction of the part
+ Direction prev_direction; //!< The previous direction of the part
+ QGraphicsPixmapItem* image; //!< The image which graphically represents the part
+ //! Updates the position and direction of the part
+ void update( const unsigned int& new_x, const unsigned int& new_y, const Direction& new_direction ) {
+ this->x = new_x;
+ this->y = new_y;
+ this->image->setOffset( 16+(new_x*32), 16+(new_y*32) );
+ this->prev_direction = this->direction;
+ this->direction = new_direction;
+ }
+};
+
+
+class Snake : public std::vector
+{
+public:
+ Snake( const bool& is_adversary=false );
+
+ QPixmap& getHeadImage();
+
+ //! Checks whether is there a part of the snake in the given position
+ const bool inTile( const unsigned int& x, const unsigned int& y, const bool& avoid_tail=true );
+
+ //! Sets the new direction (of the head)
+ void setDirection( const Direction new_direction );
+
+ //! Returns the current direction (of the head)
+ const Direction& direction();
+
+ //! Updates the position and direction of the entire snake
+ void update( QGraphicsScene* field_scene=nullptr, const bool& dry=false, const bool& is_borning=false );
+
+ // Schedules to grow the snake on the next update
+ void willGrow();
+
+ // [AI] Chooses a new direction for the snake
+ void move( Snake& adv_snake, const unsigned int& food_x, const unsigned int& food_y );
+
+
+private:
+
+ QPixmap img_snakeHead = QPixmap(":/games/games/snake/head.png");
+ QPixmap img_snakeTail = QPixmap(":/games/games/snake/tail.png");
+ QPixmap img_snakeBody = QPixmap(":/games/games/snake/body_s.png");
+ QPixmap img_snakeCurve = QPixmap(":/games/games/snake/body_c.png");
+
+ QPixmap img_snakeHead_ = QPixmap(":/games/games/snake/head_.png");
+ QPixmap img_snakeTail_ = QPixmap(":/games/games/snake/tail_.png");
+ QPixmap img_snakeBody_ = QPixmap(":/games/games/snake/body_s_.png");
+ QPixmap img_snakeCurve_ = QPixmap(":/games/games/snake/body_c_.png");
+
+ Direction head_direction;
+
+ bool will_grow;
+
+ //! Increases the length of the body of the snake of 1 part
+ void grow( const bool& is_borning );
+
+ bool adversary;
+
+ //// ADVERSARY ////
+
+ const unsigned int aggressiveness = 10 - (rand()%9);
+
+ enum Entity {
+ N, // none
+ S, // self
+ A, // adversary
+ F, // food
+ };
+
+ struct Tile {
+ Entity entity;
+ unsigned int s_index;
+ };
+
+ std::vector> field_map;
+
+ // [AI] Updates the map of the field
+ void updateFieldMap( Snake& adv_snake, const unsigned int& food_x, const unsigned int& food_y );
+
+ // [AI] As inTile(), but works on the field_map and only checks the adersary
+ const bool inTileAdv( const unsigned int& x, const unsigned int& y );
+
+ // [AI] Checks whether is there a snake in the tile, without counting as much trailing BodyParts as the number of steps
+ const bool inTileMinusSteps( const unsigned int& x, const unsigned int& y, const unsigned int& steps );
+
+ // [AI] Checks which of the surrounding positions are blocked
+ const std::vector checkAround( const Direction& direction, const unsigned int& x, const unsigned int& y );
+
+ // [AI] Checks if a direction is a closed path and should be avoided
+ const unsigned int isDeadHole( const unsigned int& start_x, const unsigned int& start_y, Direction start_direction );
+
+ // [AI] Collects data about the possible movements
+ void collectData( std::vector& data, Direction& direction, Snake& adv_snake, const unsigned int& food_x, const unsigned int& food_y );
+
+ // [AI] Processes the collected data to predict the best movement
+ const Direction predictDirection( std::vector>& data, std::vector weights, std::vector classes );
+};
+
+#endif // SNAKE_H
diff --git a/logdoctor/games/snake/snake.ui b/logdoctor/games/snake/snake.ui
new file mode 100644
index 00000000..d456f1ad
--- /dev/null
+++ b/logdoctor/games/snake/snake.ui
@@ -0,0 +1,321 @@
+
+
+ SnakeGame
+
+
+
+ 0
+ 0
+ 594
+ 666
+
+
+
+
+ 594
+ 666
+
+
+
+ Qt::StrongFocus
+
+
+ LogDoctor - Snake
+
+
+
+ :/logo/logo/logdoctor.svg
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 529
+ 14
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 13
+ 590
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 13
+ 590
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 529
+ 14
+
+
+
+
+ -
+
+
+
+ 564
+ 634
+
+
+
+
+ 564
+ 634
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+ 0
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 253
+ 234
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 256
+ 128
+
+
+
+
+ 16777215
+ 128
+
+
+
+
+ 64
+
+
+
+ PLAY
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 253
+ 234
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 135
+ 125
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 135
+ 125
+
+
+
+
+ -
+
+
+
+ 11
+
+
+ -
+
+ Classic
+
+
+ -
+
+ Hunt
+
+
+ -
+
+ Battle
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 544
+ 64
+
+
+
+
+ 544
+ 64
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 8
+
+
+ 6
+
+
+ 8
+
+
+ 6
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 1
+
+
+ 0
+
+
+
+
+
+
+ -
+
+
+
+ 544
+ 544
+
+
+
+
+ 544
+ 544
+
+
+
+ Qt::NoFocus
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/logdoctor/main.cpp b/logdoctor/main.cpp
new file mode 100644
index 00000000..35fb76a9
--- /dev/null
+++ b/logdoctor/main.cpp
@@ -0,0 +1,15 @@
+
+#include "mainwindow.h"
+
+#include
+
+
+int main(int argc, char *argv[])
+{
+ QApplication::setAttribute( Qt::AA_EnableHighDpiScaling );
+ QApplication a(argc, argv);
+
+ MainWindow w;
+ w.show();
+ return a.exec();
+}
diff --git a/logdoctor/mainwindow.cpp b/logdoctor/mainwindow.cpp
new file mode 100644
index 00000000..9ce1a721
--- /dev/null
+++ b/logdoctor/mainwindow.cpp
@@ -0,0 +1,5252 @@
+
+#include "mainwindow.h"
+#include "./ui_mainwindow.h"
+
+#include "utilities/checks.h"
+#include "utilities/colors.h"
+#include "utilities/gzip.h"
+#include "utilities/io.h"
+#include "utilities/rtf.h"
+#include "utilities/stylesheets.h"
+
+#include "modules/dialogs.h"
+#include "modules/exceptions.h"
+#include "modules/shared.h"
+
+#include
+
+#include
+#include
+
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+ , ui(new Ui::MainWindow)
+{
+ //////////////////
+ //// GRAPHICS ////
+ this->ui->setupUi(this);
+
+ // initialize the color-schemes map
+ this->TB_COLOR_SCHEMES = ColorSec::getColorSchemes();
+ // initialize the colors map
+ this->COLORS = ColorSec::getColors();
+
+ // load the main font
+ this->main_font_family = QFontDatabase::applicationFontFamilies(
+ QFontDatabase::addApplicationFont(":/fonts/Metropolis")).at(0);
+ // load the alternative font
+ this->alternative_font_family = QFontDatabase::applicationFontFamilies(
+ QFontDatabase::addApplicationFont(":/fonts/Hack")).at(0);
+ // load the script font
+ this->script_font_family = QFontDatabase::applicationFontFamilies(
+ QFontDatabase::addApplicationFont(":/fonts/3270")).at(0);
+ // initialize the fonts map
+ this->FONTS.emplace( "main", QFont(
+ this->main_font_family,
+ this->font_size ) );
+ this->FONTS.emplace( "main_italic", QFont(
+ this->main_font_family,
+ this->font_size,
+ -1, true ) );
+ this->FONTS.emplace( "main_bold", QFont(
+ this->main_font_family,
+ this->font_size,
+ 1 ) );
+ this->FONTS.emplace( "main_big", QFont(
+ this->main_font_family,
+ this->font_size_big ) );
+ this->FONTS.emplace( "main_small", QFont(
+ this->main_font_family,
+ this->font_size_small ) );
+ this->FONTS.emplace( "alternative", QFont(
+ this->alternative_font_family,
+ this->font_size ) );
+ this->FONTS.emplace( "script", QFont(
+ this->script_font_family,
+ this->font_size ) );
+
+ // parent fonts
+ this->ui->mainwidget->setFont( this->FONTS.at( "main_big" ) );
+
+ // TextBrowser for the LogFiles
+ this->TB.setColorScheme( 1, this->TB_COLOR_SCHEMES.at( 1 ) );
+ this->TB.setFont( this->FONTS.at( "main" ) );
+ this->ui->textLogFiles->setFont( this->TB.getFont() );
+
+ // adjust LogsList headers width
+ this->ui->listLogFiles->header()->resizeSection(0,200);
+ this->ui->listLogFiles->header()->resizeSection(1,100);
+
+ // blocknote
+ this->crapnote->setFont( this->FONTS.at( "main" ) );
+
+
+ //////////////
+ //// MENU ////
+ // languages
+ connect( this->ui->actionEnglish, &QAction::triggered, this, &MainWindow::menu_actionEnglish_triggered );
+ connect( this->ui->actionEspanol, &QAction::triggered, this, &MainWindow::menu_actionEspanol_triggered );
+ connect( this->ui->actionFrancais, &QAction::triggered, this, &MainWindow::menu_actionFrancais_triggered );
+ connect( this->ui->actionItaliano, &QAction::triggered, this, &MainWindow::menu_actionItaliano_triggered );
+ // tools
+ connect( this->ui->actionBlockNote, &QAction::triggered, this, &MainWindow::menu_actionBlockNote_triggered );
+ // utilities
+ connect( this->ui->actionInfos, &QAction::triggered, this, &MainWindow::menu_actionInfos_triggered );
+ connect( this->ui->actionCheckUpdates, &QAction::triggered, this, &MainWindow::menu_actionCheckUpdates_triggered );
+ // games
+ connect( this->ui->actionCrissCross, &QAction::triggered, this, &MainWindow::menu_actionCrissCross_triggered );
+ connect( this->ui->actionSnake, &QAction::triggered, this, &MainWindow::menu_actionSnake_triggered );
+
+
+ /////////////////
+ //// CONFIGS ////
+ this->defineOSspec();
+ this->readConfigs();
+
+
+ ///////////////////
+ //// POLISHING ////
+ // default tabs
+ this->switchMainTab( 0 );
+ this->switchStatsTab( 0 );
+
+ // language menu
+ if ( this->language != "en" ) {
+ this->ui->actionEnglish->setChecked( false );
+ if ( language == "es" ) {
+ this->ui->actionEspanol->setChecked( true );
+ } else if ( language == "fr" ) {
+ this->ui->actionFrancais->setChecked( true );
+ } else if ( language == "it" ) {
+ this->ui->actionItaliano->setChecked( true );
+ }
+ }
+
+ // set the default WS as the current one
+ switch ( this->default_ws ) {
+ case 11:
+ this->ui->button_LogFiles_Apache->setFlat( false );
+ this->ui->radio_ConfDefaults_Apache->setChecked( true );
+ break;
+ case 12:
+ this->ui->button_LogFiles_Nginx->setFlat( false );
+ this->ui->radio_ConfDefaults_Nginx->setChecked( true );
+ break;
+ case 13:
+ this->ui->button_LogFiles_Iis->setFlat( false );
+ this->ui->radio_ConfDefaults_Iis->setChecked( true );
+ break;
+ default:
+ // shouldn't be here
+ throw WebServerException( "Unexpected WebServer ID: "+std::to_string( this->default_ws ) );
+ }
+ this->craplog.setCurrentWSID( this->default_ws );
+
+
+ // make the Configs initialize
+ // window
+ this->ui->checkBox_ConfWindow_Geometry->setChecked( this->remember_window );
+ this->ui->box_ConfWindow_Theme->setCurrentIndex( this->window_theme_id );
+ this->ui->box_ConfWindow_Icons->setCurrentIndex( this->icons_theme_id );
+ // dialogs
+ this->ui->slider_ConfDialogs_General->setValue( this->dialogs_level );
+ this->ui->slider_ConfDialogs_Logs->setValue( this->craplog.getDialogsLevel() );
+ this->ui->slider_ConfDialogs_Stats->setValue( this->crapview.getDialogsLevel() );
+ // text browser
+ this->ui->box_ConfTextBrowser_Font->setCurrentText( this->TB.getFontFamily() );
+ this->ui->checkBox_ConfTextBrowser_WideLines->setChecked( this->TB.getWideLinesUsage() );
+ this->ui->box_ConfTextBrowser_ColorScheme->setCurrentIndex( this->TB.getColorSchemeID() );
+ this->refreshTextBrowserPreview();
+ // charts
+ this->ui->box_ConfCharts_Theme->setCurrentIndex( this->charts_theme_id );
+ this->refreshChartsPreview();
+ // databases
+ this->ui->inLine_ConfDatabases_Data_Path->setText( QString::fromStdString( this->db_data_path ) );
+ this->ui->button_ConfDatabases_Data_Save->setEnabled( false );
+ this->ui->inLine_ConfDatabases_Hashes_Path->setText( QString::fromStdString( this->db_hashes_path ) );
+ this->ui->button_ConfDatabases_Hashes_Save->setEnabled( false );
+ this->ui->spinBox_ConfDatabases_NumBackups->setValue( this->db_backups_number );
+ this->ui->checkBox_ConfDatabases_DoBackup->setChecked( this->db_do_backup );
+ // logs control
+ this->ui->checkBox_ConfControl_Usage->setChecked( this->hide_used_files );
+ this->ui->spinBox_ConfControl_Size->setValue( this->craplog.getWarningSize() / 1'048'576 );
+ if ( this->craplog.getWarningSize() > 0 ) {
+ this->ui->checkBox_ConfControl_Size->setChecked( true );
+ } else {
+ this->ui->checkBox_ConfControl_Size->setChecked( false );
+ }
+ // apache paths
+ this->ui->inLine_ConfApache_Path_String->setText( QString::fromStdString(this->craplog.getLogsPath( this->APACHE_ID )) );
+ this->ui->button_ConfApache_Path_Save->setEnabled( false );
+ // apache formats
+ this->ui->inLine_ConfApache_Format_String->setText( QString::fromStdString( this->craplog.getLogsFormatString( this->APACHE_ID ) ) );
+ this->ui->button_ConfApache_Format_Save->setEnabled( false );
+ // apache warnlists
+ this->on_box_ConfApache_Warnlist_Field_currentTextChanged( this->ui->box_ConfApache_Warnlist_Field->currentText() );
+ // apache blacklists
+ this->on_box_ConfApache_Blacklist_Field_currentTextChanged( this->ui->box_ConfApache_Blacklist_Field->currentText() );
+ // nginx paths
+ this->ui->inLine_ConfNginx_Path_String->setText( QString::fromStdString(this->craplog.getLogsPath( this->NGINX_ID )) );
+ this->ui->button_ConfNginx_Path_Save->setEnabled( false );
+ // nginx formats
+ this->ui->inLine_ConfNginx_Format_String->setText( QString::fromStdString( this->craplog.getLogsFormatString( this->NGINX_ID ) ) );
+ this->ui->button_ConfNginx_Format_Save->setEnabled( false );
+ // nginx warnlists
+ this->on_box_ConfNginx_Warnlist_Field_currentTextChanged( this->ui->box_ConfNginx_Warnlist_Field->currentText() );
+ // nginx blacklists
+ this->on_box_ConfNginx_Blacklist_Field_currentTextChanged( this->ui->box_ConfNginx_Blacklist_Field->currentText() );
+ // iis paths
+ this->ui->inLine_ConfIis_Path_String->setText( QString::fromStdString(this->craplog.getLogsPath( this->IIS_ID )) );
+ this->ui->button_ConfIis_Path_Save->setEnabled( false );
+ // iis formats
+ this->ui->inLine_ConfIis_Format_String->setText( QString::fromStdString( this->craplog.getLogsFormatString( this->IIS_ID ) ) );
+ this->ui->button_ConfIis_Format_Save->setEnabled( false );
+ // iis warnlists
+ this->on_box_ConfIis_Warnlist_Field_currentTextChanged( this->ui->box_ConfIis_Warnlist_Field->currentText() );
+ // iis blacklists
+ this->on_box_ConfIis_Blacklist_Field_currentTextChanged( this->ui->box_ConfIis_Blacklist_Field->currentText() );
+
+
+ // blocknote's font and colors
+ this->crapnote->setTextFont( this->TB.getFont() );
+ this->crapnote->setColorScheme( this->TB.getColorSchemeID() );
+
+ // text browser's default message
+ {
+ QString rich_text;
+ RichText::richLogsDefault( rich_text );
+ this->ui->textLogFiles->setText( rich_text );
+ this->ui->textLogFiles->setAlignment( Qt::AlignHCenter );
+ rich_text.clear();
+ }
+
+
+ ///////////////////
+ //// INTERFACE ////
+ this->updateUiLanguage();
+ this->updateUiTheme();
+
+
+ ///////////////
+ //// START ////
+ // get a fresh list of LogFiles
+ this->waiter_timer = new QTimer(this);
+ connect(this->waiter_timer, SIGNAL(timeout()), this, SLOT(wait_ActiveWindow()));
+ this->waiter_timer->start(250);
+}
+
+MainWindow::~MainWindow()
+{
+ delete this->ui;
+ delete this->waiter_timer;
+ delete this->craplog_timer;
+ delete this->crapview_timer;
+ delete this->craphelp;
+ delete this->crapnote;
+ delete this->crapinfo;
+ delete this->crapup;
+ delete this->crisscross;
+ delete this->snake;
+}
+
+void MainWindow::closeEvent( QCloseEvent *event )
+{
+ // save actual configurations
+ this->writeConfigs();
+ // backup the database
+ if ( this->db_do_backup && this->db_edited ) {
+ this->backupDatabase();
+ }
+ // save splitters sizes => this->ui->splitter_StatsCount->sizes();
+}
+
+
+
+////////////////////////
+//// CONFIGURATIONS ////
+////////////////////////
+// os definition
+void MainWindow::defineOSspec()
+{
+ switch ( this->OS ) {
+ case 1:
+ // unix-like
+ /*this->configs_path = home_path + "/.config/LogDoctor/logdoctor.conf";
+ this->logdoc_path = home_path + "/.local/share/LogDoctor";*/
+ this->db_data_path = this->home_path + "/.local/share/LogDoctor";
+ this->db_hashes_path = this->home_path + "/.local/share/LogDoctor";
+ break;
+
+ case 2:
+ // windows
+ /*this->configs_path = home_path + "/AppData/Local/LogDoctor/logdoctor.conf";
+ this->logdoc_path = home_path + "/AppData/Local/LogDoctor";*/
+ this->db_data_path = this->logdoc_path;
+ this->db_hashes_path = this->logdoc_path;
+ break;
+
+ case 3:
+ // darwin-based
+ /*this->configs_path = home_path + "/Lybrary/Preferences/LogDoctor/logdoctor.conf";
+ this->logdoc_path = home_path + "/Lybrary/Application Support/LogDoctor";*/
+ this->db_data_path = this->logdoc_path;
+ this->db_hashes_path = this->logdoc_path;
+ break;
+
+ default:
+ // shouldn't be here
+ throw GenericException( "Unexpected OS ID: "+std::to_string( this->OS ), true );
+ }
+}
+
+void MainWindow::readConfigs()
+{
+ bool proceed = true;
+ std::error_code err;
+ QString err_msg = "";
+ // check the file
+ if ( IOutils::exists( this->configs_path ) ) {
+ if ( IOutils::checkFile( this->configs_path ) ) {
+ if ( ! IOutils::checkFile( this->configs_path, true ) ) {
+ // file not readable, try to assign permissions
+ std::filesystem::permissions( this->configs_path,
+ std::filesystem::perms::owner_read,
+ std::filesystem::perm_options::add,
+ err );
+ if ( err.value() ) {
+ proceed = false;
+ QString file = "";
+ if ( this->dialogs_level > 0 ) {
+ file = QString::fromStdString( this->configs_path );
+ err_msg = QString::fromStdString( err.message() );
+ }
+ DialogSec::errConfFileNotReadable( file, err_msg );
+ }
+ }
+ } else {
+ // the given path doesn't point to a file
+ proceed = DialogSec::choiceFileNotFile( QString::fromStdString( this->configs_path ) );
+ if ( proceed ) {
+ proceed = IOutils::renameAsCopy( this->configs_path, err );
+ if ( ! proceed ) {
+ QString path = "";
+ if ( this->dialogs_level > 0 ) {
+ path = QString::fromStdString( this->configs_path );
+ if ( err.value() ) {
+ err_msg = QString::fromStdString( err.message() );
+ }
+ }
+ DialogSec::errRenaming( QString::fromStdString( this->configs_path ), err_msg );
+ }
+ }
+ }
+ } else {
+ // configuration file not found
+ proceed = false;
+ QString file = "";
+ if ( this->dialogs_level == 2 ) {
+ file = QString::fromStdString( this->configs_path );
+ }
+ DialogSec::warnConfFileNotFound( file );
+ }
+
+ if ( proceed ) {
+ QString err_msg="", aux_err_msg;
+ std::vector aux, configs;
+ try {
+ // reset the lists when a config file is found
+ for ( int w=this->APACHE_ID; w<=this->IIS_ID; w++ ) {
+ for ( const int& f : std::vector({11,12,20,21}) ) {
+ this->craplog.setWarnlist( w, f, {} );
+ }
+ this->craplog.setBlacklist( w, 20, {} );
+ }
+ std::string content;
+ IOutils::readFile( this->configs_path, content );
+ StringOps::splitrip( configs, content );
+ for ( const std::string& line : configs ) {
+ if ( StringOps::startsWith( line, "[") ) {
+ // section descriptor
+ continue;
+ }
+ aux.clear();
+ StringOps::splitrip( aux, line, "=" );
+ if ( aux.size() < 2 ) {
+ // nothing to do
+ continue;
+ }
+ // if here, a value is present
+ const std::string& var = aux.at( 0 ),
+ val = aux.at( 1 );
+
+ if ( val.size() == 0 ) {
+ // nothing to do, no value stored
+ continue;
+ }
+
+ if ( var == "Language" ) {
+ if ( val.size() > 2 ) {
+ // not a valid locale, keep the default
+ DialogSec::errLangLocaleInvalid( QString::fromStdString( val ) );
+ } else {
+ if ( val == "en" || val == "es" || val == "fr" || val == "it" ) {
+ this->language = val;
+ } else {
+ DialogSec::errLangNotAccepted( QString::fromStdString( val ) );
+ }
+ }
+
+ } else if ( var == "RememberGeometry" ) {
+ this->remember_window = this->s2b.at( val );
+
+ } else if ( var == "Geometry" ) {
+ this->geometryFromString( val );
+
+ } else if ( var == "WindowTheme" ) {
+ this->window_theme_id = std::stoi( val );
+
+ } else if ( var == "ChartsTheme" ) {
+ this->charts_theme_id = std::stoi( val );
+
+ } else if ( var == "IconsTheme" ) {
+ this->icons_theme_id = std::stoi( val );
+
+ } else if ( var == "MainDialogLevel" ) {
+ this->dialogs_level = std::stoi( val );
+
+ } else if ( var == "DefaultWebServer" ) {
+ this->default_ws = std::stoi( val );
+
+ } else if ( var == "DatabaseDataPath" ) {
+ this->db_data_path = this->resolvePath( val );
+
+ } else if ( var == "DatabaseHashesPath" ) {
+ this->db_hashes_path = this->resolvePath( val );
+
+ } else if ( var == "DatabaseDoBackup" ) {
+ this->db_do_backup = this->s2b.at( val );
+
+ } else if ( var == "DatabaseBackupsNumber" ) {
+ this->db_backups_number = std::stoi( val );
+
+ } else if ( var == "Font" ) {
+ this->on_box_ConfTextBrowser_Font_currentIndexChanged( std::stoi( val ) );
+
+ } else if ( var == "WideLines" ) {
+ this->TB.setWideLinesUsage( this->s2b.at( val ) );
+
+ } else if ( var == "ColorScheme" ) {
+ this->on_box_ConfTextBrowser_ColorScheme_currentIndexChanged( std::stoi( val ) );
+
+ } else if ( var == "CraplogDialogLevel" ) {
+ this->craplog.setDialogsLevel( std::stoi( val ) );
+
+ } else if ( var == "HideUsedFiles" ) {
+ hide_used_files = this->s2b.at( val );
+
+ } else if ( var == "WarningSize" ) {
+ this->craplog.setWarningSize( std::stoi( val ) );
+
+ } else if ( var == "ApacheLogsPath" ) {
+ this->craplog.setLogsPath( this->APACHE_ID, this->resolvePath( val ) );
+
+ } else if ( var == "ApacheLogsFormat" ) {
+ if ( ! this->craplog.setApacheLogFormat( val ) ) {
+ throw("");
+ }
+
+ } else if ( var == "ApacheWarnlistMethod" ) {
+ aux_err_msg = QString("Apache -> %1 (%2)")
+ .arg( TR::tr(FIELDS__METHOD.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->APACHE_ID, 11, this->string2list( val ) );
+
+ } else if ( var == "ApacheWarnlistMethodUsed" ) {
+ this->craplog.setWarnlistUsed( this->APACHE_ID, 11, this->s2b.at( val ) );
+
+ } else if ( var == "ApacheWarnlistURI" ) {
+ aux_err_msg = QString("Apache -> %1 (%2)")
+ .arg( TR::tr(FIELDS__URI.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->APACHE_ID, 12, this->string2list( val ) );
+
+ } else if ( var == "ApacheWarnlistURIUsed" ) {
+ this->craplog.setWarnlistUsed( this->APACHE_ID, 12, this->s2b.at( val ) );
+
+ } else if ( var == "ApacheWarnlistClient" ) {
+ aux_err_msg = QString("Apache -> %1 (%2)")
+ .arg( TR::tr(FIELDS__CLIENT.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->APACHE_ID, 20, this->string2list( val ) );
+
+ } else if ( var == "ApacheWarnlistClientUsed" ) {
+ this->craplog.setWarnlistUsed( this->APACHE_ID, 20, this->s2b.at( val ) );
+
+ } else if ( var == "ApacheWarnlistUserAgent" ) {
+ aux_err_msg = QString("Apache -> %1 (%2)")
+ .arg( TR::tr(FIELDS__USER_AGENT.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->APACHE_ID, 21, this->string2list( val, true ) );
+
+ } else if ( var == "ApacheWarnlistUserAgentUsed" ) {
+ this->craplog.setWarnlistUsed( this->APACHE_ID, 21, this->s2b.at( val ) );
+
+ } else if ( var == "ApacheBlacklistClient" ) {
+ aux_err_msg = QString("Apache -> %1 (%2)")
+ .arg( TR::tr(FIELDS__CLIENT.c_str()), MainWindow::tr("blacklist") );
+ this->craplog.setBlacklist( this->APACHE_ID, 20, this->string2list( val ) );
+
+ } else if ( var == "ApacheBlacklistClientUsed" ) {
+ this->craplog.setBlacklistUsed( this->APACHE_ID, 20, this->s2b.at( val ) );
+
+ } else if ( var == "NginxLogsPath" ) {
+ this->craplog.setLogsPath( this->NGINX_ID, this->resolvePath( val ) );
+
+ } else if ( var == "NginxLogsFormat" ) {
+ if ( ! this->craplog.setNginxLogFormat( val ) ) {
+ throw("");
+ }
+
+ } else if ( var == "NginxWarnlistMethod" ) {
+ aux_err_msg = QString("Nginx -> %1 (%2)")
+ .arg( TR::tr(FIELDS__METHOD.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->NGINX_ID, 11, this->string2list( val ) );
+
+ } else if ( var == "NginxWarnlistMethodUsed" ) {
+ this->craplog.setWarnlistUsed( this->NGINX_ID, 11, this->s2b.at( val ) );
+
+ } else if ( var == "NginxWarnlistURI" ) {
+ aux_err_msg = QString("Nginx -> %1 (%2)")
+ .arg( TR::tr(FIELDS__URI.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->NGINX_ID, 12, this->string2list( val ) );
+
+ } else if ( var == "NginxWarnlistURIUsed" ) {
+ this->craplog.setWarnlistUsed( this->NGINX_ID, 12, this->s2b.at( val ) );
+
+ } else if ( var == "NginxWarnlistClient" ) {
+ aux_err_msg = QString("Nginx -> %1 (%2)")
+ .arg( TR::tr(FIELDS__CLIENT.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->NGINX_ID, 20, this->string2list( val ) );
+
+ } else if ( var == "NginxWarnlistClientUsed" ) {
+ this->craplog.setWarnlistUsed( this->NGINX_ID, 20, this->s2b.at( val ) );
+
+ } else if ( var == "NginxWarnlistUserAgent" ) {
+ aux_err_msg = QString("Nginx -> %1 (%2)")
+ .arg( TR::tr(FIELDS__USER_AGENT.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->NGINX_ID, 21, this->string2list( val, true ) );
+
+ } else if ( var == "NginxWarnlistUserAgentUsed" ) {
+ this->craplog.setWarnlistUsed( this->NGINX_ID, 21, this->s2b.at( val ) );
+
+ } else if ( var == "NginxBlacklistClient" ) {
+ aux_err_msg = QString("Nginx -> %1 (%2)")
+ .arg( TR::tr(FIELDS__CLIENT.c_str()), MainWindow::tr("blacklist") );
+ this->craplog.setBlacklist( this->NGINX_ID, 20, this->string2list( val ) );
+
+ } else if ( var == "NginxBlacklistClientUsed" ) {
+ this->craplog.setBlacklistUsed( this->NGINX_ID, 20, this->s2b.at( val ) );
+
+ } else if ( var == "IisLogsPath" ) {
+ this->craplog.setLogsPath( this->IIS_ID, this->resolvePath( val ) );
+
+ } else if ( var == "IisLogsModule" ) {
+ if ( val == "1" ) {
+ this->ui->radio_ConfIis_Format_NCSA->setChecked( true );
+ } else if ( val == "2" ) {
+ this->ui->radio_ConfIis_Format_IIS->setChecked( true );
+ }
+
+ } else if ( var == "IisLogsFormat" ) {
+ int module = 0;
+ if ( this->ui->radio_ConfIis_Format_NCSA->isChecked() ) {
+ module = 1;
+ } else if ( this->ui->radio_ConfIis_Format_IIS->isChecked() ) {
+ module = 2;
+ }
+ if ( ! this->craplog.setIisLogFormat( val, module ) ) {
+ throw("");
+ }
+
+ } else if ( var == "IisWarnlistMethod" ) {
+ aux_err_msg = QString("IIS -> %1 (%2)")
+ .arg( TR::tr(FIELDS__METHOD.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->IIS_ID, 11, this->string2list( val ) );
+
+ } else if ( var == "IisWarnlistMethodUsed" ) {
+ this->craplog.setWarnlistUsed( this->IIS_ID, 11, this->s2b.at( val ) );
+
+ } else if ( var == "IisWarnlistURI" ) {
+ aux_err_msg = QString("IIS -> %1 (%2)")
+ .arg( TR::tr(FIELDS__URI.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->IIS_ID, 12, this->string2list( val ) );
+
+ } else if ( var == "IisWarnlistURIUsed" ) {
+ this->craplog.setWarnlistUsed( this->IIS_ID, 12, this->s2b.at( val ) );
+
+ } else if ( var == "IisWarnlistClient" ) {
+ aux_err_msg = QString("IIS -> %1 (%2)")
+ .arg( TR::tr(FIELDS__CLIENT.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->IIS_ID, 20, this->string2list( val ) );
+
+ } else if ( var == "IisWarnlistClientUsed" ) {
+ this->craplog.setWarnlistUsed( this->IIS_ID, 20, this->s2b.at( val ) );
+
+ } else if ( var == "IisWarnlistUserAgent" ) {
+ aux_err_msg = QString("IIS -> %1 (%2)")
+ .arg( TR::tr(FIELDS__USER_AGENT.c_str()), MainWindow::tr("warnlist") );
+ this->craplog.setWarnlist( this->IIS_ID, 21, this->string2list( val, true ) );
+
+ } else if ( var == "IisWarnlistUserAgentUsed" ) {
+ this->craplog.setWarnlistUsed( this->IIS_ID, 21, this->s2b.at( val ) );
+
+ } else if ( var == "IisBlacklistClient" ) {
+ aux_err_msg = QString("IIS -> %1 (%2)")
+ .arg( TR::tr(FIELDS__CLIENT.c_str()), MainWindow::tr("blacklist") );
+ this->craplog.setBlacklist( this->IIS_ID, 20, this->string2list( val ) );
+
+ } else if ( var == "IisBlacklistClientUsed" ) {
+ this->craplog.setBlacklistUsed( this->IIS_ID, 20, this->s2b.at( val ) );
+
+ } else if ( var == "CrapviewDialogLevel" ) {
+ this->crapview.setDialogsLevel( std::stoi( val ) );
+
+ }/* else {
+ // not valid
+ }*/
+ }
+
+ } catch ( const std::ios_base::failure& ) {
+ // failed reading
+ proceed = false;
+ err_msg = DialogSec::tr("An error occured while reading the configuration file");
+ } catch ( const LogFormatException& ) {
+ proceed = false; // message already shown
+ } catch ( const BWlistException& ) {
+ proceed = false;
+ err_msg = QString("%1:\n%2").arg(
+ DialogSec::tr("One of the lists has an invalid item"),
+ aux_err_msg );
+ } catch (...) {
+ // something failed
+ proceed = false;
+ err_msg = DialogSec::tr("An error occured while parsing configuration file's data");
+ }
+ if ( ! proceed ) {
+ DialogSec::errFailedApplyingConfigs( err_msg );
+ this->closeEvent( new QCloseEvent() );
+ }
+ }
+}
+
+void MainWindow::writeConfigs()
+{
+ std::error_code err;
+ bool proceed=true, msg_shown=false;
+ QString msg="", err_msg="";
+ // check the file first
+ if ( IOutils::exists( this->configs_path ) ) {
+ if ( IOutils::checkFile( this->configs_path ) ) {
+ if ( ! IOutils::checkFile( this->configs_path, false, true ) ) {
+ // file not writable, try to assign permissions
+ std::filesystem::permissions( this->configs_path,
+ std::filesystem::perms::owner_write,
+ std::filesystem::perm_options::add,
+ err );
+ if ( err.value() ) {
+ proceed = false;
+ QString file = "";
+ if ( this->dialogs_level > 0 ) {
+ file = QString::fromStdString( this->configs_path );
+ err_msg = QString::fromStdString( err.message() );
+ }
+ DialogSec::errConfFileNotWritable( file, err_msg );
+ msg_shown = true;
+ }
+ }
+ } else {
+ // the given path doesn't point to a file
+ proceed = DialogSec::choiceFileNotFile( QString::fromStdString( this->configs_path ) );
+ if ( proceed ) {
+ proceed = IOutils::renameAsCopy( this->configs_path, err );
+ if ( ! proceed ) {
+ QString path = "";
+ if ( this->dialogs_level > 0 ) {
+ path = QString::fromStdString( this->configs_path );
+ if ( err.value() ) {
+ err_msg = QString::fromStdString( err.message() );
+ }
+ }
+ DialogSec::errRenaming( path, err_msg );
+ msg_shown = true;
+ }
+ }
+ }
+ } else {
+ // file does not exists, check if at least the folder exists
+ const std::string base_path = this->basePath( this->configs_path );
+ if ( IOutils::exists( base_path ) ) {
+ if ( IOutils::isDir( base_path ) ) {
+ if ( ! IOutils::checkDir( base_path, false, true ) ) {
+ // directory not writable, try to assign permissions
+ std::filesystem::permissions( base_path,
+ std::filesystem::perms::owner_write,
+ std::filesystem::perm_options::add,
+ err );
+ if ( err.value() ) {
+ proceed = false;
+ QString file = "";
+ if ( this->dialogs_level > 0 ) {
+ file = QString::fromStdString( base_path );
+ err_msg = QString::fromStdString( err.message() );
+ }
+ DialogSec::errConfDirNotWritable( file, err_msg );
+ msg_shown = true;
+ }
+ }
+ } else {
+ // not a directory
+ proceed = DialogSec::choiceDirNotDir( QString::fromStdString( base_path ) );
+ if ( proceed ) {
+ proceed = IOutils::renameAsCopy( base_path, err );
+ if ( ! proceed ) {
+ QString path = "";
+ if ( this->dialogs_level > 0 ) {
+ path = QString::fromStdString( base_path );
+ err_msg = QString::fromStdString( err.message() );
+ }
+ DialogSec::errRenaming( path, err_msg );
+ msg_shown = true;
+ } else {
+ // make the new folder
+ proceed = IOutils::makeDir( base_path, err );
+ if ( ! proceed ) {
+ msg = DialogSec::tr("Failed to create the configuration file's directory");
+ if ( this->dialogs_level > 0 ) {
+ msg += ":\n"+QString::fromStdString( base_path );
+ err_msg = QString::fromStdString( err.message() );
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // the folder does not exist too
+ proceed = IOutils::makeDir( base_path, err );
+ if ( ! proceed ) {
+ msg = DialogSec::tr("Failed to create the configuration file's directory");
+ if ( this->dialogs_level > 0 ) {
+ msg += ":\n"+QString::fromStdString( base_path );
+ err_msg = QString::fromStdString( err.message() );
+ }
+ }
+ }
+ }
+ if ( !proceed && !msg_shown ) {
+ DialogSec::errConfFailedWriting( msg, err_msg );
+ }
+
+ if ( proceed ) {
+ //// USER INTERFACE ////
+ std::string configs = "";
+ configs += "\n\n[UI]";
+ configs += "\nLanguage=" + this->language;
+ configs += "\nRememberGeometry=" + this->b2s.at( this->remember_window );
+ configs += "\nGeometry=" + this->geometryToString();
+ configs += "\nWindowTheme=" + std::to_string( this->window_theme_id );
+ configs += "\nChartsTheme=" + std::to_string( this->charts_theme_id );
+ configs += "\nIconsTheme=" + std::to_string( this->icons_theme_id );
+ configs += "\nMainDialogLevel=" + std::to_string( this->dialogs_level );
+ configs += "\nDefaultWebServer=" + std::to_string( this->default_ws );
+ configs += "\nDatabaseDataPath=" + this->db_data_path;
+ configs += "\nDatabaseHashesPath=" + this->db_hashes_path;
+ configs += "\nDatabaseDoBackup=" + this->b2s.at( this->db_do_backup );
+ configs += "\nDatabaseBackupsNumber=" + std::to_string( this->db_backups_number );
+ //// TEXT BROWSER ////
+ configs += "\n\n[TextBrowser]";
+ configs += "\nFont=" + std::to_string( this->ui->box_ConfTextBrowser_Font->currentIndex() );
+ configs += "\nWideLines=" + this->b2s.at( this->TB.getWideLinesUsage() );
+ configs += "\nColorScheme=" + std::to_string( this->TB.getColorSchemeID() );
+ //// CRAPLOG ////
+ configs += "\n\n[Craplog]";
+ configs += "\nCraplogDialogLevel=" + std::to_string( this->craplog.getDialogsLevel() );
+ configs += "\nHideUsedFiles=" + this->b2s.at( this->hide_used_files );
+ configs += "\nWarningSize=" + std::to_string( this->craplog.getWarningSize() );
+ //// APACHE2 ////
+ configs += "\n\n[Apache2]";
+ configs += "\nApacheLogsPath=" + this->craplog.getLogsPath( this->APACHE_ID );
+ configs += "\nApacheLogsFormat=" + this->craplog.getLogsFormatString( this->APACHE_ID );
+ configs += "\nApacheWarnlistMethod=" + this->list2string( this->craplog.getWarnlist( this->APACHE_ID, 11 ) );
+ configs += "\nApacheWarnlistMethodUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->APACHE_ID, 11 ) );
+ configs += "\nApacheWarnlistURI=" + this->list2string( this->craplog.getWarnlist( this->APACHE_ID, 12 ) );
+ configs += "\nApacheWarnlistURIUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->APACHE_ID, 12 ) );
+ configs += "\nApacheWarnlistClient=" + this->list2string( this->craplog.getWarnlist( this->APACHE_ID, 20 ) );
+ configs += "\nApacheWarnlistClientUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->APACHE_ID, 20 ) );
+ configs += "\nApacheWarnlistUserAgent=" + this->list2string( this->craplog.getWarnlist( this->APACHE_ID, 21 ), true );
+ configs += "\nApacheWarnlistUserAgentUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->APACHE_ID, 21 ) );
+ configs += "\nApacheBlacklistClient=" + this->list2string( this->craplog.getBlacklist( this->APACHE_ID, 20 ) );
+ configs += "\nApacheBlacklistClientUsed=" + this->b2s.at( this->craplog.isBlacklistUsed( this->APACHE_ID, 20 ) );
+ //// NGINX ////
+ configs += "\n\n[Nginx]";
+ configs += "\nNginxLogsPath=" + this->craplog.getLogsPath( this->NGINX_ID );
+ configs += "\nNginxLogsFormat=" + this->craplog.getLogsFormatString( this->NGINX_ID );
+ configs += "\nNginxWarnlistMethod=" + this->list2string( this->craplog.getWarnlist( this->NGINX_ID, 11 ) );
+ configs += "\nNginxWarnlistMethodUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->NGINX_ID, 11 ) );
+ configs += "\nNginxWarnlistURI=" + this->list2string( this->craplog.getWarnlist( this->NGINX_ID, 12 ) );
+ configs += "\nNginxWarnlistURIUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->NGINX_ID, 12 ) );
+ configs += "\nNginxWarnlistClient=" + this->list2string( this->craplog.getWarnlist( this->NGINX_ID, 20 ) );
+ configs += "\nNginxWarnlistClientUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->NGINX_ID, 20 ) );
+ configs += "\nNginxWarnlistUserAgent=" + this->list2string( this->craplog.getWarnlist( this->NGINX_ID, 21 ), true );
+ configs += "\nNginxWarnlistUserAgentUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->NGINX_ID, 21 ) );
+ configs += "\nNginxBlacklistClient=" + this->list2string( this->craplog.getBlacklist( this->NGINX_ID, 20 ) );
+ configs += "\nNginxBlacklistClientUsed=" + this->b2s.at( this->craplog.isBlacklistUsed( this->NGINX_ID, 20 ) );
+ //// IIS ////
+ configs += "\n\n[IIS]";
+ configs += "\nIisLogsPath=" + this->craplog.getLogsPath( this->IIS_ID );
+ std::string module = "0";
+ if ( this->ui->radio_ConfIis_Format_NCSA->isChecked() ) {
+ module = "1";
+ } else if ( this->ui->radio_ConfIis_Format_IIS->isChecked() ) {
+ module = "2";
+ }
+ configs += "\nIisLogsModule=" + module;
+ configs += "\nIisLogsFormat=" + this->craplog.getLogsFormatString( this->IIS_ID );
+ configs += "\nIisWarnlistMethod=" + this->list2string( this->craplog.getWarnlist( this->IIS_ID, 11 ) );
+ configs += "\nIisWarnlistMethodUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->IIS_ID, 11 ) );
+ configs += "\nIisWarnlistURI=" + this->list2string( this->craplog.getWarnlist( this->IIS_ID, 12 ) );
+ configs += "\nIisWarnlistURIUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->IIS_ID, 12 ) );
+ configs += "\nIisWarnlistClient=" + this->list2string( this->craplog.getWarnlist( this->IIS_ID, 20 ) );
+ configs += "\nIisWarnlistClientUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->IIS_ID, 20 ) );
+ configs += "\nIisWarnlistUserAgent=" + this->list2string( this->craplog.getWarnlist( this->IIS_ID, 21 ), true );
+ configs += "\nIisWarnlistUserAgentUsed=" + this->b2s.at( this->craplog.isWarnlistUsed( this->IIS_ID, 21 ) );
+ configs += "\nIisBlacklistClient=" + this->list2string( this->craplog.getBlacklist( this->IIS_ID, 20 ) );
+ configs += "\nIisBlacklistClientUsed=" + this->b2s.at( this->craplog.isBlacklistUsed( this->IIS_ID, 20 ) );
+ //// CRAPVIEW ////
+ configs += "\n\n[Crapview]";
+ configs += "\nCrapviewDialogLevel=" + std::to_string( this->crapview.getDialogsLevel() );
+
+ // write on file
+ try {
+ IOutils::writeOnFile( this->configs_path, configs );
+
+ } catch ( const std::ios_base::failure&/* err*/ ) {
+ // failed writing
+ DialogSec::errGeneric( DialogSec::tr("An error occured while writing the configuration file") );
+ } catch (...) {
+ // something failed
+ DialogSec::errGeneric( DialogSec::tr("An error occured while preparing the configuration file's data") );
+ }
+ }
+}
+
+
+void MainWindow::backupDatabase()
+{
+ bool proceed = true;
+ std::error_code err;
+ QString err_msg = "";
+ if ( IOutils::checkFile( this->db_data_path+"/collection.db" ) ) {
+ // db exists and is a file
+ const std::string path = this->db_data_path+"/backups";
+ if ( std::filesystem::exists( path ) ) {
+ if ( !std::filesystem::is_directory( path, err ) ) {
+ // exists but it's not a directory, rename as copy and make a new one
+ proceed = DialogSec::choiceDirNotDir( QString::fromStdString( path ) );
+ if ( proceed ) {
+ proceed = IOutils::renameAsCopy( path, err );
+ if ( ! proceed ) {
+ QString p = "";
+ if ( this->dialogs_level > 0 ) {
+ p = QString::fromStdString( path );
+ if ( err.value() ) {
+ err_msg = QString::fromStdString( err.message() );
+ }
+ }
+ DialogSec::errRenaming( p, err_msg );
+ } else {
+ // sucesfully renamed, make the new one
+ proceed = IOutils::makeDir( path, err );
+ if ( ! proceed ) {
+ QString msg = DialogSec::tr("Failed to create the database backups' directory");
+ if ( this->dialogs_level > 0 ) {
+ msg += ":\n"+QString::fromStdString( path );
+ if ( err.value() ) {
+ err_msg = QString::fromStdString( err.message() );
+ }
+ }
+ DialogSec::errFailedMakeDir( msg, err_msg );
+ }
+ }
+ }
+ }
+ } else {
+ // backups directory doesn't exists, make it
+ proceed = IOutils::makeDir( path, err );
+ if ( ! proceed ) {
+ QString msg = DialogSec::tr("Failed to create the database backups' directory");
+ if ( this->dialogs_level > 0 ) {
+ msg += ":\n"+QString::fromStdString( path );
+ if ( err.value() ) {
+ err_msg = QString::fromStdString( err.message() );
+ }
+ }
+ DialogSec::errFailedMakeDir( msg, err_msg );
+ }
+ }
+
+ }/* else {
+ // db doesn't exists or is not a file
+ }*/
+
+ if ( proceed ) {
+ // copy the database to a new file
+ proceed = std::filesystem::copy_file(
+ this->db_data_path+"/collection.db",
+ this->db_data_path+"/backups/collection.db.0",
+ std::filesystem::copy_options::update_existing,
+ err );
+ if ( ! proceed ) {
+ // failed to copy
+ if ( err.value() ) {
+ err_msg = QString::fromStdString( err.message() );
+ }
+ DialogSec::errDatabaseFailedBackup( DialogSec::tr( "Failed to copy the database file" ), err_msg );
+ } else {
+ // succesfully copied, now rename the already existing copies (up to the choosen number of copies)
+ std::string path, new_path;
+ path = this->db_data_path+"/backups/collection.db."+std::to_string(this->db_backups_number);
+ if ( std::filesystem::exists( path ) ) {
+ std::ignore = std::filesystem::remove_all( path, err );
+ if ( err.value() ) {
+ err_msg = QString::fromStdString( err.message() );
+ proceed = false;
+ } else {
+ proceed = ! std::filesystem::exists( path );
+ }
+ if ( ! proceed ) {
+ DialogSec::errDatabaseFailedBackup( DialogSec::tr( "Failed to update the backups" ), err_msg );
+ }
+ }
+ if ( proceed ) {
+ // cascade rename
+ for ( int n=this->db_backups_number-1; n>=0; n-- ) {
+ path = this->db_data_path+"/backups/collection.db."+std::to_string( n );
+ if ( std::filesystem::exists( path ) ) {
+ new_path = this->db_data_path+"/backups/collection.db."+std::to_string( n+1 );
+ std::filesystem::rename( path, new_path, err );
+ if ( err.value() ) {
+ err_msg = QString::fromStdString( err.message() );
+ proceed = false;
+ } else {
+ proceed = ! std::filesystem::exists( path );
+ }
+ }
+ if ( ! proceed ) {
+ // seems it failed to rename
+ DialogSec::errDatabaseFailedBackup( DialogSec::tr( "Failed to update the backups" ), err_msg );
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+const std::string MainWindow::geometryToString()
+{
+ QRect geometry = this->geometry();
+ std::string string = "";
+ string += std::to_string( geometry.x() );
+ string += ",";
+ string += std::to_string( geometry.y() );
+ string += ",";
+ string += std::to_string( geometry.width() );
+ string += ",";
+ string += std::to_string( geometry.height() );
+ string += ",";
+ string += this->b2s.at( this->isMaximized() );
+ return string;
+}
+void MainWindow::geometryFromString( const std::string& geometry )
+{
+ std::vector aux;
+ StringOps::splitrip( aux, geometry, "," );
+ QRect new_geometry;
+ new_geometry.setRect( std::stoi(aux.at(0)), std::stoi(aux.at(1)), std::stoi(aux.at(2)), std::stoi(aux.at(3)) );
+ this->setGeometry( new_geometry );
+ if ( aux.at(4) == "true" ) {
+ this->showMaximized();
+ }
+}
+
+
+const std::string MainWindow::list2string( const std::vector& list, const bool& user_agent )
+{
+ std::string string;
+ if ( user_agent ) {
+ for ( const std::string& str : list ) {
+ string += StringOps::replace( str, " ", "%@#" ) + " ";
+ }
+ } else {
+ for ( const std::string& str : list ) {
+ string += str + " ";
+ }
+ }
+ return string;
+}
+const std::vector MainWindow::string2list( const std::string& string, const bool& user_agent )
+{
+ std::vector list, aux;
+ StringOps::splitrip( aux, string, " " );
+ if ( user_agent ) {
+ for ( const std::string& str : aux ) {
+ list.push_back( StringOps::replace( str, "%@#", " " ) );
+ }
+ } else {
+ for ( const std::string& str : aux ) {
+ list.push_back( str );
+ }
+ }
+ return list;
+}
+
+
+//////////////////
+//// GRAPHICS ////
+//////////////////
+void MainWindow::detectIconsTheme()
+{
+ switch ( this->window_theme_id ) {
+ case 0:
+ // system default, use window color to determine the theme
+ if ( this->palette().window().color().black() > 127 ) {
+ this->icons_theme = "light";
+ } else {
+ this->icons_theme = "dark";
+ }
+ break;
+ case 1:
+ case 3:
+ // ash / herb
+ this->icons_theme = "light";
+ break;
+ case 2:
+ case 4:
+ // candy / powder
+ this->icons_theme = "dark";
+ break;
+ default:
+ throw GenericException( "Unexpected WindowTheme ID: "+std::to_string(this->window_theme_id), true );
+ break;
+ }
+}
+
+
+void MainWindow::updateUiTheme()
+{
+ // window and fonts
+ switch ( this->window_theme_id ) {
+ case 0:
+ // window first
+ this->setStyleSheet("");
+ // icons last
+ this->updateUiIcons();
+ break;
+ case 1: case 2: case 3: case 4:
+ {
+ // icons first
+ this->updateUiIcons();
+ // window last
+ QString ss;
+ StyleSec::getStyleSheet( ss, this->icons_theme, this->window_theme_id );
+ this->setStyleSheet( ss );
+ break;
+ }
+ default:
+ // wrong
+ throw GenericException( "Unexpected WindowTheme ID: "+std::to_string(this->window_theme_id), true );
+ break;
+ }
+ // fonts
+ this->updateUiFonts();
+}
+
+void MainWindow::updateUiIcons()
+{
+ const QString old_icons_theme = this->icons_theme;
+ switch ( this->icons_theme_id ) {
+ case 0:
+ this->detectIconsTheme();
+ break;
+ case 1:
+ this->icons_theme = "light";
+ break;
+ case 2:
+ this->icons_theme = "dark";
+ break;
+ default:
+ throw GenericException( "Unexpected IconSet index: "+std::to_string(this->icons_theme_id), true );
+ break;
+ }
+
+ if ( this->icons_theme != old_icons_theme ) {
+ // main tabs
+ const int m_index = this->ui->stacked_Tabs_Pages->currentIndex();
+ this->ui->button_Tab_Log->setIcon(
+ QIcon(QString(":/icons/icons/%1/log_%2.png").arg(
+ this->icons_theme,
+ (m_index==0) ? "on" : "off" )) );
+ this->ui->button_Tab_View->setIcon(
+ QIcon(QString(":/icons/icons/%1/view_%2.png").arg(
+ this->icons_theme,
+ (m_index==1) ? "on" : "off" )) );
+ this->ui->button_Tab_Conf->setIcon(
+ QIcon(QString(":/icons/icons/%1/conf_%2.png").arg(
+ this->icons_theme,
+ (m_index==2) ? "on" : "off" )) );
+ // view logs
+ this->ui->button_LogFiles_ViewFile->setIcon(
+ QIcon(QString(":/icons/icons/%1/show_file.png").arg(this->icons_theme)) );
+ this->ui->button_LogFiles_RefreshList->setIcon(
+ QIcon(QString(":/icons/icons/%1/refresh.png").arg(this->icons_theme)) );
+ // parse logs
+ this->ui->icon_MakeStats_Size->setPixmap(
+ QPixmap(QString(":/icons/icons/%1/mk_size.png").arg(this->icons_theme)) );
+ this->ui->icon_MakeStats_Lines->setPixmap(
+ QPixmap(QString(":/icons/icons/%1/mk_lines.png").arg(this->icons_theme)) );
+ this->ui->icon_MakeStats_Time->setPixmap(
+ QPixmap(QString(":/icons/icons/%1/mk_time.png").arg(this->icons_theme)) );
+ this->ui->icon_MakeStats_Speed->setPixmap(
+ QPixmap(QString(":/icons/icons/%1/mk_speed.png").arg(this->icons_theme)) );
+ // stats
+ const int s_index = this->ui->stacked_Stats_Pages->currentIndex();
+ // stats warn
+ this->ui->button_Tab_StatsWarn->setIcon(
+ QIcon(QString(":/icons/icons/%1/warn_%2.png").arg(
+ this->icons_theme,
+ (s_index==0) ? "on" : "off" )) );
+ this->ui->button_StatsWarn_Draw->setIcon(
+ QIcon(QString(":/icons/icons/%1/draw.png").arg(this->icons_theme)) );
+ this->ui->button_StatsWarn_Update->setIcon(
+ QIcon(QString(":/icons/icons/%1/save.png").arg(this->icons_theme)) );
+ // stats speed
+ this->ui->button_Tab_StatsSpeed->setIcon(
+ QIcon(QString(":/icons/icons/%1/speed_%2.png").arg(
+ this->icons_theme,
+ (s_index==1) ? "on" : "off" )) );
+ this->ui->button_StatsSpeed_Draw->setIcon(
+ QIcon(QString(":/icons/icons/%1/draw.png").arg(this->icons_theme)) );
+ // stats count
+ this->ui->button_Tab_StatsCount->setIcon(
+ QIcon(QString(":/icons/icons/%1/count_%2.png").arg(
+ this->icons_theme,
+ (s_index==2) ? "on" : "off" )) );
+ // stats daytime
+ this->ui->button_Tab_StatsDay->setIcon(
+ QIcon(QString(":/icons/icons/%1/daytime_%2.png").arg(
+ this->icons_theme,
+ (s_index==3) ? "on" : "off" )) );
+ this->ui->button_StatsDay_Draw->setIcon(
+ QIcon(QString(":/icons/icons/%1/draw.png").arg(this->icons_theme)) );
+ // stats relational
+ this->ui->button_Tab_StatsRelat->setIcon(
+ QIcon(QString(":/icons/icons/%1/relational_%2.png").arg(
+ this->icons_theme,
+ (s_index==4) ? "on" : "off" )) );
+ this->ui->button_StatsRelat_Draw->setIcon(
+ QIcon(QString(":/icons/icons/%1/draw.png").arg(this->icons_theme)) );
+ // stats globals
+ this->ui->button_Tab_StatsGlob->setIcon(
+ QIcon(QString(":/icons/icons/%1/global_%2.png").arg(
+ this->icons_theme,
+ (s_index==5) ? "on" : "off" )) );
+ // configs
+ this->ui->button_ConfDatabases_Data_Save->setIcon(
+ QIcon(QString(":/icons/icons/%1/save.png").arg(this->icons_theme)) );
+ this->ui->button_ConfDatabases_Hashes_Save->setIcon(
+ QIcon(QString(":/icons/icons/%1/save.png").arg(this->icons_theme)) );
+ this->ui->button_ConfApache_Path_Save->setIcon(
+ QIcon(QString(":/icons/icons/%1/save.png").arg(this->icons_theme)) );
+ this->ui->button_ConfApache_Format_Save->setIcon(
+ QIcon(QString(":/icons/icons/%1/save.png").arg(this->icons_theme)) );
+ this->ui->button_ConfApache_Format_Help->setIcon(
+ QIcon(QString(":/icons/icons/%1/help.png").arg(this->icons_theme)) );
+ this->ui->button_ConfNginx_Path_Save->setIcon(
+ QIcon(QString(":/icons/icons/%1/save.png").arg(this->icons_theme)) );
+ this->ui->button_ConfNginx_Format_Save->setIcon(
+ QIcon(QString(":/icons/icons/%1/save.png").arg(this->icons_theme)) );
+ this->ui->button_ConfNginx_Format_Help->setIcon(
+ QIcon(QString(":/icons/icons/%1/help.png").arg(this->icons_theme)) );
+ this->ui->button_ConfIis_Path_Save->setIcon(
+ QIcon(QString(":/icons/icons/%1/save.png").arg(this->icons_theme)) );
+ this->ui->button_ConfIis_Format_Save->setIcon(
+ QIcon(QString(":/icons/icons/%1/save.png").arg(this->icons_theme)) );
+ this->ui->button_ConfIis_Format_Help->setIcon(
+ QIcon(QString(":/icons/icons/%1/help.png").arg(this->icons_theme)) );
+ }
+}
+
+void MainWindow::updateUiFonts()
+{
+ const QFont &small_font = this->FONTS.at( "main_small" );
+ const QFont &font = this->FONTS.at( "main" );
+ const QFont &big_font = this->FONTS.at( "main_big" );
+ QFont menu_font = this->FONTS.at( "main_small" );
+ menu_font.setPointSizeF( this->font_size_small+1.5 );
+ QFont header_font = this->FONTS.at( "main_small" );
+ header_font.setPointSizeF( this->font_size_small+2 );
+ // menu
+ this->ui->menuLanguage->setFont( menu_font );
+ this->ui->actionEnglish->setFont( menu_font );
+ this->ui->actionEspanol->setFont( menu_font );
+ this->ui->actionFrancais->setFont( menu_font );
+ this->ui->actionItaliano->setFont( menu_font );
+ this->ui->menuTools->setFont( menu_font );
+ this->ui->actionBlockNote->setFont( menu_font );
+ this->ui->menuUtilities->setFont( menu_font );
+ this->ui->actionInfos->setFont( menu_font );
+ this->ui->actionCheckUpdates->setFont( menu_font );
+ this->ui->menuGames->setFont( menu_font );
+ this->ui->actionCrissCross->setFont( menu_font );
+ this->ui->actionSnake->setFont( menu_font );
+ // log files view
+ this->ui->button_LogFiles_Apache->setFont( font );
+ this->ui->button_LogFiles_Nginx->setFont( font );
+ this->ui->button_LogFiles_Iis->setFont( font );
+ this->ui->checkBox_LogFiles_CheckAll->setFont( small_font );
+ this->ui->listLogFiles->setFont( font );
+ this->ui->listLogFiles->headerItem()->setFont( 0, header_font );
+ this->ui->listLogFiles->headerItem()->setFont( 1, header_font );
+ this->ui->textLogFiles->setFont( this->TB.getFont() );
+ // log files parse
+ this->ui->label_MakeStats_Size->setFont( font );
+ this->ui->label_MakeStats_Lines->setFont( font );
+ this->ui->label_MakeStats_Time->setFont( font );
+ this->ui->label_MakeStats_Speed->setFont( font );
+ this->ui->button_MakeStats_Start->setFont( big_font );
+ // stats warn
+ this->ui->label_StatsWarn_WebServer->setFont( font );
+ this->ui->box_StatsWarn_WebServer->setFont( font );
+ this->ui->label_StatsWarn_Year->setFont( font );
+ this->ui->box_StatsWarn_Year->setFont( font );
+ this->ui->label_StatsWarn_Month->setFont( font );
+ this->ui->box_StatsWarn_Month->setFont( font );
+ this->ui->label_StatsWarn_Day->setFont( font );
+ this->ui->box_StatsWarn_Day->setFont( font );
+ this->ui->checkBox_StatsWarn_Hour->setFont( font );
+ this->ui->box_StatsWarn_Hour->setFont( font );
+ this->ui->table_StatsWarn->setFont( font );
+ this->ui->table_StatsWarn->horizontalHeader()->setFont( header_font );
+ // stats speed
+ this->ui->box_StatsSpeed_WebServer->setFont( font );
+ this->ui->label_StatsSpeed_Year->setFont( font );
+ this->ui->box_StatsSpeed_Year->setFont( font );
+ this->ui->label_StatsSpeed_Month->setFont( font );
+ this->ui->box_StatsSpeed_Month->setFont( font );
+ this->ui->label_StatsSpeed_Day->setFont( font );
+ this->ui->box_StatsSpeed_Day->setFont( font );
+ this->ui->label_StatsSpeed_Filters->setFont( big_font );
+ this->ui->label_StatsSpeed_Protocol->setFont( font );
+ this->ui->inLine_StatsSpeed_Protocol->setFont( font );
+ this->ui->label_StatsSpeed_Method->setFont( font );
+ this->ui->inLine_StatsSpeed_Method->setFont( font );
+ this->ui->label_StatsSpeed_Uri->setFont( font );
+ this->ui->inLine_StatsSpeed_Uri->setFont( font );
+ this->ui->label_StatsSpeed_Query->setFont( font );
+ this->ui->inLine_StatsSpeed_Query->setFont( font );
+ this->ui->label_StatsSpeed_Response->setFont( font );
+ this->ui->inLine_StatsSpeed_Response->setFont( font );
+ this->ui->table_StatsSpeed->setFont( font );
+ this->ui->table_StatsSpeed->horizontalHeader()->setFont( header_font );
+ // stats count
+ this->ui->box_StatsCount_WebServer->setFont( font );
+ this->ui->label_StatsCount_Year->setFont( font );
+ this->ui->box_StatsCount_Year->setFont( font );
+ this->ui->label_StatsCount_Month->setFont( font );
+ this->ui->box_StatsCount_Month->setFont( font );
+ this->ui->label_StatsCount_Day->setFont( font );
+ this->ui->box_StatsCount_Day->setFont( font );
+ this->ui->button_StatsCount_Protocol->setFont( font );
+ this->ui->button_StatsCount_Method->setFont( font );
+ this->ui->button_StatsCount_Uri->setFont( font );
+ this->ui->button_StatsCount_Query->setFont( font );
+ this->ui->button_StatsCount_Response->setFont( font );
+ this->ui->button_StatsCount_Referrer->setFont( font );
+ this->ui->button_StatsCount_Cookie->setFont( font );
+ this->ui->button_StatsCount_UserAgent->setFont( font );
+ this->ui->button_StatsCount_Client->setFont( font );
+ this->ui->table_StatsCount->setFont( font );
+ this->ui->table_StatsCount->horizontalHeader()->setFont( header_font );
+ // stats day
+ this->ui->box_StatsDay_WebServer->setFont( font );
+ this->ui->label_StatsDay_From->setFont( font );
+ this->ui->checkBox_StatsDay_Period->setFont( font );
+ this->ui->label_StatsDay_Year->setFont( font );
+ this->ui->box_StatsDay_FromYear->setFont( font );
+ this->ui->box_StatsDay_ToYear->setFont( font );
+ this->ui->label_StatsDay_Month->setFont( font );
+ this->ui->box_StatsDay_FromMonth->setFont( font );
+ this->ui->box_StatsDay_ToMonth->setFont( font );
+ this->ui->label_StatsDay_Day->setFont( font );
+ this->ui->box_StatsDay_FromDay->setFont( font );
+ this->ui->box_StatsDay_ToDay->setFont( font );
+ this->ui->label_StatsDay_LogsField->setFont( font );
+ this->ui->box_StatsDay_LogsField->setFont( font );
+ this->ui->label_StatsDay_Filter->setFont( font );
+ this->ui->inLine_StatsDay_Filter->setFont( font );
+ // stats relat
+ this->ui->box_StatsRelat_WebServer->setFont( font );
+ this->ui->label_StatsRelat_From->setFont( font );
+ this->ui->label_StatsRelat_To->setFont( font );
+ this->ui->box_StatsRelat_FromYear->setFont( font );
+ this->ui->box_StatsRelat_ToYear->setFont( font );
+ this->ui->box_StatsRelat_FromMonth->setFont( font );
+ this->ui->box_StatsRelat_ToMonth->setFont( font );
+ this->ui->box_StatsRelat_FromDay->setFont( font );
+ this->ui->box_StatsRelat_ToDay->setFont( font );
+ this->ui->label_StatsRelat_LogsField_1->setFont( font );
+ this->ui->box_StatsRelat_LogsField_1->setFont( font );
+ this->ui->label_StatsRelat_Filter_1->setFont( font );
+ this->ui->inLine_StatsRelat_Filter_1->setFont( font );
+ this->ui->label_StatsRelat_LogsField_2->setFont( font );
+ this->ui->box_StatsRelat_LogsField_2->setFont( font );
+ this->ui->label_StatsRelat_Filter_2->setFont( font );
+ this->ui->inLine_StatsRelat_Filter_2->setFont( font );
+ // stats glob
+ this->ui->button_StatsGlob_Apache->setFont( font );
+ this->ui->button_StatsGlob_Nginx->setFont( font );
+ this->ui->button_StatsGlob_Iis->setFont( font );
+ this->ui->label_StatsGlob_Recur->setFont( big_font );
+ this->ui->label_StatsGlob_Recur_Protocol->setFont( font );
+ this->ui->label_StatsGlob_Recur_Protocol_String->setFont( font );
+ this->ui->label_StatsGlob_Recur_Protocol_Count->setFont( font );
+ this->ui->label_StatsGlob_Recur_Method->setFont( font );
+ this->ui->label_StatsGlob_Recur_Method_String->setFont( font );
+ this->ui->label_StatsGlob_Recur_Method_Count->setFont( font );
+ this->ui->label_StatsGlob_Recur_URI->setFont( font );
+ this->ui->label_StatsGlob_Recur_URI_String->setFont( font );
+ this->ui->label_StatsGlob_Recur_URI_Count->setFont( font );
+ this->ui->label_StatsGlob_Recur_UserAgent->setFont( font );
+ this->ui->label_StatsGlob_Recur_UserAgent_String->setFont( font );
+ this->ui->label_StatsGlob_Recur_UserAgent_Count->setFont( font );
+ this->ui->label_StatsGlob_Perf->setFont( big_font );
+ this->ui->label_StatsGlob_Perf_Time->setFont( font );
+ this->ui->label_StatsGlob_Perf_Time_Mean->setFont( font );
+ this->ui->label_StatsGlob_Perf_Time_Max->setFont( font );
+ this->ui->label_StatsGlob_Perf_Sent->setFont( font );
+ this->ui->label_StatsGlob_Perf_Sent_Mean->setFont( font );
+ this->ui->label_StatsGlob_Perf_Sent_Max->setFont( font );
+ this->ui->label_StatsGlob_Perf_Received->setFont( font );
+ this->ui->label_StatsGlob_Perf_Received_Mean->setFont( font );
+ this->ui->label_StatsGlob_Perf_Received_Max->setFont( font );
+ this->ui->label_StatsGlob_Traffic->setFont( big_font );
+ this->ui->label_StatsGlob_Traffic_Date->setFont( font );
+ this->ui->label_StatsGlob_Traffic_Date_String->setFont( font );
+ this->ui->label_StatsGlob_Traffic_Date_Count->setFont( font );
+ this->ui->label_StatsGlob_Traffic_Day->setFont( font );
+ this->ui->label_StatsGlob_Traffic_Day_String->setFont( font );
+ this->ui->label_StatsGlob_Traffic_Day_Count->setFont( font );
+ this->ui->label_StatsGlob_Traffic_Hour->setFont( font );
+ this->ui->label_StatsGlob_Traffic_Hour_String->setFont( font );
+ this->ui->label_StatsGlob_Traffic_Hour_Count->setFont( font );
+ this->ui->label_StatsGlob_Work->setFont( big_font );
+ this->ui->label_StatsGlob_Work_Req->setFont( font );
+ this->ui->label_StatsGlob_Work_Req_Count->setFont( font );
+ this->ui->label_StatsGlob_Work_Time->setFont( font );
+ this->ui->label_StatsGlob_Work_Time_Count->setFont( font );
+ this->ui->label_StatsGlob_Work_Sent->setFont( font );
+ this->ui->label_StatsGlob_Work_Sent_Count->setFont( font );
+ // configs tab
+ this->ui->ConfTabs->tabBar()->setFont( big_font );
+ this->ui->tabs_ConfGeneral->tabBar()->setFont( font );
+ this->ui->tabs_ConfLogs->tabBar()->setFont( font );
+ this->ui->tabs_ConfApache->tabBar()->setFont( font );
+ this->ui->tabs_ConfNginx->tabBar()->setFont( font );
+ this->ui->tabs_ConfIis->tabBar()->setFont( font );
+ // conf window
+ this->ui->label_ConfWindow_Geometry->setFont( big_font );
+ this->ui->checkBox_ConfWindow_Geometry->setFont( font );
+ this->ui->label_ConfWindow_Theme->setFont( big_font );
+ this->ui->box_ConfWindow_Theme->setFont( font );
+ this->ui->label_ConfWindow_Icons->setFont( big_font );
+ this->ui->box_ConfWindow_Icons->setFont( font );
+ // conf dialogs
+ this->ui->label_ConfDialogs_Level->setFont( big_font );
+ this->ui->label_ConfDialogs_General->setFont( font );
+ this->ui->label_ConfDialogs_Logs->setFont( font );
+ this->ui->label_ConfDialogs_Stats->setFont( font );
+ this->ui->label_ConfDialogs_Essential->setFont( small_font );
+ this->ui->label_ConfDialogs_Normal->setFont( small_font );
+ this->ui->label_ConfDialogs_Explanatory->setFont( small_font );
+ // conf text-browser
+ this->ui->label_ConfTextBrowser_Font->setFont( big_font );
+ this->ui->box_ConfTextBrowser_Font->setFont( font );
+ this->ui->label_ConfTextBrowser_Lines->setFont( big_font );
+ this->ui->checkBox_ConfTextBrowser_WideLines->setFont( font );
+ this->ui->label_ConfTextBrowser_ColorScheme->setFont( big_font );
+ this->ui->box_ConfTextBrowser_ColorScheme->setFont( font );
+ this->ui->label_ConfTextBrowser_Preview->setFont( big_font );
+ this->ui->textBrowser_ConfTextBrowser_Preview->setFont( this->TB.getFont() );
+ // conf charts
+ this->ui->label_ConfCharts_Theme->setFont( big_font );
+ this->ui->box_ConfCharts_Theme->setFont( font );
+ this->ui->label_ConfCharts_Preview->setFont( big_font );
+ // conf databases
+ this->ui->label_ConfDatabases_Paths->setFont( big_font );
+ this->ui->label_ConfDatabases_Data->setFont( font );
+ this->ui->inLine_ConfDatabases_Data_Path->setFont( font );
+ this->ui->label_ConfDatabases_Hashes->setFont( font );
+ this->ui->inLine_ConfDatabases_Hashes_Path->setFont( font );
+ this->ui->label_ConfDatabases_Backups->setFont( big_font );
+ this->ui->checkBox_ConfDatabases_DoBackup->setFont( font );
+ this->ui->spinBox_ConfDatabases_NumBackups->setFont( font );
+ // conf logs default
+ this->ui->label_ConfDefaults_WebServer->setFont( big_font );
+ this->ui->radio_ConfDefaults_Apache->setFont( font );
+ this->ui->radio_ConfDefaults_Nginx->setFont( font );
+ this->ui->radio_ConfDefaults_Iis->setFont( font );
+ // conf logs control
+ this->ui->label_ConfControl_Usage->setFont( big_font );
+ this->ui->checkBox_ConfControl_Usage->setFont( font );
+ this->ui->label_ConfControl_Size->setFont( big_font );
+ this->ui->checkBox_ConfControl_Size->setFont( font );
+ this->ui->spinBox_ConfControl_Size->setFont( font );
+ // conf apache
+ this->ui->label_ConfApache_Path_Path->setFont( font );
+ this->ui->inLine_ConfApache_Path_String->setFont( font );
+ this->ui->label_ConfApache_Format_String->setFont( font );
+ this->ui->inLine_ConfApache_Format_String->setFont( font );
+ this->ui->button_ConfApache_Format_Sample->setFont( font );
+ this->ui->preview_ConfApache_Format_Sample->setFont( this->TB.getFont() );
+ this->ui->box_ConfApache_Warnlist_Field->setFont( font );
+ this->ui->checkBox_ConfApache_Warnlist_Used->setFont( font );
+ this->ui->inLine_ConfApache_Warnlist_String->setFont( font );
+ this->ui->list_ConfApache_Warnlist_List->setFont( font );
+ this->ui->box_ConfApache_Blacklist_Field->setFont( font );
+ this->ui->checkBox_ConfApache_Blacklist_Used->setFont( font );
+ this->ui->inLine_ConfApache_Blacklist_String->setFont( font );
+ this->ui->list_ConfApache_Blacklist_List->setFont( font );
+ // conf nginx
+ this->ui->label_ConfNginx_Path_Path->setFont( font );
+ this->ui->inLine_ConfNginx_Path_String->setFont( font );
+ this->ui->label_ConfNginx_Format_String->setFont( font );
+ this->ui->inLine_ConfNginx_Format_String->setFont( font );
+ this->ui->button_ConfNginx_Format_Sample->setFont( font );
+ this->ui->preview_ConfNginx_Format_Sample->setFont( this->TB.getFont() );
+ this->ui->box_ConfNginx_Warnlist_Field->setFont( font );
+ this->ui->checkBox_ConfNginx_Warnlist_Used->setFont( font );
+ this->ui->inLine_ConfNginx_Warnlist_String->setFont( font );
+ this->ui->list_ConfNginx_Warnlist_List->setFont( font );
+ this->ui->box_ConfNginx_Blacklist_Field->setFont( font );
+ this->ui->checkBox_ConfNginx_Blacklist_Used->setFont( font );
+ this->ui->inLine_ConfNginx_Blacklist_String->setFont( font );
+ this->ui->list_ConfNginx_Blacklist_List->setFont( font );
+ // conf iis
+ this->ui->label_ConfIis_Path_Path->setFont( font );
+ this->ui->inLine_ConfIis_Path_String->setFont( font );
+ this->ui->label_ConfIis_Format_String->setFont( font );
+ this->ui->inLine_ConfIis_Format_String->setFont( font );
+ this->ui->button_ConfIis_Format_Sample->setFont( font );
+ this->ui->preview_ConfIis_Format_Sample->setFont( this->TB.getFont() );
+ this->ui->box_ConfIis_Warnlist_Field->setFont( font );
+ this->ui->checkBox_ConfIis_Warnlist_Used->setFont( font );
+ this->ui->inLine_ConfIis_Warnlist_String->setFont( font );
+ this->ui->list_ConfIis_Warnlist_List->setFont( font );
+ this->ui->box_ConfIis_Blacklist_Field->setFont( font );
+ this->ui->checkBox_ConfIis_Blacklist_Used->setFont( font );
+ this->ui->inLine_ConfIis_Blacklist_String->setFont( font );
+ this->ui->list_ConfIis_Blacklist_List->setFont( font );
+}
+
+
+
+
+//////////////////
+//// LANGUAGE ////
+//////////////////
+void MainWindow::updateUiLanguage()
+{
+ // remove the old translator
+ QCoreApplication::removeTranslator( &this->translator );
+ if ( this->translator.load( QString(":/translations/%1").arg(QString::fromStdString( this->language )) ) ) {
+ // apply the new translator
+ QCoreApplication::installTranslator( &this->translator );
+ this->ui->retranslateUi( this );
+ // stats warn table header
+ {
+ const QStringList h = {
+ this->crapview.getLogFieldString(0),
+ TR::tr( WORDS__DATE.c_str() ),
+ TR::tr( WORDS__TIME.c_str() ),
+ this->crapview.getLogFieldString(10),
+ this->crapview.getLogFieldString(11),
+ this->crapview.getLogFieldString(12),
+ this->crapview.getLogFieldString(13),
+ this->crapview.getLogFieldString(14),
+ this->crapview.getLogFieldString(18),
+ this->crapview.getLogFieldString(22),
+ this->crapview.getLogFieldString(21),
+ this->crapview.getLogFieldString(20),
+ this->crapview.getLogFieldString(17),
+ this->crapview.getLogFieldString(16),
+ this->crapview.getLogFieldString(15),
+ "rowid" };
+ this->ui->table_StatsWarn->setColumnCount( h.size() );
+ this->ui->table_StatsWarn->setHorizontalHeaderLabels( h );
+ }
+ // stats speed table header
+ {
+ const QStringList h = {
+ this->crapview.getLogFieldString(15),
+ this->crapview.getLogFieldString(12),
+ this->crapview.getLogFieldString(13),
+ this->crapview.getLogFieldString(11),
+ this->crapview.getLogFieldString(10),
+ this->crapview.getLogFieldString(14),
+ TR::tr( WORDS__TIME.c_str() ) };
+ this->ui->table_StatsSpeed->setColumnCount( h.size() );
+ this->ui->table_StatsSpeed->setHorizontalHeaderLabels( h );
+ }
+ // stats count buttons
+ this->ui->button_StatsCount_Protocol->setText( this->crapview.getLogFieldString( 10 ) );
+ this->ui->button_StatsCount_Method->setText( this->crapview.getLogFieldString( 11 ) );
+ this->ui->button_StatsCount_Uri->setText( this->crapview.getLogFieldString( 12 ) );
+ this->ui->button_StatsCount_Query->setText( this->crapview.getLogFieldString( 13 ) );
+ this->ui->button_StatsCount_Response->setText( this->crapview.getLogFieldString( 14 ) );
+ this->ui->button_StatsCount_Referrer->setText( this->crapview.getLogFieldString( 18 ) );
+ this->ui->button_StatsCount_Cookie->setText( this->crapview.getLogFieldString( 22 ) );
+ this->ui->button_StatsCount_UserAgent->setText( this->crapview.getLogFieldString( 21 ) );
+ this->ui->button_StatsCount_Client->setText( this->crapview.getLogFieldString( 20 ) );
+ // configs warn/black-lists
+ {
+ const QStringList wl = {
+ this->crapview.getLogFieldString( 11 ),
+ this->crapview.getLogFieldString( 12 ),
+ this->crapview.getLogFieldString( 21 ),
+ this->crapview.getLogFieldString( 20 ) };
+ const QStringList bl = {
+ this->crapview.getLogFieldString( 20 ) };
+ // set
+ this->ui->box_ConfApache_Warnlist_Field->clear();
+ this->ui->box_ConfApache_Warnlist_Field->addItems( wl );
+ this->ui->box_ConfNginx_Warnlist_Field->clear();
+ this->ui->box_ConfNginx_Warnlist_Field->addItems( wl );
+ this->ui->box_ConfIis_Warnlist_Field->clear();
+ this->ui->box_ConfIis_Warnlist_Field->addItems( wl );
+ this->ui->box_ConfApache_Blacklist_Field->clear();
+ this->ui->box_ConfApache_Blacklist_Field->addItems( bl );
+ this->ui->box_ConfNginx_Blacklist_Field->clear();
+ this->ui->box_ConfNginx_Blacklist_Field->addItems( bl );
+ this->ui->box_ConfIis_Blacklist_Field->clear();
+ this->ui->box_ConfIis_Blacklist_Field->addItems( bl );
+ }
+ // renew the dates
+ this->on_box_StatsWarn_WebServer_currentIndexChanged( this->ui->box_StatsWarn_WebServer->currentIndex() );
+ this->on_box_StatsSpeed_WebServer_currentIndexChanged( this->ui->box_StatsSpeed_WebServer->currentIndex() );
+ this->on_box_StatsCount_WebServer_currentIndexChanged( this->ui->box_StatsCount_WebServer->currentIndex() );
+ this->on_box_StatsDay_WebServer_currentIndexChanged( this->ui->box_StatsDay_WebServer->currentIndex() );
+ this->on_box_StatsRelat_WebServer_currentIndexChanged( this->ui->box_StatsRelat_WebServer->currentIndex() );
+ }
+}
+
+
+//////////////////////////
+//// INTEGRITY CHECKS ////
+//////////////////////////
+void MainWindow::wait_ActiveWindow()
+{
+ if ( ! this->isActiveWindow() ) {
+ QCoreApplication::processEvents( QEventLoop::AllEvents, 250 );
+ } else {
+ this->waiter_timer->stop();
+ this->makeInitialChecks();
+ }
+}
+void MainWindow::makeInitialChecks()
+{
+ bool ok = true;
+ std::error_code err;
+ QString err_msg = "";
+ // check that the sqlite plugin is available
+ if ( ! QSqlDatabase::drivers().contains("QSQLITE") ) {
+ // checks failed, abort
+ DialogSec::errSqlDriverNotFound( "QSQLITE" );
+ ok = false;
+ }
+
+ if ( ok ) {
+ // check LogDoctor's folders paths
+ for ( const std::string& path : std::vector({this->basePath(this->configs_path), this->logdoc_path, this->db_data_path, this->db_hashes_path}) ) {
+ if ( IOutils::exists( path ) ) {
+ if ( IOutils::isDir( path ) ) {
+ if ( ! IOutils::checkDir( path, true ) ) {
+ // directory not readable, try to assign permissions
+ std::filesystem::permissions( path,
+ std::filesystem::perms::owner_read,
+ std::filesystem::perm_options::add,
+ err );
+ if ( err.value() ) {
+ ok = false;
+ err_msg = QString::fromStdString( err.message() );
+ DialogSec::errDirNotReadable( QString::fromStdString( path ), err_msg );
+ }
+ }
+ if ( ok ) {
+ if ( ! IOutils::checkDir( path, false, true ) ) {
+ // directory not writable, try to assign permissions
+ std::filesystem::permissions( path,
+ std::filesystem::perms::owner_write,
+ std::filesystem::perm_options::add,
+ err );
+ if ( err.value() ) {
+ ok = false;
+ err_msg = QString::fromStdString( err.message() );
+ DialogSec::errDirNotWritable( QString::fromStdString( path ), err_msg );
+ }
+ }
+ }
+
+ } else {
+ // not a directory, rename as copy a make a new one
+ ok = DialogSec::choiceDirNotDir( QString::fromStdString( path ) );
+ if ( ok ) {
+ ok = IOutils::renameAsCopy( path, err );
+ if ( ! ok ) {
+ QString p = "";
+ if ( this->dialogs_level > 0 ) {
+ p = QString::fromStdString( path );
+ if ( err.value() ) {
+ err_msg = QString::fromStdString( err.message() );
+ }
+ }
+ DialogSec::errRenaming( p, err_msg );
+ } else {
+ ok = IOutils::makeDir( path, err );
+ if ( ! ok ) {
+ QString msg = DialogSec::tr("Failed to create the directory");
+ if ( this->dialogs_level > 0 ) {
+ msg += ":\n"+QString::fromStdString( path );
+ if ( err.value() ) {
+ err_msg = QString::fromStdString( err.message() );
+ }
+ }
+ DialogSec::errFailedMakeDir( msg, err_msg );
+ }
+ }
+ }
+ }
+
+ } else {
+ ok = IOutils::makeDir( path, err );
+ if ( ! ok ) {
+ QString msg = DialogSec::tr("Failed to create the directory");
+ if ( this->dialogs_level > 0 ) {
+ msg += ":\n"+QString::fromStdString( path );
+ if ( err.value() ) {
+ err_msg = QString::fromStdString( err.message() );
+ }
+ }
+ DialogSec::errFailedMakeDir( msg, err_msg );
+ }
+ }
+ if ( ! ok ) {
+ break;
+ }
+ }
+ }
+
+ if ( ok ) {
+ // statistics' database
+ if ( ! CheckSec::checkCollectionDatabase( this->db_data_path + "/collection.db" ) ) {
+ // checks failed, abort
+ ok = false;
+ } else {
+ this->crapview.setDbPath( this->db_data_path );
+ this->craplog.setStatsDatabasePath( this->db_data_path );
+ // used-files' hashes' database
+ if ( ! CheckSec::checkHashesDatabase( this->db_hashes_path + "/hashes.db" ) ) {
+ // checks failed, abort
+ ok = false;
+ } else {
+ this->craplog.setHashesDatabasePath( this->db_hashes_path );
+ if ( ! this->craplog.hashOps.loadUsedHashesLists( this->db_hashes_path + "/hashes.db" ) ) {
+ // failed to load the list, abort
+ ok = false;
+ }
+ }
+ }
+ }
+ if ( ! ok ) {
+ this->close();
+ //QCoreApplication::exit(0);
+ //this->destroy();
+ } else {
+ // get available stats dates
+ this->refreshStatsDates();
+ // get a fresh list of log files
+ this->on_button_LogFiles_RefreshList_clicked();
+ // set the default WS as the current one
+ switch ( this->craplog.getCurrentWSID() ) {
+ case 11:
+ this->ui->box_StatsWarn_WebServer->setCurrentIndex( 0 );
+ this->ui->box_StatsCount_WebServer->setCurrentIndex( 0 );
+ this->ui->box_StatsSpeed_WebServer->setCurrentIndex( 0 );
+ this->ui->box_StatsDay_WebServer->setCurrentIndex( 0 );
+ this->ui->box_StatsRelat_WebServer->setCurrentIndex( 0 );
+ break;
+ case 12:
+ this->ui->box_StatsWarn_WebServer->setCurrentIndex( 1 );
+ this->ui->box_StatsCount_WebServer->setCurrentIndex( 1 );
+ this->ui->box_StatsSpeed_WebServer->setCurrentIndex( 1 );
+ this->ui->box_StatsDay_WebServer->setCurrentIndex( 1 );
+ this->ui->box_StatsRelat_WebServer->setCurrentIndex( 1 );
+ break;
+ case 13:
+ this->ui->box_StatsWarn_WebServer->setCurrentIndex( 2 );
+ this->ui->box_StatsCount_WebServer->setCurrentIndex( 2 );
+ this->ui->box_StatsSpeed_WebServer->setCurrentIndex( 2 );
+ this->ui->box_StatsDay_WebServer->setCurrentIndex( 2 );
+ this->ui->box_StatsRelat_WebServer->setCurrentIndex( 2 );
+ break;
+ default:
+ // shouldn't be here
+ throw WebServerException( "Unexpected WebServer ID: "+std::to_string( this->default_ws ) );
+ }
+ this->initiating = false;
+ }
+}
+
+
+const bool& MainWindow::checkDataDB()
+{
+ if ( ! this->initiating ) { // avoid recursions
+ // check the db
+ const std::string path = this->db_data_path + "/collection.db";
+ bool ok = IOutils::checkFile( path, true );
+ if ( ! ok ) {
+ ok = CheckSec::checkCollectionDatabase( path );
+ // update ui stuff
+ if ( ! ok ) {
+ // checks failed
+ this->crapview.clearDates();
+ this->ui->box_StatsWarn_Year->clear();
+ this->ui->box_StatsSpeed_Year->clear();
+ this->ui->box_StatsCount_Year->clear();
+ this->ui->box_StatsDay_FromYear->clear();
+ if ( this->ui->checkBox_StatsDay_Period->isChecked() ) {
+ this->ui->box_StatsDay_ToYear->clear();
+ }
+ this->ui->box_StatsRelat_FromYear->clear();
+ this->ui->box_StatsRelat_ToYear->clear();
+ }
+ }
+ if ( ok && !this->db_ok ) {
+ this->db_ok = ok;
+ this->initiating = true;
+ this->refreshStatsDates();
+ this->initiating = false;
+ } else {
+ this->db_ok = ok;
+ }
+ }
+ return this->db_ok;
+}
+
+
+/////////////////////
+//// GENERAL USE ////
+/////////////////////
+const QString MainWindow::wsFromIndex( const int& index )
+{
+ switch (index) {
+ case 0:
+ return QString("apache");
+ case 1:
+ return QString("nginx");
+ case 2:
+ return QString("iis");
+ default:
+ throw WebServerException( "Unexpected WebServer index: "+std::to_string( index ) );
+ }
+}
+
+const std::string MainWindow::resolvePath( const std::string& path )
+{
+ std::string p;
+ try {
+ p = std::filesystem::canonical( StringOps::strip( path ) ).string();
+ } catch (...) {
+ ;
+ }
+ return p;
+}
+const std::string MainWindow::basePath( const std::string& path )
+{
+ const int stop = path.rfind( '/' );
+ return path.substr( 0, stop );
+}
+
+// printable size with suffix and limited decimals
+const QString MainWindow::printableSize( const int& bytes )
+{
+ std::string size_str, size_sfx=" B";
+ float size = (float)bytes;
+ if (size > 1024) {
+ size /= 1024;
+ size_sfx = " KiB";
+ if (size > 1024) {
+ size /= 1024;
+ size_sfx = " MiB";
+ }
+ }
+ // cut decimals depending on how big the floor is
+ size_str = std::to_string( size );
+ int cut_index = size_str.find('.')+1;
+ if ( cut_index == 0 ) {
+ cut_index = size_str.find(',')+1;
+ }
+ int n_decimals = 3;
+ if ( size >= 100 ) {
+ n_decimals = 2;
+ if ( size >= 1000 ) {
+ n_decimals = 1;
+ if ( size >= 10000 ) {
+ n_decimals = 0;
+ cut_index --;
+ }
+ }
+ }
+ if ( cut_index >= 1 ) {
+ cut_index += n_decimals;
+ if ( cut_index > size_str.size()-1 ) {
+ cut_index = size_str.size()-1;
+ }
+ }
+ return QString::fromStdString( size_str.substr(0, cut_index ) + size_sfx );
+}
+
+// printable speed with suffix and limited decimals
+const QString MainWindow::printableSpeed( const int& bytes, const int& secs_ )
+{
+ std::string speed_str, speed_sfx=" B/s";
+ int secs = secs_;
+ if ( secs == 0 ) {
+ secs = 1;
+ }
+ float speed = (float)bytes / (float)secs;
+ if (speed > 1024) {
+ speed /= 1024;
+ speed_sfx = " KiB/s";
+ if (speed > 1024) {
+ speed /= 1024;
+ speed_sfx = " MiB/s";
+ }
+ }
+ // cut decimals depending on how big the floor is
+ speed_str = std::to_string( speed );
+ int cut_index = speed_str.find('.')+1;
+ if ( cut_index == 0 ) {
+ cut_index = speed_str.find(',')+1;
+ }
+ int n_decimals = 3;
+ if ( speed >= 100 ) {
+ n_decimals = 2;
+ if ( speed >= 1000 ) {
+ n_decimals = 1;
+ if ( speed >= 10000 ) {
+ n_decimals = 0;
+ cut_index --;
+ }
+ }
+ }
+ if ( cut_index >= 1 ) {
+ cut_index += n_decimals;
+ if ( cut_index > speed_str.size()-1 ) {
+ cut_index = speed_str.size()-1;
+ }
+ }
+ return QString::fromStdString( speed_str.substr(0, cut_index ) + speed_sfx );
+}
+
+const QString MainWindow::printableTime( const int& secs_ )
+{
+ int secs = secs_;
+ int mins = secs / 60;
+ secs = secs - (mins*60);
+ std::string mins_str = (mins<10) ? "0"+std::to_string(mins) : std::to_string(mins);
+ std::string secs_str = (secs<10) ? "0"+std::to_string(secs) : std::to_string(secs);
+ return QString::fromStdString( mins_str +":"+ secs_str );
+}
+
+
+//////////////
+//// HELP ////
+//////////////
+void MainWindow::showHelp( const std::string& file_name )
+{
+ const std::string link = "https://github.com/elB4RTO/LogDoctor/tree/main/installation_stuff/logdocdata/help/";
+ const std::string path = this->logdoc_path+"/help/"+this->language+"/"+file_name+".html";
+ if ( IOutils::exists( path ) ) {
+ if ( IOutils::isFile( path ) ) {
+ if ( IOutils::checkFile( path, true ) ) {
+ // everything ok, delete the old help window and open a new one
+ delete this->craphelp;
+ this->craphelp = new Craphelp();
+ this->craphelp->helpLogsFormat(
+ path,
+ this->TB.getFont(),
+ this->TB.getColorSchemeID() );
+ if ( this->isMaximized() ) {
+ this->craphelp->showMaximized();
+ } else {
+ this->craphelp->show();
+ }
+ } else {
+ // resource not readable
+ DialogSec::errHelpNotReadable( QString::fromStdString( link ) );
+ }
+ } else {
+ // resource is not a file
+ DialogSec::errHelpFailed( QString::fromStdString( link ), DialogSec::tr("unrecognized entry") );
+ }
+ } else {
+ // resource not found
+ DialogSec::errHelpNotFound( QString::fromStdString( link ) );
+ }
+}
+
+
+
+/***************************************************************
+ * MainWindow'S OPERATIONS START FROM HERE
+ ***************************************************************/
+
+
+//////////////
+//// MENU ////
+/// //////////
+// switch language
+void MainWindow::menu_actionEnglish_triggered()
+{
+ this->ui->actionEnglish->setChecked( true );
+ this->ui->actionEspanol->setChecked( false );
+ this->ui->actionFrancais->setChecked( false );
+ this->ui->actionItaliano->setChecked( false );
+ this->language = "en";
+ this->updateUiLanguage();
+}
+void MainWindow::menu_actionEspanol_triggered()
+{
+ this->ui->actionEnglish->setChecked( false );
+ this->ui->actionEspanol->setChecked( true );
+ this->ui->actionFrancais->setChecked( false );
+ this->ui->actionItaliano->setChecked( false );
+ this->language = "es";
+ this->updateUiLanguage();
+}
+void MainWindow::menu_actionFrancais_triggered()
+{
+ this->ui->actionEnglish->setChecked( false );
+ this->ui->actionEspanol->setChecked( false );
+ this->ui->actionFrancais->setChecked( true );
+ this->ui->actionItaliano->setChecked( false );
+ this->language = "fr";
+ this->updateUiLanguage();
+}
+void MainWindow::menu_actionItaliano_triggered()
+{
+
+ this->ui->actionEnglish->setChecked( false );
+ this->ui->actionEspanol->setChecked( false );
+ this->ui->actionFrancais->setChecked( false );
+ this->ui->actionItaliano->setChecked( true );
+ this->language = "it";
+ this->updateUiLanguage();
+}
+
+// use a tool
+void MainWindow::menu_actionBlockNote_triggered()
+{
+ if ( this->crapnote->isVisible() ) {
+ this->crapnote->activateWindow();
+
+ } else {
+ delete this->crapnote;
+ this->crapnote = new Crapnote();
+ this->crapnote->setTextFont( this->TB.getFont() );
+ this->crapnote->setColorScheme( this->TB.getColorSchemeID() );
+ this->crapnote->show();
+ }
+}
+
+void MainWindow::menu_actionInfos_triggered()
+{
+ std::string version_ = std::to_string( this->version );
+ size_t cut = version_.find('.');
+ if ( cut == std::string::npos ) {
+ cut = version_.find(',');
+ if ( cut == std::string::npos ) {
+ cut = version_.size()-3;
+ }
+ }
+ version_ = version_.substr( 0, cut+3 );
+ delete this->crapinfo;
+ this->crapinfo = new Crapinfo(
+ this->window_theme_id,
+ QString::fromStdString( version_ ),
+ QString::fromStdString( this->resolvePath( "./" ) ),
+ QString::fromStdString( this->configs_path ),
+ QString::fromStdString( this->logdoc_path ) );
+ this->crapinfo->show();
+}
+
+void MainWindow::menu_actionCheckUpdates_triggered()
+{
+ if ( this->crapup->isVisible() ) {
+ this->crapup->activateWindow();
+
+ } else {
+ delete this->crapup;
+ this->crapup = new Crapup(
+ this->window_theme_id,
+ this->icons_theme );
+ this->crapup->show();
+ this->crapup->versionCheck( 1.0 );
+ }
+}
+
+// play a game
+void MainWindow::menu_actionCrissCross_triggered()
+{
+ if ( this->crisscross->isVisible() ) {
+ this->crisscross->activateWindow();
+
+ } else {
+ delete this->crisscross;
+ this->crisscross = new CrissCross( this->window_theme_id );
+ this->crisscross->show();
+ }
+}
+
+void MainWindow::menu_actionSnake_triggered()
+{
+ if ( this->snake->isVisible() ) {
+ this->snake->activateWindow();
+
+ } else {
+ delete this->snake;
+ this->snake = new SnakeGame( this->window_theme_id, this->FONTS.at("script") );
+ this->snake->show();
+ }
+}
+
+
+
+//////////////
+//// TABS ////
+//////////////
+void MainWindow::switchMainTab( const int& new_index )
+{
+ const int old_index = this->ui->stacked_Tabs_Pages->currentIndex();
+ // turn off the old icon
+ switch ( old_index ) {
+ case 0:
+ // make
+ this->ui->button_Tab_Log->setFlat( true );
+ this->ui->button_Tab_Log->setIcon(
+ QIcon(QString(":/icons/icons/%1/log_off.png").arg(this->icons_theme)) );
+ break;
+ case 1:
+ // view
+ this->ui->button_Tab_View->setFlat( true );
+ this->ui->button_Tab_View->setIcon(
+ QIcon(QString(":/icons/icons/%1/view_off.png").arg(this->icons_theme)) );
+ break;
+ case 2:
+ // config
+ this->ui->button_Tab_Conf->setFlat( true );
+ this->ui->button_Tab_Conf->setIcon(
+ QIcon(QString(":/icons/icons/%1/conf_off.png").arg(this->icons_theme)) );
+ break;
+ default:
+ throw("Unexpected Tabs index: "+std::to_string(old_index));
+ break;
+ }
+ // turn on the new one
+ switch ( new_index ) {
+ case 0:
+ // make
+ this->ui->button_Tab_Log->setFlat( false );
+ this->ui->button_Tab_Log->setIcon(
+ QIcon(QString(":/icons/icons/%1/log_on.png").arg(this->icons_theme)) );
+ break;
+ case 1:
+ // view
+ this->ui->button_Tab_View->setFlat( false );
+ this->ui->button_Tab_View->setIcon(
+ QIcon(QString(":/icons/icons/%1/view_on.png").arg(this->icons_theme)) );
+ break;
+ case 2:
+ // config
+ this->ui->button_Tab_Conf->setFlat( false );
+ this->ui->button_Tab_Conf->setIcon(
+ QIcon(QString(":/icons/icons/%1/conf_on.png").arg(this->icons_theme)) );
+ break;
+ default:
+ throw("Unexpected MainTabs index: "+std::to_string(new_index));
+ break;
+ }
+ this->ui->stacked_Tabs_Pages->setCurrentIndex( new_index );
+}
+
+
+void MainWindow::on_button_Tab_Log_clicked()
+{
+ this->switchMainTab( 0 );
+}
+
+void MainWindow::on_button_Tab_View_clicked()
+{
+ this->switchMainTab( 1 );
+}
+
+void MainWindow::on_button_Tab_Conf_clicked()
+{
+ this->switchMainTab( 2 );
+}
+
+
+//// STATS ////
+void MainWindow::switchStatsTab( const int& new_index )
+{
+ const int old_index = this->ui->stacked_Stats_Pages->currentIndex();
+ // turn off the old icon
+ switch ( old_index ) {
+ case 0:
+ // warning
+ this->ui->button_Tab_StatsWarn->setFlat( true );
+ this->ui->button_Tab_StatsWarn->setIcon(
+ QIcon(QString(":/icons/icons/%1/warn_off.png").arg(this->icons_theme)) );
+ break;
+ case 1:
+ // speed
+ this->ui->button_Tab_StatsSpeed->setFlat( true );
+ this->ui->button_Tab_StatsSpeed->setIcon(
+ QIcon(QString(":/icons/icons/%1/speed_off.png").arg(this->icons_theme)) );
+ break;
+ case 2:
+ // counts
+ this->ui->button_Tab_StatsCount->setFlat( true );
+ this->ui->button_Tab_StatsCount->setIcon(
+ QIcon(QString(":/icons/icons/%1/count_off.png").arg(this->icons_theme)) );
+ break;
+ case 3:
+ // daytime
+ this->ui->button_Tab_StatsDay->setFlat( true );
+ this->ui->button_Tab_StatsDay->setIcon(
+ QIcon(QString(":/icons/icons/%1/daytime_off.png").arg(this->icons_theme)) );
+ break;
+ case 4:
+ // relational
+ this->ui->button_Tab_StatsRelat->setFlat( true );
+ this->ui->button_Tab_StatsRelat->setIcon(
+ QIcon(QString(":/icons/icons/%1/relational_off.png").arg(this->icons_theme)) );
+ break;
+ case 5:
+ // globals
+ this->ui->button_Tab_StatsGlob->setFlat( true );
+ this->ui->button_Tab_StatsGlob->setIcon(
+ QIcon(QString(":/icons/icons/%1/global_off.png").arg(this->icons_theme)) );
+ break;
+ default:
+ throw("Unexpected StatsTabs index: "+std::to_string(old_index));
+ break;
+ }
+ // turn on the new one
+ switch ( new_index ) {
+ case 0:
+ // warning
+ this->ui->button_Tab_StatsWarn->setFlat( false );
+ this->ui->button_Tab_StatsWarn->setIcon(
+ QIcon(QString(":/icons/icons/%1/warn_on.png").arg(this->icons_theme)) );
+ break;
+ case 1:
+ // speed
+ this->ui->button_Tab_StatsSpeed->setFlat( false );
+ this->ui->button_Tab_StatsSpeed->setIcon(
+ QIcon(QString(":/icons/icons/%1/speed_on.png").arg(this->icons_theme)) );
+ break;
+ case 2:
+ // counts
+ this->ui->button_Tab_StatsCount->setFlat( false );
+ this->ui->button_Tab_StatsCount->setIcon(
+ QIcon(QString(":/icons/icons/%1/count_on.png").arg(this->icons_theme)) );
+ break;
+ case 3:
+ // daytime
+ this->ui->button_Tab_StatsDay->setFlat( false );
+ this->ui->button_Tab_StatsDay->setIcon(
+ QIcon(QString(":/icons/icons/%1/daytime_on.png").arg(this->icons_theme)) );
+ break;
+ case 4:
+ // relational
+ this->ui->button_Tab_StatsRelat->setFlat( false );
+ this->ui->button_Tab_StatsRelat->setIcon(
+ QIcon(QString(":/icons/icons/%1/relational_on.png").arg(this->icons_theme)) );
+ break;
+ case 5:
+ // globals
+ this->ui->button_Tab_StatsGlob->setFlat( false );
+ this->ui->button_Tab_StatsGlob->setIcon(
+ QIcon(QString(":/icons/icons/%1/global_on.png").arg(this->icons_theme)) );
+ break;
+ default:
+ throw("Unexpected StatsTabs index: "+std::to_string(new_index));
+ break;
+ }
+ this->ui->stacked_Stats_Pages->setCurrentIndex( new_index );
+}
+
+
+void MainWindow::on_button_Tab_StatsWarn_clicked()
+{
+ this->switchStatsTab( 0 );
+}
+
+void MainWindow::on_button_Tab_StatsSpeed_clicked()
+{
+ this->switchStatsTab( 1 );
+}
+
+void MainWindow::on_button_Tab_StatsCount_clicked()
+{
+ this->switchStatsTab( 2 );
+}
+
+void MainWindow::on_button_Tab_StatsDay_clicked()
+{
+ this->switchStatsTab( 3 );
+}
+
+void MainWindow::on_button_Tab_StatsRelat_clicked()
+{
+ this->switchStatsTab( 4 );
+}
+
+void MainWindow::on_button_Tab_StatsGlob_clicked()
+{
+ this->switchStatsTab( 5 );
+}
+
+
+
+////////////
+//// DB ////
+////////////
+void MainWindow::setDbWorkingState( const bool& state )
+{
+ this->db_working = state;
+ if ( ! state ) {
+ this->checkMakeStats_Makable();
+ if ( this->ui->table_StatsWarn->rowCount() > 0 ) {
+ this->ui->button_StatsWarn_Update->setEnabled( true );
+ }
+ this->checkStatsWarnDrawable();
+ this->checkStatsCountDrawable();
+ this->checkStatsSpeedDrawable();
+ this->checkStatsDayDrawable();
+ this->checkStatsRelatDrawable();
+ this->ui->button_StatsGlob_Apache->setEnabled( true );
+ this->ui->button_StatsGlob_Nginx->setEnabled( true );
+ this->ui->button_StatsGlob_Iis->setEnabled( true );
+ this->ui->page_Tab_Conf->setEnabled( true );
+ } else {
+ this->ui->button_MakeStats_Start->setEnabled( false );
+ this->ui->button_StatsWarn_Update->setEnabled( false );
+ this->ui->button_StatsWarn_Draw->setEnabled( false );
+ this->ui->scrollArea_StatsCount->setEnabled( false );
+ this->ui->button_StatsSpeed_Draw->setEnabled( false );
+ this->ui->button_StatsDay_Draw->setEnabled( false );
+ this->ui->button_StatsRelat_Draw->setEnabled( false );
+ this->ui->button_StatsGlob_Apache->setEnabled( false );
+ this->ui->button_StatsGlob_Nginx->setEnabled( false );
+ this->ui->button_StatsGlob_Iis->setEnabled( false );
+ this->ui->page_Tab_Conf->setEnabled( false );
+ }
+}
+
+
+//////////////
+//// LOGS ////
+//////////////
+// switch pages
+void MainWindow::on_button_Logs_Down_clicked()
+{
+ this->ui->stacked_Logs_Pages->setCurrentIndex( 1 );
+}
+
+
+void MainWindow::on_button_Logs_Up_clicked()
+{
+ this->ui->stacked_Logs_Pages->setCurrentIndex( 0 );
+}
+
+
+// check
+void MainWindow::checkMakeStats_Makable()
+{
+ bool state = false;
+ if ( ! this->db_working ) {
+ // db is not busy
+ if ( this->ui->checkBox_LogFiles_CheckAll->checkState() == Qt::CheckState::Checked ) {
+ // all checked
+ state = true;
+ } else if ( this->ui->checkBox_LogFiles_CheckAll->checkState() == Qt::CheckState::PartiallyChecked ) {
+ // at least one should be checked
+ QTreeWidgetItemIterator i(this->ui->listLogFiles);
+ while ( *i ) {
+ if ( (*i)->checkState(0) == Qt::CheckState::Checked ) {
+ // an entry is checked
+ state = true;
+ break;
+ }
+ ++i;
+ }
+ }
+ }
+ this->ui->button_MakeStats_Start->setEnabled( state );
+}
+
+
+// switch to apache web server
+void MainWindow::on_button_LogFiles_Apache_clicked()
+{
+ if ( this->craplog.getCurrentWSID() != 11 ) {
+ // flat/unflat
+ this->ui->button_LogFiles_Apache->setFlat( false );
+ this->ui->button_LogFiles_Nginx->setFlat( true );
+ this->ui->button_LogFiles_Iis->setFlat( true );
+ // set the WebServer
+ this->craplog.setCurrentWSID( 11 );
+ // reset the log files viewer
+ QString rich_text;
+ RichText::richLogsDefault( rich_text );
+ this->ui->textLogFiles->setText( rich_text );
+ this->ui->textLogFiles->setAlignment( Qt::AlignHCenter );
+ rich_text.clear();
+ // load the list
+ this->on_button_LogFiles_RefreshList_clicked();
+ }
+}
+// switch to nginx web server
+void MainWindow::on_button_LogFiles_Nginx_clicked()
+{
+ if ( this->craplog.getCurrentWSID() != 12 ) {
+ // flat/unflat
+ this->ui->button_LogFiles_Nginx->setFlat( false );
+ this->ui->button_LogFiles_Apache->setFlat( true );
+ this->ui->button_LogFiles_Iis->setFlat( true );
+ // set the WebServer
+ this->craplog.setCurrentWSID( 12 );
+ // reset the log files viewer
+ QString rich_text;
+ RichText::richLogsDefault( rich_text );
+ this->ui->textLogFiles->setText( rich_text );
+ this->ui->textLogFiles->setAlignment( Qt::AlignHCenter );
+ rich_text.clear();
+ // load the list
+ this->on_button_LogFiles_RefreshList_clicked();
+ }
+}
+// switch to iis web server
+void MainWindow::on_button_LogFiles_Iis_clicked()
+{
+ if ( this->craplog.getCurrentWSID() != 13 ) {
+ // flat/unflat
+ this->ui->button_LogFiles_Iis->setFlat( false );
+ this->ui->button_LogFiles_Apache->setFlat( true );
+ this->ui->button_LogFiles_Nginx->setFlat( true );
+ // set the WebServer
+ this->craplog.setCurrentWSID( 13 );
+ // reset the log files viewer
+ QString rich_text;
+ RichText::richLogsDefault( rich_text );
+ this->ui->textLogFiles->setText( rich_text );
+ this->ui->textLogFiles->setAlignment( Qt::AlignHCenter );
+ rich_text.clear();
+ // load the list
+ this->on_button_LogFiles_RefreshList_clicked();
+ }
+}
+
+// refresh the log files list
+void MainWindow::on_button_LogFiles_RefreshList_clicked()
+{
+ // clear the current tree
+ this->ui->listLogFiles->clear();
+ this->ui->checkBox_LogFiles_CheckAll->setCheckState( Qt::CheckState::Unchecked );
+ // disable elements
+ this->ui->button_LogFiles_RefreshList->setEnabled( false );
+ this->ui->button_LogFiles_ViewFile->setEnabled( false );
+ this->ui->button_LogFiles_Apache->setEnabled( false );
+ this->ui->button_LogFiles_Nginx->setEnabled( false );
+ this->ui->button_LogFiles_Iis->setEnabled( false );
+ // start refreshing as thread
+ this->refreshing_list = true;
+ delete this->craplog_timer;
+ this->craplog_timer = new QTimer(this);
+ this->craplog_timer->setSingleShot( true );
+ connect(this->craplog_timer, SIGNAL(timeout()), this, SLOT(refreshLogsList()));
+ this->craplog_timer->start(250);
+ // periodically check if thread finished
+ delete this->waiter_timer;
+ this->waiter_timer = new QTimer(this);
+ connect(this->waiter_timer, SIGNAL(timeout()), this, SLOT(check_CraplogLLT_Finished()));
+ this->waiter_timer->start(250);
+}
+
+void MainWindow::refreshLogsList()
+{
+ std::string col;
+ // iterate over elements of list
+ for ( const Craplog::LogFile& log_file : this->craplog.getLogsList(true) ) {
+ // new entry for the tree widget
+ QTreeWidgetItem *item = new QTreeWidgetItem();
+
+ // preliminary check for file usage display
+ if ( log_file.used_already ) {
+ if ( this->hide_used_files ) {
+ // do not display
+ delete item; // possible memory leak
+ continue;
+ }
+ // display with red foreground
+ item->setForeground( 0, this->COLORS.at( "red" ) );
+ }
+
+ // preliminary check on file size
+ col = "grey";
+ if ( log_file.size > this->craplog.getWarningSize() ) {
+ col = "orange";
+ }
+ item->setForeground( 1, this->COLORS.at( col ) );
+
+ // set the name
+ item->setText( 0, log_file.name );
+ // set the size
+ item->setText( 1, this->printableSize( log_file.size ) );
+ item->setFont( 1, this->FONTS.at("main_italic") );
+ // append the item (on top, forced)
+ item->setCheckState(0, Qt::CheckState::Unchecked );
+ this->ui->listLogFiles->addTopLevelItem( item );
+ }
+ if ( this->craplog.getLogsListSize() > 0 ) {
+ // sort the list alphabetically
+ this->ui->listLogFiles->sortByColumn(0, Qt::SortOrder::AscendingOrder );
+ this->ui->checkBox_LogFiles_CheckAll->setEnabled( true );
+ } else {
+ this->ui->checkBox_LogFiles_CheckAll->setCheckState( Qt::CheckState::Unchecked );
+ this->ui->checkBox_LogFiles_CheckAll->setEnabled( false );
+ }
+ refreshing_list = false;
+}
+void MainWindow::check_CraplogLLT_Finished()
+{
+ if ( ! this->refreshing_list ) {
+ this->waiter_timer->stop();
+ // back to normal state
+ this->ui->button_LogFiles_RefreshList->setEnabled( true );
+ this->ui->button_LogFiles_ViewFile->setEnabled( true );
+ this->ui->button_LogFiles_Apache->setEnabled( true );
+ this->ui->button_LogFiles_Nginx->setEnabled( true );
+ this->ui->button_LogFiles_Iis->setEnabled( true );
+ }
+}
+
+
+void MainWindow::on_checkBox_LogFiles_CheckAll_stateChanged(int arg1)
+{
+ this->checkMakeStats_Makable();
+ Qt::CheckState new_state;
+ if ( arg1 == Qt::CheckState::Checked ) {
+ // check all
+ new_state = Qt::CheckState::Checked;
+ } else if ( arg1 == Qt::CheckState::Unchecked ) {
+ // un-check all
+ new_state = Qt::CheckState::Unchecked;
+ } else {
+ // do nothing
+ return;
+ }
+ QTreeWidgetItemIterator i( this->ui->listLogFiles );
+ while ( *i ) {
+ (*i)->setCheckState( 0, new_state );
+ ++i;
+ }
+}
+
+
+void MainWindow::on_button_LogFiles_ViewFile_clicked()
+{
+ // display the selected item
+ if ( this->ui->listLogFiles->selectedItems().size() > 0 ) {
+ bool proceed = true;
+ Craplog::LogFile item;
+ // retrieve the file item
+ try {
+ item = this->craplog.getLogFileItem(
+ this->ui->listLogFiles->selectedItems().takeFirst()->text(0) );
+
+ } catch ( const GenericException& ) {
+ // failed to find file
+ proceed = false;
+ DialogSec::errFileNotFound( QString::fromStdString( item.path ), true );
+ }
+
+ // check the size
+ if ( proceed ) {
+ const long warn_size = this->craplog.getWarningSize();
+ if ( warn_size >= 0 ) {
+ if ( item.size > warn_size ) {
+ // exceeds the warning size
+ QString size_str, msg = item.name;
+ if ( this->dialogs_level >= 1 ) {
+ std::string size_sfx=" B";
+ float size = (float)item.size;
+ if (size > 1024) {
+ size /= 1024; size_sfx = " KiB";
+ if (size > 1024) {
+ size /= 1024; size_sfx = " MiB";
+ }
+ }
+ size_str = std::to_string(size).substr(0,std::to_string(size).size()-3).c_str();
+ msg += QString("\n\n%1:\n%2%3").arg( DialogSec::tr("Size of the file"), size_str, size_sfx.c_str() );
+ if ( this->dialogs_level == 2 ) {
+ size = (float)warn_size;
+ if (size > 1024) {
+ size /= 1024; size_sfx = " KiB";
+ if (size > 1024) {
+ size /= 1024; size_sfx = " MiB";
+ }
+ }
+ size_str = std::to_string(size).substr(0,std::to_string(size).size()-3).c_str();
+ msg += QString("\n\n%1:\n%2%3").arg( DialogSec::tr("Warning size parameter"), size_str, size_sfx.c_str() );
+ }
+ }
+ // ask the user what to do
+ proceed = DialogSec::choiceFileSizeWarning2( msg );
+ if ( ! proceed ) {
+ return;
+ }
+ }
+ }
+ }
+
+ if ( proceed ) {
+ // get the current log format
+ FormatOps::LogsFormat format = this->craplog.getCurrentLogFormat();
+ // read the content
+ std::string content;
+ try {
+ try {
+ // try reading as gzip compressed file
+ GZutils::readFile( item.path, content );
+
+ } catch ( const GenericException& ) {
+ // failed closing file pointer
+ throw;
+
+ } catch (...) {
+ // failed as gzip, try as text file
+ if ( content.size() > 0 ) {
+ content.clear();
+ }
+ IOutils::readFile( item.path, content );
+ }
+
+ } catch ( const GenericException& ) {
+ // failed closing gzip file pointer
+ proceed = false;
+ // >> e.what() << //
+ DialogSec::errGeneric( QString("%1:\n%2").arg(
+ DialogSec::tr("Failed to read gzipped file"),
+ item.name) );
+
+ /*} catch ( const std::ios_base::failure& err ) {
+ // failed reading as text
+ proceed = false;
+ // >> err.what() << //
+ DialogSec::errFailedReadFile( item.name );*/
+ } catch (...) {
+ // failed somehow
+ proceed = false;
+ DialogSec::errFailedReadFile( item.name );
+ }
+
+ if ( proceed ) {
+ // succesfully read, now enriched and display
+ QString rich_content;
+ RichText::enrichLogs(
+ rich_content, content,
+ format, this->TB );
+ this->ui->textLogFiles->setText( rich_content );
+ this->ui->textLogFiles->setFont( this->TB.getFont() );
+ rich_content.clear();
+ }
+ content.clear();
+ }
+ if ( ! proceed ) {
+ // failed
+ QString rich_text;
+ RichText::richLogsFailure( rich_text );
+ this->ui->textLogFiles->setText( rich_text );
+ this->ui->textLogFiles->setAlignment( Qt::AlignHCenter );
+ rich_text.clear();
+ }
+ }
+}
+
+
+void MainWindow::on_listLogFiles_itemDoubleClicked(QTreeWidgetItem *item, int column)
+{
+ this->on_button_LogFiles_ViewFile_clicked();
+}
+
+
+void MainWindow::on_listLogFiles_itemChanged(QTreeWidgetItem *item, int column)
+{
+ // control checked
+ int n_checked = 0;
+ QTreeWidgetItemIterator i(this->ui->listLogFiles);
+ while ( *i ) {
+ if ( (*i)->checkState(0) == Qt::CheckState::Checked ) {
+ n_checked++;
+ }
+ ++i;
+ }
+ if ( n_checked == 0 ) {
+ this->ui->checkBox_LogFiles_CheckAll->setCheckState(Qt::CheckState::Unchecked);
+ } else if ( n_checked == this->craplog.getLogsListSize() ) {
+ this->ui->checkBox_LogFiles_CheckAll->setCheckState(Qt::CheckState::Checked);
+ } else {
+ this->ui->checkBox_LogFiles_CheckAll->setCheckState(Qt::CheckState::PartiallyChecked);
+ }
+}
+
+
+void MainWindow::on_button_MakeStats_Start_clicked()
+{
+ if ( ! this->db_working ) {
+ bool proceed = true;
+ // check that the format has been set
+ const FormatOps::LogsFormat& lf = this->craplog.getLogsFormat( this->craplog.getCurrentWSID() );
+ if ( lf.string.size() == 0 ) {
+ // format string not set
+ proceed = false;
+ DialogSec::errLogFormatNotSet( nullptr );
+ } else if ( lf.fields.size() == 0 ) {
+ // no field, useless to parse
+ proceed = false;
+ DialogSec::errLogFormatNoFields( nullptr );
+ } else if ( lf.separators.size() < lf.fields.size()-1 ) {
+ // missing at least a separator between two (or more) fields
+ proceed = false;
+ DialogSec::errLogFormatNoSeparators( nullptr );
+ }
+
+ if ( proceed ) {
+ // take actions on Craplog's start
+ this->craplogStarted();
+
+ // feed craplog with the checked files
+ QTreeWidgetItemIterator i(this->ui->listLogFiles);
+ while ( *i ) {
+ if ( (*i)->checkState(0) == Qt::CheckState::Checked ) {
+ // tell Craplog to set this file as selected
+ if ( ! this->craplog.setLogFileSelected( (*i)->text(0) ) ) {
+ // failed to retrieve the file. this shouldn't be, but...
+ const int choice = DialogSec::choiceSelectedFileNotFound( (*i)->text(0) );
+ if ( choice == 0 ) {
+ // choosed to abort all
+ proceed = false;
+ break;
+ } else if ( choice == 1 ) {
+ // choosed to discard the file and continue
+ ;
+ } else {
+ // shouldn't be here
+ throw GenericException( "Unexpeced value returned: "+std::to_string(choice) );
+ }
+ }
+ }
+ ++i;
+ }
+
+ if ( proceed ) {
+ // check files to be used before to start
+ proceed = this->craplog.checkStuff();
+ } else {
+ this->craplogFinished();
+ }
+
+ if ( proceed ) {
+ // periodically update perfs
+ delete this->waiter_timer;
+ this->waiter_timer = new QTimer(this);
+ connect(this->waiter_timer, SIGNAL(timeout()), this, SLOT(update_Craplog_PerfData()));
+ // run craplog as thread
+ this->waiter_timer_start = std::chrono::system_clock::now();
+ delete this->craplog_timer;
+ this->craplog_timer = new QTimer(this);
+ this->craplog_timer->setSingleShot( true );
+ connect(this->craplog_timer, SIGNAL(timeout()), this, SLOT(runCraplog()));
+ // start processing
+ this->waiter_timer->start(250);
+ this->craplog_timer->start(100);
+ } else {
+ this->craplogFinished();
+ }
+ }
+ }
+}
+void MainWindow::runCraplog()
+{
+ this->craplog.run();
+}
+
+void MainWindow::reset_MakeStats_labels()
+{
+ // reset to default
+ this->ui->label_MakeStats_Size->setText( "0 B" );
+ this->ui->label_MakeStats_Lines->setText( "0" );
+ // time and speed
+ this->ui->label_MakeStats_Time->setText( "00:00" );
+ this->ui->label_MakeStats_Speed->setText( "0 B/s" );
+}
+
+void MainWindow::update_MakeStats_labels()
+{
+ // update values
+ unsigned size;
+ long secs;
+ // size and lines
+ if ( this->craplog.isParsing() ) {
+ this->craplog.collectPerfData();
+ }
+ size = this->craplog.getTotalSize();
+ //size = this->craplog.getParsedSize();
+ this->ui->label_MakeStats_Size->setText( this->printableSize( size ) );
+ this->ui->label_MakeStats_Lines->setText( QString::fromStdString(std::to_string(this->craplog.getParsedLines())) );
+ // time and speed
+ this->waiter_timer_elapsed = std::chrono::duration_cast(
+ this->waiter_timer_start - std::chrono::system_clock::now()
+ );
+ size = this->craplog.getPerfSize();
+ secs = this->waiter_timer_elapsed.count() / -1000000000;
+ this->ui->label_MakeStats_Time->setText( this->printableTime( secs ));
+ this->ui->label_MakeStats_Speed->setText( this->printableSpeed( size, secs ));
+}
+
+void MainWindow::update_Craplog_PerfData()
+{
+ // craplog is running as thread, update the values meanwhile
+ this->update_MakeStats_labels();
+ // check if Craplog has finished working
+ if ( ! this->craplog.isWorking() ) {
+ this->waiter_timer->stop();
+ this->craplogFinished();
+ }
+}
+
+void MainWindow::craplogStarted()
+{
+ // reset perfs
+ this->reset_MakeStats_labels();
+ this->craplog.logOps.resetPerfData();
+ // disable the LogFiles section
+ this->ui->stacked_Logs_Pages->setEnabled(false);
+ // disable things which needs database access
+ this->setDbWorkingState( true );
+ // enable all labels (needed only the first time)
+ this->ui->icon_MakeStats_Size->setEnabled( false );
+ this->ui->label_MakeStats_Size->setEnabled( true );
+ this->ui->icon_MakeStats_Lines->setEnabled( false );
+ this->ui->label_MakeStats_Lines->setEnabled( true );
+ this->ui->icon_MakeStats_Time->setEnabled( false );
+ this->ui->label_MakeStats_Time->setEnabled( true );
+ this->ui->icon_MakeStats_Speed->setEnabled( false );
+ this->ui->label_MakeStats_Speed->setEnabled( true );
+}
+
+void MainWindow::craplogFinished()
+{
+ // update the perf data one last time, just in case
+ this->update_MakeStats_labels();
+ this->craplog.makeChart(
+ this->CHARTS_THEMES.at( this->charts_theme_id ), this->FONTS,
+ this->ui->chart_MakeStats_Size );
+ if ( this->craplog.editedDatabase() ) {
+ // craplog succeeded
+ this->db_edited = true;
+ }
+ if ( this->craplog.getTotalSize() == 0 ) {
+ // no data
+ this->reset_MakeStats_labels();
+ }
+ // clean up temp vars
+ this->craplog.clearDataCollection();
+ this->craplog.logOps.resetPerfData();
+
+ // refresh the logs list
+ this->on_button_LogFiles_RefreshList_clicked();
+ // enable the LogFiles section
+ this->ui->stacked_Logs_Pages->setEnabled( true );
+ // enable all labels (needed only the first time each session)
+ this->ui->icon_MakeStats_Size->setEnabled( true );
+ this->ui->icon_MakeStats_Lines->setEnabled( true );
+ this->ui->icon_MakeStats_Time->setEnabled( true );
+ this->ui->icon_MakeStats_Speed->setEnabled( true );
+ // enable back
+ this->setDbWorkingState( false );
+ // get a fresh collection of available stats dates
+ this->refreshStatsDates();
+}
+
+
+
+///////////////
+//// STATS ////
+///////////////
+// refresh all the dates boxes
+void MainWindow::refreshStatsDates()
+{
+ this->crapview.refreshDates();
+ this->on_box_StatsWarn_WebServer_currentIndexChanged( this->ui->box_StatsWarn_WebServer->currentIndex() );
+ this->on_box_StatsSpeed_WebServer_currentIndexChanged( this->ui->box_StatsSpeed_WebServer->currentIndex() );
+ this->on_box_StatsCount_WebServer_currentIndexChanged( this->ui->box_StatsCount_WebServer->currentIndex() );
+ this->on_box_StatsDay_WebServer_currentIndexChanged( this->ui->box_StatsDay_WebServer->currentIndex() );
+ this->on_box_StatsRelat_WebServer_currentIndexChanged( this->ui->box_StatsRelat_WebServer->currentIndex() );
+}
+
+
+//////////////
+//// WARN ////
+void MainWindow::checkStatsWarnDrawable()
+{
+ if ( ! this->db_working ) {
+ if ( this->ui->box_StatsWarn_Year->currentIndex() >= 0
+ && this->ui->box_StatsWarn_Month->currentIndex() >= 0
+ && this->ui->box_StatsWarn_Day->currentIndex() >= 0 ) {
+ // enable the draw button
+ this->ui->button_StatsWarn_Draw->setEnabled( true );
+ } else {
+ // disable the draw button
+ this->ui->button_StatsWarn_Draw->setEnabled( false );
+ }
+ } else {
+ this->ui->button_StatsRelat_Draw->setEnabled( false );
+ }
+}
+
+void MainWindow::on_box_StatsWarn_WebServer_currentIndexChanged(int index)
+{
+ if ( this->checkDataDB() ) {
+ this->ui->box_StatsWarn_Year->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsWarn_Year->addItems(
+ this->crapview.getYears( this->wsFromIndex( index ) ));
+ this->ui->box_StatsWarn_Year->setCurrentIndex( 0 );
+ }
+ }
+ this->checkStatsWarnDrawable();
+}
+
+void MainWindow::on_box_StatsWarn_Year_currentIndexChanged(int index)
+{
+ this->ui->box_StatsWarn_Month->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsWarn_Month->addItems(
+ this->crapview.getMonths(
+ this->wsFromIndex( this->ui->box_StatsWarn_WebServer->currentIndex() ),
+ this->ui->box_StatsWarn_Year->currentText() ) );
+ this->ui->box_StatsWarn_Month->setCurrentIndex( 0 );
+ }
+ this->checkStatsWarnDrawable();
+}
+
+void MainWindow::on_box_StatsWarn_Month_currentIndexChanged(int index)
+{
+ this->ui->box_StatsWarn_Day->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsWarn_Day->addItems(
+ this->crapview.getDays(
+ this->wsFromIndex( this->ui->box_StatsWarn_WebServer->currentIndex() ),
+ this->ui->box_StatsWarn_Year->currentText(),
+ this->ui->box_StatsWarn_Month->currentText() ) );
+ this->ui->box_StatsWarn_Day->setCurrentIndex( 0 );
+ }
+ this->checkStatsWarnDrawable();
+}
+
+void MainWindow::on_box_StatsWarn_Day_currentIndexChanged(int index)
+{
+ if ( this->ui->checkBox_StatsWarn_Hour->isChecked() ) {
+ this->ui->box_StatsWarn_Hour->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsWarn_Hour->addItems( this->crapview.getHours() );
+ this->ui->box_StatsWarn_Hour->setCurrentIndex( 0 );
+ }
+ }
+ this->checkStatsWarnDrawable();
+}
+
+void MainWindow::on_checkBox_StatsWarn_Hour_stateChanged(int state)
+{
+ if ( state == Qt::CheckState::Checked ) {
+ this->ui->box_StatsWarn_Hour->setEnabled( true );
+ // add available dates
+ this->on_box_StatsWarn_Day_currentIndexChanged( 0 );
+ } else {
+ this->ui->box_StatsWarn_Hour->clear();
+ this->ui->box_StatsWarn_Hour->setEnabled( false );
+ }
+}
+
+void MainWindow::on_box_StatsWarn_Hour_currentIndexChanged(int index)
+{
+ this->checkStatsWarnDrawable();
+}
+
+void MainWindow::on_button_StatsWarn_Draw_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->setDbWorkingState( true );
+ delete this->crapview_timer;
+ this->crapview_timer = new QTimer(this);
+ this->crapview_timer->setSingleShot( true );
+ connect(this->crapview_timer, SIGNAL(timeout()), this, SLOT(drawStatsWarn()));
+ this->crapview_timer->start(250);
+ }
+}
+void MainWindow::drawStatsWarn()
+{
+ this->ui->table_StatsWarn->setRowCount(0);
+ this->crapview.drawWarn(
+ this->ui->table_StatsWarn, this->ui->chart_StatsWarn,
+ this->CHARTS_THEMES.at( this->charts_theme_id ), this->FONTS,
+ this->wsFromIndex( this->ui->box_StatsWarn_WebServer->currentIndex() ),
+ this->ui->box_StatsWarn_Year->currentText(),
+ this->ui->box_StatsWarn_Month->currentText(),
+ this->ui->box_StatsWarn_Day->currentText(),
+ (this->ui->checkBox_StatsWarn_Hour->isChecked()) ? this->ui->box_StatsWarn_Hour->currentText() : "" );
+ this->setDbWorkingState( false );
+}
+
+
+void MainWindow::on_button_StatsWarn_Update_clicked()
+{
+ this->crapview.updateWarn(
+ this->ui->table_StatsWarn,
+ this->wsFromIndex( this->ui->box_StatsWarn_WebServer->currentIndex() ) );
+ this->db_edited = true;
+}
+
+
+///////////////
+//// SPEED ////
+void MainWindow::checkStatsSpeedDrawable()
+{
+ if ( ! this->db_working ) {
+ if ( this->ui->box_StatsSpeed_Year->currentIndex() >= 0
+ && this->ui->box_StatsSpeed_Month->currentIndex() >= 0
+ && this->ui->box_StatsSpeed_Day->currentIndex() >= 0 ) {
+ // enable the draw button
+ this->ui->button_StatsSpeed_Draw->setEnabled( true );
+ } else {
+ // disable the draw button
+ this->ui->button_StatsSpeed_Draw->setEnabled( false );
+ }
+ } else {
+ this->ui->button_StatsRelat_Draw->setEnabled( false );
+ }
+}
+
+void MainWindow::on_box_StatsSpeed_WebServer_currentIndexChanged(int index)
+{
+ if ( this->checkDataDB() ) {
+ this->ui->box_StatsSpeed_Year->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsSpeed_Year->addItems(
+ this->crapview.getYears( this->wsFromIndex( index ) ) );
+ this->ui->box_StatsSpeed_Year->setCurrentIndex( 0 );
+ }
+ }
+ this->checkStatsSpeedDrawable();
+}
+
+void MainWindow::on_box_StatsSpeed_Year_currentIndexChanged(int index)
+{
+ this->ui->box_StatsSpeed_Month->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsSpeed_Month->addItems(
+ this->crapview.getMonths(
+ this->wsFromIndex( this->ui->box_StatsSpeed_WebServer->currentIndex() ),
+ this->ui->box_StatsSpeed_Year->currentText() ) );
+ this->ui->box_StatsSpeed_Month->setCurrentIndex( 0 );
+ }
+ this->checkStatsSpeedDrawable();
+}
+
+void MainWindow::on_box_StatsSpeed_Month_currentIndexChanged(int index)
+{
+ this->ui->box_StatsSpeed_Day->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsSpeed_Day->addItems(
+ this->crapview.getDays(
+ this->wsFromIndex( this->ui->box_StatsSpeed_WebServer->currentIndex() ),
+ this->ui->box_StatsSpeed_Year->currentText(),
+ this->ui->box_StatsSpeed_Month->currentText() ) );
+ this->ui->box_StatsSpeed_Day->setCurrentIndex( 0 );
+ }
+ this->checkStatsSpeedDrawable();
+}
+
+void MainWindow::on_box_StatsSpeed_Day_currentIndexChanged(int index)
+{
+ this->checkStatsSpeedDrawable();
+}
+
+void MainWindow::on_button_StatsSpeed_Draw_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->setDbWorkingState( true );
+ delete this->crapview_timer;
+ this->crapview_timer = new QTimer(this);
+ this->crapview_timer->setSingleShot( true );
+ connect(this->crapview_timer, SIGNAL(timeout()), this, SLOT(drawStatsSpeed()));
+ this->crapview_timer->start(250);
+ }
+}
+void MainWindow::drawStatsSpeed()
+{
+ this->ui->table_StatsSpeed->setRowCount(0);
+ this->crapview.drawSpeed(
+ this->ui->table_StatsSpeed,
+ this->ui->chart_SatsSpeed,
+ this->CHARTS_THEMES.at( this->charts_theme_id ), this->FONTS,
+ this->wsFromIndex( this->ui->box_StatsSpeed_WebServer->currentIndex() ),
+ this->ui->box_StatsSpeed_Year->currentText(),
+ this->ui->box_StatsSpeed_Month->currentText(),
+ this->ui->box_StatsSpeed_Day->currentText(),
+ this->crapview.parseTextualFilter( this->ui->inLine_StatsSpeed_Protocol->text() ),
+ this->crapview.parseTextualFilter( this->ui->inLine_StatsSpeed_Method->text() ),
+ this->crapview.parseTextualFilter( this->ui->inLine_StatsSpeed_Uri->text() ),
+ this->crapview.parseTextualFilter( this->ui->inLine_StatsSpeed_Query->text() ),
+ this->crapview.parseNumericFilter( this->ui->inLine_StatsSpeed_Response->text() ) );
+ this->setDbWorkingState( false );
+}
+
+
+///////////////
+//// COUNT ////
+void MainWindow::checkStatsCountDrawable()
+{
+ if ( ! this->db_working ) {
+ if ( this->ui->box_StatsCount_Year->currentIndex() >= 0
+ && this->ui->box_StatsCount_Month->currentIndex() >= 0
+ && this->ui->box_StatsCount_Day->currentIndex() >= 0 ) {
+ // enable the draw button
+ this->ui->scrollArea_StatsCount->setEnabled( true );
+ } else {
+ // disable the draw button
+ this->ui->scrollArea_StatsCount->setEnabled( false );
+ }
+ } else {
+ this->ui->button_StatsRelat_Draw->setEnabled( false );
+ }
+}
+
+void MainWindow::on_box_StatsCount_WebServer_currentIndexChanged(int index)
+{
+ if ( this->checkDataDB() ) {
+ this->ui->box_StatsCount_Year->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsCount_Year->addItems(
+ this->crapview.getYears( this->wsFromIndex( index ) ));
+ this->ui->box_StatsCount_Year->setCurrentIndex( 0 );
+ this->resetStatsCountButtons();
+ }
+ }
+ this->checkStatsCountDrawable();
+}
+
+void MainWindow::on_box_StatsCount_Year_currentIndexChanged(int index)
+{
+ this->ui->box_StatsCount_Month->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsCount_Month->addItems(
+ this->crapview.getMonths(
+ this->wsFromIndex( this->ui->box_StatsCount_WebServer->currentIndex() ),
+ this->ui->box_StatsCount_Year->currentText() ) );
+ this->ui->box_StatsCount_Month->setCurrentIndex( 0 );
+ }
+ this->checkStatsCountDrawable();
+}
+
+void MainWindow::on_box_StatsCount_Month_currentIndexChanged(int index)
+{
+ this->ui->box_StatsCount_Day->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsCount_Day->addItems(
+ this->crapview.getDays(
+ this->wsFromIndex( this->ui->box_StatsCount_WebServer->currentIndex() ),
+ this->ui->box_StatsCount_Year->currentText(),
+ this->ui->box_StatsCount_Month->currentText() ) );
+ this->ui->box_StatsCount_Day->setCurrentIndex( 0 );
+ }
+ this->checkStatsCountDrawable();
+}
+
+void MainWindow::on_box_StatsCount_Day_currentIndexChanged(int index)
+{
+ this->checkStatsCountDrawable();
+}
+
+void MainWindow::resetStatsCountButtons()
+{
+ if ( ! this->ui->button_StatsCount_Protocol->isFlat() ) {
+ this->ui->button_StatsCount_Protocol->setFlat( true );
+ }
+ if ( ! this->ui->button_StatsCount_Method->isFlat() ) {
+ this->ui->button_StatsCount_Method->setFlat( true );
+ }
+ if ( ! this->ui->button_StatsCount_Uri->isFlat() ) {
+ this->ui->button_StatsCount_Uri->setFlat( true );
+ }
+ if ( ! this->ui->button_StatsCount_Query->isFlat() ) {
+ this->ui->button_StatsCount_Query->setFlat( true );
+ }
+ if ( ! this->ui->button_StatsCount_Response->isFlat() ) {
+ this->ui->button_StatsCount_Response->setFlat( true );
+ }
+ if ( ! this->ui->button_StatsCount_Referrer->isFlat() ) {
+ this->ui->button_StatsCount_Referrer->setFlat( true );
+ }
+ if ( ! this->ui->button_StatsCount_Cookie->isFlat() ) {
+ this->ui->button_StatsCount_Cookie->setFlat( true );
+ }
+ if ( ! this->ui->button_StatsCount_UserAgent->isFlat() ) {
+ this->ui->button_StatsCount_UserAgent->setFlat( true );
+ }
+ if ( ! this->ui->button_StatsCount_Client->isFlat() ) {
+ this->ui->button_StatsCount_Client->setFlat( true );
+ }
+}
+
+void MainWindow::startCountDrawing()
+{
+ this->setDbWorkingState( true );
+ delete this->crapview_timer;
+ this->crapview_timer = new QTimer(this);
+ this->crapview_timer->setSingleShot( true );
+ connect(this->crapview_timer, SIGNAL(timeout()), this, SLOT(drawStatsCount()));
+ this->crapview_timer->start(250);
+}
+
+void MainWindow::on_button_StatsCount_Protocol_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->resetStatsCountButtons();
+ this->ui->button_StatsCount_Protocol->setFlat( false );
+ this->count_fld = this->ui->button_StatsCount_Protocol->text();
+ startCountDrawing();
+ }
+}
+
+void MainWindow::on_button_StatsCount_Method_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->resetStatsCountButtons();
+ this->count_fld = this->ui->button_StatsCount_Method->text();
+ this->ui->button_StatsCount_Method->setFlat( false );
+ startCountDrawing();
+ }
+}
+
+void MainWindow::on_button_StatsCount_Uri_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->resetStatsCountButtons();
+ this->count_fld = this->ui->button_StatsCount_Uri->text();
+ this->ui->button_StatsCount_Uri->setFlat( false );
+ startCountDrawing();
+ }
+}
+
+void MainWindow::on_button_StatsCount_Query_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->resetStatsCountButtons();
+ this->count_fld = this->ui->button_StatsCount_Query->text();
+ this->ui->button_StatsCount_Query->setFlat( false );
+ startCountDrawing();
+ }
+}
+
+void MainWindow::on_button_StatsCount_Response_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->resetStatsCountButtons();
+ this->count_fld = this->ui->button_StatsCount_Response->text();
+ this->ui->button_StatsCount_Response->setFlat( false );
+ startCountDrawing();
+ }
+}
+
+void MainWindow::on_button_StatsCount_Referrer_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->resetStatsCountButtons();
+ this->count_fld = this->ui->button_StatsCount_Referrer->text();
+ this->ui->button_StatsCount_Referrer->setFlat( false );
+ startCountDrawing();
+ }
+}
+
+void MainWindow::on_button_StatsCount_Cookie_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->resetStatsCountButtons();
+ this->count_fld = this->ui->button_StatsCount_Cookie->text();
+ this->ui->button_StatsCount_Cookie->setFlat( false );
+ startCountDrawing();
+ }
+}
+
+void MainWindow::on_button_StatsCount_UserAgent_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->resetStatsCountButtons();
+ this->count_fld = this->ui->button_StatsCount_UserAgent->text();
+ this->ui->button_StatsCount_UserAgent->setFlat( false );
+ startCountDrawing();
+ }
+}
+
+void MainWindow::on_button_StatsCount_Client_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->resetStatsCountButtons();
+ this->count_fld = this->ui->button_StatsCount_Client->text();
+ this->ui->button_StatsCount_Client->setFlat( false );
+ startCountDrawing();
+ }
+}
+
+void MainWindow::drawStatsCount()
+{
+ this->ui->table_StatsCount->setRowCount(0);
+ this->crapview.drawCount(
+ this->ui->table_StatsCount, this->ui->chart_StatsCount,
+ this->CHARTS_THEMES.at( this->charts_theme_id ), this->FONTS,
+ this->wsFromIndex( this->ui->box_StatsCount_WebServer->currentIndex() ),
+ this->ui->box_StatsCount_Year->currentText(),
+ this->ui->box_StatsCount_Month->currentText(),
+ this->ui->box_StatsCount_Day->currentText(),
+ this->count_fld );
+ this->setDbWorkingState( false );
+}
+
+
+/////////////
+//// DAY ////
+void MainWindow::checkStatsDayDrawable()
+{
+ if ( ! this->db_working ) {
+ bool aux = true;
+ // secondary date (period)
+ if ( this->ui->checkBox_StatsDay_Period->isChecked() ) {
+ if ( this->ui->box_StatsDay_ToYear->currentIndex() < 0
+ && this->ui->box_StatsDay_ToMonth->currentIndex() < 0
+ && this->ui->box_StatsDay_ToDay->currentIndex() < 0 ) {
+ aux = false;
+ } else {
+ int a,b;
+ a = this->ui->box_StatsDay_ToYear->currentText().toInt();
+ b = this->ui->box_StatsDay_FromYear->currentText().toInt();
+ if ( a < b ) {
+ // year 'to' is less than 'from'
+ aux = false;
+ } else if ( a == b ) {
+ a = this->crapview.getMonthNumber( this->ui->box_StatsDay_ToMonth->currentText() );
+ b = this->crapview.getMonthNumber( this->ui->box_StatsDay_FromMonth->currentText() );
+ if ( a < b ) {
+ // month 'to' is less than 'from'
+ aux = false;
+ } else if ( a == b ) {
+ a = this->ui->box_StatsDay_ToDay->currentText().toInt();
+ b = this->ui->box_StatsDay_FromDay->currentText().toInt();
+ if ( a < b ) {
+ // day 'to' is less than 'from'
+ aux = false;
+ }
+ }
+ }
+ }
+ }
+ // primary date
+ if ( this->ui->box_StatsDay_FromYear->currentIndex() < 0
+ && this->ui->box_StatsDay_FromMonth->currentIndex() < 0
+ && this->ui->box_StatsDay_FromDay->currentIndex() < 0 ) {
+ aux = false;
+ }
+ // check log field validity
+ if ( this->ui->box_StatsDay_LogsField->currentIndex() < 0 ) {
+ aux = false;
+ }
+ this->ui->button_StatsDay_Draw->setEnabled( aux);
+
+ } else {
+ // db busy
+ this->ui->button_StatsRelat_Draw->setEnabled( false );
+ }
+}
+
+void MainWindow::on_box_StatsDay_WebServer_currentIndexChanged(int index)
+{
+ if ( this->checkDataDB() ) {
+ this->ui->box_StatsDay_LogsField->clear();
+ this->ui->box_StatsDay_FromYear->clear();
+ this->ui->box_StatsDay_ToYear->clear();
+ if ( index != -1 ) {
+ // refresh fields
+ this->ui->box_StatsDay_LogsField->addItems(
+ this->crapview.getFields( "Daytime" ));
+ this->ui->box_StatsDay_LogsField->setCurrentIndex( 0 );
+ // refresh dates
+ QStringList years = this->crapview.getYears( this->wsFromIndex( index ) );
+ this->ui->box_StatsDay_FromYear->addItems( years );
+ this->ui->box_StatsDay_FromYear->setCurrentIndex( 0 );
+ if ( this->ui->checkBox_StatsDay_Period->isChecked() ) {
+ this->ui->box_StatsDay_ToYear->addItems( years );
+ this->ui->box_StatsDay_ToYear->setCurrentIndex( 0 );
+ }
+ years.clear();
+ }
+ }
+ this->checkStatsDayDrawable();
+}
+
+
+void MainWindow::on_box_StatsDay_LogsField_currentIndexChanged(int index)
+{
+ this->ui->inLine_StatsDay_Filter->clear();
+ this->checkStatsDayDrawable();
+}
+
+void MainWindow::on_box_StatsDay_FromYear_currentIndexChanged(int index)
+{
+ this->ui->box_StatsDay_FromMonth->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsDay_FromMonth->addItems(
+ this->crapview.getMonths(
+ this->wsFromIndex( this->ui->box_StatsDay_WebServer->currentIndex() ),
+ this->ui->box_StatsDay_FromYear->currentText() ) );
+ this->ui->box_StatsDay_FromMonth->setCurrentIndex( 0 );
+ }
+ this->checkStatsDayDrawable();
+}
+
+void MainWindow::on_box_StatsDay_FromMonth_currentIndexChanged(int index)
+{
+ this->ui->box_StatsDay_FromDay->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsDay_FromDay->addItems(
+ this->crapview.getDays(
+ this->wsFromIndex( this->ui->box_StatsDay_WebServer->currentIndex() ),
+ this->ui->box_StatsDay_FromYear->currentText(),
+ this->ui->box_StatsDay_FromMonth->currentText() ) );
+ this->ui->box_StatsDay_FromDay->setCurrentIndex( 0 );
+ }
+ this->checkStatsDayDrawable();
+}
+
+void MainWindow::on_box_StatsDay_FromDay_currentIndexChanged(int index)
+{
+ this->checkStatsDayDrawable();
+}
+
+void MainWindow::on_checkBox_StatsDay_Period_stateChanged(int state)
+{
+ if ( state == Qt::CheckState::Checked ) {
+ this->ui->box_StatsDay_ToYear->setEnabled( true );
+ this->ui->box_StatsDay_ToMonth->setEnabled( true );
+ this->ui->box_StatsDay_ToDay->setEnabled( true );
+ // add available dates
+ this->ui->box_StatsDay_ToYear->addItems( this->crapview.getYears(
+ this->wsFromIndex( this->ui->box_StatsDay_WebServer->currentIndex() ) ) );
+ this->ui->box_StatsDay_ToYear->setCurrentIndex( 0 );
+ } else {
+ this->ui->box_StatsDay_ToYear->clear();
+ this->ui->box_StatsDay_ToYear->setEnabled( false );
+ this->ui->box_StatsDay_ToMonth->clear();
+ this->ui->box_StatsDay_ToMonth->setEnabled( false );
+ this->ui->box_StatsDay_ToDay->clear();
+ this->ui->box_StatsDay_ToDay->setEnabled( false );
+ }
+}
+
+void MainWindow::on_box_StatsDay_ToYear_currentIndexChanged(int index)
+{
+ this->ui->box_StatsDay_ToMonth->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsDay_ToMonth->addItems(
+ this->crapview.getMonths(
+ this->wsFromIndex( this->ui->box_StatsDay_WebServer->currentIndex() ),
+ this->ui->box_StatsDay_ToYear->currentText() ) );
+ this->ui->box_StatsDay_ToMonth->setCurrentIndex( 0 );
+ }
+ this->checkStatsDayDrawable();
+}
+
+void MainWindow::on_box_StatsDay_ToMonth_currentIndexChanged(int index)
+{
+ this->ui->box_StatsDay_ToDay->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsDay_ToDay->addItems(
+ this->crapview.getDays(
+ this->wsFromIndex( this->ui->box_StatsDay_WebServer->currentIndex() ),
+ this->ui->box_StatsDay_ToYear->currentText(),
+ this->ui->box_StatsDay_ToMonth->currentText() ) );
+ this->ui->box_StatsDay_ToDay->setCurrentIndex( 0 );
+ }
+ this->checkStatsDayDrawable();
+}
+
+void MainWindow::on_box_StatsDay_ToDay_currentIndexChanged(int index)
+{
+ this->checkStatsDayDrawable();
+}
+
+
+void MainWindow::on_button_StatsDay_Draw_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->setDbWorkingState( true );
+ delete this->crapview_timer;
+ this->crapview_timer = new QTimer(this);
+ this->crapview_timer->setSingleShot( true );
+ connect(this->crapview_timer, SIGNAL(timeout()), this, SLOT(drawStatsDay()));
+ this->crapview_timer->start(250);
+ }
+}
+void MainWindow::drawStatsDay()
+{
+ QString filter;
+ if ( this->ui->box_StatsDay_LogsField->currentIndex() == 0 ) {
+ filter = this->crapview.parseBooleanFilter( this->ui->inLine_StatsDay_Filter->text() );
+ } else if ( this->ui->box_StatsDay_LogsField->currentIndex() == 5 ) {
+ filter = this->crapview.parseNumericFilter( this->ui->inLine_StatsDay_Filter->text() );
+ } else {
+ filter = this->crapview.parseTextualFilter( this->ui->inLine_StatsDay_Filter->text() );
+ }
+ this->crapview.drawDay(
+ this->ui->chart_StatsDay,
+ this->CHARTS_THEMES.at( this->charts_theme_id ), this->FONTS,
+ this->wsFromIndex( this->ui->box_StatsDay_WebServer->currentIndex() ),
+ this->ui->box_StatsDay_FromYear->currentText(),
+ this->ui->box_StatsDay_FromMonth->currentText(),
+ this->ui->box_StatsDay_FromDay->currentText(),
+ ( this->ui->checkBox_StatsDay_Period->isChecked() ) ? this->ui->box_StatsDay_ToYear->currentText() : "",
+ ( this->ui->checkBox_StatsDay_Period->isChecked() ) ? this->ui->box_StatsDay_ToMonth->currentText() : "",
+ ( this->ui->checkBox_StatsDay_Period->isChecked() ) ? this->ui->box_StatsDay_ToDay->currentText() : "",
+ this->ui->box_StatsDay_LogsField->currentText(),
+ filter );
+ this->setDbWorkingState( false );
+}
+
+
+
+////////////////////
+//// RELATIONAL ////
+void MainWindow::checkStatsRelatDrawable()
+{
+ if ( ! this->db_working ) {
+ bool aux = true;
+ if ( this->ui->box_StatsRelat_FromYear->currentIndex() >= 0
+ && this->ui->box_StatsRelat_FromMonth->currentIndex() >= 0
+ && this->ui->box_StatsRelat_FromDay->currentIndex() >= 0
+ && this->ui->box_StatsRelat_ToYear->currentIndex() >= 0
+ && this->ui->box_StatsRelat_ToMonth->currentIndex() >= 0
+ && this->ui->box_StatsRelat_ToDay->currentIndex() >= 0 ) {
+ // check period validity
+ int a,b;
+ a = this->ui->box_StatsRelat_ToYear->currentText().toInt();
+ b = this->ui->box_StatsRelat_FromYear->currentText().toInt();
+ if ( a < b ) {
+ // year 'to' is less than 'from'
+ aux = false;
+ } else if ( a == b ) {
+ a = this->crapview.getMonthNumber( this->ui->box_StatsRelat_ToMonth->currentText() );
+ b = this->crapview.getMonthNumber( this->ui->box_StatsRelat_FromMonth->currentText() );
+ if ( a < b ) {
+ // month 'to' is less than 'from'
+ aux = false;
+ } else if ( a == b ) {
+ a = this->ui->box_StatsRelat_ToDay->currentText().toInt();
+ b = this->ui->box_StatsRelat_FromDay->currentText().toInt();
+ if ( a < b ) {
+ // day 'to' is less than 'from'
+ aux = false;
+ }
+ }
+ }
+ } else {
+ // disable the draw button
+ aux = false;
+ }
+ // check log field validity
+ if ( this->ui->box_StatsRelat_LogsField_1->currentIndex() < 0
+ || this->ui->box_StatsRelat_LogsField_2->currentIndex() < 0 ) {
+ aux = false;
+ }
+ this->ui->button_StatsRelat_Draw->setEnabled( aux );
+
+ } else {
+ // db busy
+ this->ui->button_StatsRelat_Draw->setEnabled( false );
+ }
+}
+
+void MainWindow::on_box_StatsRelat_WebServer_currentIndexChanged(int index)
+{
+ if ( this->checkDataDB() ) {
+ this->ui->box_StatsRelat_LogsField_1->clear();
+ this->ui->box_StatsRelat_LogsField_2->clear();
+ if ( index != -1 ) {
+ // refresh fields
+ QStringList fields = this->crapview.getFields( "Relational" );
+ this->ui->box_StatsRelat_LogsField_1->addItems( fields );
+ this->ui->box_StatsRelat_LogsField_2->addItems( fields );
+ this->ui->box_StatsRelat_LogsField_1->setCurrentIndex( 0 );
+ this->ui->box_StatsRelat_LogsField_2->setCurrentIndex( 0 );
+ // refresh dates
+ QStringList years = this->crapview.getYears( this->wsFromIndex( index ) );
+ // from
+ this->ui->box_StatsRelat_FromYear->clear();
+ this->ui->box_StatsRelat_FromYear->addItems( years );
+ this->ui->box_StatsRelat_FromYear->setCurrentIndex( 0 );
+ // to
+ this->ui->box_StatsRelat_ToYear->clear();
+ this->ui->box_StatsRelat_ToYear->addItems( years );
+ this->ui->box_StatsRelat_ToYear->setCurrentIndex( 0 );
+ years.clear();
+ }
+ }
+ this->checkStatsRelatDrawable();
+}
+
+void MainWindow::on_box_StatsRelat_LogsField_1_currentIndexChanged(int index)
+{
+ this->ui->inLine_StatsRelat_Filter_1->clear();
+ this->checkStatsRelatDrawable();
+}
+
+void MainWindow::on_box_StatsRelat_LogsField_2_currentIndexChanged(int index)
+{
+ this->ui->inLine_StatsRelat_Filter_2->clear();
+ this->checkStatsRelatDrawable();
+}
+
+void MainWindow::on_box_StatsRelat_FromYear_currentIndexChanged(int index)
+{
+ this->ui->box_StatsRelat_FromMonth->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsRelat_FromMonth->addItems(
+ this->crapview.getMonths(
+ this->wsFromIndex( this->ui->box_StatsRelat_WebServer->currentIndex() ),
+ this->ui->box_StatsRelat_FromYear->currentText() ) );
+ this->ui->box_StatsRelat_FromMonth->setCurrentIndex( 0 );
+ }
+ this->checkStatsRelatDrawable();
+}
+
+void MainWindow::on_box_StatsRelat_FromMonth_currentIndexChanged(int index)
+{
+ this->ui->box_StatsRelat_FromDay->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsRelat_FromDay->addItems(
+ this->crapview.getDays(
+ this->wsFromIndex( this->ui->box_StatsRelat_WebServer->currentIndex() ),
+ this->ui->box_StatsRelat_FromYear->currentText(),
+ this->ui->box_StatsRelat_FromMonth->currentText() ) );
+ this->ui->box_StatsRelat_FromDay->setCurrentIndex( 0 );
+ }
+ this->checkStatsRelatDrawable();
+}
+
+void MainWindow::on_box_StatsRelat_FromDay_currentIndexChanged(int index)
+{
+ this->checkStatsRelatDrawable();
+}
+
+void MainWindow::on_box_StatsRelat_ToYear_currentIndexChanged(int index)
+{
+ this->ui->box_StatsRelat_ToMonth->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsRelat_ToMonth->addItems(
+ this->crapview.getMonths(
+ this->wsFromIndex( this->ui->box_StatsRelat_WebServer->currentIndex() ),
+ this->ui->box_StatsRelat_ToYear->currentText() ) );
+ this->ui->box_StatsRelat_ToMonth->setCurrentIndex( 0 );
+ }
+ this->checkStatsRelatDrawable();
+}
+
+void MainWindow::on_box_StatsRelat_ToMonth_currentIndexChanged(int index)
+{
+ this->ui->box_StatsRelat_ToDay->clear();
+ if ( index != -1 ) {
+ this->ui->box_StatsRelat_ToDay->addItems(
+ this->crapview.getDays(
+ this->wsFromIndex( this->ui->box_StatsRelat_WebServer->currentIndex() ),
+ this->ui->box_StatsRelat_ToYear->currentText(),
+ this->ui->box_StatsRelat_ToMonth->currentText() ) );
+ this->ui->box_StatsRelat_ToDay->setCurrentIndex( 0 );
+ }
+ this->checkStatsRelatDrawable();
+}
+
+void MainWindow::on_box_StatsRelat_ToDay_currentIndexChanged(int index)
+{
+ this->checkStatsRelatDrawable();
+}
+
+
+void MainWindow::on_button_StatsRelat_Draw_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->setDbWorkingState( true );
+ delete this->crapview_timer;
+ this->crapview_timer = new QTimer(this);
+ this->crapview_timer->setSingleShot( true );
+ connect(this->crapview_timer, SIGNAL(timeout()), this, SLOT(drawStatsRelat()));
+ this->crapview_timer->start(250);
+ }
+}
+void MainWindow::drawStatsRelat()
+{
+ int aux;
+ QString filter1, filter2;
+ aux = this->ui->box_StatsRelat_LogsField_1->currentIndex();
+ if ( aux == 0 ) {
+ filter1 = this->crapview.parseBooleanFilter( this->ui->inLine_StatsRelat_Filter_1->text() );
+ } else if ( aux >= 5 && aux <= 8 ) {
+ filter1 = this->crapview.parseNumericFilter( this->ui->inLine_StatsRelat_Filter_1->text() );
+ } else {
+ filter1 = this->ui->inLine_StatsRelat_Filter_1->text();
+ }
+ aux = this->ui->box_StatsRelat_LogsField_2->currentIndex();
+ if ( aux == 0 ) {
+ filter2 = this->crapview.parseBooleanFilter( this->ui->inLine_StatsRelat_Filter_2->text() );
+ } else if ( aux >= 5 && aux <= 8 ) {
+ filter2 = this->crapview.parseNumericFilter( this->ui->inLine_StatsRelat_Filter_2->text() );
+ } else {
+ filter2 = this->crapview.parseTextualFilter( this->ui->inLine_StatsRelat_Filter_2->text() );
+ }
+ this->crapview.drawRelat(
+ this->ui->chart_StatsRelat,
+ this->CHARTS_THEMES.at( this->charts_theme_id ), this->FONTS,
+ this->wsFromIndex( this->ui->box_StatsRelat_WebServer->currentIndex() ),
+ this->ui->box_StatsRelat_FromYear->currentText(),
+ this->ui->box_StatsRelat_FromMonth->currentText(),
+ this->ui->box_StatsRelat_FromDay->currentText(),
+ this->ui->box_StatsRelat_ToYear->currentText(),
+ this->ui->box_StatsRelat_ToMonth->currentText(),
+ this->ui->box_StatsRelat_ToDay->currentText(),
+ this->ui->box_StatsRelat_LogsField_1->currentText(), filter1,
+ this->ui->box_StatsRelat_LogsField_2->currentText(), filter2 );
+ this->setDbWorkingState( false );
+}
+
+
+
+////////////////
+//// GLOBAL ////
+//
+void MainWindow::makeStatsGlobals()
+{
+ if ( this->checkDataDB() ) {
+ std::vector> recur_list;
+ std::vector> traffic_list;
+ std::vector> perf_list;
+ std::vector work_list;
+
+ const bool result = this->crapview.calcGlobals(
+ recur_list, traffic_list, perf_list, work_list,
+ this->glob_ws );
+
+ if ( result ) {
+ this->ui->label_StatsGlob_Recur_Protocol_String->setText( std::get<0>( recur_list.at(0) ) );
+ this->ui->label_StatsGlob_Recur_Protocol_Count->setText( std::get<1>( recur_list.at(0) ) );
+ this->ui->label_StatsGlob_Recur_Method_String->setText( std::get<0>( recur_list.at(1) ) );
+ this->ui->label_StatsGlob_Recur_Method_Count->setText( std::get<1>( recur_list.at(1) ) );
+ this->ui->label_StatsGlob_Recur_URI_String->setText( std::get<0>( recur_list.at(2) ) );
+ this->ui->label_StatsGlob_Recur_URI_Count->setText( std::get<1>( recur_list.at(2) ) );
+ this->ui->label_StatsGlob_Recur_UserAgent_String->setText( std::get<0>( recur_list.at(3) ) );
+ this->ui->label_StatsGlob_Recur_UserAgent_Count->setText( std::get<1>( recur_list.at(3) ) );
+
+ this->ui->label_StatsGlob_Traffic_Date_String->setText( std::get<0>( traffic_list.at(0) ) );
+ this->ui->label_StatsGlob_Traffic_Date_Count->setText( std::get<1>( traffic_list.at(0) ) );
+ this->ui->label_StatsGlob_Traffic_Day_String->setText( std::get<0>( traffic_list.at(1) ) );
+ this->ui->label_StatsGlob_Traffic_Day_Count->setText( std::get<1>( traffic_list.at(1) ) );
+ this->ui->label_StatsGlob_Traffic_Hour_String->setText( std::get<0>( traffic_list.at(2) ) );
+ this->ui->label_StatsGlob_Traffic_Hour_Count->setText( std::get<1>( traffic_list.at(2) ) );
+
+ this->ui->label_StatsGlob_Perf_Time_Mean->setText( std::get<0>( perf_list.at(0) ) );
+ this->ui->label_StatsGlob_Perf_Time_Max->setText( std::get<1>( perf_list.at(0) ) );
+ this->ui->label_StatsGlob_Perf_Sent_Mean->setText( std::get<0>( perf_list.at(1) ) );
+ this->ui->label_StatsGlob_Perf_Sent_Max->setText( std::get<1>( perf_list.at(1) ) );
+ this->ui->label_StatsGlob_Perf_Received_Mean->setText( std::get<0>( perf_list.at(2) ) );
+ this->ui->label_StatsGlob_Perf_Received_Max->setText( std::get<1>( perf_list.at(2) ) );
+
+ this->ui->label_StatsGlob_Work_Req_Count->setText( work_list.at(0) );
+ this->ui->label_StatsGlob_Work_Time_Count->setText( work_list.at(1) );
+ this->ui->label_StatsGlob_Work_Sent_Count->setText( work_list.at(2) );
+
+ if ( this->glob_ws == "apache" ) {
+ if ( this->ui->button_StatsGlob_Apache->isFlat() ) {
+ // un-flat
+ this->ui->button_StatsGlob_Apache->setFlat( false );
+ this->ui->button_StatsGlob_Nginx->setFlat( true );
+ this->ui->button_StatsGlob_Iis->setFlat( true );
+ }
+ } else if ( this->glob_ws == "nginx" ) {
+ if ( this->ui->button_StatsGlob_Nginx->isFlat() ) {
+ // un-flat
+ this->ui->button_StatsGlob_Nginx->setFlat( false );
+ this->ui->button_StatsGlob_Apache->setFlat( true );
+ this->ui->button_StatsGlob_Iis->setFlat( true );
+ }
+ } else if ( this->glob_ws == "iis" ) {
+ if ( this->ui->button_StatsGlob_Iis->isFlat() ) {
+ // un-flat
+ this->ui->button_StatsGlob_Iis->setFlat( false );
+ this->ui->button_StatsGlob_Apache->setFlat( true );
+ this->ui->button_StatsGlob_Nginx->setFlat( true );
+ }
+ }
+
+ } else {
+ this->resetStatsGlobals();
+ }
+ recur_list.clear(); traffic_list.clear();
+ perf_list.clear(); work_list.clear();
+
+ } else {
+ this->resetStatsGlobals();
+ }
+ // restore
+ this->setDbWorkingState( false );
+}
+
+void MainWindow::resetStatsGlobals()
+{
+ this->ui->label_StatsGlob_Recur_Protocol_String->setText( "-" );
+ this->ui->label_StatsGlob_Recur_Protocol_Count->setText( "0" );
+ this->ui->label_StatsGlob_Recur_Method_String->setText( "-" );
+ this->ui->label_StatsGlob_Recur_Method_Count->setText( "0" );
+ this->ui->label_StatsGlob_Recur_URI_String->setText( "-" );
+ this->ui->label_StatsGlob_Recur_URI_Count->setText( "0" );
+ this->ui->label_StatsGlob_Recur_UserAgent_String->setText( "-" );
+ this->ui->label_StatsGlob_Recur_UserAgent_Count->setText( "0" );
+
+ this->ui->label_StatsGlob_Traffic_Date_String->setText( "-" );
+ this->ui->label_StatsGlob_Traffic_Date_Count->setText( "0" );
+ this->ui->label_StatsGlob_Traffic_Day_String->setText( "-" );
+ this->ui->label_StatsGlob_Traffic_Day_Count->setText( "0" );
+ this->ui->label_StatsGlob_Traffic_Hour_String->setText( "-" );
+ this->ui->label_StatsGlob_Traffic_Hour_Count->setText( "0" );
+
+ this->ui->label_StatsGlob_Perf_Time_Mean->setText( "-" );
+ this->ui->label_StatsGlob_Perf_Time_Max->setText( "-" );
+ this->ui->label_StatsGlob_Perf_Sent_Mean->setText( "-" );
+ this->ui->label_StatsGlob_Perf_Sent_Max->setText( "-" );
+ this->ui->label_StatsGlob_Perf_Received_Mean->setText( "-" );
+ this->ui->label_StatsGlob_Perf_Received_Max->setText( "-" );
+
+ this->ui->label_StatsGlob_Work_Req_Count->setText( "-" );
+ this->ui->label_StatsGlob_Work_Time_Count->setText( "-" );
+ this->ui->label_StatsGlob_Work_Sent_Count->setText( "-" );
+
+ if ( this->ui->button_StatsGlob_Apache->isChecked() ) {
+ this->ui->button_StatsGlob_Apache->setChecked( false );
+ } else if ( this->ui->button_StatsGlob_Nginx->isChecked() ) {
+ this->ui->button_StatsGlob_Nginx->setChecked( false );
+ } else if ( this->ui->button_StatsGlob_Iis->isChecked() ) {
+ this->ui->button_StatsGlob_Iis->setChecked( false );
+ }
+}
+
+
+
+void MainWindow::globalsButtonClicked()
+{
+ this->setDbWorkingState( true );
+ delete this->crapview_timer;
+ this->crapview_timer = new QTimer(this);
+ this->crapview_timer->setSingleShot( true );
+ connect(this->crapview_timer, SIGNAL(timeout()), this, SLOT(makeStatsGlobals()));
+ this->crapview_timer->start(250);
+}
+
+void MainWindow::on_button_StatsGlob_Apache_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->glob_ws = "apache";
+ this->globalsButtonClicked();
+ }
+}
+
+
+void MainWindow::on_button_StatsGlob_Nginx_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->glob_ws = "nginx";
+ this->globalsButtonClicked();
+ }
+}
+
+
+void MainWindow::on_button_StatsGlob_Iis_clicked()
+{
+ if ( this->checkDataDB() ) {
+ this->glob_ws = "iis";
+ this->globalsButtonClicked();
+ }
+}
+
+
+
+/////////////////////////
+//////// CONFIGS ////////
+/////////////////////////
+
+/////////////////
+//// GENERAL ////
+/////////////////
+
+////////////////
+//// WINDOW ////
+void MainWindow::on_checkBox_ConfWindow_Geometry_clicked(bool checked)
+{
+ this->remember_window = checked;
+}
+
+void MainWindow::on_box_ConfWindow_Theme_currentIndexChanged(int index)
+{
+ this->window_theme_id = index;
+ this->updateUiTheme();
+}
+
+void MainWindow::on_box_ConfWindow_Icons_currentIndexChanged(int index)
+{
+ this->icons_theme_id = index;
+ this->updateUiIcons();
+}
+
+
+/////////////////
+//// DIALOGS ////
+void MainWindow::on_slider_ConfDialogs_General_sliderReleased()
+{
+ this->dialogs_level = this->ui->slider_ConfDialogs_General->value();
+}
+void MainWindow::on_slider_ConfDialogs_Logs_sliderReleased()
+{
+ this->craplog.setDialogsLevel( this->ui->slider_ConfDialogs_Logs->value() );
+}
+void MainWindow::on_slider_ConfDialogs_Stats_sliderReleased()
+{
+ this->crapview.setDialogsLevel( this->ui->slider_ConfDialogs_Stats->value() );
+}
+
+
+//////////////////////
+//// TEXT BROWSER ////
+void MainWindow::on_box_ConfTextBrowser_Font_currentIndexChanged(int index)
+{
+ QFont font;
+ switch ( index ) {
+ case 0:
+ font = this->FONTS.at( "main" );
+ break;
+ case 1:
+ font = this->FONTS.at( "alternative" );
+ break;
+ case 2:
+ font = this->FONTS.at( "script" );
+ break;
+ default:
+ throw GenericException( "Unexpected Font index: "+std::to_string(index), true );
+ }
+ this->TB.setFont( font );
+ this->crapnote->setTextFont( font );
+ this->ui->textBrowser_ConfTextBrowser_Preview->setFont( font );
+ this->ui->preview_ConfApache_Format_Sample->setFont( this->TB.getFont() );
+ this->ui->preview_ConfNginx_Format_Sample->setFont( this->TB.getFont() );
+ this->ui->preview_ConfIis_Format_Sample->setFont( this->TB.getFont() );
+}
+void MainWindow::on_checkBox_ConfTextBrowser_WideLines_clicked(bool checked)
+{
+ this->TB.setWideLinesUsage( checked );
+ this->refreshTextBrowserPreview();
+}
+void MainWindow::on_box_ConfTextBrowser_ColorScheme_currentIndexChanged(int index)
+{
+ this->TB.setColorScheme( index, this->TB_COLOR_SCHEMES.at( index ) );
+ this->crapnote->setColorScheme( index );
+ this->refreshTextBrowserPreview();
+}
+void MainWindow::refreshTextBrowserPreview()
+{
+ QString content = "";
+ this->TB.makePreview( content );
+ this->ui->textBrowser_ConfTextBrowser_Preview->setText( content );
+ this->ui->textBrowser_ConfTextBrowser_Preview->setFont( this->TB.getFont() );
+}
+
+
+////////////////
+//// CHARTS ////
+void MainWindow::on_box_ConfCharts_Theme_currentIndexChanged(int index)
+{
+ this->charts_theme_id = index;
+ this->refreshChartsPreview();
+}
+void MainWindow::refreshChartsPreview()
+{
+ QColor col = Qt::GlobalColor::darkGreen;
+ QBarSet *bars_1 = new QBarSet( "" );
+ bars_1->setColor( col );
+ QBarSet *bars_2 = new QBarSet( "" );
+ bars_2->setColor( col );
+ QBarSet *bars_3 = new QBarSet( "" );
+ bars_3->setColor( col );
+ QBarSet *bars_4 = new QBarSet( "" );
+ bars_4->setColor( col );
+ QBarSet *bars_5 = new QBarSet( "" );
+ bars_5->setColor( col );
+ QBarSet *bars_6 = new QBarSet( "" );
+ bars_6->setColor( col );
+
+ int aux, max=0;
+ for ( int i=0; i<24; i++ ) {
+ aux = rand() %100; *bars_1 << aux;
+ if ( aux > max ) { max = aux; }
+ aux = rand() %100; *bars_2 << aux;
+ if ( aux > max ) { max = aux; }
+ aux = rand() %100; *bars_3 << aux;
+ if ( aux > max ) { max = aux; }
+ aux = rand() %100; *bars_4 << aux;
+ if ( aux > max ) { max = aux; }
+ aux = rand() %100; *bars_5 << aux;
+ if ( aux > max ) { max = aux; }
+ aux = rand() %100; *bars_6 << aux;
+ if ( aux > max ) { max = aux; }
+ }
+
+ QBarSeries *bars = new QBarSeries();
+ bars->append( bars_1 ); bars->append( bars_2 ); bars->append( bars_3 );
+ bars->append( bars_4 ); bars->append( bars_5 ); bars->append( bars_6 );
+ bars->setBarWidth( 1 );
+
+ QChart *t_chart = new QChart();
+ // apply the theme
+ t_chart->setTheme( this->CHARTS_THEMES.at( this->charts_theme_id ) );
+ // add the bars
+ t_chart->addSeries( bars );
+ t_chart->setTitle( "Sample preview" );
+ t_chart->setTitleFont( this->FONTS.at("main") );
+ t_chart->setAnimationOptions( QChart::SeriesAnimations );
+
+ QStringList categories;
+ categories << "00" << "01" << "02" << "03" << "04" << "05" << "06" << "07" << "08" << "09" << "10" << "11"
+ << "12" << "13" << "14" << "15" << "16" << "17" << "18" << "19" << "20" << "21" << "22" << "23";
+
+ QBarCategoryAxis *axisX = new QBarCategoryAxis();
+ axisX->append( categories );
+ axisX->setLabelsFont( this->FONTS.at( "main_small" ) );
+ axisX->setTitleText( "Infoes" );
+ axisX->setTitleFont( this->FONTS.at("main_small") );
+ t_chart->addAxis( axisX, Qt::AlignBottom );
+ bars->attachAxis( axisX );
+
+ QValueAxis *axisY = new QValueAxis();
+ axisY->setLabelFormat( "%d" );
+ axisY->setRange( 0, max );
+ axisY->setLabelsFont( this->FONTS.at( "main_small" ) );
+ t_chart->addAxis( axisY, Qt::AlignLeft );
+ bars->attachAxis( axisY) ;
+
+ t_chart->legend()->setVisible( false );
+ /*t_chart->legend()->setFont( this->FONTS.at("main_small") );
+ t_chart->legend()->setAlignment( Qt::AlignBottom );*/
+
+ this->ui->chart_ConfCharts_Preview->setChart( t_chart );
+ this->ui->chart_ConfCharts_Preview->setRenderHint( QPainter::Antialiasing );
+}
+
+
+///////////////////
+//// DATABASES ////
+// data collection
+void MainWindow::on_inLine_ConfDatabases_Data_Path_textChanged(const QString &arg1)
+{
+ if ( arg1.size() > 0 ) {
+ std::string path = this->resolvePath( arg1.toStdString() );
+ if ( IOutils::checkDir( path ) ) {
+ this->ui->icon_ConfDatabases_Data_Wrong->setVisible( false );
+ this->ui->button_ConfDatabases_Data_Save->setEnabled( true );
+ } else {
+ this->ui->icon_ConfDatabases_Data_Wrong->setVisible( true );
+ this->ui->button_ConfDatabases_Data_Save->setEnabled( false );
+ }
+ } else {
+ this->ui->icon_ConfDatabases_Data_Wrong->setVisible( true );
+ this->ui->button_ConfDatabases_Data_Save->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfDatabases_Data_Path_returnPressed()
+{
+ this->on_button_ConfDatabases_Data_Save_clicked();
+}
+void MainWindow::on_button_ConfDatabases_Data_Save_clicked()
+{
+ if ( ! this->ui->icon_ConfDatabases_Data_Wrong->isVisible() ) {
+ // set the paths
+ std::string path = this->resolvePath( this->ui->inLine_ConfDatabases_Data_Path->text().toStdString() );
+ if ( StringOps::endsWith( path, "/" ) ) {
+ path = StringOps::rstrip( path, "/" );
+ }
+ if ( ! IOutils::checkDir( path, true ) ) {
+ DialogSec::warnDirNotReadable( nullptr );
+ }
+ if ( ! IOutils::checkDir( path, false, true ) ) {
+ DialogSec::warnDirNotWritable( nullptr );
+ }
+ this->db_data_path = path;
+ this->craplog.setStatsDatabasePath( path );
+ this->crapview.setDbPath( path );
+ this->ui->inLine_ConfDatabases_Data_Path->setText( QString::fromStdString( path ) );
+ }
+ this->ui->inLine_ConfDatabases_Data_Path->setFocus();
+ this->ui->button_ConfDatabases_Data_Save->setEnabled( false );
+}
+
+// usef files hashes
+void MainWindow::on_inLine_ConfDatabases_Hashes_Path_textChanged(const QString &arg1)
+{
+ if ( arg1.size() > 0 ) {
+ std::string path = this->resolvePath( arg1.toStdString() );
+ if ( IOutils::checkDir( path ) ) {
+ this->ui->icon_ConfDatabases_Hashes_Wrong->setVisible( false );
+ this->ui->button_ConfDatabases_Hashes_Save->setEnabled( true );
+ } else {
+ this->ui->icon_ConfDatabases_Hashes_Wrong->setVisible( true );
+ this->ui->button_ConfDatabases_Hashes_Save->setEnabled( false );
+ }
+ } else {
+ this->ui->icon_ConfDatabases_Hashes_Wrong->setVisible( true );
+ this->ui->button_ConfDatabases_Hashes_Save->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfDatabases_Hashes_Path_returnPressed()
+{
+ this->on_button_ConfDatabases_Hashes_Save_clicked();
+}
+void MainWindow::on_button_ConfDatabases_Hashes_Save_clicked()
+{
+ if ( ! this->ui->icon_ConfDatabases_Hashes_Wrong->isVisible() ) {
+ // set the paths
+ std::string path = this->resolvePath( this->ui->inLine_ConfDatabases_Hashes_Path->text().toStdString() );
+ if ( StringOps::endsWith( path, "/" ) ) {
+ path = StringOps::rstrip( path, "/" );
+ }
+ if ( ! IOutils::checkDir( path, true ) ) {
+ DialogSec::warnDirNotReadable( nullptr );
+ }
+ if ( ! IOutils::checkDir( path, false, true ) ) {
+ DialogSec::warnDirNotWritable( nullptr );
+ }
+ this->db_hashes_path = path;
+ this->craplog.setHashesDatabasePath( path );
+ this->ui->inLine_ConfDatabases_Hashes_Path->setText( QString::fromStdString( path ) );
+ }
+ this->ui->inLine_ConfDatabases_Hashes_Path->setFocus();
+ this->ui->button_ConfDatabases_Hashes_Save->setEnabled( false );
+}
+
+// backups
+void MainWindow::on_checkBox_ConfDatabases_DoBackup_clicked(bool checked)
+{
+ this->db_do_backup = checked;
+ this->ui->spinBox_ConfDatabases_NumBackups->setEnabled( checked );
+ if ( checked && this->ui->spinBox_ConfDatabases_NumBackups->value() == 0 ) {
+ this->ui->spinBox_ConfDatabases_NumBackups->setValue( 1 );
+ }
+}
+void MainWindow::on_spinBox_ConfDatabases_NumBackups_valueChanged(int arg1)
+{
+ this->db_backups_number = arg1;
+ if ( arg1 == 1 ) {
+ this->ui->spinBox_ConfDatabases_NumBackups->setSuffix( " " + MainWindow::tr( "copy" ) );
+ } else {
+ this->ui->spinBox_ConfDatabases_NumBackups->setSuffix( " " + MainWindow::tr( "copies" ) );
+ if ( arg1 == 0 ) {
+ this->ui->checkBox_ConfDatabases_DoBackup->setChecked( false );
+ this->on_checkBox_ConfDatabases_DoBackup_clicked( false );
+ }
+ }
+}
+
+
+//////////////
+//// LOGS ////
+//////////////
+
+//////////////////
+//// DEFAULTS ////
+void MainWindow::on_radio_ConfDefaults_Apache_toggled(bool checked)
+{
+ this->default_ws = this->APACHE_ID;
+}
+void MainWindow::on_radio_ConfDefaults_Nginx_toggled(bool checked)
+{
+ this->default_ws = this->NGINX_ID;
+}
+void MainWindow::on_radio_ConfDefaults_Iis_toggled(bool checked)
+{
+ this->default_ws = this->IIS_ID;
+}
+
+/////////////////
+//// CONTROL ////
+void MainWindow::on_checkBox_ConfControl_Usage_clicked(bool checked)
+{
+ this->hide_used_files = checked;
+}
+void MainWindow::on_checkBox_ConfControl_Size_clicked(bool checked)
+{
+ if ( ! checked ) {
+ // disable size warning
+ this->ui->spinBox_ConfControl_Size->setEnabled( false );
+ this->craplog.setWarningSize( 0 );
+ } else {
+ // enable warning
+ this->ui->spinBox_ConfControl_Size->setEnabled( true );
+ this->craplog.setWarningSize( (this->ui->spinBox_ConfControl_Size->value() * 1'048'576) +1 );
+ }
+}
+void MainWindow::on_spinBox_ConfControl_Size_editingFinished()
+{
+ this->craplog.setWarningSize( (this->ui->spinBox_ConfControl_Size->value() * 1'048'576) +1 );
+}
+
+
+////////////////
+//// APACHE ////
+// paths
+void MainWindow::on_inLine_ConfApache_Path_String_textChanged(const QString &arg1)
+{
+ if ( arg1.size() > 0 ) {
+ std::string path = this->resolvePath( arg1.toStdString() );
+ if ( IOutils::checkDir( path ) ) {
+ this->ui->icon_ConfApache_Path_Wrong->setVisible( false );
+ this->ui->button_ConfApache_Path_Save->setEnabled( true );
+ } else {
+ this->ui->icon_ConfApache_Path_Wrong->setVisible( true );
+ this->ui->button_ConfApache_Path_Save->setEnabled( false );
+ }
+ } else {
+ this->ui->icon_ConfApache_Path_Wrong->setVisible( true );
+ this->ui->button_ConfApache_Path_Save->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfApache_Path_String_returnPressed()
+{
+ this->on_button_ConfApache_Path_Save_clicked();
+}
+void MainWindow::on_button_ConfApache_Path_Save_clicked()
+{
+ if ( ! this->ui->icon_ConfApache_Path_Wrong->isVisible() ) {
+ // set the paths
+ std::string path = this->resolvePath( this->ui->inLine_ConfApache_Path_String->text().toStdString() );
+ if ( StringOps::endsWith( path, "/" ) ) {
+ path = StringOps::rstrip( path, "/" );
+ }
+ if ( ! IOutils::checkDir( path, true ) ) {
+ DialogSec::warnDirNotReadable( nullptr );
+ }
+ this->craplog.setLogsPath( this->APACHE_ID, path );
+ this->ui->inLine_ConfApache_Path_String->setText( QString::fromStdString( path ) );
+ }
+ this->ui->button_ConfApache_Path_Save->setEnabled( false );
+}
+
+// formats
+void MainWindow::on_inLine_ConfApache_Format_String_cursorPositionChanged(int arg1, int arg2)
+{
+ if ( arg2 > 0 ) {
+ this->ui->button_ConfApache_Format_Save->setEnabled( true );
+ } else {
+ this->ui->button_ConfApache_Format_Save->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfApache_Format_String_returnPressed()
+{
+ if ( this->ui->button_ConfApache_Format_Save->isEnabled() ) {
+ this->on_button_ConfApache_Format_Save_clicked();
+ }
+}
+void MainWindow::on_button_ConfApache_Format_Save_clicked()
+{
+ const bool success = this->craplog.setApacheLogFormat(
+ this->ui->inLine_ConfApache_Format_String->text().toStdString() );
+ if ( success ) {
+ this->ui->button_ConfApache_Format_Save->setEnabled( false );
+ }
+}
+void MainWindow::on_button_ConfApache_Format_Sample_clicked()
+{
+ this->ui->preview_ConfApache_Format_Sample->setText(
+ this->craplog.getLogsFormatSample( this->APACHE_ID ) );
+}
+void MainWindow::on_button_ConfApache_Format_Help_clicked()
+{
+ this->showHelp( "apache_format" );
+}
+
+// warnlists
+void MainWindow::on_box_ConfApache_Warnlist_Field_currentTextChanged(const QString &arg1)
+{
+ if ( arg1.size() > 0 ) {
+ this->ui->inLine_ConfApache_Warnlist_String->clear();
+ this->ui->list_ConfApache_Warnlist_List->clear();
+ // update the list
+ const std::vector& list = this->craplog.getWarnlist(
+ this->APACHE_ID, this->crapview.getLogFieldID( arg1 ) );
+ for ( const std::string& item : list ) {
+ this->ui->list_ConfApache_Warnlist_List->addItem( QString::fromStdString( item ) );
+ }
+ // check/uncheck the usage option
+ const bool used = this->craplog.isWarnlistUsed(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Warnlist_Field->currentText() ) );
+ this->ui->checkBox_ConfApache_Warnlist_Used->setChecked( used );
+ this->on_checkBox_ConfApache_Warnlist_Used_clicked( used );
+ }
+}
+void MainWindow::on_checkBox_ConfApache_Warnlist_Used_clicked(bool checked)
+{
+ this->craplog.setWarnlistUsed(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Warnlist_Field->currentText() ),
+ checked );
+ if ( checked ) {
+ this->ui->inLine_ConfApache_Warnlist_String->setEnabled( true );
+ this->ui->list_ConfApache_Warnlist_List->setEnabled( true );
+ } else {
+ this->ui->inLine_ConfApache_Warnlist_String->clear();
+ this->ui->inLine_ConfApache_Warnlist_String->setEnabled( false );
+ this->ui->list_ConfApache_Warnlist_List->clearSelection();
+ this->ui->list_ConfApache_Warnlist_List->setEnabled( false );
+ }
+}
+
+void MainWindow::on_inLine_ConfApache_Warnlist_String_cursorPositionChanged(int arg1, int arg2)
+{
+ if ( arg2 > 0 ) {
+ this->ui->button_ConfApache_Warnlist_Add->setEnabled( true );
+ } else {
+ this->ui->button_ConfApache_Warnlist_Add->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfApache_Warnlist_String_returnPressed()
+{
+ this->on_button_ConfApache_Warnlist_Add_clicked();
+}
+void MainWindow::on_button_ConfApache_Warnlist_Add_clicked()
+{
+ const QString& item = this->ui->inLine_ConfApache_Warnlist_String->text();
+ if ( this->ui->list_ConfApache_Warnlist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).size() == 0 ) {
+ // not in the list yet, append
+ try {
+ this->craplog.warnlistAdd(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Warnlist_Field->currentText() ),
+ item.toStdString() );
+ this->ui->list_ConfApache_Warnlist_List->addItem( item );
+ } catch ( const BWlistException& ) {
+ DialogSec::warnInvalidItemBW();
+ return;
+ }
+ }
+ // select the item in the list, in both cases it was already in or it has been just inserted
+ this->ui->list_ConfApache_Warnlist_List->clearSelection();
+ this->ui->list_ConfApache_Warnlist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).at(0)->setSelected( true );
+ this->ui->inLine_ConfApache_Warnlist_String->clear();
+}
+
+void MainWindow::on_list_ConfApache_Warnlist_List_itemSelectionChanged()
+{
+ if ( this->ui->list_ConfApache_Warnlist_List->selectedItems().size() == 1 ) {
+ this->ui->button_ConfApache_Warnlist_Remove->setEnabled( true );
+ this->ui->button_ConfApache_Warnlist_Up->setEnabled( true );
+ this->ui->button_ConfApache_Warnlist_Down->setEnabled( true );
+ // polishing
+ const auto& item = this->ui->list_ConfApache_Warnlist_List->selectedItems().at(0);
+ const int max = this->ui->list_ConfApache_Warnlist_List->count() -1;
+ if ( max == 0 ) {
+ this->ui->button_ConfApache_Warnlist_Up->setEnabled( false );
+ this->ui->button_ConfApache_Warnlist_Down->setEnabled( false );
+ } else {
+ for ( int i=0; i<=max; i++ ) {
+ if ( this->ui->list_ConfApache_Warnlist_List->item(i) == item ) {
+ if ( i == 0 ) {
+ this->ui->button_ConfApache_Warnlist_Up->setEnabled( false );
+ } else if ( i == max ) {
+ this->ui->button_ConfApache_Warnlist_Down->setEnabled( false );
+ }
+ }
+ }
+ }
+ } else {
+ this->ui->button_ConfApache_Warnlist_Remove->setEnabled( false );
+ this->ui->button_ConfApache_Warnlist_Up->setEnabled( false );
+ this->ui->button_ConfApache_Warnlist_Down->setEnabled( false );
+ }
+}
+void MainWindow::on_button_ConfApache_Warnlist_Remove_clicked()
+{
+ const auto& item = this->ui->list_ConfApache_Warnlist_List->selectedItems().at(0);
+ this->craplog.warnlistRemove(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Warnlist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfApache_Warnlist_Field_currentTextChanged( this->ui->box_ConfApache_Warnlist_Field->currentText() );
+}
+void MainWindow::on_button_ConfApache_Warnlist_Up_clicked()
+{
+ const auto& item = this->ui->list_ConfApache_Warnlist_List->selectedItems().at(0);
+ const int i = this->craplog.warnlistMoveUp(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Warnlist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfApache_Warnlist_Field_currentTextChanged( this->ui->box_ConfApache_Warnlist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfApache_Warnlist_List->item( i )->setSelected( true );
+ this->ui->list_ConfApache_Warnlist_List->setFocus();
+}
+void MainWindow::on_button_ConfApache_Warnlist_Down_clicked()
+{
+ const auto& item = this->ui->list_ConfApache_Warnlist_List->selectedItems().at(0);
+ const int i = this->craplog.warnlistMoveDown(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Warnlist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfApache_Warnlist_Field_currentTextChanged( this->ui->box_ConfApache_Warnlist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfApache_Warnlist_List->item( i )->setSelected( true );
+ this->ui->list_ConfApache_Warnlist_List->setFocus();
+}
+
+
+// blacklist
+void MainWindow::on_box_ConfApache_Blacklist_Field_currentTextChanged(const QString &arg1)
+{
+ if ( arg1.size() > 0 ) {
+ this->ui->inLine_ConfApache_Blacklist_String->clear();
+ this->ui->list_ConfApache_Blacklist_List->clear();
+ // update the list
+ const std::vector& list = this->craplog.getBlacklist(
+ this->APACHE_ID, this->crapview.getLogFieldID( arg1 ) );
+ for ( const std::string& item : list ) {
+ this->ui->list_ConfApache_Blacklist_List->addItem( QString::fromStdString( item ) );
+ }
+ // check/uncheck the usage option
+ bool used = this->craplog.isBlacklistUsed(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Blacklist_Field->currentText() ) );
+ this->ui->checkBox_ConfApache_Blacklist_Used->setChecked( used );
+ this->on_checkBox_ConfApache_Blacklist_Used_clicked( used );
+ }
+}
+void MainWindow::on_checkBox_ConfApache_Blacklist_Used_clicked(bool checked)
+{
+ this->craplog.setBlacklistUsed(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Blacklist_Field->currentText() ),
+ checked );
+ if ( checked ) {
+ this->ui->inLine_ConfApache_Blacklist_String->setEnabled( true );
+ this->ui->list_ConfApache_Blacklist_List->setEnabled( true );
+ } else {
+ this->ui->inLine_ConfApache_Blacklist_String->clear();
+ this->ui->inLine_ConfApache_Blacklist_String->setEnabled( false );
+ this->ui->list_ConfApache_Blacklist_List->clearSelection();
+ this->ui->list_ConfApache_Blacklist_List->setEnabled( false );
+ }
+}
+
+void MainWindow::on_inLine_ConfApache_Blacklist_String_cursorPositionChanged(int arg1, int arg2)
+{
+ if ( arg2 > 0 ) {
+ this->ui->button_ConfApache_Blacklist_Add->setEnabled( true );
+ } else {
+ this->ui->button_ConfApache_Blacklist_Add->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfApache_Blacklist_String_returnPressed()
+{
+ this->on_button_ConfApache_Blacklist_Add_clicked();
+}
+void MainWindow::on_button_ConfApache_Blacklist_Add_clicked()
+{
+ const QString& item = this->ui->inLine_ConfApache_Blacklist_String->text();
+ if ( this->ui->list_ConfApache_Blacklist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).size() == 0 ) {
+ // not in the list yet, append
+ try {
+ this->craplog.blacklistAdd(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Blacklist_Field->currentText() ),
+ item.toStdString() );
+ this->ui->list_ConfApache_Blacklist_List->addItem( item );
+ } catch ( const BWlistException& ) {
+ DialogSec::warnInvalidItemBW();
+ return;
+ }
+ }
+ // select the item in the list, in both cases it was already in or it has been just inserted
+ this->ui->list_ConfApache_Blacklist_List->clearSelection();
+ this->ui->list_ConfApache_Blacklist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).at(0)->setSelected( true );
+ this->ui->inLine_ConfApache_Blacklist_String->clear();
+}
+
+void MainWindow::on_list_ConfApache_Blacklist_List_itemSelectionChanged()
+{
+ if ( this->ui->list_ConfApache_Blacklist_List->selectedItems().size() == 1 ) {
+ this->ui->button_ConfApache_Blacklist_Remove->setEnabled( true );
+ this->ui->button_ConfApache_Blacklist_Up->setEnabled( true );
+ this->ui->button_ConfApache_Blacklist_Down->setEnabled( true );
+ // polishing
+ const auto& item = this->ui->list_ConfApache_Blacklist_List->selectedItems().at(0);
+ const int max = this->ui->list_ConfApache_Blacklist_List->count() -1;
+ if ( max == 0 ) {
+ this->ui->button_ConfApache_Blacklist_Up->setEnabled( false );
+ this->ui->button_ConfApache_Blacklist_Down->setEnabled( false );
+ } else {
+ for ( int i=0; i<=max; i++ ) {
+ if ( this->ui->list_ConfApache_Blacklist_List->item(i) == item ) {
+ if ( i == 0 ) {
+ this->ui->button_ConfApache_Blacklist_Up->setEnabled( false );
+ } else if ( i == max ) {
+ this->ui->button_ConfApache_Blacklist_Down->setEnabled( false );
+ }
+ }
+ }
+ }
+ } else {
+ this->ui->button_ConfApache_Blacklist_Remove->setEnabled( false );
+ this->ui->button_ConfApache_Blacklist_Up->setEnabled( false );
+ this->ui->button_ConfApache_Blacklist_Down->setEnabled( false );
+ }
+}
+void MainWindow::on_button_ConfApache_Blacklist_Remove_clicked()
+{
+ const auto& item = this->ui->list_ConfApache_Blacklist_List->selectedItems().at(0);
+ this->craplog.blacklistRemove(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Blacklist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfApache_Blacklist_Field_currentTextChanged( this->ui->box_ConfApache_Blacklist_Field->currentText() );
+}
+void MainWindow::on_button_ConfApache_Blacklist_Up_clicked()
+{
+ const auto& item = this->ui->list_ConfApache_Blacklist_List->selectedItems().at(0);
+ const int i = this->craplog.blacklistMoveUp(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Blacklist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfApache_Blacklist_Field_currentTextChanged( this->ui->box_ConfApache_Blacklist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfApache_Blacklist_List->item( i )->setSelected( true );
+ this->ui->list_ConfApache_Blacklist_List->setFocus();
+}
+void MainWindow::on_button_ConfApache_Blacklist_Down_clicked()
+{
+ const auto& item = this->ui->list_ConfApache_Blacklist_List->selectedItems().at(0);
+ const int i = this->craplog.blacklistMoveDown(
+ this->APACHE_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfApache_Blacklist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfApache_Blacklist_Field_currentTextChanged( this->ui->box_ConfApache_Blacklist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfApache_Blacklist_List->item( i )->setSelected( true );
+ this->ui->list_ConfApache_Blacklist_List->setFocus();
+}
+
+
+////////////////
+//// NGINX ////
+// paths
+void MainWindow::on_inLine_ConfNginx_Path_String_textChanged(const QString &arg1)
+{
+ if ( arg1.size() > 0 ) {
+ std::string path = this->resolvePath( arg1.toStdString() );
+ if ( IOutils::checkDir( path ) ) {
+ this->ui->icon_ConfNginx_Path_Wrong->setVisible( false );
+ this->ui->button_ConfNginx_Path_Save->setEnabled( true );
+ } else {
+ this->ui->icon_ConfNginx_Path_Wrong->setVisible( true );
+ this->ui->button_ConfNginx_Path_Save->setEnabled( false );
+ }
+ } else {
+ this->ui->icon_ConfNginx_Path_Wrong->setVisible( true );
+ this->ui->button_ConfNginx_Path_Save->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfNginx_Path_String_returnPressed()
+{
+ this->on_button_ConfNginx_Path_Save_clicked();
+}
+void MainWindow::on_button_ConfNginx_Path_Save_clicked()
+{
+ if ( ! this->ui->icon_ConfNginx_Path_Wrong->isVisible() ) {
+ // set the paths
+ std::string path = this->resolvePath( this->ui->inLine_ConfNginx_Path_String->text().toStdString() );
+ if ( StringOps::endsWith( path, "/" ) ) {
+ path = StringOps::rstrip( path, "/" );
+ }
+ if ( ! IOutils::checkDir( path, true ) ) {
+ DialogSec::warnDirNotReadable( nullptr );
+ }
+ this->craplog.setLogsPath( this->NGINX_ID, path );
+ this->ui->inLine_ConfNginx_Path_String->setText( QString::fromStdString( path ) );
+ }
+ this->ui->button_ConfNginx_Path_Save->setEnabled( false );
+}
+
+// formats
+void MainWindow::on_inLine_ConfNginx_Format_String_cursorPositionChanged(int arg1, int arg2)
+{
+ if ( arg2 > 0 ) {
+ this->ui->button_ConfNginx_Format_Save->setEnabled( true );
+ } else {
+ this->ui->button_ConfNginx_Format_Save->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfNginx_Format_String_returnPressed()
+{
+ if ( this->ui->button_ConfNginx_Format_Save->isEnabled() ) {
+ this->on_button_ConfNginx_Format_Save_clicked();
+ }
+}
+void MainWindow::on_button_ConfNginx_Format_Save_clicked()
+{
+ const bool success = this->craplog.setNginxLogFormat(
+ this->ui->inLine_ConfNginx_Format_String->text().toStdString() );
+ if ( success ) {
+ this->ui->button_ConfNginx_Format_Save->setEnabled( false );
+ }
+}
+void MainWindow::on_button_ConfNginx_Format_Sample_clicked()
+{
+ this->ui->preview_ConfNginx_Format_Sample->setText(
+ this->craplog.getLogsFormatSample( this->NGINX_ID ) );
+}
+void MainWindow::on_button_ConfNginx_Format_Help_clicked()
+{
+ this->showHelp( "nginx_format" );
+}
+
+// warnlists
+void MainWindow::on_box_ConfNginx_Warnlist_Field_currentTextChanged(const QString &arg1)
+{
+ if ( arg1.size() > 0 ) {
+ this->ui->inLine_ConfNginx_Warnlist_String->clear();
+ this->ui->list_ConfNginx_Warnlist_List->clear();
+ // update the list
+ const std::vector& list = this->craplog.getWarnlist(
+ this->NGINX_ID, this->crapview.getLogFieldID( arg1 ) );
+ for ( const std::string& item : list ) {
+ this->ui->list_ConfNginx_Warnlist_List->addItem( QString::fromStdString( item ) );
+ }
+ // check/uncheck the usage option
+ const bool used = this->craplog.isWarnlistUsed(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Warnlist_Field->currentText() ) );
+ this->ui->checkBox_ConfNginx_Warnlist_Used->setChecked( used );
+ this->on_checkBox_ConfNginx_Warnlist_Used_clicked( used );
+ }
+}
+void MainWindow::on_checkBox_ConfNginx_Warnlist_Used_clicked(bool checked)
+{
+ this->craplog.setWarnlistUsed(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Warnlist_Field->currentText() ),
+ checked );
+ if ( checked ) {
+ this->ui->inLine_ConfNginx_Warnlist_String->setEnabled( true );
+ this->ui->list_ConfNginx_Warnlist_List->setEnabled( true );
+ } else {
+ this->ui->inLine_ConfNginx_Warnlist_String->clear();
+ this->ui->inLine_ConfNginx_Warnlist_String->setEnabled( false );
+ this->ui->list_ConfNginx_Warnlist_List->clearSelection();
+ this->ui->list_ConfNginx_Warnlist_List->setEnabled( false );
+ }
+}
+
+void MainWindow::on_inLine_ConfNginx_Warnlist_String_cursorPositionChanged(int arg1, int arg2)
+{
+ if ( arg2 > 0 ) {
+ this->ui->button_ConfNginx_Warnlist_Add->setEnabled( true );
+ } else {
+ this->ui->button_ConfNginx_Warnlist_Add->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfNginx_Warnlist_String_returnPressed()
+{
+ this->on_button_ConfNginx_Warnlist_Add_clicked();
+}
+void MainWindow::on_button_ConfNginx_Warnlist_Add_clicked()
+{
+ const QString& item = this->ui->inLine_ConfNginx_Warnlist_String->text();
+ if ( this->ui->list_ConfNginx_Warnlist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).size() == 0 ) {
+ // not in the list yet, append
+ try {
+ this->craplog.warnlistAdd(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Warnlist_Field->currentText() ),
+ item.toStdString() );
+ this->ui->list_ConfNginx_Warnlist_List->addItem( item );
+ } catch ( const BWlistException& ) {
+ DialogSec::warnInvalidItemBW();
+ return;
+ }
+ }
+ // select the item in the list, in both cases it was already in or it has been just inserted
+ this->ui->list_ConfNginx_Warnlist_List->clearSelection();
+ this->ui->list_ConfNginx_Warnlist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).at(0)->setSelected( true );
+ this->ui->inLine_ConfNginx_Warnlist_String->clear();
+}
+
+void MainWindow::on_list_ConfNginx_Warnlist_List_itemSelectionChanged()
+{
+ if ( this->ui->list_ConfNginx_Warnlist_List->selectedItems().size() == 1 ) {
+ this->ui->button_ConfNginx_Warnlist_Remove->setEnabled( true );
+ this->ui->button_ConfNginx_Warnlist_Up->setEnabled( true );
+ this->ui->button_ConfNginx_Warnlist_Down->setEnabled( true );
+ // polishing
+ const auto& item = this->ui->list_ConfNginx_Warnlist_List->selectedItems().at(0);
+ const int max = this->ui->list_ConfNginx_Warnlist_List->count() -1;
+ if ( max == 0 ) {
+ this->ui->button_ConfNginx_Warnlist_Up->setEnabled( false );
+ this->ui->button_ConfNginx_Warnlist_Down->setEnabled( false );
+ } else {
+ for ( int i=0; i<=max; i++ ) {
+ if ( this->ui->list_ConfNginx_Warnlist_List->item(i) == item ) {
+ if ( i == 0 ) {
+ this->ui->button_ConfNginx_Warnlist_Up->setEnabled( false );
+ } else if ( i == max ) {
+ this->ui->button_ConfNginx_Warnlist_Down->setEnabled( false );
+ }
+ }
+ }
+ }
+ } else {
+ this->ui->button_ConfNginx_Warnlist_Remove->setEnabled( false );
+ this->ui->button_ConfNginx_Warnlist_Up->setEnabled( false );
+ this->ui->button_ConfNginx_Warnlist_Down->setEnabled( false );
+ }
+}
+void MainWindow::on_button_ConfNginx_Warnlist_Remove_clicked()
+{
+ const auto& item = this->ui->list_ConfNginx_Warnlist_List->selectedItems().at(0);
+ this->craplog.warnlistRemove(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Warnlist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfNginx_Warnlist_Field_currentTextChanged( this->ui->box_ConfNginx_Warnlist_Field->currentText() );
+}
+void MainWindow::on_button_ConfNginx_Warnlist_Up_clicked()
+{
+ const auto& item = this->ui->list_ConfNginx_Warnlist_List->selectedItems().at(0);
+ const int i = this->craplog.warnlistMoveUp(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Warnlist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfNginx_Warnlist_Field_currentTextChanged( this->ui->box_ConfNginx_Warnlist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfNginx_Warnlist_List->item( i )->setSelected( true );
+ this->ui->list_ConfNginx_Warnlist_List->setFocus();
+}
+void MainWindow::on_button_ConfNginx_Warnlist_Down_clicked()
+{
+ const auto& item = this->ui->list_ConfNginx_Warnlist_List->selectedItems().at(0);
+ const int i = this->craplog.warnlistMoveDown(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Warnlist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfNginx_Warnlist_Field_currentTextChanged( this->ui->box_ConfNginx_Warnlist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfNginx_Warnlist_List->item( i )->setSelected( true );
+ this->ui->list_ConfNginx_Warnlist_List->setFocus();
+}
+
+
+// blacklist
+void MainWindow::on_box_ConfNginx_Blacklist_Field_currentTextChanged(const QString &arg1)
+{
+ if ( arg1.size() > 0 ) {
+ this->ui->inLine_ConfNginx_Blacklist_String->clear();
+ this->ui->list_ConfNginx_Blacklist_List->clear();
+ // update the list
+ const std::vector& list = this->craplog.getBlacklist(
+ this->NGINX_ID, this->crapview.getLogFieldID( arg1 ) );
+ for ( const std::string& item : list ) {
+ this->ui->list_ConfNginx_Blacklist_List->addItem( QString::fromStdString( item ) );
+ }
+ // check/uncheck the usage option
+ bool used = this->craplog.isBlacklistUsed(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Blacklist_Field->currentText() ) );
+ this->ui->checkBox_ConfNginx_Blacklist_Used->setChecked( used );
+ this->on_checkBox_ConfNginx_Blacklist_Used_clicked( used );
+ }
+}
+void MainWindow::on_checkBox_ConfNginx_Blacklist_Used_clicked(bool checked)
+{
+ this->craplog.setBlacklistUsed(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Blacklist_Field->currentText() ),
+ checked );
+ if ( checked ) {
+ this->ui->inLine_ConfNginx_Blacklist_String->setEnabled( true );
+ this->ui->list_ConfNginx_Blacklist_List->setEnabled( true );
+ } else {
+ this->ui->inLine_ConfNginx_Blacklist_String->clear();
+ this->ui->inLine_ConfNginx_Blacklist_String->setEnabled( false );
+ this->ui->list_ConfNginx_Blacklist_List->clearSelection();
+ this->ui->list_ConfNginx_Blacklist_List->setEnabled( false );
+ }
+}
+
+void MainWindow::on_inLine_ConfNginx_Blacklist_String_cursorPositionChanged(int arg1, int arg2)
+{
+ if ( arg2 > 0 ) {
+ this->ui->button_ConfNginx_Blacklist_Add->setEnabled( true );
+ } else {
+ this->ui->button_ConfNginx_Blacklist_Add->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfNginx_Blacklist_String_returnPressed()
+{
+ this->on_button_ConfNginx_Blacklist_Add_clicked();
+}
+void MainWindow::on_button_ConfNginx_Blacklist_Add_clicked()
+{
+ const QString& item = this->ui->inLine_ConfNginx_Blacklist_String->text();
+ if ( this->ui->list_ConfNginx_Blacklist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).size() == 0 ) {
+ // not in the list yet, append
+ try {
+ this->craplog.blacklistAdd(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Blacklist_Field->currentText() ),
+ item.toStdString() );
+ this->ui->list_ConfNginx_Blacklist_List->addItem( item );
+ } catch ( const BWlistException& ) {
+ DialogSec::warnInvalidItemBW();
+ return;
+ }
+ }
+ // select the item in the list, in both cases it was already in or it has been just inserted
+ this->ui->list_ConfNginx_Blacklist_List->clearSelection();
+ this->ui->list_ConfNginx_Blacklist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).at(0)->setSelected( true );
+ this->ui->inLine_ConfNginx_Blacklist_String->clear();
+}
+
+void MainWindow::on_list_ConfNginx_Blacklist_List_itemSelectionChanged()
+{
+ if ( this->ui->list_ConfNginx_Blacklist_List->selectedItems().size() == 1 ) {
+ this->ui->button_ConfNginx_Blacklist_Remove->setEnabled( true );
+ this->ui->button_ConfNginx_Blacklist_Up->setEnabled( true );
+ this->ui->button_ConfNginx_Blacklist_Down->setEnabled( true );
+ // polishing
+ const auto& item = this->ui->list_ConfNginx_Blacklist_List->selectedItems().at(0);
+ const int max = this->ui->list_ConfNginx_Blacklist_List->count() -1;
+ if ( max == 0 ) {
+ this->ui->button_ConfNginx_Blacklist_Up->setEnabled( false );
+ this->ui->button_ConfNginx_Blacklist_Down->setEnabled( false );
+ } else {
+ for ( int i=0; i<=max; i++ ) {
+ if ( this->ui->list_ConfNginx_Blacklist_List->item(i) == item ) {
+ if ( i == 0 ) {
+ this->ui->button_ConfNginx_Blacklist_Up->setEnabled( false );
+ } else if ( i == max ) {
+ this->ui->button_ConfNginx_Blacklist_Down->setEnabled( false );
+ }
+ }
+ }
+ }
+ } else {
+ this->ui->button_ConfNginx_Blacklist_Remove->setEnabled( false );
+ this->ui->button_ConfNginx_Blacklist_Up->setEnabled( false );
+ this->ui->button_ConfNginx_Blacklist_Down->setEnabled( false );
+ }
+}
+void MainWindow::on_button_ConfNginx_Blacklist_Remove_clicked()
+{
+ const auto& item = this->ui->list_ConfNginx_Blacklist_List->selectedItems().at(0);
+ this->craplog.blacklistRemove(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Blacklist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfNginx_Blacklist_Field_currentTextChanged( this->ui->box_ConfNginx_Blacklist_Field->currentText() );
+}
+void MainWindow::on_button_ConfNginx_Blacklist_Up_clicked()
+{
+ const auto& item = this->ui->list_ConfNginx_Blacklist_List->selectedItems().at(0);
+ const int i = this->craplog.blacklistMoveUp(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Blacklist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfNginx_Blacklist_Field_currentTextChanged( this->ui->box_ConfNginx_Blacklist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfNginx_Blacklist_List->item( i )->setSelected( true );
+ this->ui->list_ConfNginx_Blacklist_List->setFocus();
+}
+void MainWindow::on_button_ConfNginx_Blacklist_Down_clicked()
+{
+ const auto& item = this->ui->list_ConfNginx_Blacklist_List->selectedItems().at(0);
+ const int i = this->craplog.blacklistMoveDown(
+ this->NGINX_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfNginx_Blacklist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfNginx_Blacklist_Field_currentTextChanged( this->ui->box_ConfNginx_Blacklist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfNginx_Blacklist_List->item( i )->setSelected( true );
+ this->ui->list_ConfNginx_Blacklist_List->setFocus();
+}
+
+
+////////////////
+//// IIS ////
+// paths
+void MainWindow::on_inLine_ConfIis_Path_String_textChanged(const QString &arg1)
+{
+ if ( arg1.size() > 0 ) {
+ std::string path = this->resolvePath( arg1.toStdString() );
+ if ( IOutils::checkDir( path ) ) {
+ this->ui->icon_ConfIis_Path_Wrong->setVisible( false );
+ this->ui->button_ConfIis_Path_Save->setEnabled( true );
+ } else {
+ this->ui->icon_ConfIis_Path_Wrong->setVisible( true );
+ this->ui->button_ConfIis_Path_Save->setEnabled( false );
+ }
+ } else {
+ this->ui->icon_ConfIis_Path_Wrong->setVisible( true );
+ this->ui->button_ConfIis_Path_Save->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfIis_Path_String_returnPressed()
+{
+ this->on_button_ConfIis_Path_Save_clicked();
+}
+void MainWindow::on_button_ConfIis_Path_Save_clicked()
+{
+ if ( ! this->ui->icon_ConfIis_Path_Wrong->isVisible() ) {
+ // set the paths
+ std::string path = this->resolvePath( this->ui->inLine_ConfIis_Path_String->text().toStdString() );
+ if ( StringOps::endsWith( path, "/" ) ) {
+ path = StringOps::rstrip( path, "/" );
+ }
+ if ( ! IOutils::checkDir( path, true ) ) {
+ DialogSec::warnDirNotReadable( nullptr );
+ }
+ this->craplog.setLogsPath( this->IIS_ID, path );
+ this->ui->inLine_ConfIis_Path_String->setText( QString::fromStdString( path ) );
+ }
+ this->ui->button_ConfIis_Path_Save->setEnabled( false );
+}
+
+// formats
+const int MainWindow::getIisLogsModule()
+{
+ int module = 0;
+ if ( this->ui->radio_ConfIis_Format_NCSA->isChecked() ) {
+ module = 1;
+ } else if ( this->ui->radio_ConfIis_Format_IIS->isChecked() ) {
+ module = 2;
+ }
+ return module;
+}
+
+void MainWindow::on_radio_ConfIis_Format_W3C_toggled(bool checked)
+{
+ if ( checked ) {
+ const bool success = this->craplog.setIisLogFormat( "", 0 );
+ if ( success ) {
+ this->ui->inLine_ConfIis_Format_String->clear();
+ this->ui->inLine_ConfIis_Format_String->setEnabled( true );
+ this->ui->inLine_ConfIis_Format_String->setFocus();
+ }
+ }
+}
+void MainWindow::on_radio_ConfIis_Format_NCSA_toggled(bool checked)
+{
+ if ( checked ) {
+ const bool success = this->craplog.setIisLogFormat(
+ "c-ip s-sitename s-computername [date:time] sc-status sc-bytes",
+ 1 );
+ if ( success ) {
+ this->ui->inLine_ConfIis_Format_String->clear();
+ this->ui->inLine_ConfIis_Format_String->setText( QString::fromStdString( this->craplog.getLogsFormatString( this->IIS_ID ) ) );
+ this->ui->inLine_ConfIis_Format_String->setEnabled( false );
+ this->ui->button_ConfIis_Format_Save->setEnabled( false );
+ }
+ }
+}
+void MainWindow::on_radio_ConfIis_Format_IIS_toggled(bool checked)
+{
+ if ( checked ) {
+ const bool success = this->craplog.setIisLogFormat(
+ "c-ip, cs-username, date, time, s-sitename, s-computername, s-ip, time-taken, cs-bytes, sc-bytes, sc-status, sc-win32-status, cs-method, cs-uri-stem, cs-uri-query,",
+ 2 );
+ if ( success ) {
+ this->ui->inLine_ConfIis_Format_String->clear();
+ this->ui->inLine_ConfIis_Format_String->setText( QString::fromStdString( this->craplog.getLogsFormatString( this->IIS_ID ) ) );
+ this->ui->inLine_ConfIis_Format_String->setEnabled( false );
+ this->ui->button_ConfIis_Format_Save->setEnabled( false );
+ }
+ }
+}
+
+void MainWindow::on_inLine_ConfIis_Format_String_cursorPositionChanged(int arg1, int arg2)
+{
+ if ( arg2 > 0 ) {
+ this->ui->button_ConfIis_Format_Save->setEnabled( true );
+ } else {
+ this->ui->button_ConfIis_Format_Save->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfIis_Format_String_returnPressed()
+{
+ if ( this->ui->button_ConfIis_Format_Save->isEnabled() ) {
+ this->on_button_ConfIis_Format_Save_clicked();
+ }
+}
+void MainWindow::on_button_ConfIis_Format_Save_clicked()
+{
+ const bool success = this->craplog.setIisLogFormat(
+ StringOps::strip( this->ui->inLine_ConfIis_Format_String->text().toStdString() ),
+ this->getIisLogsModule() );
+ if ( success ) {
+ this->ui->button_ConfIis_Format_Save->setEnabled( false );
+ }
+}
+void MainWindow::on_button_ConfIis_Format_Sample_clicked()
+{
+ this->ui->preview_ConfIis_Format_Sample->setText(
+ this->craplog.getLogsFormatSample( this->IIS_ID ) );
+}
+void MainWindow::on_button_ConfIis_Format_Help_clicked()
+{
+ this->showHelp( "iis_format" );
+}
+
+// warnlists
+void MainWindow::on_box_ConfIis_Warnlist_Field_currentTextChanged(const QString &arg1)
+{
+ if ( arg1.size() > 0 ) {
+ this->ui->inLine_ConfIis_Warnlist_String->clear();
+ this->ui->list_ConfIis_Warnlist_List->clear();
+ // update the list
+ const std::vector& list = this->craplog.getWarnlist(
+ this->IIS_ID, this->crapview.getLogFieldID( arg1 ) );
+ for ( const std::string& item : list ) {
+ this->ui->list_ConfIis_Warnlist_List->addItem( QString::fromStdString( item ) );
+ }
+ // check/uncheck the usage option
+ const bool used = this->craplog.isWarnlistUsed(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Warnlist_Field->currentText() ) );
+ this->ui->checkBox_ConfIis_Warnlist_Used->setChecked( used );
+ this->on_checkBox_ConfIis_Warnlist_Used_clicked( used );
+ }
+}
+void MainWindow::on_checkBox_ConfIis_Warnlist_Used_clicked(bool checked)
+{
+ this->craplog.setWarnlistUsed(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Warnlist_Field->currentText() ),
+ checked );
+ if ( checked ) {
+ this->ui->inLine_ConfIis_Warnlist_String->setEnabled( true );
+ this->ui->list_ConfIis_Warnlist_List->setEnabled( true );
+ } else {
+ this->ui->inLine_ConfIis_Warnlist_String->clear();
+ this->ui->inLine_ConfIis_Warnlist_String->setEnabled( false );
+ this->ui->list_ConfIis_Warnlist_List->clearSelection();
+ this->ui->list_ConfIis_Warnlist_List->setEnabled( false );
+ }
+}
+
+void MainWindow::on_inLine_ConfIis_Warnlist_String_cursorPositionChanged(int arg1, int arg2)
+{
+ if ( arg2 > 0 ) {
+ this->ui->button_ConfIis_Warnlist_Add->setEnabled( true );
+ } else {
+ this->ui->button_ConfIis_Warnlist_Add->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfIis_Warnlist_String_returnPressed()
+{
+ this->on_button_ConfIis_Warnlist_Add_clicked();
+}
+void MainWindow::on_button_ConfIis_Warnlist_Add_clicked()
+{
+ const QString& item = this->ui->inLine_ConfIis_Warnlist_String->text();
+ if ( this->ui->list_ConfIis_Warnlist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).size() == 0 ) {
+ // not in the list yet, append
+ try {
+ this->craplog.warnlistAdd(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Warnlist_Field->currentText() ),
+ item.toStdString() );
+ this->ui->list_ConfIis_Warnlist_List->addItem( item );
+ } catch ( const BWlistException& ) {
+ DialogSec::warnInvalidItemBW();
+ return;
+ }
+ }
+ // select the item in the list, in both cases it was already in or it has been just inserted
+ this->ui->list_ConfIis_Warnlist_List->clearSelection();
+ this->ui->list_ConfIis_Warnlist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).at(0)->setSelected( true );
+ this->ui->inLine_ConfIis_Warnlist_String->clear();
+}
+
+void MainWindow::on_list_ConfIis_Warnlist_List_itemSelectionChanged()
+{
+ if ( this->ui->list_ConfIis_Warnlist_List->selectedItems().size() == 1 ) {
+ this->ui->button_ConfIis_Warnlist_Remove->setEnabled( true );
+ this->ui->button_ConfIis_Warnlist_Up->setEnabled( true );
+ this->ui->button_ConfIis_Warnlist_Down->setEnabled( true );
+ // polishing
+ const auto& item = this->ui->list_ConfIis_Warnlist_List->selectedItems().at(0);
+ const int max = this->ui->list_ConfIis_Warnlist_List->count() -1;
+ if ( max == 0 ) {
+ this->ui->button_ConfIis_Warnlist_Up->setEnabled( false );
+ this->ui->button_ConfIis_Warnlist_Down->setEnabled( false );
+ } else {
+ for ( int i=0; i<=max; i++ ) {
+ if ( this->ui->list_ConfIis_Warnlist_List->item(i) == item ) {
+ if ( i == 0 ) {
+ this->ui->button_ConfIis_Warnlist_Up->setEnabled( false );
+ } else if ( i == max ) {
+ this->ui->button_ConfIis_Warnlist_Down->setEnabled( false );
+ }
+ }
+ }
+ }
+ } else {
+ this->ui->button_ConfIis_Warnlist_Remove->setEnabled( false );
+ this->ui->button_ConfIis_Warnlist_Up->setEnabled( false );
+ this->ui->button_ConfIis_Warnlist_Down->setEnabled( false );
+ }
+}
+void MainWindow::on_button_ConfIis_Warnlist_Remove_clicked()
+{
+ const auto& item = this->ui->list_ConfIis_Warnlist_List->selectedItems().at(0);
+ this->craplog.warnlistRemove(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Warnlist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfIis_Warnlist_Field_currentTextChanged( this->ui->box_ConfIis_Warnlist_Field->currentText() );
+}
+void MainWindow::on_button_ConfIis_Warnlist_Up_clicked()
+{
+ const auto& item = this->ui->list_ConfIis_Warnlist_List->selectedItems().at(0);
+ const int i = this->craplog.warnlistMoveUp(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Warnlist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfIis_Warnlist_Field_currentTextChanged( this->ui->box_ConfIis_Warnlist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfIis_Warnlist_List->item( i )->setSelected( true );
+ this->ui->list_ConfIis_Warnlist_List->setFocus();
+}
+void MainWindow::on_button_ConfIis_Warnlist_Down_clicked()
+{
+ const auto& item = this->ui->list_ConfIis_Warnlist_List->selectedItems().at(0);
+ const int i = this->craplog.warnlistMoveDown(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Warnlist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfIis_Warnlist_Field_currentTextChanged( this->ui->box_ConfIis_Warnlist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfIis_Warnlist_List->item( i )->setSelected( true );
+ this->ui->list_ConfIis_Warnlist_List->setFocus();
+}
+
+
+// blacklist
+void MainWindow::on_box_ConfIis_Blacklist_Field_currentTextChanged(const QString &arg1)
+{
+ if ( arg1.size() > 0 ) {
+ this->ui->inLine_ConfIis_Blacklist_String->clear();
+ this->ui->list_ConfIis_Blacklist_List->clear();
+ // update the list
+ const std::vector& list = this->craplog.getBlacklist(
+ this->IIS_ID, this->crapview.getLogFieldID( arg1 ) );
+ for ( const std::string& item : list ) {
+ this->ui->list_ConfIis_Blacklist_List->addItem( QString::fromStdString( item ) );
+ }
+ // check/uncheck the usage option
+ bool used = this->craplog.isBlacklistUsed(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Blacklist_Field->currentText() ) );
+ this->ui->checkBox_ConfIis_Blacklist_Used->setChecked( used );
+ this->on_checkBox_ConfIis_Blacklist_Used_clicked( used );
+ }
+}
+void MainWindow::on_checkBox_ConfIis_Blacklist_Used_clicked(bool checked)
+{
+ this->craplog.setBlacklistUsed(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Blacklist_Field->currentText() ),
+ checked );
+ if ( checked ) {
+ this->ui->inLine_ConfIis_Blacklist_String->setEnabled( true );
+ this->ui->list_ConfIis_Blacklist_List->setEnabled( true );
+ } else {
+ this->ui->inLine_ConfIis_Blacklist_String->clear();
+ this->ui->inLine_ConfIis_Blacklist_String->setEnabled( false );
+ this->ui->list_ConfIis_Blacklist_List->clearSelection();
+ this->ui->list_ConfIis_Blacklist_List->setEnabled( false );
+ }
+}
+
+void MainWindow::on_inLine_ConfIis_Blacklist_String_cursorPositionChanged(int arg1, int arg2)
+{
+ if ( arg2 > 0 ) {
+ this->ui->button_ConfIis_Blacklist_Add->setEnabled( true );
+ } else {
+ this->ui->button_ConfIis_Blacklist_Add->setEnabled( false );
+ }
+}
+void MainWindow::on_inLine_ConfIis_Blacklist_String_returnPressed()
+{
+ this->on_button_ConfIis_Blacklist_Add_clicked();
+}
+void MainWindow::on_button_ConfIis_Blacklist_Add_clicked()
+{
+ const QString& item = this->ui->inLine_ConfIis_Blacklist_String->text();
+ if ( this->ui->list_ConfIis_Blacklist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).size() == 0 ) {
+ // not in the list yet, append
+ try {
+ this->craplog.blacklistAdd(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Blacklist_Field->currentText() ),
+ item.toStdString() );
+ this->ui->list_ConfIis_Blacklist_List->addItem( item );
+ } catch ( const BWlistException& ) {
+ DialogSec::warnInvalidItemBW();
+ return;
+ }
+ }
+ // select the item in the list, in both cases it was already in or it has been just inserted
+ this->ui->list_ConfIis_Blacklist_List->clearSelection();
+ this->ui->list_ConfIis_Blacklist_List->findItems( item, Qt::MatchFlag::MatchCaseSensitive ).at(0)->setSelected( true );
+ this->ui->inLine_ConfIis_Blacklist_String->clear();
+}
+
+void MainWindow::on_list_ConfIis_Blacklist_List_itemSelectionChanged()
+{
+ if ( this->ui->list_ConfIis_Blacklist_List->selectedItems().size() == 1 ) {
+ this->ui->button_ConfIis_Blacklist_Remove->setEnabled( true );
+ this->ui->button_ConfIis_Blacklist_Up->setEnabled( true );
+ this->ui->button_ConfIis_Blacklist_Down->setEnabled( true );
+ // polishing
+ const auto& item = this->ui->list_ConfIis_Blacklist_List->selectedItems().at(0);
+ const int max = this->ui->list_ConfIis_Blacklist_List->count() -1;
+ if ( max == 0 ) {
+ this->ui->button_ConfIis_Blacklist_Up->setEnabled( false );
+ this->ui->button_ConfIis_Blacklist_Down->setEnabled( false );
+ } else {
+ for ( int i=0; i<=max; i++ ) {
+ if ( this->ui->list_ConfIis_Blacklist_List->item(i) == item ) {
+ if ( i == 0 ) {
+ this->ui->button_ConfIis_Blacklist_Up->setEnabled( false );
+ } else if ( i == max ) {
+ this->ui->button_ConfIis_Blacklist_Down->setEnabled( false );
+ }
+ }
+ }
+ }
+ } else {
+ this->ui->button_ConfIis_Blacklist_Remove->setEnabled( false );
+ this->ui->button_ConfIis_Blacklist_Up->setEnabled( false );
+ this->ui->button_ConfIis_Blacklist_Down->setEnabled( false );
+ }
+}
+void MainWindow::on_button_ConfIis_Blacklist_Remove_clicked()
+{
+ const auto& item = this->ui->list_ConfIis_Blacklist_List->selectedItems().at(0);
+ this->craplog.blacklistRemove(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Blacklist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfIis_Blacklist_Field_currentTextChanged( this->ui->box_ConfIis_Blacklist_Field->currentText() );
+}
+void MainWindow::on_button_ConfIis_Blacklist_Up_clicked()
+{
+ const auto& item = this->ui->list_ConfIis_Blacklist_List->selectedItems().at(0);
+ const int i = this->craplog.blacklistMoveUp(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Blacklist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfIis_Blacklist_Field_currentTextChanged( this->ui->box_ConfIis_Blacklist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfIis_Blacklist_List->item( i )->setSelected( true );
+ this->ui->list_ConfIis_Blacklist_List->setFocus();
+}
+void MainWindow::on_button_ConfIis_Blacklist_Down_clicked()
+{
+ const auto& item = this->ui->list_ConfIis_Blacklist_List->selectedItems().at(0);
+ const int i = this->craplog.blacklistMoveDown(
+ this->IIS_ID,
+ this->crapview.getLogFieldID( this->ui->box_ConfIis_Blacklist_Field->currentText() ),
+ item->text().toStdString() );
+ // refresh the list
+ this->on_box_ConfIis_Blacklist_Field_currentTextChanged( this->ui->box_ConfIis_Blacklist_Field->currentText() );
+ // re-select the item
+ this->ui->list_ConfIis_Blacklist_List->item( i )->setSelected( true );
+ this->ui->list_ConfIis_Blacklist_List->setFocus();
+}
+
diff --git a/logdoctor/mainwindow.h b/logdoctor/mainwindow.h
new file mode 100644
index 00000000..50a06c96
--- /dev/null
+++ b/logdoctor/mainwindow.h
@@ -0,0 +1,870 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "ui_mainwindow.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "utilities/strings.h"
+
+#include "modules/tb.h"
+
+#include "modules/craplog/craplog.h"
+#include "modules/crapview/crapview.h"
+#include "modules/craphelp/craphelp.h"
+#include "modules/crapup/crapup.h"
+#include "modules/crapinfo/crapinfo.h"
+
+#include "tools/crapnote/crapnote.h"
+
+#include "games/crisscross/game.h"
+#include "games/snake/game.h"
+
+
+QT_BEGIN_NAMESPACE
+namespace Ui { class MainWindow; }
+QT_END_NAMESPACE
+
+
+//! MainWindow
+/*!
+ The parent window
+*/
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+
+ MainWindow( QWidget* parent=nullptr );
+ ~MainWindow();
+
+
+private slots:
+
+ void closeEvent( QCloseEvent *event ) override;
+
+
+ ////////////////
+ //// CUSTOM ////
+
+ void wait_ActiveWindow();
+
+ void update_Craplog_PerfData();
+
+ void check_CraplogLLT_Finished();
+
+
+ //////////////
+ //// TABS ////
+
+ void on_button_Tab_Log_clicked();
+
+ void on_button_Tab_View_clicked();
+
+ void on_button_Tab_Conf_clicked();
+
+ //// STATS ////
+
+ void on_button_Tab_StatsWarn_clicked();
+
+ void on_button_Tab_StatsSpeed_clicked();
+
+ void on_button_Tab_StatsCount_clicked();
+
+ void on_button_Tab_StatsDay_clicked();
+
+ void on_button_Tab_StatsRelat_clicked();
+
+ void on_button_Tab_StatsGlob_clicked();
+
+
+ /////////////////
+ //// CRAPLOG ////
+
+ void on_button_Logs_Down_clicked();
+
+ void on_button_Logs_Up_clicked();
+
+ void refreshLogsList();
+
+ void runCraplog();
+
+ void on_button_LogFiles_ViewFile_clicked();
+
+ void on_checkBox_LogFiles_CheckAll_stateChanged(int arg1);
+
+ void on_button_LogFiles_RefreshList_clicked();
+
+ void on_listLogFiles_itemDoubleClicked(QTreeWidgetItem *item, int column);
+
+ void on_listLogFiles_itemChanged(QTreeWidgetItem *item, int column);
+
+ void on_button_LogFiles_Apache_clicked();
+
+ void on_button_LogFiles_Nginx_clicked();
+
+ void on_button_LogFiles_Iis_clicked();
+
+ void on_button_MakeStats_Start_clicked();
+
+
+ //////////////////
+ //// CRAPVIEW ////
+
+ void drawStatsWarn();
+
+ void drawStatsSpeed();
+
+ void drawStatsCount();
+
+ void drawStatsDay();
+
+ void drawStatsRelat();
+
+ void makeStatsGlobals();
+
+ //// WARNINGS ////
+
+ void on_box_StatsWarn_WebServer_currentIndexChanged(int index);
+
+ void on_box_StatsWarn_Year_currentIndexChanged(int index);
+
+ void on_box_StatsWarn_Month_currentIndexChanged(int index);
+
+ void on_box_StatsWarn_Day_currentIndexChanged(int index);
+
+ void on_checkBox_StatsWarn_Hour_stateChanged(int state);
+
+ void on_box_StatsWarn_Hour_currentIndexChanged(int index);
+
+ void on_button_StatsWarn_Draw_clicked();
+
+ void on_button_StatsWarn_Update_clicked();
+
+ //// SPEED ////
+
+ void on_box_StatsSpeed_WebServer_currentIndexChanged(int index);
+
+ void on_box_StatsSpeed_Year_currentIndexChanged(int index);
+
+ void on_box_StatsSpeed_Month_currentIndexChanged(int index);
+
+ void on_box_StatsSpeed_Day_currentIndexChanged(int index);
+
+ void on_button_StatsSpeed_Draw_clicked();
+
+ //// COUNTS ////
+
+ void on_box_StatsCount_WebServer_currentIndexChanged(int index);
+
+ void on_box_StatsCount_Year_currentIndexChanged(int index);
+
+ void on_box_StatsCount_Month_currentIndexChanged(int index);
+
+ void on_box_StatsCount_Day_currentIndexChanged(int index);
+
+ void on_button_StatsCount_Protocol_clicked();
+
+ void on_button_StatsCount_Method_clicked();
+
+ void on_button_StatsCount_Uri_clicked();
+
+ void on_button_StatsCount_Query_clicked();
+
+ void on_button_StatsCount_Response_clicked();
+
+ void on_button_StatsCount_Referrer_clicked();
+
+ void on_button_StatsCount_Cookie_clicked();
+
+ void on_button_StatsCount_UserAgent_clicked();
+
+ void on_button_StatsCount_Client_clicked();
+
+ //// DAY-TIME ////
+
+ void on_box_StatsDay_WebServer_currentIndexChanged(int index);
+
+ void on_box_StatsDay_LogsField_currentIndexChanged(int index);
+
+ void on_box_StatsDay_FromYear_currentIndexChanged(int index);
+
+ void on_box_StatsDay_FromMonth_currentIndexChanged(int index);
+
+ void on_box_StatsDay_FromDay_currentIndexChanged(int index);
+
+ void on_checkBox_StatsDay_Period_stateChanged(int state);
+
+ void on_box_StatsDay_ToYear_currentIndexChanged(int index);
+
+ void on_box_StatsDay_ToMonth_currentIndexChanged(int index);
+
+ void on_box_StatsDay_ToDay_currentIndexChanged(int index);
+
+ void on_button_StatsDay_Draw_clicked();
+
+ //// RELATIONAL ////
+
+ void on_box_StatsRelat_WebServer_currentIndexChanged(int index);
+
+ void on_box_StatsRelat_LogsField_1_currentIndexChanged(int index);
+
+ void on_box_StatsRelat_LogsField_2_currentIndexChanged(int index);
+
+ void on_box_StatsRelat_FromYear_currentIndexChanged(int index);
+
+ void on_box_StatsRelat_FromMonth_currentIndexChanged(int index);
+
+ void on_box_StatsRelat_FromDay_currentIndexChanged(int index);
+
+ void on_box_StatsRelat_ToYear_currentIndexChanged(int index);
+
+ void on_box_StatsRelat_ToMonth_currentIndexChanged(int index);
+
+ void on_box_StatsRelat_ToDay_currentIndexChanged(int index);
+
+ void on_button_StatsRelat_Draw_clicked();
+
+ void on_button_StatsGlob_Apache_clicked();
+
+ void on_button_StatsGlob_Nginx_clicked();
+
+ void on_button_StatsGlob_Iis_clicked();
+
+
+ /////////////////
+ //// CRAPSET ////
+
+ //// WINDOW ////
+
+ void on_checkBox_ConfWindow_Geometry_clicked(bool checked);
+
+ void on_box_ConfWindow_Theme_currentIndexChanged(int index);
+
+ void on_box_ConfWindow_Icons_currentIndexChanged(int index);
+
+ //// DIALOGS ////
+
+ void on_slider_ConfDialogs_General_sliderReleased();
+
+ void on_slider_ConfDialogs_Logs_sliderReleased();
+
+ void on_slider_ConfDialogs_Stats_sliderReleased();
+
+ //// TEXT BROWSER ////
+
+ void on_box_ConfTextBrowser_Font_currentIndexChanged(int index);
+
+ void on_checkBox_ConfTextBrowser_WideLines_clicked(bool checked);
+
+ void on_box_ConfTextBrowser_ColorScheme_currentIndexChanged(int index);
+
+ //// CHARTS ////
+
+ void on_box_ConfCharts_Theme_currentIndexChanged(int index);
+
+ //// DATABASES ////
+
+ void on_inLine_ConfDatabases_Data_Path_textChanged(const QString &arg1);
+
+ void on_inLine_ConfDatabases_Data_Path_returnPressed();
+
+ void on_button_ConfDatabases_Data_Save_clicked();
+
+ void on_inLine_ConfDatabases_Hashes_Path_textChanged(const QString &arg1);
+
+ void on_inLine_ConfDatabases_Hashes_Path_returnPressed();
+
+ void on_button_ConfDatabases_Hashes_Save_clicked();
+
+ void on_checkBox_ConfDatabases_DoBackup_clicked(bool checked);
+
+ void on_spinBox_ConfDatabases_NumBackups_valueChanged(int arg1);
+
+ //// LOGS DEFAULTS ////
+
+ void on_radio_ConfDefaults_Apache_toggled(bool checked);
+
+ void on_radio_ConfDefaults_Nginx_toggled(bool checked);
+
+ void on_radio_ConfDefaults_Iis_toggled(bool checked);
+
+ //// LOGS CONTROL ////
+
+ void on_checkBox_ConfControl_Usage_clicked(bool checked);
+
+ void on_checkBox_ConfControl_Size_clicked(bool checked);
+
+ void on_spinBox_ConfControl_Size_editingFinished();
+
+ //// APACHE ////
+
+ void on_inLine_ConfApache_Path_String_textChanged(const QString &arg1);
+
+ void on_inLine_ConfApache_Path_String_returnPressed();
+
+ void on_button_ConfApache_Path_Save_clicked();
+
+ void on_inLine_ConfApache_Format_String_cursorPositionChanged(int arg1, int arg2);
+
+ void on_inLine_ConfApache_Format_String_returnPressed();
+
+ void on_button_ConfApache_Format_Save_clicked();
+
+ void on_button_ConfApache_Format_Sample_clicked();
+
+ void on_button_ConfApache_Format_Help_clicked();
+
+ void on_box_ConfApache_Warnlist_Field_currentTextChanged(const QString &arg1);
+
+ void on_checkBox_ConfApache_Warnlist_Used_clicked(bool checked);
+
+ void on_inLine_ConfApache_Warnlist_String_cursorPositionChanged(int arg1, int arg2);
+
+ void on_inLine_ConfApache_Warnlist_String_returnPressed();
+
+ void on_button_ConfApache_Warnlist_Add_clicked();
+
+ void on_list_ConfApache_Warnlist_List_itemSelectionChanged();
+
+ void on_button_ConfApache_Warnlist_Remove_clicked();
+
+ void on_button_ConfApache_Warnlist_Up_clicked();
+
+ void on_button_ConfApache_Warnlist_Down_clicked();
+
+ void on_box_ConfApache_Blacklist_Field_currentTextChanged(const QString &arg1);
+
+ void on_checkBox_ConfApache_Blacklist_Used_clicked(bool checked);
+
+ void on_inLine_ConfApache_Blacklist_String_cursorPositionChanged(int arg1, int arg2);
+
+ void on_inLine_ConfApache_Blacklist_String_returnPressed();
+
+ void on_button_ConfApache_Blacklist_Add_clicked();
+
+ void on_list_ConfApache_Blacklist_List_itemSelectionChanged();
+
+ void on_button_ConfApache_Blacklist_Remove_clicked();
+
+ void on_button_ConfApache_Blacklist_Up_clicked();
+
+ void on_button_ConfApache_Blacklist_Down_clicked();
+
+ //// NGINX ////
+
+ void on_inLine_ConfNginx_Path_String_textChanged(const QString &arg1);
+
+ void on_inLine_ConfNginx_Path_String_returnPressed();
+
+ void on_button_ConfNginx_Path_Save_clicked();
+
+ void on_inLine_ConfNginx_Format_String_cursorPositionChanged(int arg1, int arg2);
+
+ void on_inLine_ConfNginx_Format_String_returnPressed();
+
+ void on_button_ConfNginx_Format_Save_clicked();
+
+ void on_button_ConfNginx_Format_Sample_clicked();
+
+ void on_button_ConfNginx_Format_Help_clicked();
+
+ void on_box_ConfNginx_Warnlist_Field_currentTextChanged(const QString &arg1);
+
+ void on_checkBox_ConfNginx_Warnlist_Used_clicked(bool checked);
+
+ void on_inLine_ConfNginx_Warnlist_String_cursorPositionChanged(int arg1, int arg2);
+
+ void on_inLine_ConfNginx_Warnlist_String_returnPressed();
+
+ void on_button_ConfNginx_Warnlist_Add_clicked();
+
+ void on_list_ConfNginx_Warnlist_List_itemSelectionChanged();
+
+ void on_button_ConfNginx_Warnlist_Remove_clicked();
+
+ void on_button_ConfNginx_Warnlist_Up_clicked();
+
+ void on_button_ConfNginx_Warnlist_Down_clicked();
+
+ void on_box_ConfNginx_Blacklist_Field_currentTextChanged(const QString &arg1);
+
+ void on_checkBox_ConfNginx_Blacklist_Used_clicked(bool checked);
+
+ void on_inLine_ConfNginx_Blacklist_String_cursorPositionChanged(int arg1, int arg2);
+
+ void on_inLine_ConfNginx_Blacklist_String_returnPressed();
+
+ void on_button_ConfNginx_Blacklist_Add_clicked();
+
+ void on_list_ConfNginx_Blacklist_List_itemSelectionChanged();
+
+ void on_button_ConfNginx_Blacklist_Remove_clicked();
+
+ void on_button_ConfNginx_Blacklist_Up_clicked();
+
+ void on_button_ConfNginx_Blacklist_Down_clicked();
+
+ //// IIS ////
+
+ void on_inLine_ConfIis_Path_String_textChanged(const QString &arg1);
+
+ void on_inLine_ConfIis_Path_String_returnPressed();
+
+ void on_button_ConfIis_Path_Save_clicked();
+
+ void on_radio_ConfIis_Format_W3C_toggled(bool checked);
+
+ void on_radio_ConfIis_Format_NCSA_toggled(bool checked);
+
+ void on_radio_ConfIis_Format_IIS_toggled(bool checked);
+
+ void on_inLine_ConfIis_Format_String_cursorPositionChanged(int arg1, int arg2);
+
+ void on_inLine_ConfIis_Format_String_returnPressed();
+
+ void on_button_ConfIis_Format_Save_clicked();
+
+ void on_button_ConfIis_Format_Sample_clicked();
+
+ void on_button_ConfIis_Format_Help_clicked();
+
+ void on_box_ConfIis_Warnlist_Field_currentTextChanged(const QString &arg1);
+
+ void on_checkBox_ConfIis_Warnlist_Used_clicked(bool checked);
+
+ void on_inLine_ConfIis_Warnlist_String_cursorPositionChanged(int arg1, int arg2);
+
+ void on_inLine_ConfIis_Warnlist_String_returnPressed();
+
+ void on_button_ConfIis_Warnlist_Add_clicked();
+
+ void on_list_ConfIis_Warnlist_List_itemSelectionChanged();
+
+ void on_button_ConfIis_Warnlist_Remove_clicked();
+
+ void on_button_ConfIis_Warnlist_Up_clicked();
+
+ void on_button_ConfIis_Warnlist_Down_clicked();
+
+ void on_box_ConfIis_Blacklist_Field_currentTextChanged(const QString &arg1);
+
+ void on_checkBox_ConfIis_Blacklist_Used_clicked(bool checked);
+
+ void on_inLine_ConfIis_Blacklist_String_cursorPositionChanged(int arg1, int arg2);
+
+ void on_inLine_ConfIis_Blacklist_String_returnPressed();
+
+ void on_button_ConfIis_Blacklist_Add_clicked();
+
+ void on_list_ConfIis_Blacklist_List_itemSelectionChanged();
+
+ void on_button_ConfIis_Blacklist_Remove_clicked();
+
+ void on_button_ConfIis_Blacklist_Up_clicked();
+
+ void on_button_ConfIis_Blacklist_Down_clicked();
+
+
+ ///////////////
+ //// MENU ////
+
+ //// LANGUAGE ////
+
+ void menu_actionEnglish_triggered();
+
+ void menu_actionEspanol_triggered();
+
+ void menu_actionFrancais_triggered();
+
+ void menu_actionItaliano_triggered();
+
+ //// TOOLS ////
+
+ void menu_actionBlockNote_triggered();
+
+ //// UTILITIES ////
+
+ void menu_actionInfos_triggered();
+
+ void menu_actionCheckUpdates_triggered();
+
+ //// GAMES ////
+
+ void menu_actionCrissCross_triggered();
+
+ void menu_actionSnake_triggered();
+
+
+private:
+ Ui::MainWindow *ui;
+
+ // current version of LogDoctor
+ const float version = 2.01;
+
+ // web servers ID constants
+ const unsigned int APACHE_ID=11, NGINX_ID=12, IIS_ID=13;
+ const QString wsFromIndex( const int& index );
+
+
+ //////////////////////////
+ //// OPERATING SYSTEM ////
+
+ const std::string home_path = StringOps::rstrip( QStandardPaths::locate( QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory ).toStdString(), "/" );
+
+ // 1: linux, 2:windows, 3:mac
+ #if defined( Q_OS_DARWIN )
+ // Darwin-based systems: macOS, iOS, watchOS and tvOS.
+ const unsigned int OS = 3;
+ const std::string configs_path = this->home_path + "/Lybrary/Preferences/LogDoctor/logdoctor.conf";
+ const std::string logdoc_path = this->home_path + "/Lybrary/Application Support/LogDoctor";
+ #elif defined( Q_OS_WIN )
+ // Microsoft Windows systems
+ const unsigned int OS = 2;
+ const std::string configs_path = this->home_path + "/AppData/Local/LogDoctor/logdoctor.conf";
+ const std::string logdoc_path = this->home_path + "/AppData/Local/LogDoctor";
+ #elif defined( Q_OS_UNIX )
+ // Unix-like systems: Linux, BSD and SysV
+ const unsigned int OS = 1;
+ const std::string configs_path = this->home_path + "/.config/LogDoctor/logdoctor.conf";
+ const std::string logdoc_path = "/usr/share/LogDoctor";
+ #else
+ #error "System not supported"
+ #endif
+
+
+ //! Defines OS specific stuff
+ void defineOSspec();
+
+
+ ////////////////////////
+ //// CONFIGURATIONS ////
+ ////////////////////////
+
+ //! Reads the configurations file and apply the resulting configuration
+ void readConfigs();
+
+ //! Writes the current configuration on file
+ void writeConfigs();
+
+ //! Converts a list of items to a string
+ /*!
+ \param list The list to stringify
+ \param user_agents Whether to apply the special rule to parse user-agents lists or not
+ \return The resulting string
+ \see writeConfigs()
+ */
+ const std::string list2string( const std::vector& list, const bool& user_agent=false );
+
+ //! Retrieves a list of items from the given string
+ /*!
+ \param list The list to stringify
+ \param user_agents Whether to apply the special rule to parse user-agents lists or not
+ \return The resulting list
+ \see readConfigs()
+ */
+ const std::vector string2list( const std::string& string, const bool& user_agent=false );
+
+ // string to bool and vice versa
+ const std::unordered_map s2b = { {"true",true}, {"false",false} };
+ const std::unordered_map b2s = { {true,"true"}, {false,"false"} };
+
+
+ //////////////////
+ //// LANGUAGE ////
+
+ QTranslator translator;
+
+ std::string language = "en";
+
+ //! Translates the UI to the selected language
+ void updateUiLanguage();
+
+
+ /////////////////////////
+ //// WINDOW GEOMETRY ////
+
+ //! Converts the window's geometry to string
+ /*!
+ \see writeConfigs()
+ */
+ const std::string geometryToString();
+
+ //! Retrieves the window geometry from the given string
+ /*!
+ \see readConfigs()
+ */
+ void geometryFromString( const std::string& geometry );
+
+
+ /////////////////
+ //// GENERAL ////
+
+ // quantoty of informational dialogs to display
+ int dialogs_level = 2; // 0: essential, 1: usefull, 2: explanatory
+
+ // default web server
+ int default_ws = 11;
+
+
+ //////////////////
+ //// GRAPHICS ////
+ //////////////////
+
+ // remember window position and sizes
+ bool remember_window = true;
+
+ // themes
+ int window_theme_id = 0;
+ int charts_theme_id = 0;
+ int icons_theme_id = 0;
+
+ QString icons_theme;
+
+ //! Auto-detects the icon-set to use depending on the current window theme
+ void detectIconsTheme();
+
+ //! Updates the icons on the window
+ void updateUiIcons();
+
+ //! Updates the window theme
+ void updateUiTheme();
+
+ //! Updates the fonts on the window
+ void updateUiFonts();
+
+ const std::vector CHARTS_THEMES = {
+ QChart::ChartTheme::ChartThemeLight,
+ QChart::ChartTheme::ChartThemeDark,
+ QChart::ChartTheme::ChartThemeBrownSand,
+ QChart::ChartTheme::ChartThemeBlueCerulean
+ };
+
+ // color schemes
+ std::unordered_map> TB_COLOR_SCHEMES;
+
+ // colors
+ std::unordered_map COLORS;
+
+ // fonts
+ std::unordered_map FONTS;
+
+ int font_size = 13;
+ int font_size_big = 16;
+ int font_size_small = 10;
+
+ // base font families, to build fonts from
+ QString main_font_family;
+ QString alternative_font_family;
+ QString script_font_family;
+
+
+ /////////////////////
+ //// GENERAL USE ////
+ /////////////////////
+
+ //! Printable size, including suffix
+ const QString printableSize( const int& bytes );
+
+ //! Printable time, including suffix(es)
+ const QString printableTime( const int& seconds );
+
+ //! Printable speed, namely printable size over printable time
+ const QString printableSpeed( const int& bytes, const int& secs );
+
+
+ //! Resolves the given path and returns the canonical path
+ const std::string resolvePath( const std::string& path );
+
+ //! Returns the parent folder of the given path
+ const std::string basePath( const std::string& path );
+
+
+ ////////////////
+ //// CHECKS ////
+ ////////////////
+
+ bool initiating = true;
+
+ bool db_ok = true;
+
+ //! Makes the initial integrity checks
+ void makeInitialChecks();
+
+ //! Checks the integrity of the logs data collection database
+ const bool& checkDataDB();
+
+
+ ///////////////////
+ //// DATABASES ////
+ ///////////////////
+
+ bool db_edited = false;
+
+ bool db_do_backup = true;
+
+ unsigned db_backups_number = 3;
+
+ //! Backs-up the logs data collection database
+ void backupDatabase();
+
+ std::string db_data_path;
+ std::string db_hashes_path;
+
+ // actions when working on a db
+ bool db_working = false;
+
+ //! Called when a member begins/ends performing operations on the database
+ void setDbWorkingState( const bool& state );
+
+
+ //////////////////
+ //// CRAPTABS ////
+ //////////////////
+
+ void switchMainTab( const int& new_index );
+
+
+ /////////////////
+ //// CRAPLOG ////
+ /////////////////
+
+ Craplog craplog;
+
+ QTimer* craplog_timer = new QTimer();
+ QTimer* waiter_timer;
+
+ std::chrono::system_clock::time_point waiter_timer_start;
+ std::chrono::system_clock::duration waiter_timer_elapsed;
+
+ //! The logs parser started working
+ void craplogStarted();
+
+ //! The logs parser finished working
+ void craplogFinished();
+
+ void checkMakeStats_Makable();
+
+
+ //////////////
+ //// LOGS ////
+
+ bool loading_LogsList = false;
+
+ void disableAllButtons_LogFiles_WS();
+ void enableAllButtons_LogFiles_WS();
+
+ bool hide_used_files = false;
+ bool refreshing_list = false;
+
+
+ //////////////////////
+ //// TEXT BROWSER ////
+
+ TextBrowser TB;
+
+
+ //////////////////////////
+ //// LOGS PERFORMANCE ////
+
+ void update_MakeStats_labels();
+
+ void update_MakeStats_graphs();
+
+ void reset_MakeStats_labels();
+
+
+ //////////////////
+ //// CRAPVIEW ////
+ //////////////////
+
+ Crapview crapview;
+
+ QTimer *crapview_timer = new QTimer();
+
+ // change tab
+ void switchStatsTab( const int& new_index );
+
+ //! Queries the available dates from the db and apply to the tabs
+ /*!
+ \see Crapview::refreshDates()
+ */
+ void refreshStatsDates();
+
+ // check if drawing conditions are met
+ void checkStatsWarnDrawable();
+ void checkStatsSpeedDrawable();
+ void checkStatsCountDrawable();
+ void checkStatsDayDrawable();
+ void checkStatsRelatDrawable();
+
+ // count
+ QString count_fld;
+ void startCountDrawing();
+ void resetStatsCountButtons();
+
+ // globals
+ QString glob_ws;
+ void globalsButtonClicked();
+ void resetStatsGlobals();
+
+
+ /////////////////
+ //// CRAPSET ////
+ /////////////////
+
+ void refreshTextBrowserPreview();
+
+ void refreshChartsPreview();
+
+ const int getIisLogsModule();
+
+
+ //////////////////
+ //// CRAPHELP ////
+ //////////////////
+
+ Craphelp* craphelp = new Craphelp();
+
+ void showHelp( const std::string& file_name );
+
+
+ ////////////////
+ //// CRAPUP ////
+ ////////////////
+
+ Crapup *crapup = new Crapup(0,"");
+
+
+ //////////////////
+ //// CRAPNOTE ////
+ //////////////////
+
+ Crapnote* crapnote = new Crapnote();
+
+
+ //////////////////
+ //// CRAPINFO ////
+ //////////////////
+
+ Crapinfo* crapinfo = new Crapinfo(0,"","","","");
+
+
+ ///////////////////
+ //// CRAPGAMES ////
+ ///////////////////
+
+ CrissCross* crisscross = new CrissCross( 0 );
+
+ SnakeGame* snake = new SnakeGame( 0, QFont() );
+
+};
+#endif // MAINWINDOW_H
diff --git a/logdoctor/mainwindow.ui b/logdoctor/mainwindow.ui
new file mode 100644
index 00000000..f4c2f32d
--- /dev/null
+++ b/logdoctor/mainwindow.ui
@@ -0,0 +1,11354 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 924
+ 574
+
+
+
+
+ 0
+ 0
+
+
+
+ LogDoctor
+
+
+
+ :/logo/logo/logdoctor.svg :/logo/logo/logdoctor.svg
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 8
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 68
+ 0
+
+
+
+
+ 68
+ 16777215
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+ 0
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 68
+ 84
+
+
+
+
+ 68
+ 84
+
+
+
+ PointingHandCursor
+
+
+
+
+
+
+ 48
+ 64
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 68
+ 84
+
+
+
+
+ 68
+ 84
+
+
+
+ PointingHandCursor
+
+
+
+
+
+
+ 48
+ 64
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 68
+ 84
+
+
+
+
+ 68
+ 84
+
+
+
+ PointingHandCursor
+
+
+
+
+
+
+ 48
+ 64
+
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 273
+
+
+
+
+
+
+
+ -
+
+
+
+ 16
+
+
+
+
+
+
+ 0
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::NoFocus
+
+
+ 0
+
+
+
+
+ 4
+
+
+ 0
+
+
+ 4
+
+
+ 0
+
+
+ 8
+
+ -
+
+
+
+ 320
+ 48
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+
+ 4
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+
+ 96
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Parse logs from the Apache2 web server
+
+
+ Apache2
+
+
+ true
+
+
+
+ -
+
+
+
+ 96
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Parse logs from the Nginx web server
+
+
+ Nginx
+
+
+ true
+
+
+
+ -
+
+
+
+ 96
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Parse logs from the Microsoft IIS web server
+
+
+ IIS
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+ false
+
+
+
+
+
+ QTextEdit::NoWrap
+
+
+
+
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+hr { height: 1px; border-width: 0; }
+li.unchecked::marker { content: "\2610"; }
+li.checked::marker { content: "\2612"; }
+</style></head><body style=" font-family:'Noto Sans'; font-size:16pt; font-weight:400; font-style:normal;">
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html>
+
+
+ Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
+
+
+ false
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 10
+
+
+
+ PointingHandCursor
+
+
+ false
+
+
+ Select/deselect all the files
+
+
+ All
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 90
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 36
+
+
+
+
+ 32
+ 36
+
+
+
+ PointingHandCursor
+
+
+ Inspect a log file
+
+
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 320
+ 16777215
+
+
+
+
+ 13
+
+
+
+ true
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+ true
+
+
+ true
+
+
+ 75
+
+
+ 150
+
+
+ true
+
+
+
+ Name
+
+
+
+ 12
+
+
+
+
+
+ Size
+
+
+
+ 12
+ false
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Refresh the list
+
+
+
+
+
+
+ 16
+ 16
+
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 280
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 154
+ 20
+
+
+
+
+ -
+
+
+
+ 512
+ 32
+
+
+
+
+ 512
+ 32
+
+
+
+ PointingHandCursor
+
+
+
+
+
+
+ :/icons/icons/midtone/down.png :/icons/icons/midtone/down.png
+
+
+
+ 512
+ 32
+
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 153
+ 20
+
+
+
+
+
+
+
+
+
+
+
+
+ 4
+
+
+ 4
+
+
+ 0
+
+
+ 4
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 154
+ 20
+
+
+
+
+ -
+
+
+
+ 512
+ 32
+
+
+
+
+ 512
+ 32
+
+
+
+ PointingHandCursor
+
+
+
+
+
+
+ :/icons/icons/midtone/up.png :/icons/icons/midtone/up.png
+
+
+
+ 512
+ 32
+
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 153
+ 20
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 112
+
+
+
+
+ 16777215
+ 112
+
+
+
+
+ 4
+
+
+ 8
+
+
+ 0
+
+
+ 8
+
+
+ 0
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 16
+ 98
+
+
+
+
+ -
+
+
+
+ 96
+ 0
+
+
+
+
+ 128
+ 16777215
+
+
+
+ Total size of the parsed data
+
+
+
+ 8
+
+
+ 4
+
+ -
+
+
+ false
+
+
+
+ 64
+ 64
+
+
+
+
+ 32
+
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+ 64
+ 32
+
+
+
+
+ 192
+ 128
+
+
+
+
+ 13
+
+
+
+ 0 KiB
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 16
+ 98
+
+
+
+
+ -
+
+
+
+ 96
+ 0
+
+
+
+
+ 128
+ 16777215
+
+
+
+ Total number of parsed lines
+
+
+
+ 8
+
+
+ 4
+
+ -
+
+
+ false
+
+
+
+ 64
+ 64
+
+
+
+
+ 32
+
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+ 64
+ 32
+
+
+
+
+ 192
+ 128
+
+
+
+
+ 13
+
+
+
+ 0
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 16
+ 98
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+ 128
+ 64
+
+
+
+
+ 192
+ 64
+
+
+
+ PointingHandCursor
+
+
+ Qt::TabFocus
+
+
+ Start parsing the selected files
+
+
+ START
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 16
+ 98
+
+
+
+
+ -
+
+
+
+ 96
+ 0
+
+
+
+
+ 128
+ 16777215
+
+
+
+ Time elapsed since the start
+
+
+
+ 8
+
+
+ 4
+
+ -
+
+
+ false
+
+
+
+ 64
+ 64
+
+
+
+
+ 32
+
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+ 64
+ 32
+
+
+
+
+ 192
+ 128
+
+
+
+
+ 13
+
+
+
+ 00:00
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 16
+ 98
+
+
+
+
+ -
+
+
+
+ 96
+ 0
+
+
+
+
+ 128
+ 16777215
+
+
+
+ Average speed, in parsed data size per second
+
+
+ QFrame::Raised
+
+
+
+ 8
+
+
+ 4
+
+ -
+
+
+ false
+
+
+
+ 64
+ 64
+
+
+
+
+ 32
+
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+ 64
+ 32
+
+
+
+
+ 192
+ 128
+
+
+
+
+ 13
+
+
+
+ 0 Kib/s
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 16
+ 98
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 128
+
+
+
+ Qt::NoFocus
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+ 8
+
+
+ 0
+
+
+ 8
+
+
+ 0
+
+ -
+
+
+ 0
+
+
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ -
+
+
+
+ 64
+ 88
+
+
+
+
+ 64
+ 88
+
+
+
+
+ 23
+
+
+
+ PointingHandCursor
+
+
+ Draw the chart
+
+
+
+ 44
+ 44
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+ -
+
+
+
+ 13
+
+
+
+ Web Server
+
+
+
+ -
+
+
+
+ 100
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a Web Server
+
+
+ QComboBox::AdjustToContents
+
+ -
+
+ Apache2
+
+
+ -
+
+ Nginx
+
+
+ -
+
+ IIS
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 356
+ 16777215
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Year
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Month
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Day
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 13
+
+
+
+ Qt::LeftToRight
+
+
+ Hour
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 72
+ 32
+
+
+
+
+ 72
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 120
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 56
+ 32
+
+
+
+
+ 56
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+ 64
+ 32
+
+
+
+
+ 64
+ 32
+
+
+
+
+ 13
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 82
+ 20
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 64
+ 88
+
+
+
+
+ 64
+ 88
+
+
+
+
+ 23
+
+
+
+ PointingHandCursor
+
+
+ Update the database with current Warning States
+
+
+
+ 44
+ 44
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+
+ 13
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ false
+
+
+ false
+
+
+ QAbstractItemView::ScrollPerPixel
+
+
+ true
+
+
+ 64
+
+
+ 128
+
+
+ true
+
+
+
+ Warning
+
+
+ Log line marked as Warning
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Date
+
+
+ Date when the request arrived (YYYY-MM-DD)
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Time
+
+
+ Time when the request arrived (hh:mm:ss)
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Protocol
+
+
+ Protocol of the request
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Method
+
+
+ Method of the request
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ URI
+
+
+ URI of the requested page
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Query
+
+
+ Query carried along with the URI
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Response code
+
+
+ Response code from the server
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Referrer
+
+
+ The URL which redirected the Client to the requested page
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Cookie
+
+
+ Cookie used for the request
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ User-agent
+
+
+ User-agent of the client which made the request
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Client
+
+
+ IP address of the Client which made the request
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Bytes received
+
+
+ Size ib Bytes of the request, usually includes header and data
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Bytes sent
+
+
+ Size in Bytes of the served content, usually includes header and data
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Time taken
+
+
+ Time taken by the server to serve the content, in milliseconds
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ rowid
+
+
+
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+
+
+
+
+
+
+
+ 8
+
+
+ 18
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 321
+ 0
+
+
+
+
+ 0
+ 140
+
+
+
+
+ 8
+
+
+ 0
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 112
+ 32
+
+
+
+
+ 112
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a Web Server
+
+
+ QComboBox::AdjustToContents
+
+ -
+
+ Apache2
+
+
+ -
+
+ Nginx
+
+
+ -
+
+ IIS
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 8
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 64
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 80
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 120
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Year
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Month
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Day
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+
+ 13
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+ 64
+
+
+ 128
+
+
+ true
+
+
+
+ Time taken
+
+
+ Time taken by the server to serve the content, in milliseconds
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ URI
+
+
+ URI of the requested page
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Query
+
+
+ Query carried along with the URI
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Method
+
+
+ Method of the request
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Protocol
+
+
+ Protocol of the request
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Response code
+
+
+ Response code from the server
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Time
+
+
+ Time when the request arrived (hh:mm:ss)
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 328
+
+
+
+
+ 321
+ 16777215
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Only use lines in which the field is starting with this string
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Only use lines in which the field is starting with this string
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Protocol of the request
+
+
+ Protocol:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ Noto Sans
+ 13
+
+
+
+ Method of the request
+
+
+ Method:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Only use lines in which the field is matching this statement.
+Use '!', '=','<' or '>' to declare what to use
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Only use lines in which the field is starting with this string
+
+
+
+ -
+
+
+
+ Noto Sans
+ 13
+
+
+
+ Response code from the server
+
+
+ Response:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ Noto Sans
+ 13
+
+
+
+ Query carried along with the URI
+
+
+ Query:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Only use lines in which the field is starting with this string
+
+
+
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ URI of the requested page
+
+
+ URI:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 64
+
+
+
+
+ 16777215
+ 64
+
+
+
+
+ 23
+
+
+
+ PointingHandCursor
+
+
+
+ 44
+ 44
+
+
+
+
+ -
+
+
+
+ 16777215
+ 28
+
+
+
+
+ Noto Sans
+ 16
+
+
+
+ Filters
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+
+
+
+
+
+ 8
+
+
+ 18
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+ Qt::Vertical
+
+
+
+
+ 13
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+ 64
+
+
+ 128
+
+
+ true
+
+
+ false
+
+
+
+ Count
+
+
+ Number of occurrences
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+ Item
+
+
+ Value of the field
+
+
+ AlignLeading|AlignVCenter
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 321
+ 0
+
+
+
+
+ 321
+ 16777215
+
+
+
+
+ 8
+
+
+ 0
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::AlignHCenter|Qt::AlignTop
+
+
+
+
+ 14
+ 0
+ 263
+ 402
+
+
+
+
+ 0
+ 0
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 240
+ 0
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Protocol of the request
+
+
+ Protocol
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 240
+ 0
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Method of the request
+
+
+ Method
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 240
+ 0
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ URI of the requested page
+
+
+ URI
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 240
+ 0
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Query carried along with the URI
+
+
+ Query
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 240
+ 0
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Response code from the server
+
+
+ Response code
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 240
+ 0
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ The URL which redirected the Client to the requested page
+
+
+ Referrer
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 240
+ 0
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Cookie used for the request
+
+
+ Cookie
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 240
+ 0
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ User-agent of the client which made the request
+
+
+ User-agent
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 240
+ 0
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ IP address of the Client which made the request
+
+
+ Client
+
+
+ true
+
+
+
+
+
+
+
+ -
+
+
+
+ 112
+ 32
+
+
+
+
+ 112
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a Web Server
+
+
+ QComboBox::AdjustToContents
+
+ -
+
+ Apache2
+
+
+ -
+
+ Nginx
+
+
+ -
+
+ IIS
+
+
+
+
+ -
+
+
+
+ 0
+ 8
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 80
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 120
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Day
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Year
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 64
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Month
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 298
+ 8
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+ -
+
+
+
+ 13
+
+
+
+ Month
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Day
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ From:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 32
+
+
+
+
+ 72
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 72
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Year
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 56
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ To:
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 32
+
+
+
+
+ 56
+ 32
+
+
+
+
+ 13
+
+
+
+
+ -
+
+
+
+ 120
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ With strings, only the lines in which the field is starting with this string will be used.
+With numbers, use '!', '=','<' or '>' to declare what to use
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 298
+ 44
+
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Field:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a log field to view
+
+
+
+ -
+
+
+
+ 13
+
+
+
+ Filter:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 100
+ 88
+
+
+
+
+ 100
+ 88
+
+
+
+
+ 23
+
+
+
+ PointingHandCursor
+
+
+ Draw the chart
+
+
+
+ 44
+ 44
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 10
+
+
+
+
+ -
+
+
+
+ 100
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a Web Server
+
+
+ QComboBox::AdjustToContents
+
+ -
+
+ Apache2
+
+
+ -
+
+ Nginx
+
+
+ -
+
+ IIS
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+ 8
+
+
+ 2
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 192
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 4
+
+
+
+
+ -
+
+
+
+ 100
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a Web Server
+
+
+ QComboBox::AdjustToContents
+
+ -
+
+ Apache2
+
+
+ -
+
+ Nginx
+
+
+ -
+
+ IIS
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 6
+
+ -
+
+
+
+ 44
+ 0
+
+
+
+
+ 13
+
+
+
+ From:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 72
+ 32
+
+
+
+
+ 13
+
+
+
+ Year
+
+
+ Year
+
+
+
+ -
+
+
+
+ 120
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Month
+
+
+ Month
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 60
+ 32
+
+
+
+
+ 13
+
+
+
+ Day
+
+
+ Day
+
+
+
+ -
+
+
+
+ 44
+ 0
+
+
+
+
+ 44
+ 16777215
+
+
+
+
+ 13
+
+
+
+ To:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 72
+ 32
+
+
+
+
+ 13
+
+
+
+ Year
+
+
+ Year
+
+
+
+ -
+
+
+
+ 120
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Month
+
+
+ Month
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 60
+ 32
+
+
+
+
+ 13
+
+
+
+ Day
+
+
+ Day
+
+
+
+
+
+
+ -
+
+
+
+ 100
+ 88
+
+
+
+
+ 100
+ 88
+
+
+
+
+ 23
+
+
+
+ PointingHandCursor
+
+
+ Draw the chart
+
+
+
+ 44
+ 44
+
+
+
+
+ -
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+ -
+
+
+
+ 48
+ 0
+
+
+
+
+ 13
+
+
+
+ Filter:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ With strings, only the lines in which the field is starting with this string will be used.
+With numbers, use '!', '=','<' or '>' to declare what to use
+
+
+
+ -
+
+
+
+ 160
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a log field to view
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ With strings, only the lines in which the field is starting with this string will be used.
+With numbers, use '!', '=','<' or '>' to declare what to use
+
+
+
+ -
+
+
+
+ 160
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a log field to view
+
+
+
+ -
+
+
+
+ 44
+ 0
+
+
+
+
+ 13
+
+
+
+ Field:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 48
+ 0
+
+
+
+
+ 13
+
+
+
+ Filter:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 44
+ 0
+
+
+
+
+ 13
+
+
+
+ Field:
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 64
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 234
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 48
+
+
+
+
+ 320
+ 48
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+
+ 96
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Parse logs from the Apache2 web server
+
+
+ Apache2
+
+
+ true
+
+
+
+ -
+
+
+
+ 96
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Parse logs from the Nginx web server
+
+
+ Nginx
+
+
+ true
+
+
+
+ -
+
+
+
+ 96
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ PointingHandCursor
+
+
+ Parse logs from the Microsoft IIS web server
+
+
+ IIS
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 234
+ 20
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::Horizontal
+
+
+ 8
+
+
+ false
+
+
+
+ Qt::Vertical
+
+
+ 8
+
+
+ false
+
+
+
+ -
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 317
+ 348
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 20
+
+
+
+
+ 10
+
+
+
+ 0
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Protocol
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 20
+
+
+
+
+ 10
+
+
+
+ 0
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Method
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ URI
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 20
+
+
+
+
+ 10
+
+
+
+ 0
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ User-agent
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 20
+
+
+
+
+ 10
+
+
+
+ 0
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Most recurrent
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Most trafficked
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 317
+ 348
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Date ever
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 20
+
+
+
+
+ 10
+
+
+
+ 0
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Day of the week
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 20
+
+
+
+
+ 10
+
+
+
+ 0
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Hour of the day
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 20
+
+
+
+
+ 10
+
+
+
+ 0
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+
+
+
+
+
+
+ Qt::Vertical
+
+
+ 8
+
+
+ false
+
+
+
+ -
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 429
+ 276
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Time taken
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Bytes sent
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Bytes received
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Mean/Max performances
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+
+ -
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 429
+ 276
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Bytes sent
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Requests received
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Time taken
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+
+
+
+ -
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Total work
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 34
+
+
+
+
+ 16777215
+ 34
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Sunken
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+
+
+
+ 32
+ 32
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+
+
+
+ 32
+ 32
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+
+
+
+ 32
+ 32
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+
+
+
+ 32
+ 32
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+
+
+
+ 32
+ 32
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+
+
+
+ 32
+ 32
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+ QTabWidget::West
+
+
+ 0
+
+
+
+ General
+
+
+ General settings
+
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+
+ 13
+
+
+
+ 0
+
+
+
+ Window
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Remember the window's position and size
+
+
+ Remember position and size
+
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Remember window's position and size
+
+
+ Geometry
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Theme to use for the window
+
+
+ Theme
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 160
+ 32
+
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+
+ 10
+
+ -
+
+ None (System)
+
+
+ -
+
+ Ash
+
+
+ -
+
+ Candy
+
+
+ -
+
+ Herb
+
+
+ -
+
+ Powder
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 569
+ 32
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Theme to use for the window
+
+
+ Icons
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 160
+ 32
+
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+
+ 10
+
+ -
+
+ Auto (Default)
+
+
+ -
+
+ Light
+
+
+ -
+
+ Dark
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 569
+ 32
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 748
+ 260
+
+
+
+
+
+
+
+
+ Dialogs
+
+
+ -
+
+
+
+ 560
+ 0
+
+
+
+ -
+
+
+
+ 180
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Define the quantity of dialog mesages shown
+
+
+ Dialogs level
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 180
+ 18
+
+
+
+
+ -
+
+
+
+ 128
+ 24
+
+
+
+
+ 128
+ 32
+
+
+
+
+ 10
+
+
+
+ Reduced quantity of dialog messages shown
+
+
+ Essential
+
+
+ Qt::AlignBottom|Qt::AlignHCenter
+
+
+
+ -
+
+
+
+ 164
+ 24
+
+
+
+
+ 164
+ 32
+
+
+
+
+ 10
+
+
+
+ Normal quantity of dialog messages shown
+
+
+ Normal
+
+
+ Qt::AlignBottom|Qt::AlignHCenter
+
+
+
+ -
+
+
+
+ 128
+ 24
+
+
+
+
+ 128
+ 32
+
+
+
+
+ 10
+
+
+
+ Augmented quantity of dialog messages shown
+
+
+ Explanatory
+
+
+ Qt::AlignBottom|Qt::AlignHCenter
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 98
+ 138
+
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 93
+
+
+
+
+ -
+
+
+
+ 192
+ 32
+
+
+
+
+ 192
+ 32
+
+
+
+
+ 13
+
+
+
+ Dialogs from the main processes
+
+
+ General
+
+
+
+ -
+
+
+
+ 320
+ 24
+
+
+
+
+ 320
+ 32
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ Qt::Horizontal
+
+
+ QSlider::TicksBothSides
+
+
+ 1
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 40
+ 93
+
+
+
+
+ -
+
+
+
+ 192
+ 32
+
+
+
+
+ 192
+ 32
+
+
+
+
+ 13
+
+
+
+ Dialogs emitted when parsing logs
+
+
+ Logs parser
+
+
+
+ -
+
+
+
+ 320
+ 24
+
+
+
+
+ 320
+ 32
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ Qt::Horizontal
+
+
+ QSlider::TicksBothSides
+
+
+ 1
+
+
+
+ -
+
+
+
+ 192
+ 32
+
+
+
+
+ 192
+ 32
+
+
+
+
+ 13
+
+
+
+ Dialogs emitted when viewing statistics
+
+
+ Statistics viewer
+
+
+
+ -
+
+
+
+ 320
+ 24
+
+
+
+
+ 320
+ 32
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ Qt::Horizontal
+
+
+ QSlider::TicksAbove
+
+
+ 1
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 718
+ 271
+
+
+
+
+
+
+
+
+ TextBrowser
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 160
+ 32
+
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+ -
+
+ Metropolis
+
+
+ -
+
+ Hack
+
+
+ -
+
+ 3270
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 569
+ 32
+
+
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Font to use for the Text Browser
+
+
+ Font
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Double-spaced lines
+
+
+ Use wide lines
+
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Define the spacing between lines
+
+
+ Lines spacing
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 160
+ 32
+
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+ -
+
+ None
+
+
+ -
+
+ Breeze
+
+
+ -
+
+ Monokai
+
+
+ -
+
+ Radical
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 566
+ 32
+
+
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Color scheme to use for the Text Browser
+
+
+ Color scheme
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 32
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ The appearance of the Text Browser with the current settings
+
+
+ Preview
+
+
+
+ -
+
+
+
+ 0
+ 64
+
+
+
+ false
+
+
+ false
+
+
+ QTextEdit::NoWrap
+
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+hr { height: 1px; border-width: 0; }
+li.unchecked::marker { content: "\2610"; }
+li.checked::marker { content: "\2612"; }
+</style></head><body style=" font-family:'Noto Sans'; font-size:13pt; font-weight:400; font-style:normal;">
+<p align="center" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html>
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ Charts
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 160
+ 32
+
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+ -
+
+ Default (Light)
+
+
+ -
+
+ Dark
+
+
+ -
+
+ Sand
+
+
+ -
+
+ Cerulean
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 569
+ 32
+
+
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Theme to use for the Charts
+
+
+ Theme
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Preview
+
+
+
+ -
+
+
+
+ 0
+ 256
+
+
+
+
+
+
+
+
+
+
+
+ Databases
+
+
+ -
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 16
+
+
+
+ PointingHandCursor
+
+
+ Apply the current path
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Path where the logs data collection database is located
+
+
+ Logs data
+
+
+
+ -
+
+
+ true
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ a Arbei Berry
+ 16
+
+
+
+ The given path doen't exists, or doesn't point to a folder
+
+
+
+
+
+ :/icons/icons/err.png
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 16
+
+
+
+ PointingHandCursor
+
+
+ Apply the current path
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Path where the used files hashes database is located
+
+
+ Used files
+
+
+
+ -
+
+
+ true
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ a Arbei Berry
+ 16
+
+
+
+ The given path doen't exists, or doesn't point to a folder
+
+
+
+
+
+ :/icons/icons/err.png
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 8
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Insert the base path only, file name excluded
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 178
+
+
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Path for the database files
+
+
+ Paths
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Insert the base path only, file name excluded
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Backup options for the Logs Data database
+
+
+ Backups
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 28
+
+
+
+
+ -
+
+
+ A new backup is made when closing LogDoctor after having succesfully edited the database
+
+
+ Backup the Logs Data database and keep
+
+
+
+ -
+
+
+
+ 100
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ copies
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 284
+ 20
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Expanding
+
+
+
+ 668
+ 285
+
+
+
+
+
+
+
+
+
+
+
+
+ Logs
+
+
+ Logs related settings
+
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+
+ 8
+
+ -
+
+
+
+ 13
+
+
+
+ 0
+
+
+
+ Defaults
+
+
+ -
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Remember window's position and size
+
+
+ Default WebServer
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 98
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Apache2
+
+
+ true
+
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Nginx
+
+
+
+ -
+
+
+
+ 128
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ IIS
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Expanding
+
+
+
+ 568
+ 303
+
+
+
+
+
+
+
+
+ Control
+
+
+ Options about the log files usage control
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 758
+ 8
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 80
+
+
+
+
+ 16777215
+ 84
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Remember window's position and size
+
+
+ Usage control
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Remember window's position and size
+
+
+ Hide already used files
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 758
+ 13
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 80
+
+
+
+
+ 16777215
+ 84
+
+
+
+
+ 16
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Remember window's position and size
+
+
+ Size warnings
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Remember window's position and size
+
+
+ Warn me when using files with a size exceeding:
+
+
+ true
+
+
+
+ -
+
+
+
+ 104
+ 32
+
+
+
+
+ 104
+ 32
+
+
+
+
+ 13
+
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ QAbstractSpinBox::PlusMinus
+
+
+ MiB
+
+
+ 1
+
+
+ 1023
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 228
+ 20
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Expanding
+
+
+
+ 758
+ 232
+
+
+
+
+
+
+
+
+ Apache2
+
+
+ Configure Apache2 specific options
+
+
+ -
+
+
+
+ 13
+
+
+
+ 0
+
+
+
+ Paths
+
+
+ -
+
+
+
+ 16777215
+ 184
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ -
+
+
+ true
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ a Arbei Berry
+ 16
+
+
+
+ The given path doen't exists, or doesn't point to a folder
+
+
+
+
+
+ :/icons/icons/err.png
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 16
+
+
+
+ PointingHandCursor
+
+
+ Apply the current path
+
+
+
+ 22
+ 22
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Logs folder
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Expanding
+
+
+
+ 706
+ 262
+
+
+
+
+
+
+
+
+ Format
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Logs format string
+
+
+ Format string
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Insert the format string you're using
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 16
+
+
+
+ PointingHandCursor
+
+
+ Apply the current string
+
+
+
+ 22
+ 22
+
+
+
+
+ -
+
+
+
+ 0
+ 16
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 160
+ 32
+
+
+
+
+ 160
+ 32
+
+
+
+ Generate a sample log line from the currently saved string, to check if it gets formatted correctly.
+Any field not considered by LogDoctor will appear as 'DISCARDED'
+
+
+ Generate sample
+
+
+
+ -
+
+
+
+ 16777215
+ 64
+
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 718
+ 54
+
+
+
+ -
+
+
+
+ 0
+ 36
+
+
+
+
+ 16777215
+ 36
+
+
+
+
+ 13
+
+
+
+ Please check the correctness of this line.
+Fields marked as 'DISCARDED' got parsed correctly, but are not considered by LogDoctor
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 8
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 728
+ 109
+
+
+
+
+ -
+
+
+
+ 0
+ 76
+
+
+
+
+ 16777215
+ 76
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 536
+ 18
+
+
+
+
+ -
+
+
+
+ 128
+ 56
+
+
+
+
+ 16777215
+ 56
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Open an help window
+
+
+ Qt::RightToLeft
+
+
+ Help
+
+
+
+ :/icons/icons/help.png
+
+
+
+
+ 48
+ 48
+
+
+
+
+
+
+
+
+
+
+
+ Warnlists
+
+
+ -
+
+
+ -
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a log field
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 8
+ 20
+
+
+
+
+ -
+
+
+ Use warnlist for this field
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Add the current line to the list
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/add.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Remove the selected item from the list
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/rem.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/up.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/down.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 196
+
+
+
+
+
+
+
+
+
+
+
+ Blacklists
+
+
+ -
+
+
+ -
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a log field
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 8
+ 20
+
+
+
+
+ -
+
+
+ Use blacklist for this field
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Add line
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/add.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Remove selection
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/rem.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/up.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/down.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 196
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Nginx
+
+
+ Configure Nginx specific options
+
+
+ -
+
+
+
+ 13
+
+
+
+ 0
+
+
+
+ Paths
+
+
+ -
+
+
+
+ 16777215
+ 184
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ -
+
+
+ true
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ a Arbei Berry
+ 16
+
+
+
+ The given path doen't exists, or doesn't point to a folder
+
+
+
+
+
+ :/icons/icons/err.png
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 16
+
+
+
+ PointingHandCursor
+
+
+ Apply the current path
+
+
+
+ 22
+ 22
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Logs folder
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Expanding
+
+
+
+ 706
+ 262
+
+
+
+
+
+
+
+
+ Format
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Logs format string
+
+
+ Format string
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Insert the format string you're using
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 16
+
+
+
+ PointingHandCursor
+
+
+ Apply the current string
+
+
+
+ 22
+ 22
+
+
+
+
+ -
+
+
+
+ 0
+ 16
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 160
+ 32
+
+
+
+
+ 160
+ 32
+
+
+
+ Generate a sample log line from the currently saved string, to check if it gets formatted correctly.
+Any field not considered by LogDoctor will appear as 'DISCARDED'
+
+
+ Generate sample
+
+
+
+ -
+
+
+
+ 16777215
+ 64
+
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 62
+ 54
+
+
+
+ -
+
+
+
+ 0
+ 36
+
+
+
+
+ 16777215
+ 36
+
+
+
+
+ 13
+
+
+
+ Please check the correctness of this line.
+Fields marked as 'DISCARDED' got parsed correctly, but are not considered by LogDoctor
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 8
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 728
+ 109
+
+
+
+
+ -
+
+
+
+ 0
+ 76
+
+
+
+
+ 16777215
+ 76
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 536
+ 18
+
+
+
+
+ -
+
+
+
+ 128
+ 56
+
+
+
+
+ 16777215
+ 56
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Open an help window
+
+
+ Qt::RightToLeft
+
+
+ Help
+
+
+
+ :/icons/icons/help.png
+
+
+
+
+ 48
+ 48
+
+
+
+
+
+
+
+
+
+
+
+ Warnlists
+
+
+ -
+
+
+ -
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a log field
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 8
+ 20
+
+
+
+
+ -
+
+
+ Use warnlist for this field
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Add line
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/add.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Remove selection
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/rem.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/up.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/down.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 196
+
+
+
+
+
+
+
+
+
+
+
+ Blacklists
+
+
+ -
+
+
+ -
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a log field
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 8
+ 20
+
+
+
+
+ -
+
+
+ Use blacklist for this field
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Add line
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/add.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Remove selection
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/rem.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/up.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/down.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 196
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ IIS
+
+
+ Configure IIS specific options
+
+
+ -
+
+
+
+ 13
+
+
+
+ 0
+
+
+
+ Paths
+
+
+ -
+
+
+
+ 16777215
+ 184
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Logs folder
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ -
+
+
+ true
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ a Arbei Berry
+ 16
+
+
+
+ The given path doen't exists, or doesn't point to a folder
+
+
+
+
+
+ :/icons/icons/err.png
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 16
+
+
+
+ PointingHandCursor
+
+
+ Apply the current path
+
+
+
+ 22
+ 22
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Expanding
+
+
+
+ 706
+ 262
+
+
+
+
+
+
+
+
+ Format
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 48
+
+
+
+ -
+
+
+
+ 88
+ 32
+
+
+
+
+ 88
+ 32
+
+
+
+ W3C
+
+
+ true
+
+
+
+ -
+
+
+
+ 88
+ 32
+
+
+
+
+ 88
+ 32
+
+
+
+ NCSA
+
+
+
+ -
+
+
+
+ 88
+ 32
+
+
+
+
+ 88
+ 32
+
+
+
+ IIS
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Logs format string
+
+
+ Format string
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+ Insert the format string you're using
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 16
+
+
+
+ PointingHandCursor
+
+
+ Apply the current string
+
+
+
+ 22
+ 22
+
+
+
+
+ -
+
+
+
+ 0
+ 16
+
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 160
+ 32
+
+
+
+
+ 160
+ 32
+
+
+
+ Generate a sample log line from the currently saved string, to check if it gets formatted correctly.
+Any field not considered by LogDoctor will appear as 'DISCARDED'
+
+
+ Generate sample
+
+
+
+ -
+
+
+
+ 16777215
+ 64
+
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 62
+ 54
+
+
+
+ -
+
+
+
+ 0
+ 36
+
+
+
+
+ 16777215
+ 36
+
+
+
+
+ 13
+
+
+
+ Please check the correctness of this line.
+Fields marked as 'DISCARDED' got parsed correctly, but are not considered by LogDoctor
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 8
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 725
+ 37
+
+
+
+
+ -
+
+
+
+ 0
+ 76
+
+
+
+
+ 16777215
+ 76
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 536
+ 18
+
+
+
+
+ -
+
+
+
+ 128
+ 56
+
+
+
+
+ 16777215
+ 56
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Open an help window
+
+
+ Qt::RightToLeft
+
+
+ Help
+
+
+
+ :/icons/icons/help.png
+
+
+
+
+ 48
+ 48
+
+
+
+
+
+
+
+
+
+
+
+ Warnlists
+
+
+ -
+
+
+ -
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a log field
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 8
+ 20
+
+
+
+
+ -
+
+
+ Use warnlist for this field
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Add line
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/add.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Remove selection
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/rem.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/up.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/down.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 196
+
+
+
+
+
+
+
+
+
+
+
+ Blacklists
+
+
+ -
+
+
+ -
+
+
+
+ 160
+ 32
+
+
+
+
+ 13
+
+
+
+ Select a log field
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 8
+ 20
+
+
+
+
+ -
+
+
+ Use blacklist for this field
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Add line
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/add.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Remove selection
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/rem.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/up.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 20
+
+
+
+ PointingHandCursor
+
+
+ Move the selected item down
+
+
+ Qt::RightToLeft
+
+
+
+
+
+
+ :/icons/icons/down.png
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 196
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+
+
+
+ :/flags/flags/en.png :/flags/flags/en.png
+
+
+ English
+
+
+ English
+
+
+
+
+ true
+
+
+
+ :/flags/flags/es.png :/flags/flags/es.png
+
+
+ Español
+
+
+ Español
+
+
+
+
+ true
+
+
+
+ :/flags/flags/fr.png :/flags/flags/fr.png
+
+
+ Francais
+
+
+ Francais
+
+
+
+
+ true
+
+
+
+ :/flags/flags/it.png :/flags/flags/it.png
+
+
+ Italiano
+
+
+ Italiano
+
+
+
+
+ Check updates
+
+
+ Perform a version-check
+
+
+
+
+ Infos
+
+
+
+
+ BlockNote
+
+
+ Open a block-note like window to write temporary text
+
+
+
+
+ CrissCross
+
+
+ Play CrissCross
+
+
+
+
+ Snake
+
+
+ Play Snake
+
+
+
+
+
+ QChartView
+ QGraphicsView
+
+
+
+
+
+
+
+
diff --git a/logdoctor/modules/craphelp/craphelp.cpp b/logdoctor/modules/craphelp/craphelp.cpp
new file mode 100644
index 00000000..442eb940
--- /dev/null
+++ b/logdoctor/modules/craphelp/craphelp.cpp
@@ -0,0 +1,116 @@
+
+#include "craphelp.h"
+#include "ui_craphelp.h"
+
+#include "modules/exceptions.h"
+
+#include "utilities/io.h"
+
+
+Craphelp::Craphelp(QWidget *parent) :
+ QWidget(parent),
+ ui(new Ui::Craphelp)
+{
+ ui->setupUi(this);
+}
+
+Craphelp::~Craphelp()
+{
+ delete ui;
+}
+
+
+const std::unordered_map Craphelp::getColorScheme( const int& scheme_id )
+{
+ switch ( scheme_id ) {
+ case 0:
+ // none
+ return {
+ {"background", ""},
+ {"text", ""},
+ {"h1", ""},
+ {"h3", ""},
+ {"code", ""},
+ {"link", ""} };
+ case 1:
+ // breeze
+ return {
+ {"background", "#ffffff"},
+ {"text", "#1f1c1b"},
+ {"italic", "#9c9c9b"},
+ {"h1", "#006e28"},
+ {"h3", "#54b8ff"},
+ {"code", "#644a9b"},
+ {"link", "#d24f4f"} };
+
+ case 2:
+ // monokai
+ return {
+ {"background", "#272822"},
+ {"text", "#d1d1cb"},
+ {"italic", "#c1b864"},
+ {"h1", "#a6e22e"},
+ {"h3", "#9773db"},
+ {"code", "#57adbc"},
+ {"link", "#f92672"} };
+
+ case 3:
+ // radical
+ return {
+ {"background", "#141322"},
+ {"text", "#a8c0c2"},
+ {"italic", "#fda8bc"},
+ {"h1", "#d5358f"},
+ {"h3", "#56e8e4"},
+ {"code", "#7c9c9e"},
+ {"link", "#ff85a1"} };
+
+ default:
+ // wrong, shouldn't be here
+ throw GenericException( "Unexpected ColorScheme ID: "+std::to_string( scheme_id ), true ); // leave un-catched
+ }
+}
+
+
+void Craphelp::helpLogsFormat( const std::string& path, const QFont& font, const int &color_scheme_id )
+{
+ std::unordered_map color_scheme = this->getColorScheme( color_scheme_id );
+ std::string aux;
+ IOutils::readFile( path, aux );
+ QString content;
+ if ( color_scheme_id == 0 ) {
+ // remove the style for the colors
+ while (true) {
+ const int start = aux.find( "background-color:" );
+ if ( start < 0 || start > aux.size() ) {
+ break;
+ }
+ const int stop = aux.find( ";\n", start ) + 2;
+ aux = aux.substr( 0, start ) + aux.substr( stop );
+ }
+ while (true) {
+ const int start = aux.find( "color:" );
+ if ( start < 0 || start > aux.size() ) {
+ break;
+ }
+ const int stop = aux.find( ";\n", start ) + 2;
+ aux = aux.substr( 0, start ) + aux.substr( stop );
+ }
+ content = QString::fromStdString( aux );
+
+ } else {
+ // replace with colors
+ content = QString::fromStdString( aux )
+ .replace( "$BG$", color_scheme.at("background") )
+ .replace( "$TEXT$", color_scheme.at("text") )
+ .replace( "$IT$", color_scheme.at("italic") )
+ .replace( "$H1$", color_scheme.at("h1") )
+ .replace( "$H3$", color_scheme.at("h3") )
+ .replace( "$CODE$", color_scheme.at("code") )
+ .replace( "$LINK$", color_scheme.at("link") );
+ }
+ // show the content
+ this->ui->helpBrowser->setText( content );
+ // apply the font
+ this->ui->helpBrowser->setFont( font );
+}
diff --git a/logdoctor/modules/craphelp/craphelp.h b/logdoctor/modules/craphelp/craphelp.h
new file mode 100644
index 00000000..97b5d1b1
--- /dev/null
+++ b/logdoctor/modules/craphelp/craphelp.h
@@ -0,0 +1,39 @@
+#ifndef CRAPHELP_H
+#define CRAPHELP_H
+
+#include
+
+#include
+
+
+namespace Ui {
+ class Craphelp;
+}
+
+//! Craphelp
+/*!
+ Displays an help window
+*/
+class Craphelp : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit Craphelp( QWidget* parent=nullptr );
+ ~Craphelp();
+
+ //! Provides help about log formats
+ /*!
+ \param path The path of the file resource to be displayed
+ \param font The font to be used
+ \param color_scheme_id The ID of the color-scheme to be used
+ */
+ void helpLogsFormat( const std::string& path, const QFont& font, const int& color_scheme_id );
+
+private:
+ Ui::Craphelp *ui;
+
+ const std::unordered_map getColorScheme( const int& scheme_id );
+};
+
+#endif // CRAPHELP_H
diff --git a/logdoctor/modules/craphelp/craphelp.ui b/logdoctor/modules/craphelp/craphelp.ui
new file mode 100644
index 00000000..bc4ce7b3
--- /dev/null
+++ b/logdoctor/modules/craphelp/craphelp.ui
@@ -0,0 +1,51 @@
+
+
+ Craphelp
+
+
+
+ 0
+ 0
+ 747
+ 544
+
+
+
+ LogDoctor - Help
+
+
+
+ :/logo/logo/logdoctor.svg :/logo/logo/logdoctor.svg
+
+
+
+
+
+ -
+
+
+
+
+
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+hr { height: 1px; border-width: 0; }
+</style></head><body style=" font-family:'Noto Sans'; font-size:13pt; font-weight:400; font-style:normal;">
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html>
+
+
+ Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
+
+
+ false
+
+
+
+
+
+
+
+
+
+
diff --git a/logdoctor/modules/crapinfo/crapinfo.cpp b/logdoctor/modules/crapinfo/crapinfo.cpp
new file mode 100644
index 00000000..e76e5711
--- /dev/null
+++ b/logdoctor/modules/crapinfo/crapinfo.cpp
@@ -0,0 +1,290 @@
+
+#include "crapinfo.h"
+#include "ui_crapinfo.h"
+
+#include "modules/exceptions.h"
+
+#include // leave this for OSX
+
+#include
+
+
+Crapinfo::Crapinfo( const int& window_theme_id, const QString& version, const QString& exec_path, const QString& conf_path, const QString& logdoc_path, QWidget *parent ) :
+ QWidget(parent),
+ ui(new Ui::Crapinfo)
+{
+ ui->setupUi(this);
+ QString stylesheet = "";
+ this->getStyleSheet( stylesheet, window_theme_id );
+ this->setStyleSheet( stylesheet );
+
+ // fonts
+ const QString ff = QFontDatabase::applicationFontFamilies(
+ QFontDatabase::addApplicationFont(":/fonts/Metropolis")).at(0);
+ const QFont font( ff, 13 );
+ const QString ff_s = QFontDatabase::applicationFontFamilies(
+ QFontDatabase::addApplicationFont(":/fonts/Hack")).at(0);
+ const QFont font_script( ff_s, 13 );
+
+ // apply the fonts
+ this->ui->toolBox_Infos->setFont( font );
+ // version
+ this->ui->label_Verion_Number->setText( QString(version).replace(",",".") );
+ this->ui->label_Verion_Number->setFont( font_script );
+ // paths
+ this->ui->inLine_ExecPath->setText( exec_path );
+ this->ui->inLine_ExecPath->setCursorPosition( 0 );
+ this->ui->inLine_ConfPath->setText( conf_path );
+ this->ui->inLine_ConfPath->setCursorPosition( 0 );
+ this->ui->inLine_DataPath->setText( logdoc_path );
+ this->ui->inLine_DataPath->setCursorPosition( 0 );
+
+}
+
+Crapinfo::~Crapinfo()
+{
+ delete ui;
+}
+
+
+void Crapinfo::getStyleSheet( QString& stylesheet, const int& theme_id )
+{
+ std::unordered_map style;
+ switch ( theme_id ) {
+ case 0:
+ break;
+ case 1:
+ style = {
+ {"text",
+ "rgb( 248, 248, 248 )"},
+ {"text_unselected",
+ "rgb( 192, 192, 192 )"},
+ {"window_primary",
+ "rgb( 32, 32, 32 )"},
+ {"window_secondary",
+ "rgb( 16, 16, 16 )"},
+ {"border",
+ "rgb( 96, 96, 96 )"},
+ {"border_hover",
+ "rgb( 128, 128, 128 )"},
+ {"border_unselected",
+ "rgb( 64, 64, 64 )"},
+ {"toolbox_tab_base",
+ "rgb( 24, 24, 24 )"},
+ {"toolbox_tab_base_selected",
+ "rgb( 48, 48, 48 )"},
+ {"scrollbar_base",
+ "rgb( 96, 96, 96 )"},
+ {"scrollbar_handler",
+ "rgb( 192, 192, 192 )"},
+ {"linedit_text",
+ "rgb( 16, 16, 16 )"},
+ {"linedit_base",
+ "rgb( 216, 216, 216 )"},
+ {"linedit_text_selection",
+ "rgb( 16, 16, 16 )"},
+ {"linedit_base_selection",
+ "rgb( 192, 192, 192 )"},
+ {"paths_frame_base",
+ "rgb( 48, 48, 48 )"},
+ {"lines",
+ "rgb( 192, 192, 192 )"}
+ };
+ break;
+ case 2:
+ style = {
+ {"text",
+ "rgb( 45, 0, 30 )"},
+ {"text_unselected",
+ "rgb( 115, 30, 70 )"},
+ {"window_primary",
+ "rgb( 255, 140, 141 )"},
+ {"window_secondary",
+ "rgb( 255, 204, 143 )"},
+ {"border",
+ "rgb( 195, 80, 81 )"},
+ {"border_hover",
+ "rgb( 215, 100, 101 )"},
+ {"border_unselected",
+ "rgb( 175, 60, 61 )"},
+ {"toolbox_tab_base",
+ "rgb( 245, 172, 142 )"},
+ {"toolbox_tab_base_selected",
+ "rgb( 245, 130, 131 )"},
+ {"scrollbar_base",
+ "rgb( 145, 100, 130 )"},
+ {"scrollbar_handler",
+ "rgb( 45, 0, 30 )"},
+ {"linedit_text",
+ "rgb( 45, 0, 30 )"},
+ {"linedit_base",
+ "rgb( 113, 154, 225 )"},
+ {"linedit_text_selection",
+ "rgb( 45, 0, 30 )"},
+ {"linedit_base_selection",
+ "rgb( 145, 100, 130 )"},
+ {"paths_frame_base",
+ "rgb( 245, 172, 142 )"},
+ {"lines",
+ "rgb( 195, 80, 81 )"}
+ };
+ break;
+ case 3:
+ style = {
+ {"text",
+ "rgb( 220, 211, 187 )"},
+ {"text_unselected",
+ "rgb( 163, 145, 99 )"},
+ {"window_primary",
+ "rgb( 34, 58, 10 )"},
+ {"window_secondary",
+ "rgb( 14, 28, 0 )"},
+ {"border",
+ "rgb( 163, 145, 99 )"},
+ {"border_hover",
+ "rgb( 193, 175, 129 )"},
+ {"border_unselected",
+ "rgb( 133, 115, 69 )"},
+ {"toolbox_tab_base",
+ "rgb( 24, 48, 0 )"},
+ {"toolbox_tab_base_selected",
+ "rgb( 54, 78, 30 )"},
+ {"scrollbar_base",
+ "rgb( 94, 118, 70 )"},
+ {"scrollbar_handler",
+ "rgb( 174, 198, 150 )"},
+ {"linedit_text",
+ "rgb( 4, 18, 0 )"},
+ {"linedit_base",
+ "rgb( 193, 175, 129 )"},
+ {"linedit_text_selection",
+ "rgb( 4, 18, 0 )"},
+ {"linedit_base_selection",
+ "rgb( 173, 155, 109 )"},
+ {"paths_frame_base",
+ "rgb( 54, 78, 30 )"},
+ {"lines",
+ "rgb( 124, 148, 100 )"}
+ };
+ break;
+ case 4:
+ style = {
+ {"text",
+ "rgb( 30, 21, 0 )"},
+ {"text_unselected",
+ "rgb( 60, 51, 27 )"},
+ {"window_primary",
+ "rgb( 170, 161, 137 )"},
+ {"window_secondary",
+ "rgb( 230, 221, 197 )"},
+ {"border",
+ "rgb( 80, 71, 47 )"},
+ {"border_hover",
+ "rgb( 60, 51, 27 )"},
+ {"border_unselected",
+ "rgb( 100, 91, 67 )"},
+ {"toolbox_tab_base",
+ "rgb( 190, 181, 157 )"},
+ {"toolbox_tab_base_selected",
+ "rgb( 150, 141, 117 )"},
+ {"scrollbar_base",
+ "rgb( 100, 91, 67 )"},
+ {"scrollbar_handler",
+ "rgb( 210, 201, 177 )"},
+ {"linedit_text",
+ "rgb( 30, 21, 0 )"},
+ {"linedit_base",
+ "rgb( 210, 201, 177 )"},
+ {"linedit_text_selection",
+ "rgb( 30, 21, 0 )"},
+ {"linedit_base_selection",
+ "rgb( 170, 161, 137 )"},
+ {"paths_frame_base",
+ "rgb( 190, 181, 157 )"},
+ {"lines",
+ "rgb( 60, 51, 27 )"}
+ };
+ break;
+ default:
+ throw GenericException( "Unexpected WindowTheme ID: "+std::to_string(theme_id), true );
+ break;
+ }
+ if ( theme_id != 0 ) {
+ stylesheet =
+ "QWidget#Crapinfo {"
+ " color: "+style.at("text")+";"
+ " background-color: "+style.at("window_secondary")+";"
+ "}"
+ "QFrame {"
+ " background-color: transparent;"
+ "}"
+ "QLabel {"
+ " color: "+style.at("text")+";"
+ "}"
+ "QPushButton:pressed {"
+ " background-color: "+style.at("window_primary")+";"
+ "}"
+ "QWidget#page_LogDoc,"
+ "QWidget#page_Paths,"
+ "QWidget#scrollAreaContents_LogDoc {"
+ " border: 1px solid "+style.at("border")+";"
+ " border-top: 0px;"
+ " border-radius: 4px;"
+ " border-top-left-radius: 0px;"
+ " border-top-right-radius: 0px;"
+ " background-color: "+style.at("window_primary")+";"
+ "}"
+ "QToolBox::tab {"
+ " border: 1px solid "+style.at("border_unselected")+";"
+ " border-radius: 4px;"
+ " color: "+style.at("text_unselected")+";"
+ " background-color: "+style.at("toolbox_tab_base")+";"
+ "}"
+ "QToolBox::tab:!selected:hover {"
+ " border-color: "+style.at("border_hover")+";"
+ " color: "+style.at("text")+";"
+ " background-color: "+style.at("toolbox_tab_base")+";"
+ "}"
+ "QToolBox::tab:selected {"
+ " border-color: "+style.at("border")+";"
+ " border-top: 4px solid "+style.at("border")+";"
+ " border-bottom: 0px;"
+ " border-bottom-left-radius: 0px;"
+ " border-bottom-right-radius: 0px;"
+ " color: "+style.at("text")+";"
+ " background-color: "+style.at("toolbox_tab_base_selected")+";"
+ "}"
+ "QScrollBar:horizontal {"
+ " height: 12px;"
+ " background-color: "+style.at("scrollbar_base")+";"
+ "}"
+ "QScrollBar::handle:horizontal {"
+ " min-width: 16px;"
+ " margin: 5px 12px 5px 12px;"
+ " background-color: "+style.at("scrollbar_handler")+";"
+ "}"
+ "QScrollBar::handle:horizontal:hover {"
+ " margin: 4px 12px 4px 12px;"
+ "}"
+ "QLineEdit {"
+ " border-radius: 4px;"
+ " color: "+style.at("linedit_text")+";"
+ " selection-color: "+style.at("linedit_text_selection")+";"
+ " background-color: "+style.at("linedit_base")+";"
+ " selection-background-color: "+style.at("linedit_base_selection")+";"
+ "}"
+ "QWidget#scrollAreaContents_Paths_Executable,"
+ "QWidget#scrollAreaContents_Paths_ConfigFile,"
+ "QWidget#scrollAreaContents_Paths_AppData {"
+ " border-radius: 4px;"
+ " background-color: "+style.at("paths_frame_base")+";"
+ "}"
+ "QFrame#gline_Version,"
+ "QFrame#gline_StatsCount {"
+ " border: 1px solid "+style.at("window_secondary")+";"
+ " margin: 2px 0px 3px 0px;"
+ " background-color: "+style.at("lines")+";"
+ "}"
+ "";
+ }
+}
diff --git a/logdoctor/modules/crapinfo/crapinfo.h b/logdoctor/modules/crapinfo/crapinfo.h
new file mode 100644
index 00000000..d3ff4956
--- /dev/null
+++ b/logdoctor/modules/crapinfo/crapinfo.h
@@ -0,0 +1,39 @@
+#ifndef CRAPINFO_H
+#define CRAPINFO_H
+
+#include
+
+
+namespace Ui {
+ class Crapinfo;
+}
+
+//! Crapinfo
+/*!
+ Displays informations
+*/
+class Crapinfo : public QWidget
+{
+ Q_OBJECT
+
+public:
+
+ //! Class constructor
+ /*!
+ \param window_theme_id The ID of the theme in use on the main window
+ \param version The version of LogDoctor actually running
+ \param exec_path The path of the executable
+ \param conf_path The path of the configuration file
+ \param logdoc_path The path of the additional resources folder
+ \param parent The parent Widget
+ */
+ Crapinfo( const int& window_theme_id, const QString& version, const QString& exec_path, const QString& conf_path, const QString& logdoc_path, QWidget* parent=nullptr );
+ ~Crapinfo();
+
+private:
+ Ui::Crapinfo *ui;
+
+ void getStyleSheet( QString& stylesheet, const int& theme_id );
+};
+
+#endif // CRAPINFO_H
diff --git a/logdoctor/modules/crapinfo/crapinfo.ui b/logdoctor/modules/crapinfo/crapinfo.ui
new file mode 100644
index 00000000..41745024
--- /dev/null
+++ b/logdoctor/modules/crapinfo/crapinfo.ui
@@ -0,0 +1,753 @@
+
+
+ Crapinfo
+
+
+
+ 0
+ 0
+ 853
+ 597
+
+
+
+ LogDoctor - Infos
+
+
+
+ :/logo/logo/logdoctor.svg :/logo/logo/logdoctor.svg
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ 4
+
+
+
+
+ 0
+ 0
+ 835
+ 521
+
+
+
+ LogDoctor
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 835
+ 521
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 788
+ 40
+
+
+
+
+ -
+
+
+
+ 16777215
+ 303
+
+
+
+ -
+
+
+ true
+
+
+
+ 280
+ 280
+
+
+
+
+ 280
+ 280
+
+
+
+
+
+
+
+ :/logo/logo/logdoctor.svg :/logo/logo/logdoctor.svg
+
+
+
+ 256
+ 256
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 400
+ 0
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 16
+
+
+
+ Version
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 13
+
+
+
+ Currently installed version of the software
+
+
+
+
+
+ Qt::AlignHCenter|Qt::AlignTop
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 0
+ 32
+
+
+
+
+ 16777215
+ 32
+
+
+
+
+ 15
+
+
+
+ Repository links
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 0
+ 24
+
+
+
+
+ 16777215
+ 24
+
+
+
+
+ true
+
+
+
+ false
+
+
+ GitHub
+
+
+ https://github.com/elB4RTO/LogDoctor
+
+
+ false
+
+
+ Qt::AlignCenter
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 24
+
+
+
+
+ 16777215
+ 24
+
+
+
+
+ true
+
+
+
+ false
+
+
+ Disroot
+
+
+ https://git.disroot.org/elB4RTO/LogDoctor
+
+
+ false
+
+
+ Qt::AlignCenter
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 24
+
+
+
+
+ 16777215
+ 24
+
+
+
+
+ true
+
+
+
+ false
+
+
+ GitLab
+
+
+ https://gitlab.com/elB4RTO/LogDoctor
+
+
+ false
+
+
+ Qt::AlignCenter
+
+
+ true
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 32
+ 408
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 788
+ 40
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 835
+ 521
+
+
+
+ Paths
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ -
+
+
+
+ 0
+ 28
+
+
+
+
+ 16777215
+ 28
+
+
+
+
+ 13
+
+
+
+ The path of the executable file
+
+
+ Executable
+
+
+
+ -
+
+
+
+ 0
+ 60
+
+
+
+
+ 16777215
+ 60
+
+
+
+ true
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+
+ 0
+ 0
+ 815
+ 58
+
+
+
+ -
+
+
+
+ 0
+ 24
+
+
+
+
+ 16777215
+ 24
+
+
+
+ false
+
+
+
+
+
+ false
+
+
+ true
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 28
+
+
+
+
+ 16777215
+ 28
+
+
+
+
+ 13
+
+
+
+ The path where the configuration file gets saved and searched in
+
+
+ Configuration file
+
+
+
+ -
+
+
+
+ 0
+ 60
+
+
+
+
+ 16777215
+ 60
+
+
+
+ true
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+
+ 0
+ 0
+ 815
+ 58
+
+
+
+ -
+
+
+
+ 0
+ 24
+
+
+
+
+ 16777215
+ 24
+
+
+
+ false
+
+
+
+
+
+ false
+
+
+ true
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 32
+
+
+
+
+ -
+
+
+
+ 0
+ 28
+
+
+
+
+ 16777215
+ 28
+
+
+
+
+ 13
+
+
+
+ The path where the application searches for extra resources
+
+
+ Application data
+
+
+
+ -
+
+
+
+ 0
+ 60
+
+
+
+
+ 16777215
+ 60
+
+
+
+ true
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
+
+
+
+
+ 0
+ 0
+ 815
+ 58
+
+
+
+ -
+
+
+
+ 0
+ 24
+
+
+
+
+ 16777215
+ 24
+
+
+
+ false
+
+
+
+
+
+ false
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 88
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/logdoctor/modules/craplog/craplog.cpp b/logdoctor/modules/craplog/craplog.cpp
new file mode 100644
index 00000000..a476d982
--- /dev/null
+++ b/logdoctor/modules/craplog/craplog.cpp
@@ -0,0 +1,1167 @@
+
+#include "craplog.h"
+
+#include "utilities/checks.h"
+#include "utilities/gzip.h"
+#include "utilities/io.h"
+#include "utilities/strings.h"
+
+#include "modules/dialogs.h"
+#include "modules/exceptions.h"
+#include "modules/shared.h"
+
+#include "modules/craplog/modules/donuts.h"
+#include "modules/craplog/modules/store.h"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+
+Craplog::Craplog()
+{
+ ////////////////////////
+ //// INITIALIZATION ////
+ ////////////////////////
+ // blacklists / whitelists
+ for ( int i=this->APACHE_ID; i<=this->IIS_ID; i++ ) {
+ this->warnlists.emplace( i, std::unordered_map() );
+ this->blacklists.emplace( i, std::unordered_map() );
+ // default data
+ this->warnlists.at( i ).emplace( 11, BWlist{ .used=false, .list={"DELETE","HEAD","OPTIONS","PUT","PATCH"} } );
+ this->warnlists.at( i ).emplace( 12, BWlist{ .used=true, .list={"/robots.txt","/../","/./","/.env","/.htaccess","/phpmyadmin","/wp-admin","/wp-content","/wp-config.php","/config.py","/views.py","/routes.py","/setup.cgi","/cgi-bin"} } );
+ this->warnlists.at( i ).emplace( 20, BWlist{ .used=false, .list={} } );
+ this->warnlists.at( i ).emplace( 21, BWlist{ .used=false, .list={} } );
+ this->blacklists.at( i ).emplace( 20, BWlist{ .used=true, .list={} } );
+ }
+
+ // default format strings
+ this->logs_format_strings.emplace(
+ this->APACHE_ID, "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" );
+ this->logs_format_strings.emplace(
+ this->NGINX_ID, "$remote_addr - $remote_user [$time_local] \"$request\" $status $bytes_sent \"$http_referer\" \"$http_user_agent\"" );
+ this->logs_format_strings.emplace(
+ this->IIS_ID, "date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken" );
+
+ // initialize formats
+ this->logs_formats.emplace(
+ this->APACHE_ID, this->formatOps.processApacheFormatString( this->logs_format_strings.at(this->APACHE_ID) ) );
+ this->logs_formats.emplace(
+ this->NGINX_ID, this->formatOps.processNginxFormatString( this->logs_format_strings.at(this->NGINX_ID) ) );
+ this->logs_formats.emplace(
+ this->IIS_ID, this->formatOps.processIisFormatString( this->logs_format_strings.at(this->IIS_ID), 0 ) );
+
+ this->current_LF = this->logs_formats.at( this->APACHE_ID );
+
+ // apache2 access/error logs location
+ this->logs_paths.emplace( this->APACHE_ID, "/var/log/apache2" );
+ // nginx access/error logs location
+ this->logs_paths.emplace( this->NGINX_ID, "/var/log/nginx" );
+ // iis access/error logs location
+ this->logs_paths.emplace( this->IIS_ID, "C:/inetpub/logs/LogFiles" );
+
+ // apache2 access/error log files' names
+ this->logs_base_names.emplace( this->APACHE_ID, LogName{ .starts = "access.log.",
+ .contains = "",
+ .ends = "" } );
+ // nginx access/error log files' names
+ this->logs_base_names.emplace( this->NGINX_ID, LogName{ .starts = "access.log.",
+ .contains = "",
+ .ends = "" });
+ // iis access/error log files' names
+ this->logs_base_names.emplace( this->IIS_ID, LogName{ .starts = "",
+ .contains = "_ex",
+ .ends = ".log" });
+
+}
+
+
+//////////////////
+//// SETTINGS ////
+const int& Craplog::getDialogsLevel()
+{
+ return this->dialogs_level;
+}
+void Craplog::setDialogsLevel( const int& new_level )
+{
+ this->dialogs_level = new_level;
+ this->hashOps.setDialogLevel( new_level );
+}
+
+const std::string& Craplog::getStatsDatabasePath()
+{
+ return this->db_stats_path;
+}
+const std::string& Craplog::getHashesDatabasePath()
+{
+ return this->db_hashes_path;
+}
+
+void Craplog::setStatsDatabasePath( const std::string& path )
+{
+ this->db_stats_path = path + "/collection.db";
+}
+void Craplog::setHashesDatabasePath( const std::string& path )
+{
+ this->db_hashes_path = path + "/hashes.db";
+}
+
+const long& Craplog::getWarningSize()
+{
+ return this->warning_size;
+}
+
+void Craplog::setWarningSize(const long& new_size )
+{
+ this->warning_size = new_size;
+}
+
+
+////////////////////
+//// WARN/BLACK ////
+const bool& Craplog::isBlacklistUsed( const int& web_server_id, const int& log_field_id )
+{
+ return this->blacklists.at( web_server_id ).at( log_field_id ).used;
+}
+const bool& Craplog::isWarnlistUsed( const int& web_server_id, const int& log_field_id )
+{
+ return this->warnlists.at( web_server_id ).at( log_field_id ).used;
+}
+
+void Craplog::setBlacklistUsed( const int& web_server_id, const int& log_field_id, const bool& used )
+{
+ this->blacklists.at( web_server_id ).at( log_field_id ).used = used;
+}
+void Craplog::setWarnlistUsed( const int& web_server_id, const int& log_field_id, const bool& used )
+{
+ this->warnlists.at( web_server_id ).at( log_field_id ).used = used;
+}
+
+const std::vector& Craplog::getBlacklist( const int& web_server_id, const int& log_field_id )
+{
+ return this->blacklists.at( web_server_id ).at( log_field_id ).list;
+}
+const std::vector& Craplog::getWarnlist( const int& web_server_id, const int& log_field_id )
+{
+ return this->warnlists.at( web_server_id ).at( log_field_id ).list;
+}
+
+void Craplog::setBlacklist( const int& web_server_id, const int& log_field_id, const std::vector& new_list )
+{
+ this->blacklists.at( web_server_id ).at( log_field_id ).list.clear();
+ for ( const std::string& item : new_list ) {
+ this->blacklistAdd( web_server_id, log_field_id, item );
+ }
+}
+void Craplog::setWarnlist( const int& web_server_id, const int& log_field_id, const std::vector& new_list )
+{
+ this->warnlists.at( web_server_id ).at( log_field_id ).list.clear();
+ for ( const std::string& item : new_list ) {
+ this->warnlistAdd( web_server_id, log_field_id, item );
+ }
+}
+
+void Craplog::blacklistAdd( const int& web_server_id, const int& log_field_id, const std::string& new_item )
+{
+ this->blacklists.at( web_server_id ).at( log_field_id ).list.push_back(
+ this->sanitizeBWitem( log_field_id, new_item ) );
+}
+void Craplog::warnlistAdd( const int& web_server_id, const int& log_field_id, const std::string& new_item )
+{
+ this->warnlists.at( web_server_id ).at( log_field_id ).list.push_back(
+ this->sanitizeBWitem( log_field_id, new_item ) );
+}
+
+void Craplog::blacklistRemove( const int& web_server_id, const int& log_field_id, const std::string& item )
+{
+ auto& list = this->blacklists.at( web_server_id ).at( log_field_id ).list;
+ // move the item to the end, then pop it
+ for ( int i=0; iwarnlists.at( web_server_id ).at( log_field_id ).list;
+ // move the item to the end, then pop it
+ for ( int i=0; iblacklists.at( web_server_id ).at( log_field_id ).list;
+ for ( i=1; iwarnlists.at( web_server_id ).at( log_field_id ).list;
+ for ( i=1; iblacklists.at( web_server_id ).at( log_field_id ).list;
+ for ( i=0; iwarnlists.at( web_server_id ).at( log_field_id ).list;
+ for ( i=0; ilogs_format_strings.at( web_server_id );
+}
+
+// get the logs format
+const FormatOps::LogsFormat& Craplog::getLogsFormat(const int& web_server_id )
+{
+ return this->logs_formats.at( web_server_id );
+}
+
+// set the logs format
+const bool Craplog::setApacheLogFormat( const std::string& format_string )
+{
+ // apache
+ bool success = true;
+ try {
+ this->logs_formats.at( this->APACHE_ID ) =
+ this->formatOps.processApacheFormatString( format_string );
+ this->logs_format_strings.at( this->APACHE_ID ) = format_string;
+ } catch ( LogFormatException& e ) {
+ success = false;
+ DialogSec::errInvalidLogFormatString( e.what() );
+ } catch (...) {
+ success = false;
+ DialogSec::errGeneric( DialogSec::tr("An error occured while parsing the format string"), true );
+ }
+ return success;
+}
+const bool Craplog::setNginxLogFormat( const std::string& format_string )
+{
+ // nginx
+ bool success = true;
+ try {
+ this->logs_formats.at( this->NGINX_ID ) =
+ this->formatOps.processNginxFormatString( format_string );
+ this->logs_format_strings.at( this->NGINX_ID ) = format_string;
+ } catch ( LogFormatException& e ) {
+ success = false;
+ DialogSec::errInvalidLogFormatString( e.what() );
+ } catch (...) {
+ success = false;
+ DialogSec::errGeneric( DialogSec::tr("An error occured while parsing the format string"), true );
+ }
+ return success;
+}
+const bool Craplog::setIisLogFormat( const std::string& format_string, const int& log_module )
+{
+ // iis
+ bool success = true;
+ try {
+ this->logs_formats.at( this->IIS_ID ) =
+ this->formatOps.processIisFormatString( format_string, log_module );
+ this->logs_format_strings.at( this->IIS_ID ) = format_string;
+ this->changeIisLogsBaseNames( log_module );
+ } catch ( LogFormatException& e ) {
+ success = false;
+ DialogSec::errInvalidLogFormatString( e.what() );
+ } catch (...) {
+ success = false;
+ DialogSec::errGeneric( DialogSec::tr("An error occured while parsing the format string"), true );
+ }
+ return success;
+}
+
+const QString Craplog::getLogsFormatSample( const int& web_server_id )
+{
+ QString sample;
+ if ( web_server_id == this->APACHE_ID ) {
+ sample = this->formatOps.getApacheLogSample( this->logs_formats.at( web_server_id ) );
+ } else if ( web_server_id == this->NGINX_ID ) {
+ sample = this->formatOps.getNginxLogSample( this->logs_formats.at( web_server_id ) );
+ } else if ( web_server_id == this->IIS_ID ) {
+ sample = this->formatOps.getIisLogSample( this->logs_formats.at( web_server_id ) );
+ } else {
+ // unexpected WebServer
+ throw WebServerException( "Unexpected WebServerID: " + std::to_string( web_server_id ) );
+ }
+ return sample;
+}
+
+
+// set the current Web Server
+void Craplog::setCurrentWSID( const int& web_server_id )
+{
+ this->current_WS = web_server_id;
+ this->setCurrentLogFormat();
+}
+
+const int& Craplog::getCurrentWSID()
+{
+ return this->current_WS;
+}
+
+// set the current access logs format
+void Craplog::setCurrentLogFormat()
+{
+ this->current_LF = this->logs_formats.at( this->current_WS );
+}
+
+// get the current access logs format
+const FormatOps::LogsFormat& Craplog::getCurrentLogFormat()
+{
+ return this->current_LF;
+}
+
+
+///////////////////
+//// LOGS PATH ////
+const std::string& Craplog::getLogsPath( const int& web_server )
+{
+ return this->logs_paths.at( web_server );
+}
+void Craplog::setLogsPath( const int& web_server, const std::string& new_path )
+{
+ this->logs_paths.at( web_server ) = new_path;
+}
+
+
+///////////////////
+//// LOGS LIST ////
+// return the size of the list
+const int Craplog::getLogsListSize() {
+ return this->logs_list.size();
+}
+
+// return the list. rescan if fresh is true
+const std::vector& Craplog::getLogsList( const bool& fresh )
+{
+ if ( fresh ) {
+ this->scanLogsDir();
+ }
+ return this->logs_list;
+}
+
+
+// return the path of the file matching the given name
+const Craplog::LogFile& Craplog::getLogFileItem( const QString& file_name )
+{
+ for ( const Craplog::LogFile& item : this->logs_list ) {
+ if ( item.name == file_name ) {
+ return item;
+ }
+ }
+ // should be unreachable
+ throw GenericException("File item not found");
+}
+
+
+// set a file as selected
+const bool Craplog::setLogFileSelected( const QString& file_name )
+{
+ bool result = false;
+ for ( Craplog::LogFile& item : this->logs_list ) {
+ if ( item.name == file_name ) {
+ item.selected = true;
+ result = true;
+ break;
+ }
+ }
+ return result;
+}
+
+
+// scan the logs path to update the log files list
+void Craplog::scanLogsDir()
+{
+ bool successful = true;
+ this->logs_list.clear();
+ std::string &logs_path = this->logs_paths.at( this->current_WS );
+ if ( ! IOutils::isDir( logs_path ) ) {
+ // this directory doesn't exists
+ if ( IOutils::exists( logs_path ) ) {
+ DialogSec::errDirNotExists( QString::fromStdString( logs_path ) );
+ }
+ successful = false;
+ }
+ if ( successful ) {
+ int size;
+ QString name;
+ std::string path;
+ // iterate over entries in the logs folder
+ for ( const auto& dir_entry : std::filesystem::directory_iterator{logs_path}) {
+ // get the attributes
+ path = dir_entry.path().string();
+ name = QString::fromStdString( dir_entry.path().filename().string() );
+ // check if it is actually a file
+ if ( IOutils::checkFile( path ) ) {
+ // it's a file, check the readability
+ if ( ! IOutils::checkFile( path, true ) ) {
+ // not readable, skip
+ if ( this->dialogs_level == 2 ) {
+ DialogSec::warnFileNotReadable( name );
+ }
+ continue;
+ }
+ // it's readable, get the size
+ size = dir_entry.file_size();
+ } else {
+ continue;
+ }
+
+ std::vector content;
+ try {
+ // read 32 random lines
+ IOutils::randomLines( path, content, 32 );
+
+ } catch ( GenericException& e ) {
+ // failed closing gzip file pointer
+ DialogSec::errGeneric( e.what() );
+ continue;
+ }
+
+ if ( content.size() == 0 ) {
+ if ( this->dialogs_level == 2 ) {
+ DialogSec::warnEmptyFile( name );
+ }
+ continue;
+ }
+
+ LogOps::LogType log_type = this->logOps.defineFileType(
+ content, this->logs_formats.at( this->current_WS ) );
+ content.clear();
+ if ( log_type == LogOps::LogType::Failed ) {
+ // failed to get the log type, do not append
+ DialogSec::errFailedDefiningLogType( name );
+ continue;
+ } else if ( log_type == LogOps::LogType::Discarded ) {
+ // skip
+ continue;
+ }
+
+ // match only valid files names
+ if ( ! this->isFileNameValid( name.toStdString() ) ) {
+ continue;
+ }
+
+ std::string hash;
+ try {
+ hash = this->hashOps.digestFile( path );
+ } catch ( GenericException& e ) {
+ // failed to digest
+ DialogSec::errGeneric( e.what() );
+ continue;
+ }
+
+ LogFile logfile = {
+ .selected = false,
+ .used_already = this->hashOps.hasBeenUsed( hash, this->current_WS ),
+ .size = size,
+ .name = name,
+ .hash = hash,
+ .path = path
+ };
+ // push in the list
+ this->logs_list.push_back( logfile );
+ }
+ }
+}
+
+
+void Craplog::changeIisLogsBaseNames( const int& module_id )
+{
+ switch ( module_id ) {
+ case 0: // W3C
+ this->logs_base_names.at( 13 ).contains = "_ex"; break;
+ case 1: // NCSA
+ this->logs_base_names.at( 13 ).contains = "_nc"; break;
+ case 2: // IIS
+ this->logs_base_names.at( 13 ).contains = "_in"; break;
+
+ default: // shouldn't be reachable
+ throw GenericException( "Unexpected LogFormatModule ID: "+std::to_string( module_id ), true ); // leave un-catched
+ }
+}
+const bool Craplog::isFileNameValid( const std::string& name )
+{
+ bool valid = true;
+ if ( this->logs_base_names.at( this->current_WS ).starts != "" ) {
+ if ( ! StringOps::startsWith( name, this->logs_base_names.at( this->current_WS ).starts ) ) {
+ return false;
+ }
+ }
+ if ( this->logs_base_names.at( this->current_WS ).contains != "" ) {
+ if ( ! StringOps::contains(
+ name.substr( this->logs_base_names.at( this->current_WS ).starts.size() ),
+ this->logs_base_names.at( this->current_WS ).contains ) ) {
+ return false;
+ }
+ }
+ if ( this->logs_base_names.at( this->current_WS ).ends != "" ) {
+ if ( ! StringOps::endsWith( name, this->logs_base_names.at( this->current_WS ).ends )
+ && ! StringOps::endsWith( name, ".gz" ) ) {
+ return false;
+ }
+ }
+
+ switch ( this->current_WS ) {
+ size_t start, stop;
+ case 11 | 12:
+ // further checks for apache / nginx
+ start = StringOps::findLast( name, ".log." )+5;
+ if ( start == std::string::npos ) {
+ valid = false;
+ break;
+ }
+ stop = name.size()-1;
+ if ( StringOps::endsWith( name, ".gz" ) ) {
+ stop -= 3;
+ }
+ // serach for incremental numbers
+ for ( int i=start; i<=stop; i++ ) {
+ if ( ! StringOps::isNumeric( name.at( i ) ) ) {
+ valid = false;
+ break;
+ }
+ }
+ break;
+
+ case 13:
+ // further checks for iis
+ start = name.find( this->logs_base_names.at( 13 ).contains ) + 3;
+ if ( start == std::string::npos ) {
+ valid = false;
+ break;
+ }
+ stop = name.size()-5; // removing the finel '.log' extension
+ if ( StringOps::endsWith( name, ".gz" ) ) {
+ stop -= 3;
+ }
+ // search for date
+ std::string date;
+ for ( int i=start; i<=stop; i++ ) {
+ if ( ! StringOps::isNumeric( name.at( i ) ) ) {
+ valid = false;
+ break;
+ }
+ date.push_back( name.at( i ) );
+ }
+ if ( valid ) {
+ // check if the file has today's date
+ time_t t;
+ struct tm *tmp;
+ char aux_date[7];
+ time( &t );
+ tmp = localtime( &t );
+ // using strftime to display time
+ strftime( aux_date, 7, "%y%m%d", tmp );
+ valid = false;
+ for ( int i=0; i<6; i++ ) {
+ if ( date.at(i) != aux_date[i] ) {
+ // different date, valid
+ valid = true;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return valid;
+}
+
+
+///////////////
+//// WORKK ////
+void Craplog::startWorking()
+{
+ this->working = true;
+ this->parsing = true;
+ this->proceed = true;
+
+ this->perf_size = 0;
+ this->total_size = 0;
+ this->parsed_size = 0;
+ this->warnlisted_size = 0;
+ this->blacklisted_size = 0;
+ this->total_lines = 0;
+ this->parsed_lines = 0;
+
+ this->data_collection.clear();
+ this->logs_lines.clear();
+ this->used_files_hashes.clear();
+}
+void Craplog::stopWorking()
+{
+ this->working = false;
+ this->parsing = false;
+}
+const bool& Craplog::isWorking()
+{
+ return this->working;
+}
+const bool& Craplog::isParsing()
+{
+ return this->parsing;
+}
+const bool& Craplog::editedDatabase()
+{
+ return this->db_edited;
+}
+
+// performances
+const unsigned int &Craplog::getPerfSize()
+{
+ return this->perf_size;
+}
+/*void Craplog::sumPerfSize( const unsigned& size )
+{
+ this->perf_size += size;
+ this->parsed_size += size;
+}*/
+const unsigned int &Craplog::getTotalSize()
+{
+ return this->total_size;
+}
+/*const unsigned int &Craplog::getParsedSize()
+{
+ return this->parsed_size;
+}*/
+const unsigned int &Craplog::getParsedLines()
+{
+ return this->parsed_lines;
+}
+
+void Craplog::sumWarningsSize( const unsigned int& size )
+{
+ this->warnlisted_size += size;
+}
+void Craplog::sumBlacklistededSize( const unsigned int& size )
+{
+ this->blacklisted_size += size;
+}
+
+void Craplog::collectPerfData()
+{
+ this->parsed_size = this->logOps.getParsedSize();
+ this->parsed_lines = this->logOps.getParsedLines();
+ this->perf_size = this->parsed_size;
+}
+
+void Craplog::clearDataCollection()
+{
+ this->data_collection.clear();
+}
+
+
+void Craplog::run()
+{
+ this->startWorking();
+ try {
+ if ( this->proceed ) {
+ // collect log lines
+ this->joinLogLines();
+ }
+ if ( this->proceed ) {
+ // parse the log lines to fill the collection
+ this->parseLogLines();
+ // finished parsing logs
+ this->parsing = false;
+ this->total_size = this->logOps.getTotalSize();
+ this->parsed_size = this->logOps.getParsedSize();
+ this->parsed_lines = this->logOps.getParsedLines();
+ this->perf_size = this->parsed_size;
+ }
+ // clear log lines data
+ this->logs_lines.clear();
+
+ if ( this->proceed ) {
+ // store the new data
+ this->storeLogLines();
+ }
+
+ if ( this->proceed ) {
+ // succesfully updated the database
+ if ( this->parsed_size > 0 ) {
+ this->db_edited = true;
+ }
+ // insert the hashes of the used files
+ this->hashOps.insertUsedHashes( this->db_hashes_path, this->used_files_hashes, this->current_WS );
+ }
+ this->used_files_hashes.clear();
+
+ } catch ( GenericException& e ) {
+ DialogSec::errGeneric( e.what() );
+ this->proceed = false;
+
+ } catch ( LogParserException& e ) {
+ DialogSec::errFailedParsingLogs( e.what() );
+ this->proceed = false;
+ }
+
+ this->stopWorking();
+}
+
+
+
+const bool Craplog::checkStuff()
+{
+ this->proceed = true;
+ this->log_files_to_use.clear();
+ for ( const LogFile& file : this->logs_list ) {
+
+ if ( ! this->proceed ) { break; }
+
+ if ( ! file.selected ) {
+ // not selected, skip
+ continue;
+ }
+
+ // check if the file has been used already
+ if ( file.used_already ) {
+ // already used
+ QString msg = file.name;
+ if ( this->dialogs_level == 2 ) {
+ msg += "\n" + QString::fromStdString( file.hash );
+ }
+ const int choice = DialogSec::choiceFileAlreadyUsed( msg );
+ if ( choice == 0 ) {
+ // choosed to abort all
+ this->proceed = false;
+ break;
+ } else if ( choice == 1 ) {
+ // choosed to discard the file and continue
+ continue;
+ } else if ( choice == 2 ) {
+ // choosed to ignore and use the file anyway
+ ;
+ } else {
+ // shouldn't be here
+ throw GenericException( "Unexpeced value returned: "+std::to_string(choice) );
+ }
+ }
+
+ // check if the file respects the warning size
+ if ( this->warning_size >= 0 ) {
+ if ( file.size > this->warning_size ) {
+ // exceeds the warning size
+ QString size_str, msg = file.name;
+ if ( this->dialogs_level >= 1 ) {
+ std::string size_sfx=" B";
+ float size = (float)file.size;
+ if (size > 1024) {
+ size /= 1024; size_sfx = " KiB";
+ if (size > 1024) {
+ size /= 1024; size_sfx = " MiB";
+ }
+ }
+ size_str = std::to_string(size).substr(0,std::to_string(size).size()-3).c_str();
+ msg += QString("\n\n%1:\n%2%3").arg( DialogSec::tr("Size of the file"), size_str, size_sfx.c_str() );
+ if ( this->dialogs_level == 2 ) {
+ size = (float)this->warning_size;
+ if (size > 1024) {
+ size /= 1024; size_sfx = " KiB";
+ if (size > 1024) {
+ size /= 1024; size_sfx = " MiB";
+ }
+ }
+ size_str = std::to_string(size).substr(0,std::to_string(size).size()-3).c_str();
+ msg += QString("\n\n%1:\n%2%3").arg( DialogSec::tr("Warning size parameter"), size_str, size_sfx.c_str() );
+ }
+ }
+ const int choice = DialogSec::choiceFileSizeWarning( msg );
+ if ( choice == 0 ) {
+ // choosed to abort all
+ this->proceed = false;
+ break;
+ } else if ( choice == 1 ) {
+ // choosed to discard the file and continue
+ continue;
+ } else if ( choice == 2 ) {
+ // choosed to ignore and use the file anyway
+ ;
+ } else {
+ // shouldn't be here
+ throw GenericException( "Unexpeced value returned: "+std::to_string(choice) );
+ }
+ }
+ }
+
+ // check if the statistics' database is fune
+ if ( ! CheckSec::checkCollectionDatabase( this->db_stats_path ) ) {
+ // checks failed, abort
+ this->proceed = false;
+ break;
+ }
+ if ( ! CheckSec::checkHashesDatabase( this->db_hashes_path ) ) {
+ // checks failed, abort
+ this->proceed = false;
+ break;
+ }
+
+ this->log_files_to_use.push_back( file );
+ }
+
+ return this->proceed;
+}
+
+
+void Craplog::joinLogLines()
+{
+ std::string aux;
+ std::vector content;
+ for ( const LogFile& file : this->log_files_to_use ) {
+
+ if ( ! this->proceed ) { break; }
+
+ // collect lines
+ try {
+ // try reading
+ content.clear();
+ aux = "";
+ try {
+ // try as gzip compressed archive first
+ GZutils::readFile( file.path, aux );
+
+ } catch ( const GenericException& ) {
+ // failed closing file pointer
+ throw;
+
+ } catch (...) {
+ // fallback on reading as normal file
+ if ( aux.size() > 0 ) {
+ aux = "";
+ }
+ IOutils::readFile( file.path, aux );
+ }
+ StringOps::splitrip( content, aux );
+ if ( this->current_WS == this->IIS_ID ) {
+ this->logOps.cleanLines( content );
+ }
+
+ // re-catched in run()
+ } catch ( const GenericException& ) {
+ // failed closing gzip file pointer
+ throw GenericException( QString("%1:\n%2").arg(
+ DialogSec::tr("An error accured while reading the gzipped file"),
+ QString::fromStdString( file.path )
+ ).toStdString() );
+
+ } catch ( const std::ios_base::failure& ) {
+ // failed reading as text
+ throw GenericException( QString("%1:\n%2").arg(
+ DialogSec::tr("An error accured while reading the file"),
+ QString::fromStdString( file.path )
+ ).toStdString() );
+
+ } catch (...) {
+ // failed somehow
+ throw GenericException( QString("%1:\n%2").arg(
+ DialogSec::tr("Something failed while handling the file"),
+ QString::fromStdString( file.path )
+ ).toStdString() );
+ }
+
+ // append to the relative list
+ this->logs_lines.insert( this->logs_lines.end(), content.begin(), content.end() );
+ this->used_files_hashes.push_back( file.hash );
+ this->total_lines += content.size();
+ }
+ aux.clear();
+ content.clear();
+ this->log_files_to_use.clear();
+}
+
+
+void Craplog::parseLogLines()
+{
+ if ( this-> proceed && this->logs_lines.size() > 0 ) {
+ this->logOps.parseLines(
+ this->data_collection,
+ this->logs_lines,
+ this->logs_formats.at( this->current_WS ) );
+ }
+}
+
+
+
+void Craplog::storeLogLines()
+{
+ QString db_path = QString::fromStdString( this->db_stats_path );
+ QString db_name = QString::fromStdString( this->db_stats_path.substr( this->db_stats_path.find_last_of( '/' ) + 1 ) );
+
+ QSqlDatabase db;
+ if ( QSqlDatabase::contains("qt_sql_default_connection") ) {
+ db = QSqlDatabase::database("qt_sql_default_connection");
+ } else {
+ db = QSqlDatabase::addDatabase("QSQLITE");
+ }
+ db.setDatabaseName( db_path );
+
+ if ( ! db.open() ) {
+ // error opening database
+ this->proceed = false;
+ QString err_msg = "";
+ if ( this->dialogs_level == 2 ) {
+ err_msg = db.lastError().text();
+ }
+ DialogSec::errDatabaseFailedOpening( db_name, err_msg );
+
+ } else {
+
+ bool successful;
+ try {
+ // ACID transaction
+ if ( ! db.transaction() ) {
+ // error opening database
+ this->proceed = false;
+ QString stmt_msg="", err_msg = "";
+ if ( this->dialogs_level > 0 ) {
+ stmt_msg = "db.transaction()";
+ if ( this->dialogs_level == 2 ) {
+ err_msg = db.lastError().text();
+ }
+ }
+ DialogSec::errDatabaseFailedExecuting( db_name, stmt_msg, err_msg );
+ }
+
+ if ( this->proceed && this->data_collection.size() > 0 ) {
+ successful = StoreOps::storeData( db, *this, this->data_collection );
+ this->proceed = successful;
+ }
+
+ if ( this->proceed ) {
+ // commit the transaction
+ if ( ! db.commit() ) {
+ // error opening database
+ this->proceed = false;
+ QString stmt_msg="", err_msg = "";
+ if ( this->dialogs_level > 0 ) {
+ stmt_msg = "db.commit()";
+ if ( this->dialogs_level == 2 ) {
+ err_msg= db.lastError().text();
+ }
+ }
+ DialogSec::errDatabaseFailedExecuting( db_name, stmt_msg, err_msg );
+ }
+ }
+ if ( ! proceed ) {
+ // rollback
+ throw (std::exception());
+ }
+
+ } catch (...) {
+ // wrongthing w3nt some.,.
+ this->proceed = false;
+ bool err_shown = false;
+ // rollback the transaction
+ if ( ! db.rollback() ) {
+ // error rolling back commits
+ QString stmt_msg="", err_msg = "";
+ if ( this->dialogs_level > 0 ) {
+ stmt_msg = "db.rollback()";
+ if ( this->dialogs_level == 2 ) {
+ err_msg = db.lastError().text();
+ }
+ }
+ DialogSec::errDatabaseFailedExecuting( db_name, stmt_msg, err_msg );
+ err_shown = true;
+ }
+ if ( ! err_shown ) {
+ // show a message
+ DialogSec::errGeneric( QString("%1\n\n%2").arg(
+ DialogSec::tr("An error occured while working on the database"),
+ DialogSec::tr("Aborting") ) );
+ }
+ }
+
+ db.close();
+ }
+
+}
+
+
+const QString Craplog::printableSize( const unsigned int& bytes )
+{
+ std::string size_str, size_sfx=" B";
+ float size = (float)bytes;
+ if (size > 1024) {
+ size /= 1024;
+ size_sfx = " KiB";
+ if (size > 1024) {
+ size /= 1024;
+ size_sfx = " MiB";
+ }
+ }
+ // cut decimals depending on how big the floor is
+ size_str = std::to_string( size );
+ size_t cut_index = size_str.find('.');
+ if ( cut_index == std::string::npos ) {
+ cut_index = size_str.find(',');
+ if ( cut_index == std::string::npos ) {
+ cut_index = 0;
+ }
+ }
+ if ( cut_index != 0 ) {
+ cut_index ++;
+ }
+ short n_decimals = 3;
+ if ( size >= 100 ) {
+ n_decimals = 2;
+ if ( size >= 1000 ) {
+ n_decimals = 1;
+ if ( size >= 10000 ) {
+ n_decimals = 0;
+ cut_index --;
+ }
+ }
+ }
+ if ( cut_index > 0 ) {
+ cut_index += n_decimals;
+ if ( cut_index > size_str.size()-1 ) {
+ cut_index = size_str.size()-1;
+ }
+ }
+ return QString::fromStdString( size_str.substr(0, cut_index ) + size_sfx );
+}
+
+
+void Craplog::makeChart( const QChart::ChartTheme& theme, const std::unordered_map& fonts, QChartView* size_chart )
+{
+ const QString
+ size_chart_name = TR::tr("Logs Size Breakdown"),
+ ignored_slice_name = TR::tr("Ignored"),
+ parsed_slice_name = TR::tr("Parsed"),
+ warning_slice_name = TR::tr("Warnings"),
+ blacklisted_slice_name = TR::tr("Blacklisted");
+
+ // logs size donut chart
+ QPieSeries *parsedSize_donut = new QPieSeries();
+ parsedSize_donut->setName( this->printableSize( this->parsed_size ) );
+ parsedSize_donut->append(
+ "P@" + parsed_slice_name + "@" + this->printableSize( this->parsed_size-this->warnlisted_size ),
+ this->parsed_size-this->warnlisted_size );
+ parsedSize_donut->append(
+ "W@" + warning_slice_name + "@" + this->printableSize( this->warnlisted_size ),
+ this->warnlisted_size );
+ parsedSize_donut->append(
+ "B@" + blacklisted_slice_name + "@" + this->printableSize( this->blacklisted_size ),
+ this->blacklisted_size );
+
+ // logs size donut chart
+ QPieSeries *ignoredSize_donut = new QPieSeries();
+ ignoredSize_donut->setName( this->printableSize( this->total_size-this->parsed_size-this->blacklisted_size ) );
+ ignoredSize_donut->append(
+ "I@#" + ignored_slice_name + "@#" + this->printableSize( this->total_size-this->parsed_size-this->blacklisted_size ),
+ this->total_size-this->parsed_size-this->blacklisted_size );
+ ignoredSize_donut->setLabelsVisible( false );
+
+ DonutBreakdown *sizeBreakdown = new DonutBreakdown();
+ sizeBreakdown->setTheme( theme );
+ sizeBreakdown->setAnimationOptions( QChart::AllAnimations );
+ sizeBreakdown->setTitle( size_chart_name );
+ sizeBreakdown->setTitleFont( fonts.at("main") );
+ if ( this->proceed && this->total_size > 0 ) {
+ sizeBreakdown->legend()->setAlignment( Qt::AlignRight );
+ sizeBreakdown->addBreakdownSeries( parsedSize_donut, Qt::GlobalColor::darkCyan, fonts.at("main_small") );
+ sizeBreakdown->addBreakdownSeries( ignoredSize_donut, Qt::GlobalColor::gray, fonts.at("main_small") );
+ } else {
+ sizeBreakdown->legend()->setVisible( false );
+ sizeBreakdown->setTitle("");
+ }
+ sizeBreakdown->legend()->setFont( fonts.at("main") );
+ sizeBreakdown->legend()->markers( ignoredSize_donut ).first()->setVisible( false );
+
+ size_chart->setChart( sizeBreakdown );
+ size_chart->setRenderHint( QPainter::Antialiasing );
+}
diff --git a/logdoctor/modules/craplog/craplog.h b/logdoctor/modules/craplog/craplog.h
new file mode 100644
index 00000000..4e4e8ad5
--- /dev/null
+++ b/logdoctor/modules/craplog/craplog.h
@@ -0,0 +1,648 @@
+#ifndef CRAPLOG_H
+#define CRAPLOG_H
+
+#include
+#include
+
+#include
+#include
+
+#include "modules/craplog/modules/formats.h"
+#include "modules/craplog/modules/hash.h"
+#include "modules/craplog/modules/logs.h"
+
+
+//! Craplog
+/*!
+ Performs operations related to the logs
+*/
+class Craplog
+{
+public:
+ Craplog();
+
+ //! Main work method
+ /*!
+ Manages the operations which need to be done to parse the logs
+ when the START button is pressed
+ */
+ void run();
+
+
+ /////////////////
+ //// DIALOGS ////
+
+ //! Returns the Dialogs level
+ const int& getDialogsLevel();
+
+ //! Sets the new Dialogs level
+ void setDialogsLevel( const int& new_level );
+
+ ///////////////////
+ //// DATABASES ////
+
+ //! Returns the path of the logs Collection database
+ const std::string& getStatsDatabasePath();
+
+ //! Returns the path of the log files' Hashes database
+ const std::string& getHashesDatabasePath();
+
+ //! Sets the new path for the logs Collection database
+ /*!
+ \param The new path of the database file
+ */
+ void setStatsDatabasePath( const std::string& path );
+
+ //! Sets the new path for the log files' Hashes database
+ /*!
+ \param The new path of the database file
+ */
+ void setHashesDatabasePath( const std::string& path );
+
+
+ ////////////////////////
+ //// CURRENTLY USED ////
+
+ //! Sets the currently used Web Server ID
+ /*!
+ \param web_server_id The new currently used Web Server
+ */
+ void setCurrentWSID( const int& web_server_id );
+
+ //! Returns the currently used Web Server ID
+ /*!
+ \return The Web Server ID
+ */
+ const int& getCurrentWSID();
+
+ //! Returns the currently used LogsFormat
+ /*!
+ \return The LogsFormat
+ \see FormatOps::LogsFormat
+ */
+ const FormatOps::LogsFormat& getCurrentLogFormat();
+
+
+ ////////////////////
+ //// LOGS PATHS ////
+
+ //! Returns the logs' path for the given web Server
+ /*!
+ \param web_server The ID of the Web Server
+ \return The path of the logs' folder
+ */
+ const std::string& getLogsPath( const int& web_server );
+
+ //! Sets a new path for the given Web Server to search the logs in
+ /*!
+ \param web_server The ID of the Web Server
+ \param new_path The new path
+ */
+ void setLogsPath( const int& web_server, const std::string& new_path );
+
+
+ ///////////////////
+ //// LOG FILES ////
+
+ //! Structure which holds informations about a log file
+ struct LogFile {
+ bool selected; //!< Wheter the file has been selected to be use or not
+ bool used_already; //!< Wheter the file has been used already or not
+ int size; //!< The size of the file
+ QString name; //!< The name of the file, to be displayed in the list
+ std::string hash; //!< The sha256 hash of the content
+ std::string path; //!< The path of the file, including the file name
+ };
+
+ //! Checks if a file name respects the relative criterions
+ /*!
+ \param name The name of the file
+ \return Wheter it does respect the criterions or not
+ \see LogName
+ */
+ const bool isFileNameValid( const std::string& name );
+
+
+ ///////////////////
+ //// LOGS LIST ////
+
+ //! Returns the list of log files
+ /*!
+ \param fresh Whether to refresh the list before to return it or not
+ \return The list of log files
+ \see LogFile, logs_list, scanLogsDir()
+ */
+ const std::vector& getLogsList( const bool& fresh=false );
+
+ //! Returns the amount of log files in the list
+ /*!
+ \return The number of files actually in the list
+ \see logs_list
+ */
+ const int getLogsListSize();
+
+ //! Returns the LogFile instance of the given file
+ /*!
+ \param file_name The name of the file
+ \return The LogFile instance
+ \throw GenericException
+ \see LogFile, logs_list
+ */
+ const LogFile& getLogFileItem( const QString& file_name );
+
+ /*const std::string& getLogFilePath( const QString& file_name );*/
+
+ //! Sets a file in the list as selected
+ /*!
+ \param file_name The name of the file
+ \return Wheter the given file name has been found in the list
+ \see LogFile, logs_list
+ */
+ const bool setLogFileSelected( const QString& file_name );
+
+
+ //////////////////////
+ //// LOGS FORMATS ////
+
+ //! Sets the Apache2 LogsFormat from the given format string
+ /*!
+ \param format_string The logs format string
+ \return Whether the process was successful or not
+ \see FormatOps, FormatOps::LogsFormat, FormatOps::processApacheFormatString()
+ */
+ const bool setApacheLogFormat( const std::string& format_string );
+
+ //! Sets the Nginx LogsFormat from the given format string
+ /*!
+ \param format_string The logs format string
+ \return Whether the process was successful or not
+ \see FormatOps, FormatOps::LogsFormat, FormatOps::processNginxFormatString()
+ */
+ const bool setNginxLogFormat( const std::string& format_string );
+
+ //! Sets the IIS LogsFormat from the given format string
+ /*!
+ \param format_string The logs format string
+ \param log_module The IIS logs module to be used to parse the format string
+ \return Whether the process was successful or not
+ \see FormatOps, FormatOps::LogsFormat, FormatOps::processIisFormatString()
+ */
+ const bool setIisLogFormat( const std::string& format_string, const int& log_module );
+
+ //! Returns the logs format string for the given Web Server
+ /*!
+ \param web_server_id ID of the Web Server
+ \return The format string
+ \see FormatOps::LogsFormat
+ */
+ const std::string& getLogsFormatString( const int& web_server_id );
+
+ //! Returns the LogsFormat currently set for the given Web Server
+ /*!
+ \param web_server_id ID of the Web Server
+ \return The LogsFormat instance
+ \see FormatOps::LogsFormat
+ */
+ const FormatOps::LogsFormat& getLogsFormat( const int& web_server_id );
+
+ //! Returns a sample log line for the given Web Server using the relative LogsFormat
+ /*!
+ \param web_server_id ID of the Web Server
+ \return The sample of a log line
+ \throw WebServerException
+ \see FormatOps::getApacheLogSample(), FormatOps::getNginxLogSample(), FormatOps::getIisLogSample()
+ */
+ const QString getLogsFormatSample( const int& web_server_id );
+
+
+
+ //////////////////////
+ //// WARNING SIZE ////
+
+ //! Returns the currently set warning size for the log files
+ const long& getWarningSize();
+
+ //! Sets the new warning size for the log files
+ void setWarningSize( const long& new_size );
+
+
+ ////////////////////
+ //// OPERATIONS ////
+
+ // logs usage control
+ HashOps hashOps;
+
+ // operations on logs
+ LogOps logOps;
+
+ //////////////////////////////
+ //// BLACKLIST / WARNLIST ////
+
+ //! Structure to hold the items of a blacklist/warnlist
+ struct BWlist {
+ bool used; //!< Whether the list is set to be used or not
+ std::vector list; //!< The list of items
+ };
+
+ //! Returns whether the relative blacklist is set to be used or not
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \return Whether the list is used or not
+ \see BWlist
+ */
+ const bool& isBlacklistUsed( const int& web_server_id, const int& log_field_id );
+
+ //! Returns whether the relative warnlist is set to be used or not
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \return Whether the list is used or not
+ \see BWlist
+ */
+ const bool& isWarnlistUsed( const int& web_server_id, const int& log_field_id );
+
+ //! Sets the relative blacklist to be used or not
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param used Whether the list is to be used or not
+ \see BWlist
+ */
+ void setBlacklistUsed( const int& web_server_id, const int& log_field_id, const bool& used );
+
+ //! Sets the relative warnlist to be used or not
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param used Whether the list is to be used or not
+ \see BWlist
+ */
+ void setWarnlistUsed( const int& web_server_id, const int& log_field_id, const bool& used );
+
+ //! Returns the relative items list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \return The list of items in the given blacklist
+ \see BWlist
+ */
+ const std::vector& getBlacklist( const int& web_server_id, const int& log_field_id );
+
+ //! Returns the relative items list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \return The list of items in the givenwarnlist
+ \see BWlist
+ */
+ const std::vector& getWarnlist( const int& web_server_id, const int& log_field_id );
+
+ //! Sets the relative items list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param new_list The new items list
+ \see BWlist
+ */
+ void setBlacklist( const int& web_server_id, const int& log_field_id, const std::vector& new_list );
+
+ //! Sets the relative items list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param new_list The new items list
+ \see BWlist
+ */
+ void setWarnlist( const int& web_server_id, const int& log_field_id, const std::vector& new_list );
+
+ //! Adds an item to the relative list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param new_list The new items list
+ \see BWlist
+ */
+ void blacklistAdd( const int& web_server_id, const int& log_field_id, const std::string& new_item );
+
+ //! Adds an item to the relative list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param new_item The new item to add to the list
+ \see BWlist
+ */
+ void warnlistAdd( const int& web_server_id, const int& log_field_id, const std::string& new_item );
+
+ //! Removes an item from the relative list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param item The item to remove from the list
+ \see BWlist
+ */
+ void blacklistRemove( const int& web_server_id, const int& log_field_id, const std::string& item );
+
+ //! Removes an item from the relative list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param item The item to remove from the list
+ \see BWlist
+ */
+ void warnlistRemove( const int& web_server_id, const int& log_field_id, const std::string& item );
+
+ //! Moves an item one position up in the relative list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param item The item to move
+ \see BWlist
+ */
+ const int blacklistMoveUp( const int& web_server_id, const int& log_field_id, const std::string& item );
+
+ //! Moves an item one position up in the relative list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param item The item to move
+ \see BWlist
+ */
+ const int warnlistMoveUp( const int& web_server_id, const int& log_field_id, const std::string& item );
+
+ //! Moves an item one position down in the relative list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param item The item to move
+ \see BWlist
+ */
+ const int blacklistMoveDown( const int& web_server_id, const int& log_field_id, const std::string& item );
+
+ //! Moves an item one position down in the relative list
+ /*!
+ \param web_server_id The ID of the Web Server
+ \param log_field_id The ID of the log field
+ \param item The item to move
+ \see BWlist
+ */
+ const int warnlistMoveDown( const int& web_server_id, const int& log_field_id, const std::string& item );
+
+
+ //////////////
+ //// WORK ////
+
+ //! Returns whether the database has been edited or not during the process
+ const bool& editedDatabase();
+
+ //! Various checks to be made before starting a new process
+ /*!
+ Checks the databases, the selected files and their size
+ \return Whether the checks has been successful or not
+ \throw GenericException
+ */
+ const bool checkStuff();
+
+ //! Erases the data collection when a process is done
+ void clearDataCollection();
+
+ //! Returns whether the process is still running or not
+ const bool& isWorking();
+
+ //! Returns whether the process is still parsing or not
+ const bool& isParsing();
+
+
+ //////////////////////
+ //// PERFORMANCES ////
+
+ //! Collects performances data from the sub-modules
+ /*!
+ \see LogOps::getParsedSize(), LogOps::getParsedLines()
+ */
+ void collectPerfData();
+
+ //! Sums the given size to the warnlisted size
+ void sumWarningsSize( const unsigned int& size );
+
+ //! Sums the given size to the blacklisted size
+ void sumBlacklistededSize( const unsigned int& size );
+
+ /*void sumPerfSize( const unsigned& size );*/
+
+ //! Returns the size to be displayed in the main window
+ const unsigned int& getPerfSize();
+
+ //! Returns the total logs size
+ const unsigned int& getTotalSize();
+
+ //! Returns the parsed logs lines
+ const unsigned int& getParsedLines();
+
+ /*const unsigned int& getParsedSize();*/
+
+ //! Builds and draws the chart to be displayed in the main window
+ /*!
+ \param theme The theme to use for the Chart
+ \param fonts The map holding the fonts
+ \param size_chart The widget which will display the chart
+ \see DonutBreakdown
+ */
+ void makeChart( const QChart::ChartTheme& theme, const std::unordered_map& fonts, QChartView* size_chart );
+
+
+
+private:
+
+ /////////////////
+ //// DIALOGS ////
+
+ // quantity of information to display throught dialogs
+ int dialogs_level = 2; // 0: essential, 1: usefull, 2: explanatory
+
+ /////////////////////////
+ //// WEB SERVERS IDs ////
+
+ const unsigned int APACHE_ID = 11; //!< ID of the Apache2 Web Server
+ const unsigned int NGINX_ID = 12; //!< ID of the Nginx Web Server
+ const unsigned int IIS_ID = 13; //!< ID of the IIS Web Server
+
+
+ ///////////////////
+ //// DATABASES ////
+
+ std::string db_stats_path;
+ std::string db_hashes_path;
+
+
+ //////////////
+ //// WORK ////
+
+ bool db_edited = false;
+ bool working = false;
+ bool parsing = false;
+ bool proceed = false;
+
+ //! Sets the working state
+ /*!
+ \see isWorking()
+ */
+ void startWorking();
+
+ //! Un-sets the working state
+ /*!
+ \see isWorking()
+ */
+ void stopWorking();
+
+
+ //////////////////////
+ //// PERFORMANCES ////
+
+ unsigned int total_lines = 0; // total number of logs lines
+ unsigned int parsed_lines = 0; // number of parsed logs lines
+ unsigned int perf_size = 0; // final size to show in the main window
+ unsigned int total_size = 0; // total size of the logs
+ unsigned int parsed_size = 0; // size of the logs which have been used
+ unsigned int warnlisted_size = 0; // size of the logs which caused a warning
+ unsigned int blacklisted_size = 0; // size of the logs which has been blacklisted
+
+ //! Returns a printable size to be displayed in the chart, including the suffix
+ /*!
+ \param bytes The size in bytes
+ \return The string to be displayed
+ \see makeChart()
+ */
+ const QString printableSize( const unsigned int& bytes );
+
+ ////////////////////
+ //// LOGS ITEMS ////
+
+ // collection of logs items, each item results from a log line
+ /* structure
+ [ { log_field_id : "data" } ]
+
+ log_field_ids
+ 99: warning,
+ 1: year, 2: month, 3: day, 4: hour, 5: minute, 6:second,
+ 10: request_protocol, 11: request_method, 12: request_uri, 13: request_query, 14: response_code,
+ 15: time_taken, 16: bytes_sent, 17: bytes_received, 18: referrer,
+ 20: client, 21: user_agent, 22: cookie
+ */
+ std::vector> data_collection;
+
+ // the selected log files to be parsed during the process
+ std::vector log_files_to_use;
+
+ // the entire stack of lines which have been read from the log files
+ std::vector logs_lines;
+
+ //! Reads the selected files and append the resulting lines to the list
+ /*!
+ \throw GenericException
+ */
+ void joinLogLines();
+
+ //! Parses the lines in the list and stores their data in the data collection
+ /*!
+ \see LogOps::parseLines()
+ */
+ void parseLogLines();
+
+ //! Stores the data collection in the database
+ /*!
+ \see StoreOps::storeData()
+ */
+ void storeLogLines();
+
+ // used files
+ std::vector used_files_hashes;
+
+
+ //////////////////////
+ //// LOGS CONTROL ////
+
+ // warning size, in Bytes
+ long warning_size = (1'048'576 * 50) +1; // => 1 MiB * x
+
+
+ //////////////////////////////
+ //// BLACKLIST / WARNLIST ////
+
+ // { web_server_id : { log_field_id : BWlist } }
+ std::unordered_map> blacklists;
+ std::unordered_map> warnlists;
+
+ //! Ssnitizes an item removing the unwanted elements
+ /*!
+ Called when adding a new item to a list
+ \param log_field_id The ID of the log field
+ \param new_item The item to be sanitized
+ \return The sanitized item
+ \throw BWlistException, GenericException
+ \see BWlist
+ */
+ const std::string sanitizeBWitem( const int& log_field_id, const std::string& new_item );
+
+
+ ////////////////////
+ //// WEB SERVER ////
+
+ // currently used web server
+ int current_WS = this->APACHE_ID;
+
+ std::unordered_map logs_paths;
+
+ //! Web Server specific file names criterions
+ /*!
+ The rules to be used to decide whether a file name is valid or not
+ \see isFileNameValid()
+ */
+ struct LogName {
+ std::string starts; //!< What should be initial part of the name
+ std::string contains; //!< What should be contained in the middle of the name
+ std::string ends; //!< What should be final part of the name
+ };
+
+ std::unordered_map logs_base_names;
+
+ //! Changes the name criterions for IIS logs files names depending on the given module
+ /*!
+ \param module_id The ID of the module to use to set the criterions
+ \throw GenericException
+ \see LogName
+ */
+ void changeIisLogsBaseNames( const int& module_id );
+
+
+ ///////////////////
+ //// LOGS LIST ////
+
+ // list of the log files found in the logs path
+ std::vector logs_list;
+
+ //! Scans the logs directory to get a list of log files
+ void scanLogsDir();
+
+
+ /////////////////////
+ //// LOGS FORMAT ////
+
+ FormatOps formatOps;
+
+ std::unordered_map logs_format_strings;
+
+ std::unordered_map logs_formats;
+
+ //! Uses the current Web Server to set the relative logs format
+ /*!
+ \see LogOps::LogsFormat
+ */
+ void setCurrentLogFormat();
+
+ // currently used logs format
+ FormatOps::LogsFormat current_LF;
+
+};
+
+#endif // CRAPLOG_H
diff --git a/logdoctor/modules/craplog/modules/datetime.cpp b/logdoctor/modules/craplog/modules/datetime.cpp
new file mode 100644
index 00000000..203d32d1
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/datetime.cpp
@@ -0,0 +1,223 @@
+
+#include "datetime.h"
+
+#include "modules/exceptions.h"
+#include "utilities/strings.h"
+
+#include
+
+#include
+
+
+DateTimeOps::DateTimeOps()
+{
+
+}
+
+
+const std::string DateTimeOps::convertMonth( const std::string& month )
+{
+ std::string m;
+ if ( month == "Jan" ) {
+ m = "1";
+ } else if ( month == "Feb" ) {
+ m = "2";
+ } else if ( month == "Mar" ) {
+ m = "3";
+ } else if ( month == "Apr" ) {
+ m = "4";
+ } else if ( month == "May" ) {
+ m = "5";
+ } else if ( month == "Jun" ) {
+ m = "6";
+ } else if ( month == "Jul" ) {
+ m = "7";
+ } else if ( month == "Aug" ) {
+ m = "8";
+ } else if ( month == "Sep" ) {
+ m = "9";
+ } else if ( month == "Oct" ) {
+ m = "10";
+ } else if ( month == "Nov" ) {
+ m = "11";
+ } else if ( month == "Dec" ) {
+ m = "12";
+ } else {
+ // nope
+ throw DateTimeException("Unexpected month format: "+month);
+ }
+ return m;
+}
+
+
+
+const std::vector DateTimeOps::processDateTime( const std::string& datetime_, const std::string& format )
+{
+ std::string aux, datetime=datetime_;
+ std::string year="", month="", day="", hour="", minute="", second="";
+
+ if ( format == "ncsa" ) {
+ datetime = StringOps::strip( datetime, "[ ]" );
+ day = datetime.substr( 0, 2 );
+ month = DateTimeOps::convertMonth( datetime.substr( 3, 3 ) );
+ year = datetime.substr( 7, 4 );
+ hour = datetime.substr( 12, 2 );
+ minute = datetime.substr( 15, 2 );
+ second = datetime.substr( 18, 2 );
+
+ } else if ( format == "mcs" ) {
+ month = DateTimeOps::convertMonth( datetime.substr( 4, 3 ) );
+ day = datetime.substr( 8, 2 );
+ hour = datetime.substr( 11, 2 );
+ minute = datetime.substr( 14, 2 );
+ second = datetime.substr( 17, 2 );
+ year = datetime.substr( datetime.size()-5 );
+
+ } else if ( format == "gmt" ) {
+ int start = datetime.find( ", " ) + 2;
+ day = datetime.substr( start, 2 );
+ start += 3;
+ month = DateTimeOps::convertMonth( datetime.substr( start, 3 ) );
+ start += 4;
+ year = datetime.substr( start, 4 );
+ start += 5;
+ hour = datetime.substr( start, 2 );
+ start += 3;
+ minute = datetime.substr( start, 2 );
+ start += 3;
+ second = datetime.substr( start, 2 );
+
+ } else if ( StringOps::startsWith( format, "iso" ) ) {
+ year = datetime.substr( 0, 4 );
+ month = datetime.substr( 5, 2 );
+ day = datetime.substr( 8, 2 );
+ hour = datetime.substr( 11, 2 );
+ minute = datetime.substr( 14, 2 );
+ second = datetime.substr( 17, 2 );
+
+ } else if ( StringOps::startsWith( format, "utc" ) ) {
+ if ( format == "utc_d" ) {
+ // date
+ year = datetime.substr( 0, 4 );
+ month = datetime.substr( 5, 2 );
+ day = datetime.substr( 8, 2 );
+ } else {
+ // time
+ hour = datetime.substr( 0, 2 );
+ minute = datetime.substr( 3, 2 );
+ second = datetime.substr( 6, 2 );
+ }
+
+ } else if ( StringOps::startsWith( format, "epoch_" ) ) {
+ aux = format.substr( 6 );
+ // convert to seconds
+ if ( aux == "us" ) {
+ // from microseconds
+ datetime = datetime.substr( 0, datetime.size()-6 );
+ } else if ( aux == "ms" ) {
+ // from milliseconds
+ datetime = datetime.substr( 0, datetime.size()-3 );
+ } else if ( aux == "s.ms" ) {
+ // from seconds.milliseconds
+ datetime = std::to_string( std::stoi( datetime ) );
+ }
+ // convert to iso date format
+ QDateTime e = QDateTime::fromSecsSinceEpoch( std::stoi( datetime ) );
+ datetime = e.toString( "yyyy-MM-dd HH:mm:ss" ).toStdString();
+
+ // parse
+ year = datetime.substr( 0, 4 );
+ month = datetime.substr( 5, 2 );
+ day = datetime.substr( 8, 2 );
+ hour = datetime.substr( 11, 2 );
+ minute = datetime.substr( 14, 2 );
+ second = datetime.substr( 17, 2 );
+
+ } else {
+ if ( format == "YYYYMMDD" ) {
+ year = datetime.substr( 0, 4 );
+ month = datetime.substr( 5, 2 );
+ day = datetime.substr( 8, 2 );
+
+ } else if ( format == "MMDDYY" ) {
+ month = datetime.substr( 0, 2 );
+ day = datetime.substr( 3, 2 );
+ year = "20" + datetime.substr( 6, 2 );
+
+ } else if ( format == "MDYY" ) {
+ int aux_;
+ if ( datetime.at(2) == '/' ) {
+ month = datetime.substr( 0, 2 );
+ aux_ = 3;
+ } else {
+ month = "0" + datetime.substr( 0, 1 );
+ aux_ = 2;
+ }
+ if ( datetime.at(aux_+2) == '/' ) {
+ day = datetime.substr( aux_, 2 );
+ aux_ += 3;
+ } else {
+ day = "0" + datetime.substr( aux_, 1 );
+ aux_ = +2;
+ }
+ year = "20" + datetime.substr( aux_ );
+
+ } else if ( StringOps::startsWith( format, "year" ) ) {
+ year = datetime;
+ if ( format == "year_short" ) {
+ year = "20" + year;
+ }
+
+ } else if ( StringOps::startsWith( format, "month" ) ) {
+ if ( format.size() <= 5 ) {
+ month = datetime;
+ } else {
+ datetime = datetime.substr( 0, 3 ); // may be the full name
+ month = DateTimeOps::convertMonth( datetime );
+ }
+
+ } else if ( format == "day" ) {
+ day = datetime;
+
+ } else if ( StringOps::startsWith( format, "clock_" ) ) {
+ aux = format.substr( 6 );
+ if ( aux == "24" ) {
+ hour = datetime.substr( 0, 2 );
+ minute = datetime.substr( 3, 2 );
+ second = datetime.substr( 6, 2 );
+
+ } else if ( aux == "12" ) {
+ hour = datetime.substr( 0, 2 );
+ minute = datetime.substr( 3, 2 );
+ second = datetime.substr( 6, 2 );
+ if ( datetime.substr( 9, 2 ) == "pm" ) {
+ hour = std::to_string( 12 + std::stoi(hour) );
+ }
+
+ } else if ( aux == "short" ) {
+ hour = datetime.substr( 0, 2 );
+ minute = datetime.substr( 3, 2 );
+
+ } else if ( aux == "meridian" ) {
+ if ( datetime == "pm" ) {
+ hour = "PM"; // to mark for final update
+ }
+ }
+
+ } else if ( format == "hour" ) {
+ hour = datetime;
+
+ } else if ( format == "minute" ) {
+ minute = datetime;
+
+ } else if ( format == "second" ) {
+ second = datetime;
+
+ } else {
+ // wronthing went some ...
+ throw DateTimeException("Unexpected DateTime format: "+datetime_);
+ }
+ }
+
+ return std::vector({ year, month, day, hour, minute, second });
+}
diff --git a/logdoctor/modules/craplog/modules/datetime.h b/logdoctor/modules/craplog/modules/datetime.h
new file mode 100644
index 00000000..f853e55f
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/datetime.h
@@ -0,0 +1,39 @@
+#ifndef DATETIME_H
+#define DATETIME_H
+
+#include
+#include
+
+
+//! DateTimeOps
+/*!
+ Operations for the dates
+*/
+class DateTimeOps
+{
+public:
+ DateTimeOps();
+
+ //! Returns a standardized list of items representing the given date and time
+ /*!
+ \param datetime The given date and time string
+ \param format The format of the given string
+ \throw DateTimeException
+ \return The list of items
+ */
+ static const std::vector processDateTime( const std::string& datetime, const std::string& format );
+
+private:
+
+ //! Converts a month from the short-name to the number
+ /*!
+ \param month The short-name of the month
+ \return The month number in the calendar
+ \throw DateTimeException
+ \see processDateTime
+ */
+ static const std::string convertMonth( const std::string& month );
+
+};
+
+#endif // DATETIME_H
diff --git a/logdoctor/modules/craplog/modules/donuts.cpp b/logdoctor/modules/craplog/modules/donuts.cpp
new file mode 100644
index 00000000..6c6e5291
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/donuts.cpp
@@ -0,0 +1,142 @@
+
+#include "donuts.h"
+
+#include "utilities/strings.h"
+
+
+/////////////////////////
+//// DONUT BREAKDOWN ////
+/////////////////////////
+MainSlice::MainSlice( QPieSeries* breakdownSeries, QObject* parent )
+ : QPieSlice(parent),
+ m_breakdownSeries(breakdownSeries)
+{
+ connect( this, &MainSlice::percentageChanged, this, &MainSlice::updateLabel );
+}
+
+
+QPieSeries *MainSlice::breakdownSeries() const
+{
+ return this->m_breakdownSeries;
+}
+
+void MainSlice::setName( QString name )
+{
+ this->m_name = name;
+}
+
+QString MainSlice::name() const
+{
+ return this->m_name;
+}
+
+
+void MainSlice::updateLabel()
+{
+ this->setLabel( this->m_name );
+ //this->setLabel(QString("%1 %2%").arg(this->m_name).arg(this->percentage() * 100, 0, 'f', 2));
+}
+
+
+
+DonutBreakdown::DonutBreakdown( QGraphicsItem* parent, Qt::WindowFlags wFlags )
+ : QChart(QChart::ChartTypeCartesian, parent, wFlags)
+{
+ // create the series for main center pie
+ this->m_mainSeries = new QPieSeries();
+ this->m_mainSeries->setPieSize( 0.7 );
+ QChart::addSeries( this->m_mainSeries );
+}
+
+
+
+void DonutBreakdown::addBreakdownSeries( QPieSeries* breakdownSeries, const QColor& color, const QFont& font )
+{
+ // add breakdown series as a slice to center pie
+ MainSlice *mainSlice = new MainSlice(breakdownSeries);
+ mainSlice->setName(breakdownSeries->name());
+ mainSlice->setValue(breakdownSeries->sum());
+ m_mainSeries->append(mainSlice);
+
+ // customize the slice
+ mainSlice->setBrush( color );
+ mainSlice->setLabelVisible();
+ mainSlice->setLabelColor( Qt::white );
+ mainSlice->setLabelPosition( QPieSlice::LabelInsideHorizontal );
+ mainSlice->setLabelFont( font );
+
+ // position and customize the breakdown series
+ breakdownSeries->setPieSize( 0.8 );
+ breakdownSeries->setHoleSize( 0.7 );
+ breakdownSeries->setLabelsVisible();
+ const auto slices = breakdownSeries->slices();
+ for (QPieSlice *slice : slices) {
+ if ( StringOps::startsWith( slice->label().toStdString(), "B" ) ) {
+ slice->setBrush( Qt::GlobalColor::black );
+ } else if ( StringOps::startsWith( slice->label().toStdString(), "W" ) ) {
+ slice->setBrush( QColor( 255, 140, 0, 255 ) );
+ } else if ( StringOps::startsWith( slice->label().toStdString(), "I" ) ) {
+ slice->setBrush( Qt::GlobalColor::transparent );
+ breakdownSeries->setPieSize( 0.0 );
+ } else {
+ slice->setBrush( color.lighter( 150 ) );
+ }
+ slice->setLabelFont( font );
+ if ( slice->value() == 0 ) {
+ slice->setLabelVisible( false );
+ }
+ }
+
+ // add the series to the chart
+ QChart::addSeries(breakdownSeries);
+
+ // recalculate breakdown donut segments
+ this->recalculateAngles();
+
+ // update customize legend markers
+ this->updateLegendMarkers();
+}
+
+
+
+void DonutBreakdown::recalculateAngles()
+{
+ qreal angle = 0;
+ const auto slices = m_mainSeries->slices();
+ for (QPieSlice *slice : slices) {
+ QPieSeries *breakdownSeries = qobject_cast(slice)->breakdownSeries();
+ breakdownSeries->setPieStartAngle(angle);
+ angle += slice->percentage() * 360.0; // full pie is 360.0
+ breakdownSeries->setPieEndAngle(angle);
+ }
+}
+
+
+void DonutBreakdown::updateLegendMarkers()
+{
+ // go through all markers
+ const auto allseries = series();
+ for (QAbstractSeries *series : allseries) {
+ const auto markers = legend()->markers(series);
+ for (QLegendMarker *marker : markers) {
+ QPieLegendMarker *pieMarker = qobject_cast(marker);
+ if (series == m_mainSeries) {
+ // hide markers from main series
+ pieMarker->setVisible(false);
+ } else {
+ // modify markers from breakdown series
+ std::string aux = pieMarker->slice()->label().toStdString();
+ if ( aux.at( aux.find('@')+1 ) != '#' ) {
+ pieMarker->setLabel( QString("%1 %2%")
+ .arg( QString::fromStdString( aux.substr( 0, aux.find('@') ) ) )
+ .arg( pieMarker->slice()->percentage() * 100, 0, 'f', 2) );
+ pieMarker->setFont( QFont("Arial", 8) );
+ pieMarker->slice()->setLabel( QString::fromStdString( aux.substr( aux.find('@')+1 ) ) );
+ } else {
+ pieMarker->setLabel( "" );
+ pieMarker->slice()->setLabel( "" );
+ }
+ }
+ }
+ }
+}
diff --git a/logdoctor/modules/craplog/modules/donuts.h b/logdoctor/modules/craplog/modules/donuts.h
new file mode 100644
index 00000000..f76f629d
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/donuts.h
@@ -0,0 +1,85 @@
+#ifndef DONUTS_H
+#define DONUTS_H
+
+#include
+#include
+#include
+
+#include
+
+
+//! DonutBreakdon
+/*!
+ Builds the donut chart to be displayed
+*/
+class DonutBreakdown : public QChart
+{
+public:
+
+ DonutBreakdown( QGraphicsItem* parent=nullptr, Qt::WindowFlags wFlags={} );
+
+ //! Adds a slice to the donut
+ /*!
+ \param series The series to add
+ \param color The color of the slice
+ \param font The font to be used
+ \see Craplog::makeChart()
+ */
+ void addBreakdownSeries( QPieSeries* series, const QColor& color, const QFont& font );
+
+
+private:
+
+ // The main series (a.k.a. the donut)
+ QPieSeries* m_mainSeries;
+
+ //! Recalculates the size of every slice in percentage over the total
+ void recalculateAngles();
+
+ //! Updates the position of the markers to be in the middle of the relative slice
+ void updateLegendMarkers();
+};
+
+
+
+//! Represents a slice
+/*!
+ A slice to be added at the donut
+*/
+class MainSlice : public QPieSlice
+{
+ Q_OBJECT
+
+public:
+
+ MainSlice( QPieSeries *breakdownSeries, QObject *parent=0 );
+
+ //! Returns the series
+ QPieSeries *breakdownSeries() const;
+
+ //! Sets the name
+ /*!
+ \param name The name to be used
+ */
+ void setName( QString name );
+
+ //! Returns the name
+ QString name() const;
+
+
+public Q_SLOTS:
+
+ //! Updates the label using the name
+ void updateLabel();
+
+
+private:
+
+ // The series of the slice
+ QPieSeries* m_breakdownSeries;
+
+ // The name of the slice
+ QString m_name;
+};
+
+#endif // DONUTS_H
diff --git a/logdoctor/modules/craplog/modules/formats.cpp b/logdoctor/modules/craplog/modules/formats.cpp
new file mode 100644
index 00000000..8e52fef8
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/formats.cpp
@@ -0,0 +1,705 @@
+
+#include "formats.h"
+
+#include "modules/exceptions.h"
+#include "utilities/strings.h"
+
+
+FormatOps::FormatOps()
+{
+
+}
+
+
+// count the new lines
+const int FormatOps::countNewLines( const std::string& initial, const std::string& final, const std::vector& separators )
+{
+ int nl = 0;
+ nl += StringOps::count( initial, "\n" );
+ nl += StringOps::count( final, "\n" );
+ for ( const std::string& sep : separators ) {
+ nl += StringOps::count( sep, "\n" );
+ }
+ return nl;
+}
+
+
+// process escapes like apache
+const std::string FormatOps::parseApacheEscapes( const std::string& string , const bool& strftime )
+{
+ int i = 0,
+ max = string.size()-1;
+ std::string str1 = "",
+ str2 = "";
+ char c, cc;
+ // parse the first time, no control-character added
+ while (true) {
+ if ( i >= max ) {
+ // no need to check the final char
+ if ( i == max ) {
+ str1.push_back( string.at( i ) );
+ }
+ break;
+ }
+ c = string.at( i );
+ cc = string.at( i+1 );
+ if ( c == '\\' && (cc == '\\' || cc == '"') ) {
+ str1.push_back( cc );
+ i++;
+ } else if ( c == '%' && cc == '%' ) {
+ str1.push_back( c );
+ i++;
+ } else {
+ str1.push_back( c );
+ }
+ i++;
+ }
+ i = 0;
+ max = str1.size()-1;
+ // parse the second time, adding control-characters
+ while (true) {
+ if ( i >= max ) {
+ // no need to check the final char
+ if ( i == max ) {
+ str2.push_back( str1.at( i ) );
+ }
+ break;
+ }
+ c = str1.at( i );
+ cc = str1.at( i+1 );
+ if ( c == '\\' ) {
+ // just the ones supported
+ if ( cc == '\\' || cc == '"' ) {
+ str2.push_back( cc );
+ i++;
+ } else {
+ if ( strftime ) {
+ // when parsing for strftime, any other backslashed characters result in a backslash+character
+ str2.push_back( c );
+ str2.push_back( cc );
+ i++;
+
+ } else {
+ if ( cc == 'n' ) {
+ str2.push_back( '\n' );
+ i++;
+ } else if ( cc == 'r' ) {
+ // not supported
+ throw LogFormatException( "LogDoctor doesn't support the usage of the Carriage Return: '\\r'." );
+ } else if ( cc == 't' ) {
+ str2.push_back( '\t' );
+ i++;
+ } else {
+ // any other backslashed characters result in a backslash+character
+ str2.push_back( c );
+ str2.push_back( cc );
+ i++;
+ }
+ }
+ }
+ } else if ( c == '%' && strftime ) {
+ // strftime control-characters
+ if ( cc == 'n' ) {
+ str2.push_back( '\n' );
+ i++;
+ } else if ( cc == 't' ) {
+ str2.push_back( '\t' );
+ i++;
+ } else {
+ // any other characters result in a percent+character
+ str2.push_back( c );
+ str2.push_back( cc );
+ i++;
+ }
+
+ } else {
+ str2.push_back( c );
+ }
+ i++;
+ }
+
+ return str2;
+}
+
+// process escapes like nginx
+const std::string FormatOps::parseNginxEscapes( const std::string& string )
+{
+ int i = 0,
+ max = string.size()-1;
+ char c, cc;
+ std::string str = "";
+ // parse once
+ while (true) {
+ if ( i >= max ) {
+ // no need to check the final char
+ if ( i == max ) {
+ str.push_back( string.at( i ) );
+ }
+ break;
+ }
+ c = string.at( i );
+ cc = string.at( i+1 );
+ if ( c == '\\' ) {
+ // just the ones supported by nginx
+ if ( cc == '\\' || cc == '\'' || cc == '"' ) {
+ str.push_back( cc );
+ i++;
+ } else if ( cc == 'n' ) {
+ str.push_back( '\n' );
+ i++;
+ } else if ( cc == 'r' ) {
+ // not supported
+ throw LogFormatException( "LogDoctor doesn't support the usage of the Carriage Return: '\\r'." );
+ } else if ( cc == 't' ) {
+ str.push_back( '\t' );
+ i++;
+ } else {
+ // not a control-character, resulting in a backslash+character
+ str.push_back( c );
+ str.push_back( cc );
+ i++;
+ }
+ } else {
+ str.push_back( c );
+ }
+ i++;
+ }
+
+ return str;
+}
+
+// find where the field ends
+const size_t FormatOps::findNginxFieldEnd( const std::string& string, const int& start )
+{
+ size_t stop=start;
+ const int max=string.size()-1;
+ if ( start < max ) { // if start equals max there's no need to loop
+ for ( int i=start; i<=max; i++ ) {
+ const char& c = string.at( i );
+ if ( StringOps::isAlnum( c ) || c == '_' ) {
+ stop = i;
+ } else {
+ break;
+ }
+ }
+ }
+ return stop;
+}
+
+// check the given format string for unwanted characters
+void FormatOps::checkIisString( const std::string& string )
+{
+ for ( const char& chr : string ) {
+ if ( !(StringOps::isAlnum( chr ) || chr == ' ' || chr == '-' || chr == ',' || chr == ':' || chr == '(' || chr == ')' || chr == '[' || chr == ']') ) {
+ // unwanted character
+ const std::string c( 1, chr );
+ throw LogFormatException( "Unexpected character found: "+c );
+ }
+ }
+}
+
+
+
+const FormatOps::LogsFormat FormatOps::processApacheFormatString( const std::string& f_str )
+{
+ const auto &f_map = this->APACHE_ALF;
+ const auto &f_map_v = this->APACHE_ALF_V;
+
+ std::string initial="", final="";
+ std::vector separators, fields;
+ // parse the string to convert keyargs in craplog's fields format
+ int n_fld=0;
+ size_t start, stop=0, aux, aux_start, aux_stop;
+ const size_t max=f_str.size()-1;
+ std::string aux_fld, aux_fld_v, cur_fld, cur_sep;
+ // find and convert any field
+ while (true) {
+ // start after the last found field
+ start = stop;
+ cur_sep = "";
+ if ( cur_fld == "date_time_ncsa" ) {
+ // NCAS time format is always enclosed inside brackets
+ cur_sep += "]";
+ }
+ while (true) {
+ // loop until a valid field is found (doens't matter if considered or not)
+ while (true) {
+ // hunt the next field
+ aux = f_str.find( '%', stop );
+ // check if false positive
+ if ( aux == max ) {
+ // invalid, can't end with a single '%'
+ throw LogFormatException( "Invalid format string: ending with a single '%'." );
+
+ } else if ( aux != std::string::npos ) {
+ // apache only escapes a format field using the double percent sign
+ // backslashes are valid for control-characters only, or get reduced
+ // single percent-signs are considered invalid
+ const char c = f_str.at(aux+1);
+ if ( c == ',' || c == '{' || c == '<' || c == '>' ) {
+ // in the first case: status code(s) may follow, or may not
+ // in the second case: a composed format code may follow
+ // in the third and fourth cases: expressing a status code field
+ // in any case is considered valid
+ ;
+ } else if ( c == '%' ) {
+ // the percent sign character, will be used as separator, skip
+ stop = aux + 2;
+ continue;
+ } else if ( ! StringOps::isAlnum( c ) ) {
+ // invalid, there must be a field code, a status code or a percent sign after a '%'
+ const std::string chr( 1, c );
+ throw LogFormatException( "Invalid format: there must be a valid format code, a status code or a percent sign character after a '%'.\nFound: '%"+chr+"'." );
+ }
+ }
+ break;
+ }
+
+ if ( aux == std::string::npos ) {
+ // no more fields, append the last section as final separator
+ cur_sep += f_str.substr( start );
+ n_fld = -1;
+ break;
+ }
+
+ // append the current separator
+ cur_sep += f_str.substr( start, aux-start );
+ aux ++;
+
+ char c = f_str.at( aux );
+ // remove the per-status directives (if any)
+ if ( StringOps::isNumeric( c )
+ || c == ',' ) {
+ // per-status, not important for LogDoctor
+ size_t aux_aux = aux+1;
+ while (true) {
+ if ( aux_aux > max ) {
+ break;
+ }
+ c = f_str.at( aux_aux );
+ if ( StringOps::isNumeric( c )
+ || c == ',' ) {
+ // skip these chars
+ aux_aux ++;
+ continue;
+ } else {
+ // stop
+ aux = aux_aux;
+ break;
+ }
+ }
+ }
+
+ c = f_str.at( aux );
+ // define if normal or composed
+ if ( c == '{' ) {
+ // composed
+ aux_start = aux + 1;
+ aux = f_str.find( '}', aux_start );
+ if ( aux == std::string::npos ) {
+ // closer bracket not found, resulting in an invalid field
+ throw LogFormatException( "Invalid format code, no closing bracket found: '%{'." );
+ }
+ aux_stop = aux + 2;
+ // get the varname(s)
+ aux_fld = f_str.substr(
+ aux_start,
+ aux - aux_start );
+ // get the module
+ aux_fld_v = f_str.at( aux+1 );
+ if ( aux_fld_v == "^" ) {
+ aux_stop += 2;
+ aux_fld_v = f_str.substr( aux+1, 3 );
+ }
+ if ( f_map_v.find( aux_fld_v ) == f_map_v.end() ) {
+ // invalid module, abort
+ throw LogFormatException( "Invalid format code found: '%{...}"+aux_fld_v+"'." );
+ } else {
+ // module is valud
+ const auto &aux_map = f_map_v.at( aux_fld_v );
+ if ( aux_map.size() == 0 ) {
+ // module not considered and always giving out something, even if invalid varname is passed
+ fields.push_back( "NONE" );
+ separators.push_back( this->parseApacheEscapes( cur_sep ) );
+ cur_sep = "";
+
+ } else if ( aux_fld_v == "p" || aux_fld_v == "P" || aux_fld_v == "T" ) {
+ // still not considered (except 'T'), but invalid fields get used as text
+ // field concatenation not allowed, whole content used as varname
+ if ( aux_map.find( aux_fld ) != aux_map.end() ) {
+ // valid varname
+ fields.push_back( aux_map.at( aux_fld ) );
+ } else {
+ // invalid varname, use as text
+ fields.push_back( "NONE" );
+ cur_sep += aux_fld;
+ }
+ separators.push_back( this->parseApacheEscapes( cur_sep ) );
+ cur_sep = "";
+
+ } else if ( aux_fld_v == "a" || aux_fld_v == "h" ) {
+ // client, in any case
+ fields.push_back( "client" );
+ separators.push_back( this->parseApacheEscapes( cur_sep ) );
+ cur_sep = "";
+
+ } else if ( aux_fld_v == "i" ) {
+ // always giving a result, may the varname be valid or not ('-' if invalid)
+ // field concatenation not allowed, the entire content is used as varname
+ if ( aux_map.find( aux_fld ) != aux_map.end() ) {
+ fields.push_back( aux_map.at( aux_fld ) );
+ } else {
+ fields.push_back( "NONE" );
+ }
+ separators.push_back( this->parseApacheEscapes( cur_sep ) );
+ cur_sep = "";
+
+ } else /*if ( aux_fld_v == "t" )*/ {
+ // only 't' remaining
+ size_t aux_aux = aux_fld.find( '%' );
+ if ( aux_aux == std::string::npos ) {
+ // no concatenation, only valid fields used, anything else used as text
+ // whole content used as varname
+ if ( aux_map.find( aux_fld ) != aux_map.end() ) {
+ // valid
+ fields.push_back( aux_map.at( aux_fld ) );
+ separators.push_back( this->parseApacheEscapes( cur_sep, true ) );
+ cur_sep = "";
+ } else {
+ // invalid, append to current separator and restart hunting
+ cur_sep += aux_fld;
+ }
+
+ } else {
+ // concatenation allowed, only strftime() value used as fields, everything else treated as text
+ size_t aux_aux_start,
+ aux_aux_stop = 0;
+ std::string aux_aux_fld;
+ while (true) {
+ // loop inside the composed field
+ aux_aux_start = aux_aux_stop;
+ while (true) {
+ // hunt the next field
+ aux_aux = aux_fld.find( '%', aux_aux_stop );
+ // check if false positive
+ if ( aux_aux != std::string::npos ) {
+ // same escape rules as before, but single percent-signs are considered valid and treated as text
+ const char c_ = aux_fld.at( aux_aux+1 );
+ if ( c_ == '%' || c_ == 'n' || c_ == 't' ) {
+ // control characters, will be used as separator, skip
+ aux_aux_stop = aux_aux + 2;
+ continue;
+ }
+ }
+ break;
+ }
+
+ if ( aux_aux == std::string::npos ) {
+ // no more fields, append the last section as separator
+ cur_sep += aux_fld.substr( aux_aux_start );
+ break;
+ }
+
+ // append the current separator
+ cur_sep += aux_fld.substr( aux_aux_start, aux_aux-aux_aux_start );
+ // and get the possible field
+ aux_aux_fld = aux_fld.substr( aux_aux, 2 );
+ aux_aux_stop = aux_aux+2;
+ // check if the field is valid
+ if ( aux_map.find( aux_aux_fld ) != aux_map.end() ) {
+ // valid, append
+ cur_fld = aux_map.at( aux_aux_fld );
+ fields.push_back( cur_fld );
+ // append to separators list
+ separators.push_back( this->parseApacheEscapes( cur_sep, true ) );
+ cur_sep = "";
+
+ } else {
+ // invalid, append as separator and keep hunting
+ cur_sep += aux_aux_fld;
+ }
+ }
+ }
+ }
+ // items already appended as needed, next main hunting loop round
+ start = stop = aux_stop; // re-starting after the previously found module
+ continue;
+ }
+
+ } else {
+ // normal
+ aux_fld = c;
+ aux_stop = aux+1;
+ if ( aux_fld == ">" || aux_fld == "<" ) {
+ aux_fld += f_str.at( aux+1 );
+ aux_stop ++;
+ }
+ // check if the module is valid
+ if ( f_map.find( aux_fld ) != f_map.end() ) {
+ // valid
+ cur_fld = f_map.at( aux_fld );
+ if ( cur_fld == "date_time_ncsa" ) {
+ // apache's NCSA time format is always enclosed inside brackets
+ cur_sep += "[";
+ }
+ stop = aux_stop;
+ break;
+ } else {
+ // invalid format field, abort
+ throw LogFormatException( "Invalid format code found: '%"+aux_fld+"'." );
+ }
+ }
+ }
+ // outside hunting loop
+
+ if ( n_fld < 0 ) {
+ // final reached, stop looping
+ final = this->parseApacheEscapes( cur_sep );
+ break;
+
+ } else if ( n_fld == 0 ) {
+ // first field found, assign the separator as the initial one
+ initial = this->parseApacheEscapes( cur_sep );
+
+ } else {
+ // append to separators list
+ separators.push_back( this->parseApacheEscapes( cur_sep ) );
+ }
+
+ // append the field
+ fields.push_back( cur_fld );
+ n_fld++;
+ }
+
+ return FormatOps::LogsFormat{
+ .string = f_str,
+ .initial = initial,
+ .final = final,
+ .separators = separators,
+ .fields = fields,
+ .new_lines = this->countNewLines( initial, final, separators )
+ };
+
+}
+// sample
+const QString FormatOps::getApacheLogSample( const LogsFormat& log_format )
+{
+ QString sample = "";
+ const std::unordered_map& map = this->APACHE_ALF_SAMPLES;
+
+ // append the initial characters
+ sample += QString::fromStdString( log_format.initial );
+ for ( int i=0; i 0 ) {
+ sample += map.at( log_format.fields.back() );
+ }
+ // and the final characters
+ sample += QString::fromStdString( log_format.final );
+ return sample;
+}
+
+
+
+const FormatOps::LogsFormat FormatOps::processNginxFormatString( const std::string& f_str )
+{
+ const auto& f_map = this->NGINX_ALF;
+
+ std::string initial="", final="";
+ std::vector separators, fields;
+ // parse the string to convert keyargs in craplog's fields format
+ bool finished = false;
+ size_t start, aux, stop=0;
+ const size_t max=f_str.size()-1;
+ std::string cur_fld, cur_sep;
+ // find and convert any field
+ while (true) {
+ // start after the last found field
+ start = stop;
+ // find the next field
+ aux = f_str.find( '$', start );
+ if ( aux == std::string::npos ) {
+ // not found, append as final and stop searching
+ final = this->parseNginxEscapes( f_str.substr( start ) );
+ break;
+ }
+ aux ++;
+ // find the end of the current field
+ stop = this->findNginxFieldEnd( f_str, aux ) + 1;
+ if ( stop == max ) {
+ // this is the last field, and ther's no final separator
+ finished = true;
+ }
+
+ cur_sep = f_str.substr( start, aux-start-1 );
+ cur_fld = f_str.substr( aux, stop-aux );
+
+ // fixes for varnames
+ if ( StringOps::startsWith( cur_fld, "cookie_" ) ) {
+ cur_fld = "cookie_";
+ } else if ( StringOps::startsWith( cur_fld, "http_" )
+ && cur_fld != "http_user_agent" && cur_fld != "http_referer" ) {
+ cur_fld = "http_";
+ } else if ( StringOps::startsWith( cur_fld, "arg_" ) ) {
+ cur_fld = "arg_";
+ } else if ( StringOps::startsWith( cur_fld, "sent_http_" ) ) {
+ cur_fld = "sent_http_";
+ } else if ( StringOps::startsWith( cur_fld, "upstream_cookie_" ) ) {
+ cur_fld = "upstream_cookie_";
+ } else if ( StringOps::startsWith( cur_fld, "upstream_http_" ) ) {
+ cur_fld = "upstream_http_";
+ }
+
+ // check if the field is valid
+ if ( f_map.find( cur_fld ) != f_map.end() ) {
+ // valid, append
+ if ( start == 0 ) {
+ initial = this->parseNginxEscapes( cur_sep );
+ } else {
+ separators.push_back( this->parseNginxEscapes( cur_sep ) );
+ }
+ fields.push_back( f_map.at( cur_fld ) );
+ if ( finished ) {
+ // this was the last field
+ break;
+ }
+ } else {
+ // invalid, abort
+ throw LogFormatException( "Invalid format code found: '$"+cur_fld+"'." );
+ }
+ }
+
+ return FormatOps::LogsFormat{
+ .string = f_str,
+ .initial = initial,
+ .final = final,
+ .separators = separators,
+ .fields = fields,
+ .new_lines = this->countNewLines( initial, final, separators )
+ };
+}
+// sample
+const QString FormatOps::getNginxLogSample( const LogsFormat& log_format )
+{
+ QString sample = "";
+ const std::unordered_map& map = this->NGINX_ALF_SAMPLES;
+
+ // append the initial characters
+ sample += QString::fromStdString( log_format.initial );
+ for ( int i=0; i 0 ) {
+ sample += map.at( log_format.fields.back() );
+ }
+ // and the final characters
+ sample += QString::fromStdString( log_format.final );
+ return sample;
+}
+
+
+
+const FormatOps::LogsFormat FormatOps::processIisFormatString( const std::string& f_str, const int& l_mod )
+{
+ this->checkIisString( f_str );
+ std::string initial="", final="";
+ std::vector separators, fields;
+ switch ( l_mod ) {
+ case 2:
+ // IIS logging module
+ final = ",";
+ separators = {", ",", ",", ",", ",", ",", ",", ",", ",", ",", ",", ",", ",", ",", "};
+ fields = {"client","NONE","date_time_MDYY","date_time_utc_t","NONE","NONE","NONE","time_taken_ms","bytes_received","bytes_sent","response_code","NONE","request_method","request_uri","request_query"};
+ break;
+ case 1:
+ // NCSA logging module
+ separators = {" "," "," [","] \"","\" "," "};
+ fields = {"client","NONE","NONE","date_time_ncsa","request_full","response_code","bytes_sent"};
+ break;
+ case 0:
+ // W3C logging module
+ if ( f_str.size() > 0 ) {
+ bool finished = false;
+ size_t start, stop=0;
+ const size_t max=f_str.size()-1;
+ std::string cur_fld;
+ const std::string cur_sep = " ";
+ const auto &f_map = this->IIS_ALF;
+ // parse the string to convert keyargs in craplog's fields format
+ while (true) {
+ // start after the last found separator
+ start = stop;
+ // find the next separator, which is always a single whitespace, in this case
+ stop = f_str.find( cur_sep, start );
+ if ( stop == std::string::npos ) {
+ // not found, this is the last field
+ stop = max+1;
+ finished = true;
+ }
+
+ // set the current field
+ cur_fld = f_str.substr( start, stop-start );
+ // step over the separator
+ stop++;
+
+ // check if the module is valid
+ if ( f_map.find( cur_fld ) != f_map.end() ) {
+ // valid, append
+ fields.push_back( f_map.at( cur_fld ) );
+ if ( ! finished ) {
+ separators.push_back( cur_sep );
+ } else {
+ // this was the last field
+ break;
+ }
+ } else {
+ // invalid, abort
+ throw LogFormatException( "Invalid format code found: '"+cur_fld+"'." );
+ }
+ }
+ }
+ // outside search loop, killing the switch
+ break;
+
+ default:
+ // shouldn't be here
+ throw LogFormatException( "Unexpected LogModule for IIS: "+std::to_string( l_mod ) );
+ }
+
+ return FormatOps::LogsFormat{
+ .string = f_str,
+ .initial = initial,
+ .final = final,
+ .separators = separators,
+ .fields = fields,
+ .new_lines = 0
+ };
+}
+// sample
+const QString FormatOps::getIisLogSample( const LogsFormat& log_format )
+{
+ QString sample = "";
+ const std::unordered_map& map = this->IIS_ALF_SAMPLES;
+
+ // append the initial characters
+ sample += QString::fromStdString( log_format.initial );
+ for ( int i=0; i 0 ) {
+ sample += map.at( log_format.fields.back() );
+ }
+ // and the final characters
+ sample += QString::fromStdString( log_format.final );
+ return sample;
+}
diff --git a/logdoctor/modules/craplog/modules/formats.h b/logdoctor/modules/craplog/modules/formats.h
new file mode 100644
index 00000000..1cb16625
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/formats.h
@@ -0,0 +1,490 @@
+#ifndef FORMATS_H
+#define FORMATS_H
+
+#include
+
+#include
+#include
+#include
+
+
+//! FormatOps
+/*!
+ Operations for the logs formats
+*/
+class FormatOps
+{
+public:
+ FormatOps();
+
+ //! Structure which holds informations about a log format
+ struct LogsFormat {
+ std::string string; //!< The logs format string
+ std::string initial; //!< The initial separator
+ std::string final; //!< The final separator
+ std::vector separators; //!< The separators in the middle
+ std::vector fields; //!< The fields
+ int new_lines; //!< The number of new lines
+ };
+
+
+ //! Processes the given string to extrapolate the format for Apache2
+ /*!
+ \param format_string The format string to process
+ \return The logs format
+ \throw LogFormatException
+ \see LogsFormat
+ */
+ const LogsFormat processApacheFormatString( const std::string& format_string );
+
+ //! Processes the given string to extrapolate the format for Nginx
+ /*!
+ \param format_string The format string to process
+ \return The logs format
+ \throw LogFormatException
+ \see LogsFormat
+ */
+ const LogsFormat processNginxFormatString( const std::string& format_string );
+
+ //! Processes the given string to extrapolate the format for the IIS
+ /*!
+ \param format_string The format string to process
+ \param log_module The ID of the log module to use
+ \return The logs format
+ \throw LogFormatException
+ \see LogsFormat
+ */
+ const LogsFormat processIisFormatString( const std::string& format_string, const int& log_module );
+
+
+ /////////////////
+ //// SAMPLES ////
+
+ //! Returns a log line sample based on the given format
+ /*!
+ \param log_format The logs format to use to build the sample
+ \return The sample line
+ \see LogsFormat, Craplog::getLogsFormatSample()
+ */
+ const QString getApacheLogSample( const LogsFormat& log_format );
+
+ //! Returns a log line sample based on the given format
+ /*!
+ \param log_format The logs format to use to build the sample
+ \return The sample line
+ \see LogsFormat, Craplog::getLogsFormatSample()
+ */
+ const QString getNginxLogSample( const LogsFormat& log_format );
+
+ //! Returns a log line sample based on the given format
+ /*!
+ \param log_format The logs format to use to build the sample
+ \return The sample line
+ \see LogsFormat, Craplog::getLogsFormatSample()
+ */
+ const QString getIisLogSample( const LogsFormat& log_format );
+
+
+private:
+
+ //! Parses the escapes (backslashes) and returns the resulting string
+ /*!
+ Used to obtain the same result as on Apache2
+ \param string The string to parse
+ \param strftime Whether to apply the strftime special rule when parsing or not
+ \return The resulting string
+ \throw LogFormatException
+ \see processApacheFormatString()
+ */
+ const std::string parseApacheEscapes( const std::string& string, const bool& strftime=false );
+
+ //! Parses the escapes (backslashes) and returns the resulting string
+ /*!
+ Used to obtain the same result as on Nginx
+ \param string The string to parse
+ \return The resulting string
+ \throw LogFormatException
+ \see processNginxFormatString()
+ */
+ const std::string parseNginxEscapes( const std::string& string );
+
+ //! Conuts how many new lines are there in the format
+ /*!
+ Used to join log lines which refer to the same log line
+ \param initial The initial separator
+ \param final The final separator
+ \param separators The separators in the middle
+ \return The number of new lines in a single log line
+ \see LogsFormat, processApacheFormatString(), processNginxFormatString()
+ */
+ const int countNewLines( const std::string& initial, const std::string& final, const std::vector& separators );
+
+ //! Finds the end of a Nginx log field
+ /*!
+ \param string The format string
+ \param start The starting point of the field in the string
+ \return The ending poin of the field in the string
+ \see processNginxFormatString()
+ */
+ const size_t findNginxFieldEnd( const std::string& string, const int& start );
+
+ //! Checks whether the format string contains invalid characters or not
+ /*!
+ \param string The format string
+ \throw LogFormatException
+ \see processIisFormatString
+ */
+ void checkIisString( const std::string& string );
+
+
+ /////////////////
+ //// APACHE2 ////
+
+ //!< Access logs fields formats
+ const std::unordered_map APACHE_ALF = {
+ {"h", "client"},
+ {"t", "date_time_ncsa"},
+ {"r", "request_full"},
+ {"H", "request_protocol"},
+ {"m", "request_method"},
+ {"U", "request_uri"},
+ {"q", "request_query"},
+ {"s", "response_code"},
+ {"s", "response_code"},
+ {"O", "bytes_sent"},
+ {"I", "bytes_received"},
+ {"T", "time_taken_s"},
+ {"D", "time_taken_ms"},
+ // not in use, will be discarded
+ {"a", "NONE"},
+ {"A", "NONE"},
+ {"b", "NONE"},
+ {"B", "NONE"},
+ {"e", "NONE"},
+ {"f", "NONE"},
+ {"k", "NONE"},
+ {"l", "NONE"},
+ {"L", "NONE"},
+ {"p", "NONE"},
+ {"P", "NONE"},
+ {"R", "NONE"},
+ {"S", "NONE"},
+ {"u", "NONE"},
+ {"v", "NONE"},
+ {"V", "NONE"},
+ {"X", "NONE"} };
+
+ //!< Composed access logs fields formats
+ const std::unordered_map> APACHE_ALF_V = {
+ {"a", { {"c", "client"}}},
+ {"h", { {"c", "client"}}},
+ {"i", { {"Cookie", "cookie"},
+ {"User-agent", "user_agent"},
+ {"Referer", "referer"}}},
+ {"t", { /* not actually date-time fields but text, used as separators
+ {"\%n", "NONE"}, // NEW LINE
+ {"\%t", "NONE"}, // HORIZONTAL TAB
+ {"\%%", "NONE"},*/
+ {"sec", "date_time_epoch_s"},
+ {"msec", "date_time_epoch_ms"},
+ {"usec", "date_time_epoch_us"},
+ {"msec_frac", "NONE"},
+ {"usec_frac", "NONE"},
+ {"\%a", "NONE"},
+ {"\%A", "NONE"},
+ {"\%b", "date_time_month_str"},
+ {"\%B", "date_time_month_str"},
+ {"\%c", "date_time_mcs"},
+ {"\%C", "NONE"},
+ {"\%d", "date_time_day"},
+ {"\%D", "date_time_MMDDYY"},
+ {"\%e", "date_time_day"},
+ {"\%F", "date_time_YYYYMMDD"},
+ {"\%g", "NONE"},
+ {"\%G", "NONE"},
+ {"\%h", "date_time_month_str"},
+ {"\%H", "date_time_hour"},
+ {"\%I", "NONE"},
+ {"\%j", "NONE"},
+ {"\%m", "date_time_month"},
+ {"\%M", "date_time_minute"},
+ {"\%p", "NONE"},
+ {"\%r", "date_time_clock_12"},
+ {"\%R", "date_time_clock_short"},
+ {"\%S", "date_time_second"},
+ {"\%T", "date_time_clock_24"},
+ {"\%u", "NONE"},
+ {"\%U", "NONE"},
+ {"\%V", "NONE"},
+ {"\%w", "NONE"},
+ {"\%W", "NONE"},
+ {"\%x", "date_time_MMDDYY"},
+ {"\%X", "date_time_clock_24"},
+ {"\%y", "date_time_year_short"},
+ {"\%Y", "date_time_year"},
+ {"\%z", "NONE"},
+ {"\%Z", "NONE"}}},
+ {"T", { {"s", "time_taken_s"},
+ {"ms", "time_taken_ms"},
+ {"us", "time_taken_us"}}},
+ // composed not in use
+ {"C", {}},
+ {"e", {}},
+ {"L", {}},
+ {"n", {}},
+ {"o", {}},
+ {"p", {{"canonical", "NONE"},
+ {"local", "NONE"},
+ {"remote", "NONE"}}},
+ {"P", {{"pid", "NONE"},
+ {"tid", "NONE"},
+ {"hextid", "NONE"}}},
+ {"^ti", {}},
+ {"^to", {}} };
+
+ // Access logs fields formats samples
+ const std::unordered_map APACHE_ALF_SAMPLES = {
+ {"NONE", "DISCARDED "},
+ {"date_time_epoch_s", "946771199 "},
+ {"date_time_epoch_ms", "946771199000 "},
+ {"date_time_epoch_us", "946771199000000 "},
+ {"date_time_ncsa", "01/Jan/2000:23:59:59 +0000 "},
+ {"date_time_mcs", "Sat Jan 01 23:59:59 2000 "},
+ {"date_time_YYYYMMDD", "2000-01-01 "},
+ {"date_time_MMDDYY", "01/01/00 "},
+ {"date_time_year", "2000 "},
+ {"date_time_year_short", "00 "},
+ {"date_time_month_str", "January "},
+ {"date_time_month", "01 "},
+ {"date_time_day", "01 "},
+ {"date_time_clock_12", "11:59:59 pm "},
+ {"date_time_clock_24", "23:59:59 "},
+ {"date_time_clock_short", "23:59 "},
+ {"date_time_hour", "23 "},
+ {"date_time_minute", "59 "},
+ {"date_time_second", "59 "},
+ {"request_full", "GET /index.php?query=x HTTP/1.1 "},
+ {"request_protocol", "HTTP/1.1 "},
+ {"request_method", "GET "},
+ {"request_uri", "/index.php "},
+ {"request_query", "query=x "},
+ {"response_code", "404 "},
+ {"bytes_sent", "1234 "},
+ {"bytes_received", "123 "},
+ {"time_taken_s", "1 "},
+ {"time_taken_ms", "1000 "},
+ {"time_taken_us", "1000000 "},
+ {"referer", "http://www.referrer.site "},
+ {"cookie", "aCookie=abc123 "},
+ {"user_agent", "UserAgent/3.0 (Details stuff) Info/123 "},
+ {"client", "192.168.1.123 "} };
+
+
+ ///////////////
+ //// NGINX ////
+
+ //!< Access logs fields formats
+ const std::unordered_map NGINX_ALF = {
+ {"remote_addr", "client"},
+ {"realip_remote_addr", "client"},
+ {"time_local", "date_time_ncsa"},
+ {"time_iso8601", "date_time_iso"},
+ {"date_gmt", "date_time_gmt"},
+ {"msec", "date_time_epoch_s.ms"},
+ {"request", "request_full"},
+ {"server_protocol", "request_protocol"},
+ {"request_method", "request_method"},
+ {"request_uri", "request_uri_query"},
+ {"uri", "request_uri"},
+ {"query_string", "request_query"},
+ {"status", "response_code"},
+ {"bytes_sent", "bytes_sent"},
+ {"request_length", "bytes_received"},
+ {"request_time", "time_taken_s.ms"},
+ {"http_referer", "referer"},
+ {"cookie_", "cookie"},
+ {"http_user_agent", "user_agent"},
+ // not in use, will be discarded
+ {"ancient_browser", "NONE"},
+ {"arg_", "NONE"},
+ {"args", "NONE"},
+ {"binary_remote_addr", "NONE"},
+ {"body_bytes_sent", "NONE"},
+ {"connection", "NONE"},
+ {"connection_requests", "NONE"},
+ {"connections_active", "NONE"},
+ {"connections_reading", "NONE"},
+ {"connections_waiting", "NONE"},
+ {"connections_writing", "NONE"},
+ {"content_length", "NONE"},
+ {"content_type", "NONE"},
+ {"date_local", "NONE"},
+ {"document_root", "NONE"},
+ {"document_uri", "NONE"},
+ {"fastcgi_path_info", "NONE"},
+ {"fastcgi_script_name", "NONE"},
+ {"geoip_area_code", "NONE"},
+ {"geoip_city", "NONE"},
+ {"geoip_city_continent_code", "NONE"},
+ {"geoip_city_country_code", "NONE"},
+ {"geoip_city_country_code3", "NONE"},
+ {"geoip_city_country_name", "NONE"},
+ {"geoip_country_code", "NONE"},
+ {"geoip_country_code3", "NONE"},
+ {"geoip_country_name", "NONE"},
+ {"geoip_dma_code", "NONE"},
+ {"geoip_latitude", "NONE"},
+ {"geoip_longitude", "NONE"},
+ {"geoip_org", "NONE"},
+ {"geoip_postal_code", "NONE"},
+ {"geoip_region", "NONE"},
+ {"geoip_region_name", "NONE"},
+ {"gzip_ratio", "NONE"},
+ {"host", "NONE"},
+ {"hostname", "NONE"},
+ {"http2", "NONE"},
+ {"http_", "NONE"},
+ {"https", "NONE"},
+ {"invalid_referer", "NONE"},
+ {"is_args", "NONE"},
+ {"limit_rate", "NONE"},
+ {"memcached_key", "NONE"},
+ {"modern_browser", "NONE"},
+ {"msie", "NONE"},
+ {"nginx_version", "NONE"},
+ {"pid", "NONE"},
+ {"pipe", "NONE"},
+ {"proxy_add_x_forwarded_for", "NONE"},
+ {"proxy_host", "NONE"},
+ {"proxy_port", "NONE"},
+ {"proxy_protocol_addr", "NONE"},
+ {"proxy_protocol_port", "NONE"},
+ {"realip_remote_port", "NONE"},
+ {"realpath_root", "NONE"},
+ {"remote_port", "NONE"},
+ {"remote_user", "NONE"},
+ {"request_body", "NONE"},
+ {"request_body_file", "NONE"},
+ {"request_completion", "NONE"},
+ {"request_filename", "NONE"},
+ {"request_id", "NONE"},
+ {"scheme", "NONE"},
+ {"secure_link", "NONE"},
+ {"secure_link_expires", "NONE"},
+ {"sent_http_", "NONE"},
+ {"server_addr", "NONE"},
+ {"server_name", "NONE"},
+ {"server_port", "NONE"},
+ {"session_log_binary_id", "NONE"},
+ {"session_log_id", "NONE"},
+ {"slice_range", "NONE"},
+ {"spdy", "NONE"},
+ {"spdy_request_priority", "NONE"},
+ {"ssl_cipher", "NONE"},
+ {"ssl_client_cert", "NONE"},
+ {"ssl_client_fingerprint", "NONE"},
+ {"ssl_client_i_dn", "NONE"},
+ {"ssl_client_raw_cert", "NONE"},
+ {"ssl_client_s_dn", "NONE"},
+ {"ssl_client_serial", "NONE"},
+ {"ssl_client_verify", "NONE"},
+ {"ssl_protocol", "NONE"},
+ {"ssl_server_name", "NONE"},
+ {"ssl_session_id", "NONE"},
+ {"ssl_session_reused", "NONE"},
+ {"tcpinfo_rtt,", "NONE"},
+ {"tcpinfo_rttvar,", "NONE"},
+ {"tcpinfo_snd_cwnd,", "NONE"},
+ {"tcpinfo_rcv_space", "NONE"},
+ {"uid_got", "NONE"},
+ {"uid_reset", "NONE"},
+ {"uid_set", "NONE"},
+ {"upstream_addr", "NONE"},
+ {"upstream_cache_status", "NONE"},
+ {"upstream_connect_time", "NONE"},
+ {"upstream_cookie_", "NONE"},
+ {"upstream_header_time", "NONE"},
+ {"upstream_http_", "NONE"},
+ {"upstream_response_length", "NONE"},
+ {"upstream_response_time", "NONE"},
+ {"upstream_status", "NONE"} };
+
+ // Access logs fields formats samples
+ const std::unordered_map NGINX_ALF_SAMPLES = {
+ {"NONE", "DISCARDED "},
+ {"date_time_epoch_s.ms", "946771199.000 "},
+ {"date_time_ncsa", "01/Jan/2000:23:59:59 +0000 "},
+ {"date_time_iso", "2000-01-01T23:59:59+00:00 "},
+ {"date_time_gmt", "Saturday, 01-Jan-2000 23:59:59 UTC "},
+ {"request_full", "GET /index.php?query=x HTTP/1.1 "},
+ {"request_protocol", "HTTP/1.1 "},
+ {"request_method", "GET "},
+ {"request_uri_query", "/index.php?query=x "},
+ {"request_uri", "/index.php "},
+ {"request_query", "query=x "},
+ {"response_code", "404 "},
+ {"bytes_sent", "1234 "},
+ {"bytes_received", "123 "},
+ {"time_taken_s.ms", "1.000 "},
+ {"referer", "http://www.referrer.site "},
+ {"cookie", "aCookie=abc123 "},
+ {"user_agent", "UserAgent/3.0 (Details stuff) Info/123 "},
+ {"client", "192.168.1.123 "} };
+
+
+
+ /////////////
+ //// IIS ////
+
+ //!< Access logs fields formats
+ const std::unordered_map IIS_ALF = {
+ {"date", "date_time_utc_d"},
+ {"time", "date_time_utc_t"},
+ {"cs-version", "request_protocol"},
+ {"cs-method", "request_method"},
+ {"cs-uri-stem", "request_uri"},
+ {"cs-uri-query", "request_query"},
+ {"sc-status", "response_code"},
+ {"sc-bytes", "bytes_sent"},
+ {"cs-bytes", "bytes_received"},
+ {"time-taken", "time_taken_ms"},
+ {"cs(Referer)", "referer"},
+ {"cs(Cookie)", "cookie"},
+ {"cs(User-Agent)", "user_agent"},
+ {"c-ip", "client"},
+ // not in use, will be discarded
+ {"s-sitename", "NONE"},
+ {"s-computername", "NONE"},
+ {"s-ip", "NONE"},
+ {"s-port", "NONE"},
+ {"cs-username", "NONE"},
+ {"cs-host", "NONE"},
+ {"sc-substatus", "NONE"},
+ {"sc-win32-status", "NONE"},
+ {"streamid", "NONE"} };
+
+ // Access logs fields formats samples
+ const std::unordered_map IIS_ALF_SAMPLES = {
+ {"NONE", "DISCARDED "},
+ {"date_time_ncsa", "01/Jan/2000:23:59:59 +0000 "},
+ {"date_time_MDYY", "1/1/00 "},
+ {"date_time_utc_d", "2000-01-01 "},
+ {"date_time_utc_t", "23:59:59 "},
+ {"request_full", "GET /index.php?query=x HTTP/1.1 "},
+ {"request_protocol", "HTTP/1.1 "},
+ {"request_method", "GET "},
+ {"request_uri", "/index.php "},
+ {"request_query", "query=x "},
+ {"response_code", "404 "},
+ {"bytes_sent", "1234 "},
+ {"bytes_received", "123 "},
+ {"time_taken_ms", "1000 "},
+ {"referer", "http://www.referrer.site "},
+ {"cookie", "aCookie=abc123 "},
+ {"user_agent", "UserAgent/3.0+(Details+stuff)+Info/123 "},
+ {"client", "192.168.1.123 "} };
+
+};
+
+#endif // FORMATS_H
diff --git a/logdoctor/modules/craplog/modules/hash.cpp b/logdoctor/modules/craplog/modules/hash.cpp
new file mode 100644
index 00000000..0a7aaf7f
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/hash.cpp
@@ -0,0 +1,271 @@
+
+#include "hash.h"
+
+#include "utilities/gzip.h"
+#include "utilities/io.h"
+#include "utilities/vectors.h"
+
+#include "modules/dialogs.h"
+#include "modules/exceptions.h"
+#include "modules/craplog/modules/sha256.h"
+
+#include
+
+
+HashOps::HashOps()
+{
+
+}
+
+void HashOps::setDialogLevel( const int& new_level )
+{
+ this->dialog_level = new_level;
+}
+
+
+// reads the database holding the already used hashes
+bool HashOps::loadUsedHashesLists( const std::string& db_path )
+{
+ bool successful = true;
+ const QString db_name = QString::fromStdString( db_path.substr( db_path.find_last_of( '/' ) + 1 ) );
+
+ QSqlDatabase db;
+ if ( QSqlDatabase::contains("qt_sql_default_connection") ) {
+ db = QSqlDatabase::database("qt_sql_default_connection");
+ } else {
+ db = QSqlDatabase::addDatabase("QSQLITE");
+ }
+ db.setDatabaseName( QString::fromStdString( db_path ) );
+
+ if ( ! db.open() ) {
+ // error opening database
+ successful = false;
+ QString err_msg = "";
+ if ( this->dialog_level == 2 ) {
+ err_msg = db.lastError().text();
+ }
+ DialogSec::errDatabaseFailedOpening( db_name, err_msg );
+
+ } else {
+ QSqlQuery query = QSqlQuery( db );
+ for ( const auto& [wid,name] : this->ws_names ) {
+ if ( ! query.exec("SELECT hash FROM "+name+";") ) {
+ // error querying database
+ successful = false;
+ DialogSec::errDatabaseFailedExecuting( db_name, query.lastQuery(), query.lastError().text() );
+ break;
+ } else {
+ // iterate over results
+ while ( query.next() ) {
+ std::string hash = query.value(0).toString().toStdString();
+ if ( hash.size() != 64 ) {
+ // not a valid sha256 hash
+ continue;
+ }
+ this->hashes.at( wid ).push_back( hash );
+ }
+ }
+ if ( ! successful ) { break; }
+ }
+ }
+ db.close();
+ return successful;
+}
+
+
+// returns the hash
+std::string HashOps::digestFile( const std::string& file_path )
+{
+ std::string content;
+ try {
+ try {
+ // try reading as gzip compressed file
+ GZutils::readFile( file_path, content );
+
+ } catch ( const GenericException& ) {
+ // failed closing file pointer
+ throw;
+
+ } catch (...) {
+ // failed as gzip, try as text file
+ if ( content.size() > 0 ) {
+ content.clear();
+ }
+ IOutils::readFile( file_path, content );
+ }
+
+ // re-catched in craplog
+ } catch ( const GenericException& ) {
+ // failed closing gzip file pointer
+ throw GenericException( QString("%1:\n%2").arg(
+ DialogSec::tr("An error accured while reading the gzipped file"),
+ QString::fromStdString( file_path )
+ ).toStdString() );
+
+ } catch ( const std::ios_base::failure& ) {
+ // failed reading as text
+ throw GenericException( QString("%1:\n%2").arg(
+ DialogSec::tr("An error accured while reading the file"),
+ QString::fromStdString( file_path )
+ ).toStdString() );
+
+ } catch (...) {
+ // failed somehow
+ throw GenericException( QString("%1:\n%2").arg(
+ DialogSec::tr("Something failed while handling the file"),
+ QString::fromStdString( file_path )
+ ).toStdString() );
+ }
+
+ SHA256 sha;
+ sha.update( content );
+ content.clear();
+ uint8_t * digest = sha.digest();
+ // return the hex digest
+ return SHA256::toString(digest);
+}
+
+
+// check if the given hash is from a file which has been used already
+bool HashOps::hasBeenUsed( const std::string &file_hash, const int& web_server_id)
+{
+ bool found = false;
+ for ( const std::string &hash : this->hashes.at( web_server_id ) ) {
+ if ( file_hash == hash ) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+}
+
+
+// insert the given hash/es in the relative list
+bool HashOps::insertUsedHash( QSqlQuery& query, const QString& db_name, const std::string& hash, const int& web_server_id )
+{
+ bool successful = true;
+ try {
+ if( ! VecOps::contains( this->hashes.at( web_server_id ), hash ) ) {
+ this->hashes.at( web_server_id ).push_back( hash );
+ // insert tnto the database
+ QString stmt = QString("INSERT INTO %1 ( hash ) VALUES ( '%2' );")
+ .arg( this->ws_names.at(web_server_id), QString::fromStdString(hash).replace("'","''") );
+ if ( ! query.exec( stmt ) ) {
+ // error opening database
+ successful = false;
+ QString query_msg="", err_msg="";
+ if ( this->dialog_level > 0 ) {
+ query_msg = "query.exec()";
+ if ( this->dialog_level == 2 ) {
+ err_msg = query.lastError().text();
+ }
+ }
+ DialogSec::errDatabaseFailedExecuting( db_name, query_msg, err_msg );
+ }
+ }/* else {
+ // hash already stored
+ }*/
+ } catch (...) {
+ // failed to insert the hash
+ successful = false;
+ }
+ query.finish();
+ return successful;
+}
+
+
+bool HashOps::insertUsedHashes( const std::string& db_path, const std::vector &hashes, const int& web_server_id )
+{
+ bool proceed = true;
+
+ const QString db_name = QString::fromStdString( db_path.substr( db_path.find_last_of( '/' ) + 1 ) );
+ QSqlDatabase db;
+ if ( QSqlDatabase::contains("qt_sql_default_connection") ) {
+ db = QSqlDatabase::database("qt_sql_default_connection");
+ } else {
+ db = QSqlDatabase::addDatabase("QSQLITE");
+ }
+ db.setDatabaseName( QString::fromStdString( db_path ) );
+
+ if ( ! db.open() ) {
+ // error opening database
+ proceed = false;
+ QString err_msg = "";
+ if ( this->dialog_level == 2 ) {
+ err_msg = db.lastError().text();
+ }
+ DialogSec::errDatabaseFailedOpening( db_name, err_msg );
+
+ } else {
+ QSqlQuery query = QSqlQuery( db );
+ if ( ! db.transaction() ) {
+ // error opening database
+ proceed = false;
+ QString stmt_msg="", err_msg = "";
+ if ( this->dialog_level > 0 ) {
+ stmt_msg = "db.transaction()";
+ if ( this->dialog_level == 2 ) {
+ err_msg = db.lastError().text();
+ }
+ }
+ DialogSec::errDatabaseFailedExecuting( db_name, stmt_msg, err_msg );
+
+ } else {
+
+ try {
+ for ( const std::string& hash : hashes ) {
+ proceed = this->insertUsedHash( query, db_name, hash, web_server_id );
+ if ( ! proceed ) {
+ break;
+ }
+ }
+ query.finish();
+
+ if ( proceed ) {
+ // commit the transaction
+ if ( ! db.commit() ) {
+ // error opening database
+ proceed = false;
+ QString stmt_msg="", err_msg = "";
+ if ( this->dialog_level > 0 ) {
+ stmt_msg = "db.commit()";
+ if ( this->dialog_level == 2 ) {
+ err_msg= db.lastError().text();
+ }
+ }
+ DialogSec::errDatabaseFailedExecuting( db_name, stmt_msg, err_msg );
+ }
+ }
+ if ( ! proceed ) {
+ // rollback
+ throw (std::exception());
+ }
+
+ } catch (...) {
+ // wrongthing w3nt some.,.
+ proceed = false;
+ bool err_shown = false;
+ // rollback the transaction
+ if ( ! db.rollback() ) {
+ // error rolling back commits
+ QString stmt_msg="", err_msg = "";
+ if ( this->dialog_level > 0 ) {
+ stmt_msg = "db.rollback()";
+ if ( this->dialog_level == 2 ) {
+ err_msg = db.lastError().text();
+ }
+ }
+ DialogSec::errDatabaseFailedExecuting( db_name, stmt_msg, err_msg );
+ err_shown = true;
+ }
+ if ( ! err_shown ) {
+ // show a message
+ QString msg = DialogSec::tr("An error occured while working on the database\n\nAborting");
+ DialogSec::errGeneric( msg );
+ }
+ }
+ }
+ }
+ db.close();
+ return proceed;
+}
diff --git a/logdoctor/modules/craplog/modules/hash.h b/logdoctor/modules/craplog/modules/hash.h
new file mode 100644
index 00000000..1335c1a9
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/hash.h
@@ -0,0 +1,101 @@
+#ifndef HASH_H
+#define HASH_H
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+
+//! HashOps
+/*!
+ Operations for the hashes
+*/
+class HashOps
+{
+public:
+ HashOps();
+
+
+ //! Sets the new Dialogs level
+ void setDialogLevel( const int& new_level );
+
+ //! Retrieves the lists of hashes from the database file
+ /*!
+ \param db_path The path of the log files' Hashes database
+ \return Whether the operation has been successful or not
+ \see hashes
+ */
+ bool loadUsedHashesLists( const std::string& db_path );
+
+ //! Returns the hash resulting from the content of the given file
+ /*!
+ \param file_path The path of the file to process
+ \return The sha256 hash
+ \throw GenericException
+ \see SHA256
+ */
+ std::string digestFile( const std::string& file_path );
+
+ //! Checks if the given hash equals one which is already in the list
+ /*!
+ \param file_hash The sha256 hash to compare
+ \param web_server_id The ID of the Web Server which generated the file
+ \return Whether the hash is already in the list or not
+ \see hashes
+ */
+ bool hasBeenUsed( const std::string& file_hash, const int& web_server_id );
+
+ //! Inserts a hashe in the corresponding database table
+ /*!
+ \param db_query Query instance, already initialized
+ \param db_name The name of the database, eventually used by dialogs
+ \param hash The hash to insert
+ \param web_server_id The ID of the Web Server which generated the file
+ \return Whether the operation has been successful or not
+ \see insertUsedHashes()
+ */
+ bool insertUsedHash( QSqlQuery& query, const QString& db_name, const std::string& hash, const int& web_server_id );
+
+ //! Inserts multiple hashes in the corresponding database table
+ /*!
+ \param db_path The path of the Hashes database
+ \param hashes The list of hashes to insert
+ \param web_server_id The ID of the Web Server which generated the file
+ \return Whether the operation has been successful or not
+ \see insertUsedHash()
+ */
+ bool insertUsedHashes( const std::string& db_path, const std::vector& hashes, const int& web_server_id );
+
+private:
+
+ // Quantity of information to display throught dialogs
+ int dialog_level = 2;
+
+ // id constants
+ const int APACHE_ID = 11;
+ const int NGINX_ID = 12;
+ const int IIS_ID = 13;
+
+ // List of Web Servers names
+ const std::unordered_map ws_names = {
+ {this->APACHE_ID, "apache"},
+ {this->NGINX_ID, "nginx"},
+ {this->IIS_ID, "iis"} };
+
+
+ // Lists of used files' hashes
+ // { web_server_id : { hashes } }
+ std::unordered_map> hashes = {
+ {this->APACHE_ID, {}},
+ {this->NGINX_ID, {}},
+ {this->IIS_ID, {}}
+ };
+
+};
+
+#endif // HASH_H
diff --git a/logdoctor/modules/craplog/modules/logs.cpp b/logdoctor/modules/craplog/modules/logs.cpp
new file mode 100644
index 00000000..e628956f
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/logs.cpp
@@ -0,0 +1,465 @@
+
+#include "logs.h"
+
+#include "modules/exceptions.h"
+#include "modules/craplog/modules/datetime.h"
+
+#include "utilities/strings.h"
+
+
+LogOps::LogOps()
+{
+
+}
+
+
+LogOps::LogType LogOps::defineFileType( const std::vector& lines, const FormatOps::LogsFormat& format )
+{
+ if ( lines.size() == 0 ) {
+ // empty content
+ return LogOps::LogType::Failed;
+ }
+
+ int n_access=0, n_other=0;
+ LogOps::LogType log_type;
+
+ // real type assignment
+ log_type = this->LogType::Failed;
+ for ( const std::string& line : lines ) {
+ // scan
+ if ( this->deepTypeCheck( line, format ) ) {
+ n_access++;
+ } else {
+ n_other++;
+ }
+ }
+
+ // final decision
+ if ( n_access > 0 && n_other == 0 ) {
+ // access logs
+ log_type = LogOps::LogType::Access;
+ } else if ( n_other > 0 && n_access == 0 ) {
+ // other format, maybe error logs
+ log_type = LogOps::LogType::Discarded;
+ } else {
+ // something is wrong with this file, keep the Failed type
+ }
+ return log_type;
+}
+
+
+bool LogOps::deepTypeCheck( const std::string& line, const FormatOps::LogsFormat& format )
+{
+ int n_sep_found=0, n_blank_sep=0,
+ n_sep = format.separators.size();
+ size_t found_at, aux_found_at1=0, aux_found_at2;
+ std::string sep, aux_sep1, aux_sep2;
+ // check the initial part
+ if ( format.initial.size() > 0 ) {
+ if ( StringOps::startsWith( line, format.initial ) ) {
+ n_sep_found ++;
+ }
+ } else {
+ n_sep_found ++;
+ n_blank_sep ++;
+ }
+ // check the middle part
+ for ( int i=0; i 0 ) {
+ if ( StringOps::endsWith( line, format.final ) ) {
+ n_sep_found ++;
+ }
+ } else {
+ n_sep_found ++;
+ n_blank_sep ++;
+ }
+
+ // add the initial and final seps now
+ n_sep += 2;
+
+ // the result is considered ture if more then a half of the seps was found
+ // and more than a half of the found separators was not blank
+ bool result = false;
+ if ( n_sep_found >= n_sep-1
+ && n_blank_sep <= n_sep_found/2 ) {
+ result = true;
+ }
+
+ return result;
+}
+
+
+void LogOps::cleanLines( std::vector &lines )
+{
+ std::vector aux;
+ for ( const std::string& line : lines ) {
+ if ( !StringOps::startsWith( line, "#" ) ) {
+ // not a commented line
+ aux.push_back( line );
+ }
+ }
+ lines = aux;
+}
+
+
+
+const std::unordered_map LogOps::parseLine( const std::string& line, const FormatOps::LogsFormat& format )
+{
+ std::unordered_map data;
+ std::string sep, fld, fld_str;
+ bool add_pm=false;
+ size_t start, stop=0, aux_start, aux_stop,
+ line_size = line.size()-1;
+ int i=0, n_sep=format.separators.size()-1;
+
+ // add the initial chars
+ stop = format.initial.size();
+
+ while (true) {
+ // split fields
+ start = stop; // stop updated at the end of the loop
+ if ( i <= n_sep ) {
+ sep = format.separators.at( i );
+ stop = line.find( sep, start );
+ } else if ( i == n_sep+1 ) {
+ // final separator
+ sep = format.final;
+ if ( sep == "" ) {
+ stop = line_size+1;
+ } else {
+ stop = line.find( sep, start );
+ if ( stop == std::string::npos ) {
+ stop = line_size +1;
+ }
+ }
+ } else {
+ // no more separators
+ break;
+ }
+ if ( stop == std::string::npos ) {
+ // separator not found, abort
+ throw LogParserException( "Separator not found", sep );
+ }
+
+ // get the field
+ fld = format.fields.at( i );
+ if ( fld != "NONE" ) {
+ // only parse the considered fields
+ fld_str = StringOps::strip( line.substr(start, stop-start), " " );
+
+ if ( i+1 <= n_sep ) {
+ // not the last separator, check for mistakes
+ bool ok = true;
+ aux_stop = stop;
+
+ if ( sep == " " ) {
+ // whitespace-separated-values fields
+ int c = StringOps::count( fld_str, sep ),
+ n = 0;
+ if ( fld == "request_full" ) {
+ n = 2;
+ } else if ( fld == "date_time_mcs" ) {
+ n = 4;
+ } else if ( fld == "date_time_ncsa" ) {
+ n = 1;
+ } else if ( fld == "date_time_gmt" ) {
+ n = 3;
+ }
+ if ( n > 0 && c < n ) {
+ // loop until the correct number of whitespaces is reached
+ aux_start = stop + 1;
+ while ( c < n ) {
+ aux_stop = line.find( sep, aux_start );
+ if ( aux_stop == std::string::npos ) {
+ // not found
+ ok = false;
+ break;
+ }
+ aux_start = aux_stop + 1;
+ c++;
+ }
+ }
+
+ } else if ( fld == "user_agent" && StringOps::startsWith( sep, "\"" ) ) {
+ // atm the only support is for escaped quotes
+ if ( fld_str.back() == '\\' ) {
+ aux_start = stop + sep.size();
+ while (true) {
+ aux_stop = line.find( sep, aux_start );
+ if ( aux_stop == std::string::npos ) {
+ // not found
+ break;
+ } else if ( line.at( aux_stop-1 ) != '\\' ) {
+ // non-backslashed quotes
+ break;
+ }
+ aux_start = aux_stop + sep.size();
+ }
+ }
+ }
+
+ // finally update if needed
+ if ( ok && aux_stop >= stop ) {
+ stop = aux_stop;
+ fld_str = StringOps::strip( line.substr(start, stop-start), " " );
+ }
+ }
+
+ // process the field
+ this->parsed_size += fld_str.size();
+
+ if ( fld_str != "" ) {
+ int fld_id = this->field2id.at(fld);
+ if ( fld_id > 0 ) {
+ // no need to process, append directly if non-empty
+ data.emplace( fld_id, fld_str );
+
+ } else {
+ // process the field
+
+ // process the date to get year, month, day, hour and minute
+ if ( StringOps::startsWith( fld, "date_time" ) ) {
+ const std::vector dt = DateTimeOps::processDateTime( fld_str, fld.substr( 10 ) ); // cut away the "date_time_" part which is useless from now on
+ if ( dt.at( 0 ) != "" ) {
+ // year
+ data.emplace( this->field2id.at("date_time_year"), dt.at( 0 ) );
+ }
+ if ( dt.at( 1 ) != "" ) {
+ // month
+ data.emplace( this->field2id.at("date_time_month"), dt.at( 1 ) );
+ }
+ if ( dt.at( 2 ) != "" ) {
+ // day
+ data.emplace( this->field2id.at("date_time_day"), dt.at( 2 ) );
+ }
+ if ( dt.at( 3 ) != "" ) {
+ // hour
+ if ( dt.at( 3 ) == "PM" ) {
+ add_pm = true;
+ } else {
+ data.emplace( this->field2id.at("date_time_hour"), dt.at( 3 ) );
+ }
+ }
+ if ( dt.at( 4 ) != "" ) {
+ // minute
+ data.emplace( this->field2id.at("date_time_minute"), dt.at( 4 ) );
+ }
+ if ( dt.at( 5 ) != "" ) {
+ // second
+ data.emplace( this->field2id.at("date_time_second"), dt.at( 5 ) );
+ }
+
+
+ // process the request to get the protocol, method, resource and query
+ } else if ( fld == "request_full" ) {
+ size_t aux;
+ std::string aux_fld, protocol="", method="", page="", query="";
+ aux_fld = fld_str;
+ // method
+ aux = aux_fld.find( ' ' );
+ if ( aux != std::string::npos ) {
+ method = aux_fld.substr( 0, aux );
+ aux_fld = StringOps::lstrip( aux_fld.substr( aux ) );
+
+ // page & query
+ aux = aux_fld.find( ' ' );
+ if ( aux != std::string::npos ) {
+ std::string aux_str = aux_fld.substr( 0, aux );
+ // search for the query
+ int aux_ = aux_str.find( '?' );
+ if ( aux_ != std::string::npos ) {
+ page = aux_str.substr( 0, aux_ );
+ query = aux_str.substr( aux_+1 );
+ } else {
+ // query not found
+ page = aux_str;
+ }
+ aux_fld = StringOps::lstrip( aux_fld.substr( aux ) );
+
+ // protocol
+ protocol = aux_fld;
+ }
+ }
+ // append non-empty data
+ if ( protocol != "" ) {
+ data.emplace( this->field2id.at("request_protocol"), protocol );
+ }
+ if ( method != "" ) {
+ data.emplace( this->field2id.at("request_method"), method );
+ }
+ if ( page != "" ) {
+ data.emplace( this->field2id.at("request_uri"), page );
+ }
+ if ( query != "" ) {
+ data.emplace( this->field2id.at("request_query"), query );
+ }
+
+
+
+ // process the request to get uri and query
+ } else if ( fld == "request_uri_query" ) {
+ // search for the query
+ std::string page, query;
+ size_t aux_ = fld_str.find( '?' );
+ if ( aux_ != std::string::npos ) {
+ page = fld_str.substr( 0, aux_ );
+ query = fld_str.substr( aux_+1 );
+ } else {
+ // query not found
+ page = fld_str;
+ }
+ if ( page != "" ) {
+ data.emplace( this->field2id.at("request_uri"), page );
+ }
+ if ( query != "" ) {
+ data.emplace( this->field2id.at("request_query"), query );
+ }
+
+
+
+ // process the time taken to convert to milliseconds
+ } else if ( StringOps::startsWith( fld, "time_taken_" ) ) {
+ float t = std::stof( fld_str );
+ fld = fld.substr( 11 );
+ if ( fld == "us" ) {
+ // from microseconds
+ t /= 1000;
+ } else if ( fld == "s" || fld == "s.ms" ) {
+ // from seconds
+ t *= 1000;
+ }
+ data.emplace( this->field2id.at("time_taken"), std::to_string( (int)t ) );
+
+
+ // something went wrong
+ } else {
+ // hmmm.. no...
+ throw LogParserException( "Unexpected LogField", fld );
+ }
+ }
+ }
+ }
+
+
+ // update the stop for the next start
+ stop += sep.size();
+ i++;
+ if ( stop > line_size ) {
+ // this was the final separator
+ break;
+ }
+ }
+
+ if ( add_pm ) {
+ try {
+ // add +12 hours for PM
+ data.at( 4 ) = std::to_string( 12 + std::stoi(data.at( 4 )) );
+ } catch (...) {
+ // no hour data
+ }
+ }
+
+ // set the default warning mark ( 0=false ) to default status
+ data.emplace( 99, "0" );
+
+ this->total_size += line_size;
+ this->parsed_lines ++;
+
+ return data;
+}
+
+void LogOps::parseLines( std::vector>& data, const std::vector& lines, const FormatOps::LogsFormat& format )
+{
+ data.clear();
+ data.shrink_to_fit();
+ int nl = format.new_lines;
+ if ( nl == 0 ) {
+ data.reserve( lines.size() );
+ for ( const std::string& line : lines ) {
+ data.push_back( this->parseLine( line, format ) );
+ }
+ } else {
+ data.reserve( parsed_size / (nl+1) );
+ parsed_size --;
+ for ( int i=0; iparseLine( line, format ) );
+ }
+ }
+ if ( data.size() < data.capacity() ) {
+ data.shrink_to_fit();
+ }
+}
+
+
+void LogOps::resetPerfData()
+{
+ this->total_size = 0;
+ this->parsed_size = 0;
+ this->parsed_lines = 0;
+}
+const unsigned LogOps::getTotalSize()
+{
+ return this->total_size;
+}
+const unsigned LogOps::getParsedSize()
+{
+ return this->parsed_size;
+}
+const unsigned LogOps::getParsedLines()
+{
+ return this->parsed_lines;
+}
diff --git a/logdoctor/modules/craplog/modules/logs.h b/logdoctor/modules/craplog/modules/logs.h
new file mode 100644
index 00000000..3454c70f
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/logs.h
@@ -0,0 +1,152 @@
+#ifndef LOGS_H
+#define LOGS_H
+
+#include
+#include
+#include
+
+#include "modules/craplog/modules/formats.h"
+
+
+//! LogOps
+/*!
+ Operations for the logs
+*/
+class LogOps
+{
+public:
+ LogOps();
+
+ //! Enumerates log file types
+ /*!
+ File types used to decide whether a file should be considered valid or not
+ \see defineFileType()
+ */
+ enum LogType {
+ Failed = -1, //!< Failed to determine the type
+ Discarded = 0, //!< Not a valid file, will be discarded
+ Access = 1 //!< Valid access logs file type
+ };
+
+ //! Defines the type of a file
+ /*!
+ \param lines A list of (randomly picked) lines from the file to examine
+ \param format The logs format to use to determine if the file is valid or not
+ \return The resulting file type
+ \see LogType, deepTypeCheck(), FormatOps::LogsFormat
+ */
+ LogType defineFileType(
+ const std::vector& lines,
+ const FormatOps::LogsFormat& format );
+
+ //! Removes commented lines from the given list
+ /*!
+ \param lines The lines to clean
+ */
+ void cleanLines(
+ std::vector& lines );
+
+ //! Parses log lines to extract data
+ /*!
+ \param data The data collection which will hold the data
+ \param lines The list of lines to parse
+ \param format The logs format to use
+ \throw LogParserException
+ \see parseLine(), Craplog::parseLogLines(), FormatOps::LogsFormat
+ */
+ void parseLines(
+ std::vector>& data,
+ const std::vector& lines,
+ const FormatOps::LogsFormat& format );
+
+ //! Resets the performances data
+ void resetPerfData();
+
+ // share perf data with craplog
+ const unsigned getTotalSize(); //!< Returns the total size of the logs lines. \see total_size
+ const unsigned getParsedSize(); //!< Returns the parsed logs size. \see parsed_size
+ const unsigned getParsedLines(); //!< Returns the number of parsed log lines. \see parsed_lines
+
+private:
+
+ // Map to convert log fields to field IDs
+ const std::unordered_map field2id = {
+ // date-time
+ {"date_time_year", 1},
+ {"date_time_month", 2},
+ {"date_time_day", 3},
+ {"date_time_hour", 4},
+ {"date_time_minute", 5},
+ {"date_time_second", 6},
+ {"date_time_ncsa", 0},
+ {"date_time_iso", 0},
+ {"date_time_mcs", 0},
+ {"date_time_gmt", 0},
+ {"date_time_utc_d", 0},
+ {"date_time_utc_t", 0},
+ {"date_time_epoch_s", 0},
+ {"date_time_epoch_s.ms", 0},
+ {"date_time_epoch_ms", 0},
+ {"date_time_epoch_us", 0},
+ {"date_time_YYYYMMDD", 0},
+ {"date_time_MMDDYY", 0},
+ {"date_time_MDYY", 0},
+ {"date_time_year_short", 0},
+ {"date_time_month_str", 0},
+ {"date_time_clock_12", 0},
+ {"date_time_clock_24", 0},
+ {"date_time_clock_short", 0},
+ // request
+ {"request_protocol", 10},
+ {"request_method", 11},
+ {"request_uri", 12},
+ {"request_query", 13},
+ {"response_code", 14},
+ {"request_full", 0},
+ // performance
+ {"time_taken_ms", 15},
+ {"time_taken_us", 0},
+ {"time_taken_s.ms", 0},
+ {"time_taken_s", 0},
+ {"bytes_sent", 16},
+ {"bytes_received", 17},
+ // referer
+ {"referer", 18},
+ // client data
+ {"client", 20},
+ {"user_agent", 21},
+ {"cookie", 22}
+ };
+
+ //! Parse the given line using the given format
+ /*!
+ \param line The log line to check
+ \param format The logs format to use
+ \return Whether the line respects the format or not
+ \see defineFileType(), FormatOps::LogsFormat
+ */
+ bool deepTypeCheck(
+ const std::string& line,
+ const FormatOps::LogsFormat& format );
+
+ //! Parses a line to extract data
+ /*!
+ \param line The log line to parse
+ \param format The logs format to use
+ \return A data collection item
+ \throw LogParserException
+ \see parseLines(), Craplog::data_collection, FormatOps::LogsFormat
+ */
+ const std::unordered_map parseLine(
+ const std::string& line,
+ const FormatOps::LogsFormat& format );
+
+ // temporary vars
+ unsigned total_size=0; //!< Total size of the parsed logs. \see getTotalSize()
+ unsigned parsed_size=0; //!< Size of the parsed logs. \see getParsedSize()
+ unsigned parsed_lines=0; //!< Number of parsed logs lines. \see getParsedLines()
+
+
+};
+
+#endif // LOGS_H
diff --git a/logdoctor/modules/craplog/modules/sha256.LICENSE b/logdoctor/modules/craplog/modules/sha256.LICENSE
new file mode 100644
index 00000000..9f47dce7
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/sha256.LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Jérémy LAMBERT (SystemGlitch)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/logdoctor/modules/craplog/modules/sha256.cpp b/logdoctor/modules/craplog/modules/sha256.cpp
new file mode 100644
index 00000000..8c0e56a6
--- /dev/null
+++ b/logdoctor/modules/craplog/modules/sha256.cpp
@@ -0,0 +1,179 @@
+/*
+MIT License
+
+Copyright (c) 2021 Jérémy LAMBERT (SystemGlitch)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include "sha256.h"
+
+#include
+#include