├── VERSION ├── .gitattributes ├── .htaccess ├── .gitignore ├── tasks ├── reset_database.txt ├── hMSLogConfig.dist.ps1 ├── hMSLogHandler.ps1 ├── hMSLogFunctions.ps1 ├── hMSLogStatsCurrent.ps1 └── hMSLogSetupTasks.ps1 ├── README.md ├── foo.php ├── header.php ├── iframemsg.php ├── foot.php ├── includes ├── old │ ├── dialsettings.php │ ├── chartRejectionsPerHour.php │ ├── chartConnectionsPerHour.php │ ├── chartRejectionsPerDay.php │ ├── chartConnectionsPerDay.php │ ├── dialTodayMsgs.php │ ├── dialTodayReject.php │ ├── dialTodayConnect.php │ ├── dialTodayMsgsJQ.php │ ├── dialTodayRejectJQ.php │ └── dialTodayConnectJQ.php ├── mapData.php ├── datepicker.php ├── chartConnectionsPerHourCJS.php ├── chartRejectionsPerHourCJS.php ├── datepicker_logon.php ├── chartTodayConnections.php ├── chartConnectionsPerDayCJS.php ├── chartRejectionsPerDayCJS.php ├── chartTodayRejections.php ├── dialTodayMsgsCG.php ├── dialTodayConnectCG.php └── dialTodayRejectCG.php ├── link.php ├── config.dist.php ├── css ├── iframe.css ├── svgMap.min.css ├── svgMap.css └── stylesheet.css ├── iframechecksa.php ├── head.php ├── login.php ├── extra.php ├── functions_log.php ├── unsubscribe.php ├── logviewer.php ├── iframebayes.php ├── unread.php ├── iframemovetospam.php ├── iframemovetoinbox.php ├── iframeeml.php ├── iframelogviewer.php ├── logsearch.php ├── msg.php ├── messages.php ├── autoban.php ├── index.php ├── ids.php └── js └── chartjs-plugin-datalabels.min.js /VERSION: -------------------------------------------------------------------------------- 1 | 3.14 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | RedirectMatch 403 ^/tasks/.*$ 2 | RedirectMatch 403 ^/events/.*$ 3 | RedirectMatch 403 ^/temp/.*$ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore config file 2 | /config.php 3 | /tasks/hMSLogConfig.ps1 4 | /tasks/Temp 5 | /tasks/DBError-* 6 | /temp/* 7 | /statsCurrentData.php 8 | /statsData.php 9 | /test/ -------------------------------------------------------------------------------- /tasks/reset_database.txt: -------------------------------------------------------------------------------- 1 | DELETE FROM hm_log_attr; 2 | ALTER TABLE hm_log_attr AUTO_INCREMENT = 1; 3 | DELETE FROM hm_log_awstats; 4 | ALTER TABLE hm_log_awstats AUTO_INCREMENT = 1; 5 | DELETE FROM hm_log_ip; 6 | DELETE FROM hm_log_logon; 7 | ALTER TABLE hm_log_logon AUTO_INCREMENT = 1; 8 | DELETE FROM hm_log_msg; 9 | ALTER TABLE hm_log_msg AUTO_INCREMENT = 1; 10 | DELETE FROM hm_log_smtp; 11 | ALTER TABLE hm_log_smtp AUTO_INCREMENT = 1; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hMailServer-MySQL-Connection-Log 2 | 3 | Searchable Event Log for hMailServer 4 | 5 | # Prerequisites: 6 | * MySQL 7 | * Apache/IIS with PHP 8 | * hMailServer 5.7 (or custom build w/newer features) 9 | 10 | # Instructions: 11 | 12 | 1) Clone to web accessible folder 13 | 2) Rename `/config.dist.php` and `/tasks/hMSLogConfig.dist.ps1` and fill in the variables 14 | 3) Run `/tasks/hMSLogSetupTasks.ps1` to create database tables and scheduled tasks 15 | 4) Use examples in `/events/EventHandlers.vbs` to modify your own EventHandlers.vbs -------------------------------------------------------------------------------- /foo.php: -------------------------------------------------------------------------------- 1 | 17 |

[ERROR] 404

18 |
Shortlink created by hmailserver is invalid. Try again.
19 | " 20 | 21 | ?> 22 | 23 | -------------------------------------------------------------------------------- /header.php: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | 14 | 17 | 20 | 21 |
22 |
23 | -------------------------------------------------------------------------------- /iframemsg.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT message FROM hm_log_msg WHERE id = ".$msgid.";"); 17 | $sql->execute(); 18 | 19 | function link_repair($matches) {return "link.php?link=".urlencode($matches[0]);} 20 | 21 | while($row = $sql->fetch(PDO::FETCH_ASSOC)){ 22 | echo preg_replace_callback('/((?:https?:\/\/)[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~\%:\/?#[\]@!\$&\'\(\)\*\+,;=.]+)/',"link_repair",$row['message']); 23 | } 24 | ?> -------------------------------------------------------------------------------- /foot.php: -------------------------------------------------------------------------------- 1 | 13 | 24 | 25 | 26 | 27 | "; 28 | 29 | ?> -------------------------------------------------------------------------------- /includes/old/dialsettings.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /includes/mapData.php: -------------------------------------------------------------------------------- 1 | 19 | var svgMapDataHits = { 20 | data: { 21 | acc: { 22 | name: 'Accepted', 23 | format: '{0}', 24 | thousandSeparator: ',' 25 | }, 26 | rej: { 27 | name: 'Rejected', 28 | format: '{0}', 29 | thousandSeparator: ',' 30 | }, 31 | ratio: { 32 | name: 'Ratio', 33 | format: '{0} %', 34 | thresholdMax: 100, 35 | thresholdMin: 0 36 | } 37 | }, 38 | applyData: 'ratio', 39 | values: ".$mapjson." 40 | }; 41 | 42 | "; 53 | 54 | ?> -------------------------------------------------------------------------------- /link.php: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | hMailServer SQL Log 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 |

Dead Link

33 | This page exists to prevent you from clicking on potentially dangerous links.

34 | If you REALLY REALLY want to go to this link, go ahead and click it. But you were warned!

35 | 38 |
39 |
40 |
41 | 42 | "; 43 | 44 | ?> -------------------------------------------------------------------------------- /tasks/hMSLogConfig.dist.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | ╦ ╦╔╦╗╔═╗╦╦ ╔═╗╔═╗╦═╗╦ ╦╔═╗╦═╗ 3 | ╠═╣║║║╠═╣║║ ╚═╗║╣ ╠╦╝╚╗╔╝║╣ ╠╦╝ 4 | ╩ ╩╩ ╩╩ ╩╩╩═╝╚═╝╚═╝╩╚═ ╚╝ ╚═╝╩╚═ 5 | ╔═╗╦ ╦╔═╗╔═╗╦═╗ ╦ ╔═╗╔═╗ 6 | ╚═╗║ ║╠═╝║╣ ╠╦╝ ║ ║ ║║ ╦ 7 | ╚═╝╚═╝╩ ╚═╝╩╚═ ╩═╝╚═╝╚═╝ 8 | 9 | .SYNOPSIS 10 | 11 | 12 | .DESCRIPTION 13 | 14 | 15 | .FUNCTIONALITY 16 | 17 | 18 | .NOTES 19 | 20 | 21 | .EXAMPLE 22 | 23 | 24 | #> 25 | 26 | <### HMAILSERVER VARIABLES ###> 27 | $hMSAdminPass = "supersecretpassword" # hMailServer Administrator Password 28 | 29 | <### DATABASE EXPIRY VARIABLES ###> 30 | $ExpireDataLogDays = 30 # Number of days before expiring DATA log tables 31 | $ExpireMsgLogDays = 60 # Number of days before expiring MESSAGE log tables 32 | $ExpireLogonLogDays = 10 # Number of days before expiring LOGON log tables 33 | $ExpireFailedLogons = $False # True to expire, False to leave failed logons forever 34 | $ExpireIDS = 180 # Number of minutes before expiring IDS entries 35 | 36 | <### MySQL VARIABLES ###> 37 | $MySQLUserName = "hmailserver" 38 | $MySQLPassword = "supersecretpassword" 39 | $MySQLDatabase = "hmailserver" 40 | $MySQLHost = "localhost" 41 | $MySQLPort = 3306 42 | $MySQLSSL = "none" 43 | $MySQLConnectTimeout = 300 44 | $MySQLCommandTimeOut = 9000000 # Leave high if read errors 45 | 46 | <### STATS DATA VARIABLES ###> 47 | $wwwFolder = "C:\xampp\htdocs\hmslog" # Location of webserver folder for php admin files -------------------------------------------------------------------------------- /includes/datepicker.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT DATE(timestamp) FROM hm_log_smtp ORDER BY DATE(timestamp) ASC LIMIT 1;"); 15 | $minDate_sql->execute(); 16 | $minDateDB = $minDate_sql->fetchColumn(); 17 | $minDx = explode("-",$minDateDB); 18 | $minDate = $minDx[0].", ".($minDx[1]-1).", ".$minDx[2]; 19 | 20 | $maxDate_sql = $pdo->prepare("SELECT DATE(timestamp) FROM hm_log_smtp ORDER BY DATE(timestamp) DESC LIMIT 1;"); 21 | $maxDate_sql->execute(); 22 | $maxDateDB = $maxDate_sql->fetchColumn(); 23 | $maxDx = explode("-",$maxDateDB); 24 | $maxDate = $maxDx[0].", ".($maxDx[1]-1).", ".$maxDx[2]; 25 | 26 | echo " 27 | 47 | "; 48 | ?> -------------------------------------------------------------------------------- /includes/chartConnectionsPerHourCJS.php: -------------------------------------------------------------------------------- 1 | 17 | new Chart('chart_connections_per_hour', { 18 | type: 'bar', 19 | plugins: [ChartDataLabels], 20 | data: { 21 | labels: [".implode(",",$labelArrCPH)."], 22 | datasets: [ 23 | { 24 | data: [".implode(",",$dataArrCPH)."], 25 | backgroundColor: 'red', 26 | borderColor: 'red', 27 | datalabels: { 28 | color: 'white', 29 | font: { 30 | size: 10 31 | }, 32 | anchor: 'end', 33 | align: 'bottom', 34 | rotation: -90, 35 | }, 36 | }, 37 | ] 38 | }, 39 | options: { 40 | elements: { 41 | point:{ 42 | radius: 0 43 | } 44 | }, 45 | scales: { 46 | x: { 47 | ticks: { 48 | autoSkip: true, 49 | autoSkipPadding: 50, 50 | maxRotation: 0, 51 | minRotation: 0 52 | } 53 | }, 54 | }, 55 | responsive: true, 56 | maintainAspectRatio: false, 57 | plugins: { 58 | legend: { 59 | display: false, 60 | position: 'top', 61 | }, 62 | title: { 63 | display: false 64 | } 65 | } 66 | }, 67 | }); 68 | "; 69 | 70 | ?> -------------------------------------------------------------------------------- /includes/chartRejectionsPerHourCJS.php: -------------------------------------------------------------------------------- 1 | 17 | new Chart('chart_rejections_per_hour', { 18 | type: 'bar', 19 | plugins: [ChartDataLabels], 20 | data: { 21 | labels: [".implode(",",$labelArrRPH)."], 22 | datasets: [ 23 | { 24 | data: [".implode(",",$dataArrRPH)."], 25 | backgroundColor: 'black', 26 | borderColor: 'black', 27 | datalabels: { 28 | color: 'white', 29 | font: { 30 | size: 10 31 | }, 32 | anchor: 'end', 33 | align: 'bottom', 34 | rotation: -90, 35 | }, 36 | }, 37 | ] 38 | }, 39 | options: { 40 | elements: { 41 | point:{ 42 | radius: 0 43 | } 44 | }, 45 | scales: { 46 | x: { 47 | ticks: { 48 | autoSkip: true, 49 | autoSkipPadding: 50, 50 | maxRotation: 0, 51 | minRotation: 0 52 | } 53 | }, 54 | }, 55 | responsive: true, 56 | maintainAspectRatio: false, 57 | plugins: { 58 | legend: { 59 | display: false, 60 | position: 'top', 61 | }, 62 | title: { 63 | display: false 64 | } 65 | } 66 | }, 67 | }); 68 | "; 69 | 70 | ?> -------------------------------------------------------------------------------- /includes/datepicker_logon.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT DATE(timestamp) FROM hm_log_logon ORDER BY DATE(timestamp) ASC LIMIT 1;"); 15 | $minDate_sql->execute(); 16 | $minDateDB = $minDate_sql->fetchColumn(); 17 | $minDx = explode("-",$minDateDB); 18 | $minDate = $minDx[0].", ".($minDx[1]-1).", ".$minDx[2]; 19 | 20 | $maxDate_sql = $pdo->prepare("SELECT DATE(timestamp) FROM hm_log_logon ORDER BY DATE(timestamp) DESC LIMIT 1;"); 21 | $maxDate_sql->execute(); 22 | $maxDateDB = $maxDate_sql->fetchColumn(); 23 | $maxDx = explode("-",$maxDateDB); 24 | $maxDate = $maxDx[0].", ".($maxDx[1]-1).", ".$maxDx[2]; 25 | 26 | echo " 27 | 47 | "; 48 | ?> -------------------------------------------------------------------------------- /includes/old/chartRejectionsPerHour.php: -------------------------------------------------------------------------------- 1 | 14 | 57 | -------------------------------------------------------------------------------- /includes/chartTodayConnections.php: -------------------------------------------------------------------------------- 1 | 17 | new Chart('chart_today_con', { 18 | type: 'bar', 19 | plugins: [ChartDataLabels], 20 | data: { 21 | labels: [".$labelArrART."], 22 | datasets: [ 23 | { 24 | data: [".$dataArrART."], 25 | backgroundColor: 'red', 26 | borderColor: 'red', 27 | datalabels: { 28 | color: 'white', 29 | font: { 30 | size: 10 31 | }, 32 | anchor: 'end', 33 | align: 'left', 34 | }, 35 | }, 36 | ] 37 | }, 38 | options: { 39 | indexAxis: 'y', 40 | scaleShowValues: true, 41 | scales: { 42 | x: { 43 | type: 'logarithmic', 44 | display: false 45 | }, 46 | y: { 47 | ticks: { 48 | autoSkip: false, 49 | font: { 50 | size: 10, 51 | }, 52 | }, 53 | }, 54 | }, 55 | elements: { 56 | point:{ 57 | radius: 0 58 | } 59 | }, 60 | responsive: true, 61 | maintainAspectRatio: false, 62 | plugins: { 63 | legend: { 64 | display: false, 65 | position: 'top', 66 | }, 67 | title: { 68 | display: false 69 | } 70 | } 71 | }, 72 | }); 73 | "; 74 | 75 | ?> -------------------------------------------------------------------------------- /includes/old/chartConnectionsPerHour.php: -------------------------------------------------------------------------------- 1 | 14 | 57 | -------------------------------------------------------------------------------- /includes/chartConnectionsPerDayCJS.php: -------------------------------------------------------------------------------- 1 | 24 | new Chart('chart_connections_per_day', { 25 | type: 'line', 26 | data: { 27 | datasets: [ 28 | { 29 | data: [".implode(",",$dataArrCPD)."], 30 | backgroundColor: 'red', 31 | borderColor: 'red', 32 | }, 33 | { 34 | data: [".implode(",",$trendlineArrCPD)."], 35 | backgroundColor: 'black', 36 | borderColor: 'black', 37 | } 38 | ] 39 | }, 40 | options: { 41 | elements: { 42 | point:{ 43 | radius: 0 44 | } 45 | }, 46 | scales: { 47 | x: { 48 | ticks: { 49 | display: false 50 | } 51 | }, 52 | }, 53 | responsive: true, 54 | maintainAspectRatio: false, 55 | plugins: { 56 | legend: { 57 | display: false, 58 | position: 'top', 59 | }, 60 | title: { 61 | display: false 62 | } 63 | } 64 | }, 65 | }); 66 | "; 67 | 68 | ?> -------------------------------------------------------------------------------- /includes/chartRejectionsPerDayCJS.php: -------------------------------------------------------------------------------- 1 | 24 | new Chart('chart_rejections_per_day', { 25 | type: 'line', 26 | data: { 27 | datasets: [ 28 | { 29 | data: [".implode(",",$dataArrRPD)."], 30 | backgroundColor: 'black', 31 | borderColor: 'black', 32 | }, 33 | { 34 | data: [".implode(",",$trendlineArrRPD)."], 35 | backgroundColor: 'red', 36 | borderColor: 'red', 37 | } 38 | ] 39 | }, 40 | options: { 41 | elements: { 42 | point:{ 43 | radius: 0 44 | } 45 | }, 46 | scales: { 47 | x: { 48 | ticks: { 49 | display: false 50 | } 51 | }, 52 | }, 53 | responsive: true, 54 | maintainAspectRatio: false, 55 | plugins: { 56 | legend: { 57 | display: false, 58 | position: 'top', 59 | }, 60 | title: { 61 | display: false 62 | } 63 | } 64 | }, 65 | }); 66 | "; 67 | 68 | ?> -------------------------------------------------------------------------------- /includes/chartTodayRejections.php: -------------------------------------------------------------------------------- 1 | 18 | new Chart('chart_today_rej', { 19 | type: 'bar', 20 | plugins: [ChartDataLabels], 21 | data: { 22 | labels: [".$labelArrRRT."], 23 | datasets: [ 24 | { 25 | data: [".$dataArrRRT."], 26 | backgroundColor: 'black', 27 | borderColor: 'black', 28 | datalabels: { 29 | color: 'white', 30 | font: { 31 | size: 10 32 | }, 33 | anchor: 'end', 34 | align: 'left', 35 | }, 36 | }, 37 | ] 38 | }, 39 | options: { 40 | indexAxis: 'y', 41 | scaleShowValues: true, 42 | scales: { 43 | x: { 44 | type: 'logarithmic', 45 | display: false 46 | }, 47 | y: { 48 | ticks: { 49 | autoSkip: false, 50 | font: { 51 | size: 10, 52 | }, 53 | }, 54 | }, 55 | }, 56 | elements: { 57 | point:{ 58 | radius: 0 59 | } 60 | }, 61 | responsive: true, 62 | maintainAspectRatio: false, 63 | plugins: { 64 | legend: { 65 | display: false, 66 | position: 'top', 67 | }, 68 | title: { 69 | display: false 70 | } 71 | } 72 | }, 73 | }); 74 | "; 75 | 76 | ?> -------------------------------------------------------------------------------- /includes/old/chartRejectionsPerDay.php: -------------------------------------------------------------------------------- 1 | 14 | 57 | -------------------------------------------------------------------------------- /includes/old/chartConnectionsPerDay.php: -------------------------------------------------------------------------------- 1 | 14 | 57 | -------------------------------------------------------------------------------- /includes/old/dialTodayMsgs.php: -------------------------------------------------------------------------------- 1 | prepare(" 16 | SELECT 17 | ROUND(((COUNT(timestamp)) * 1.2), -1) AS dailymax, 18 | DATE(timestamp) AS daily 19 | FROM ".$Database['table_msg']." 20 | WHERE DATE(timestamp) < DATE(NOW()) 21 | GROUP BY daily 22 | ORDER BY dailymax DESC 23 | LIMIT 1; 24 | "); 25 | $sqlmax->execute(); 26 | $redTo = $sqlmax->fetchColumn(); 27 | 28 | //Set guage color marker points 29 | if (!$sqlmax->rowCount() > 0){$redTo = 100;} 30 | $redFrom = ($redTo / 1.2); 31 | $yellowTo = $redFrom; 32 | $yellowFrom = ($yellowTo * 0.75); 33 | 34 | //Get current (today's) bans 35 | $sql = $pdo->prepare("SELECT COUNT(*) FROM ".$Database['table_msg']." WHERE DATE(timestamp) = DATE(NOW());"); 36 | $sql->execute(); 37 | $hits = $sql->fetchColumn(); 38 | 39 | echo " 40 | "; 59 | ?> 60 | -------------------------------------------------------------------------------- /includes/old/dialTodayReject.php: -------------------------------------------------------------------------------- 1 | prepare(" 16 | SELECT 17 | ROUND(((COUNT(ip)) * 1.2), -2) AS dailymax, 18 | DATE(timestamp) AS daily 19 | FROM ".$Database['table_smtp']." a 20 | JOIN ".$Database['table_smtpa']." b ON a.id = b.id 21 | WHERE acc=0 AND DATE(timestamp) < DATE(NOW()) 22 | GROUP BY daily 23 | ORDER BY dailymax DESC 24 | LIMIT 1; 25 | "); 26 | $sqlmax->execute(); 27 | $redTo = $sqlmax->fetchColumn(); 28 | 29 | //Set guage color marker points 30 | if (!$sqlmax->rowCount() > 0){$redTo = 100;} 31 | $redFrom = ($redTo / 1.2); 32 | $yellowTo = $redFrom; 33 | $yellowFrom = ($yellowTo * 0.75); 34 | 35 | //Get current (today's) bans 36 | $sql = $pdo->prepare(" 37 | SELECT 38 | COUNT(ip) AS hits 39 | FROM ( 40 | SELECT * FROM (SELECT id AS ida, timestamp, ip FROM ".$Database['table_smtp'].") a 41 | JOIN (SELECT id AS idb, acc FROM ".$Database['table_smtpa'].") b ON a.ida = b.idb 42 | WHERE '".date('Y-m-d')." 00:00:00' <= timestamp AND acc=0 43 | ) AS A 44 | WHERE timestamp <= '".date('Y-m-d')." 23:59:59' AND acc=0; 45 | "); 46 | $sql->execute(); 47 | $hits = $sql->fetchColumn(); 48 | echo " 49 | "; 68 | ?> -------------------------------------------------------------------------------- /tasks/hMSLogHandler.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | ╦ ╦╔╦╗╔═╗╦╦ ╔═╗╔═╗╦═╗╦ ╦╔═╗╦═╗ 3 | ╠═╣║║║╠═╣║║ ╚═╗║╣ ╠╦╝╚╗╔╝║╣ ╠╦╝ 4 | ╩ ╩╩ ╩╩ ╩╩╩═╝╚═╝╚═╝╩╚═ ╚╝ ╚═╝╩╚═ 5 | ╔═╗╦ ╦╔═╗╔═╗╦═╗ ╦ ╔═╗╔═╗ 6 | ╚═╗║ ║╠═╝║╣ ╠╦╝ ║ ║ ║║ ╦ 7 | ╚═╝╚═╝╩ ╚═╝╩╚═ ╩═╝╚═╝╚═╝ 8 | 9 | .SYNOPSIS 10 | 11 | 12 | .DESCRIPTION 13 | 14 | 15 | .FUNCTIONALITY 16 | 17 | 18 | .NOTES 19 | 20 | 21 | .EXAMPLE 22 | 23 | 24 | #> 25 | 26 | <# Include required files #> 27 | Try { 28 | .("$PSScriptRoot\hMSLogConfig.ps1") 29 | .("$PSScriptRoot\hMSLogFunctions.ps1") 30 | } 31 | Catch { 32 | Write-Output "$((Get-Date).ToString(`"yy/MM/dd HH:mm:ss.ff`")) : ERROR : Unable to load supporting PowerShell Scripts : $query `n$($Error[0])" | Out-File "$PSScriptRoot\PSError.log" -Append 33 | } 34 | 35 | <### START SCRIPT ###> 36 | 37 | <# Get hMailServer log folder location #> 38 | $hMS = hMSAuthenticate 39 | $LogDir = $hMS.Settings.Directories.LogDirectory 40 | 41 | <# Import AWStats Log into database #> 42 | Get-Content "$LogDir\hmailserver_awstats.log" | ConvertFrom-String -Delimiter "`t" -PropertyNames TimeStamp, Sender, Recipient, ConnectionSender, ConnectionRecipient, Protocol, QuestionMark, StatusCode, MessageSize | ForEach { 43 | $Query = " 44 | INSERT INTO hm_log_awstats (timestamp, sender, recipient, connsender, connrecipient, statuscode) 45 | VALUES ( 46 | '$(($_.TimeStamp).ToString('yyyy-MM-dd HH:mm:ss'))', 47 | '$($_.Sender)', 48 | '$($_.Recipient)', 49 | '$($_.ConnectionSender)', 50 | '$($_.ConnectionRecipient)', 51 | '$($_.StatusCode)' 52 | ); 53 | " 54 | MySQLQuery $Query 55 | } 56 | 57 | <# Clear out AWStats Log #> 58 | Clear-Content "$LogDir\hmailserver_awstats.log" 59 | 60 | <# Update Msg Log table with statuscode #> 61 | $Query = " 62 | UPDATE hm_log_msg 63 | LEFT JOIN hm_log_awstats ON hm_log_msg.envelopeFrom = hm_log_awstats.sender AND hm_log_msg.envelopeTo = hm_log_awstats.recipient 64 | SET hm_log_msg.statuscode = hm_log_awstats.statuscode 65 | WHERE hm_log_msg.statuscode = 600 AND hm_log_awstats.timestamp < hm_log_msg.timestamp + INTERVAL 30 SECOND AND hm_log_awstats.timestamp > hm_log_msg.timestamp - INTERVAL 30 SECOND; 66 | " 67 | MySQLQuery $Query 68 | 69 | <# Expire IDS entries #> 70 | $Query = "DELETE FROM hm_ids WHERE timestamp < NOW() - INTERVAL $ExpireIDS MINUTE;" 71 | MySQLQuery $Query 72 | -------------------------------------------------------------------------------- /includes/old/dialTodayConnect.php: -------------------------------------------------------------------------------- 1 | prepare(" 16 | SELECT 17 | ROUND(((COUNT(ip)) * 1.2), -2) AS dailymax, 18 | DATE(timestamp) AS daily 19 | FROM ".$Database['table_smtp']." a 20 | JOIN ".$Database['table_smtpa']." b ON a.id = b.id 21 | WHERE acc=1 AND reason='Client_Connection' AND DATE(timestamp) < DATE(NOW()) 22 | GROUP BY daily 23 | ORDER BY dailymax DESC 24 | LIMIT 1; 25 | "); 26 | $sqlmax->execute(); 27 | $redTo = $sqlmax->fetchColumn(); 28 | 29 | //Set guage color marker points 30 | if (!$sqlmax->rowCount() > 0){$redTo = 100;} 31 | $redFrom = ($redTo / 1.2); 32 | $yellowTo = $redFrom; 33 | $yellowFrom = ($yellowTo * 0.75); 34 | 35 | //Get current (today's) bans 36 | $sql = $pdo->prepare(" 37 | SELECT 38 | COUNT(ip) AS hits 39 | FROM ( 40 | SELECT * FROM (SELECT id AS ida, timestamp, ip FROM ".$Database['table_smtp'].") a 41 | JOIN (SELECT id AS idb, acc, reason FROM ".$Database['table_smtpa'].") b ON a.ida = b.idb 42 | WHERE '".date('Y-m-d')." 00:00:00' <= timestamp AND acc=1 AND reason='Client_Connection' 43 | ) AS x 44 | WHERE timestamp <= '".date('Y-m-d')." 23:59:59' AND acc=1 AND reason='Client_Connection'; 45 | "); 46 | $sql->execute(); 47 | $hits = $sql->fetchColumn(); 48 | echo " 49 | "; 68 | ?> -------------------------------------------------------------------------------- /includes/dialTodayMsgsCG.php: -------------------------------------------------------------------------------- 1 | "; 64 | 65 | ?> -------------------------------------------------------------------------------- /includes/dialTodayConnectCG.php: -------------------------------------------------------------------------------- 1 | "; 64 | 65 | ?> -------------------------------------------------------------------------------- /includes/dialTodayRejectCG.php: -------------------------------------------------------------------------------- 1 | "; 64 | 65 | ?> -------------------------------------------------------------------------------- /tasks/hMSLogFunctions.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | ╦ ╦╔╦╗╔═╗╦╦ ╔═╗╔═╗╦═╗╦ ╦╔═╗╦═╗ 3 | ╠═╣║║║╠═╣║║ ╚═╗║╣ ╠╦╝╚╗╔╝║╣ ╠╦╝ 4 | ╩ ╩╩ ╩╩ ╩╩╩═╝╚═╝╚═╝╩╚═ ╚╝ ╚═╝╩╚═ 5 | ╔═╗╦ ╦╔═╗╔═╗╦═╗ ╦ ╔═╗╔═╗ 6 | ╚═╗║ ║╠═╝║╣ ╠╦╝ ║ ║ ║║ ╦ 7 | ╚═╝╚═╝╩ ╚═╝╩╚═ ╩═╝╚═╝╚═╝ 8 | 9 | .SYNOPSIS 10 | 11 | 12 | .DESCRIPTION 13 | 14 | 15 | .FUNCTIONALITY 16 | 17 | 18 | .NOTES 19 | 20 | 21 | .EXAMPLE 22 | 23 | 24 | #> 25 | 26 | <# MySQL Run Query Function #> 27 | Function MySQLQuery($Query) { 28 | $Today = (Get-Date).ToString("yyyyMMdd") 29 | $DBErrorLog = "$PSScriptRoot\DBError-$Today.log" 30 | $ConnectionString = "server=" + $MySQLHost + ";port=" + $MySQLPort + ";uid=" + $MySQLUserName + ";pwd=" + $MySQLPassword + ";database=" + $MySQLDatabase + ";SslMode=" + $MySQLSSL + ";Default Command Timeout=" + $MySQLCommandTimeOut + ";Connect Timeout=" + $MySQLConnectTimeout + ";" 31 | Try { 32 | [void][System.Reflection.Assembly]::LoadWithPartialName("MySql.Data") 33 | $Connection = New-Object MySql.Data.MySqlClient.MySqlConnection 34 | $Connection.ConnectionString = $ConnectionString 35 | $Connection.Open() 36 | $Command = New-Object MySql.Data.MySqlClient.MySqlCommand($Query, $Connection) 37 | $DataAdapter = New-Object MySql.Data.MySqlClient.MySqlDataAdapter($Command) 38 | $DataSet = New-Object System.Data.DataSet 39 | $RecordCount = $dataAdapter.Fill($dataSet, "data") 40 | $DataSet.Tables[0] 41 | } 42 | Catch { 43 | Write-Output "$(Get-Date -f G) : ERROR : Unable to run query : $Query" | Out-File $DBErrorLog -Append 44 | Write-Output "$(Get-Date -f G) : ERROR : $($Error[0])" | Out-File $DBErrorLog -Append 45 | } 46 | Finally { 47 | $Connection.Close() 48 | } 49 | } 50 | 51 | <# hMS Authentication Function #> 52 | Function hMSAuthenticate(){ 53 | $hMS = New-Object -COMObject hMailServer.Application 54 | $hMS.Authenticate("Administrator", $hMSAdminPass) | Out-Null 55 | return $hMS 56 | } 57 | 58 | <# Test For Local Account Function #> 59 | Function IsLocalAccount($Address){ 60 | If ($Address) { 61 | $hMS = hMSAuthenticate 62 | $Domain = ($Address).Split("@")[1] 63 | Try { 64 | $hMSAccount = ($hMS.Domains.ItemByName($Domain)).Accounts.ItemByAddress($Address) 65 | If ($hMSAccount) {Return $True} 66 | } 67 | Catch { 68 | Return $False 69 | Exit 70 | } 71 | } Else { 72 | Return $False 73 | } 74 | } 75 | 76 | <# Test if scheduled task exists #> 77 | Function TaskExists($ScheduledTaskName) { 78 | Try { 79 | Get-ScheduledTask -TaskName $ScheduledTaskName -TaskPath "\" -ErrorAction Stop 80 | Return $True 81 | } 82 | Catch { 83 | Return $False 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /config.dist.php: -------------------------------------------------------------------------------- 1 | 'localhost', 48 | 'username' => 'hmailserver', 49 | 'password' => 'supersecretpassword', 50 | 'dbname' => 'hmailserver', 51 | 'driver' => 'mysql', 52 | 'port' => '3306', 53 | 'dsn' => 'MariaDB ODBC 3.0 Driver' 54 | ); 55 | 56 | 57 | ?> -------------------------------------------------------------------------------- /css/iframe.css: -------------------------------------------------------------------------------- 1 | /* 2 | ╦ ╦╔╦╗╔═╗╦╦ ╔═╗╔═╗╦═╗╦ ╦╔═╗╦═╗ 3 | ╠═╣║║║╠═╣║║ ╚═╗║╣ ╠╦╝╚╗╔╝║╣ ╠╦╝ 4 | ╩ ╩╩ ╩╩ ╩╩╩═╝╚═╝╚═╝╩╚═ ╚╝ ╚═╝╩╚═ 5 | ╔═╗╦ ╦╔═╗╔═╗╦═╗ ╦ ╔═╗╔═╗ 6 | ╚═╗║ ║╠═╝║╣ ╠╦╝ ║ ║ ║║ ╦ 7 | ╚═╝╚═╝╩ ╚═╝╩╚═ ╩═╝╚═╝╚═╝ 8 | */ 9 | 10 | body { 11 | font-family: consolas; 12 | font-size: 12px; 13 | } 14 | .clear { 15 | clear: both; 16 | } 17 | .pagehead { 18 | padding:5px; 19 | /* background-color:#ffffcc; */ 20 | border:1px solid black; 21 | border-radius:5px; 22 | font-family:consolas; 23 | font-weight:bold; 24 | } 25 | .warning { 26 | font-family: consolas; 27 | background-color: yellow; 28 | font-weight: bold; 29 | } 30 | .success { 31 | font-family: consolas; 32 | background-color: #99ff99; 33 | font-weight: bold; 34 | } 35 | .err { 36 | padding:5px; 37 | background-color: yellow; 38 | border:1px solid black; 39 | border-radius:5px; 40 | font-family:consolas; 41 | font-weight:bold; 42 | width: fit-content; 43 | } 44 | pre { 45 | font-family: Consolas, monospace; 46 | margin: 0; 47 | padding: 0; 48 | font-size: 12px; 49 | } 50 | input[type=button], input[type=submit], .button { 51 | min-width: 65px; 52 | position: relative; 53 | cursor: pointer; 54 | } 55 | input[type=text], textarea, input[type=button], input[type=submit], .button { 56 | border: 1px solid black; 57 | border-radius:3px; 58 | } 59 | .spinleft { 60 | float: left; 61 | } 62 | .spinright { 63 | margin-left: 14px; 64 | } 65 | .button--loading::after { 66 | content: ""; 67 | position: absolute; 68 | width: 10px; 69 | height: 10px; 70 | top: 0; 71 | left: 0; 72 | right: 0; 73 | bottom: 0; 74 | margin: 0; 75 | border: 4px solid transparent; 76 | border-top-color: #999999; 77 | border-radius: 50%; 78 | animation: button-loading-spinner 1s ease infinite; 79 | } 80 | @keyframes button-loading-spinner { 81 | from { 82 | transform: rotate(0turn); 83 | } 84 | 85 | to { 86 | transform: rotate(1turn); 87 | } 88 | } 89 | .logline { 90 | display: block; 91 | padding-bottom: 0; 92 | } 93 | .logGroupContainer { 94 | padding-bottom: 10px; 95 | } 96 | .logGroupHeader { 97 | font-weight:bold; 98 | text-decoration-line: underline; 99 | text-decoration-style: dotted; 100 | text-decoration-thickness: 1px; 101 | } 102 | .logGroupDataContainer { 103 | padding-left:10px; 104 | } 105 | .logGroupDataDate { 106 | float:left; 107 | /* color:blue; */ 108 | } 109 | .logGroupDataOutput { 110 | margin-left:165px; 111 | } 112 | 113 | @media only screen and (max-width: 629px) { 114 | .logline { 115 | padding-bottom: 10px; 116 | } 117 | .logGroupDataDate { 118 | float:none; 119 | } 120 | .logGroupDataOutput { 121 | margin-left:0; 122 | } 123 | 124 | } -------------------------------------------------------------------------------- /iframechecksa.php: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | hMailServer SQL Log 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | "; 31 | 32 | echo "
Spamassasin Report on file: ".$fn."


"; 33 | if ($fn) { 34 | if (file_exists($fn)) { 35 | $hMS = hMSAuthenticate(); 36 | $SAHost = $hMS->Settings->AntiSpam->SpamAssassinHost; 37 | $SAPort = $hMS->Settings->AntiSpam->SpamAssassinPort; 38 | $script_command = "\"".$spamassassinPath."\spamc.exe\" -R < \"".$fn."\""; 39 | try { 40 | $shell = shell_exec($script_command); 41 | if (!isset($shell)){ 42 | throw new Exception("UNKNOWN ERROR"); 43 | } 44 | echo " 45 | 46 | 47 |

"; 48 | echo "
".htmlentities($shell)."
"; 49 | echo " 50 | "; 66 | } catch (Exception $ex) { 67 | echo "[ERROR] Running command \"".$script_command."\" resulted in an error!

"; 68 | echo "[ERROR] ".$ex.""; 69 | } 70 | } else { 71 | echo "ERROR - EML file could not be found"; 72 | } 73 | } else { 74 | echo "ERROR - EML filename not specified
"; 75 | } 76 | 77 | echo " 78 | 79 | "; 80 | 81 | ?> -------------------------------------------------------------------------------- /head.php: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 27 | hMailServer Super Log 28 | 29 | 30 | 31 | 32 | 33 | "; 34 | 35 | 36 | // DatePicker 37 | echo " 38 | 39 | 40 | "; 41 | if (preg_match('/(data|ip|messages)\.php/', $_SERVER['PHP_SELF'])) { 42 | include("./includes/datepicker.php"); 43 | } 44 | if (preg_match('/logons\.php/', $_SERVER['PHP_SELF'])) { 45 | include("./includes/datepicker_logon.php"); 46 | } 47 | 48 | 49 | // Canvas Gauge Dials 50 | if (preg_match('/index\.php$/', $_SERVER['PHP_SELF'])) { 51 | echo " 52 | "; 53 | } 54 | 55 | 56 | // Charts.js Charts 57 | if (preg_match('/index\.php$/', $_SERVER['PHP_SELF'])) { 58 | echo " 59 | 60 | "; 61 | } 62 | 63 | 64 | // Map tools 65 | if (preg_match('/(index|index-old)\.php$/', $_SERVER['PHP_SELF'])) { 66 | echo " 67 | 68 | 69 | "; 70 | } 71 | 72 | 73 | // iFrame Toggle 74 | if (preg_match('/msg\.php/', $_SERVER['PHP_SELF'])) { 75 | echo " 76 | "; 81 | } 82 | 83 | 84 | // Style Options in config.php 85 | if (preg_match('/(index|indexnewdials|unsubscribe|unread)\.php$/', $_SERVER['PHP_SELF'])) { 86 | echo " 87 | "; 88 | } else { 89 | echo " 90 | "; 91 | } 92 | 93 | 94 | // Start Page 95 | echo " 96 | 97 | "; 98 | 99 | include_once("header.php"); 100 | 101 | echo " 102 |
"; 103 | 104 | ?> -------------------------------------------------------------------------------- /login.php: -------------------------------------------------------------------------------- 1 | 50 | 51 | 52 | 53 | 54 | 55 | Log In 56 | 57 | 58 | 62 | 63 | 64 |
65 |

Log In

66 |
" method="post"> 67 |
68 | 69 | 70 |
71 |
72 | 73 | 74 |
75 |
76 | 77 | 78 |
79 |
80 | 81 | 82 |
83 |
84 | "; 87 | echo "alert('Username/Password Invalid');"; 88 | echo ""; 89 | } 90 | ?> 91 |
92 | 93 | -------------------------------------------------------------------------------- /includes/old/dialTodayMsgsJQ.php: -------------------------------------------------------------------------------- 1 | prepare(" 19 | SELECT 20 | ROUND(((COUNT(timestamp)) * 1.2), -1) AS dailymax, 21 | DATE(timestamp) AS daily 22 | FROM ".$Database['table_msg']." 23 | WHERE DATE(timestamp) < DATE(NOW()) 24 | GROUP BY daily 25 | ORDER BY dailymax DESC 26 | LIMIT 1; 27 | "); 28 | $sqlmax->execute(); 29 | $gaugeMaxM = $sqlmax->fetchColumn(); 30 | 31 | //Set guage color marker points 32 | if (!$sqlmax->rowCount() > 0){$gaugeMaxM = 100;} 33 | $gauge100M = ($gaugeMaxM / 1.25); 34 | $gauge75M = ($gauge100M * 0.75); 35 | $gauge50M = ($gauge100M * 0.5); 36 | $gauge25M = ($gauge100M * 0.25); 37 | 38 | //Get current (today's) bans 39 | $sql = $pdo->prepare("SELECT COUNT(*) FROM ".$Database['table_msg']." WHERE DATE(timestamp) = DATE(NOW());"); 40 | $sql->execute(); 41 | $hitsM = $sql->fetchColumn(); 42 | echo " 43 | "; 88 | ?> -------------------------------------------------------------------------------- /extra.php: -------------------------------------------------------------------------------- 1 | 17 |

Extra Items

18 |
"; 19 | 20 | // Autoban 21 | echo " 22 |
Autoban:
23 | 24 |
"; 25 | 26 | // Unread Messages Count 27 | echo " 28 |
Unread:
29 | 30 |
"; 31 | 32 | $hMS = hMSAuthenticate(); 33 | $logFolder = $hMS->Settings->Directories->LogDirectory; 34 | 35 | $results_array = array(); 36 | if (is_dir($logFolder)) { 37 | if ($handle = opendir($logFolder)) { 38 | while(($file = readdir($handle)) !== FALSE) { 39 | $results_array[] = $file; 40 | } 41 | closedir($handle); 42 | } 43 | } 44 | 45 | // Log Search 46 | echo " 47 |
hMailserver Log Search:
48 | 49 |
"; 50 | 51 | // WinDefAntiVirus Log 52 | echo " 53 |
hMailserver WinDef Scanner Logs:
54 |
"; 55 | foreach($results_array as $value) { 56 | if (preg_match("/^WinDefAntiVirus.log/",$value)) { 57 | echo "".$value."
"; 58 | } 59 | } 60 | echo " 61 |
62 |
"; 63 | 64 | // hMailServer Service Logs 65 | echo " 66 |
hMailserver Service Logs:
67 |
"; 68 | foreach($results_array as $value) { 69 | if (preg_match("/^hmailserver_\d.+\.log/",$value)) { 70 | echo "".$value."
"; 71 | } 72 | } 73 | echo " 74 |
75 |
"; 76 | 77 | // hMailServer Error Logs 78 | echo " 79 |
hMailserver Error Logs:
80 |
"; 81 | foreach($results_array as $value) { 82 | if (preg_match("/^ERROR_hmailserver_\d.+\.log/",$value)) { 83 | echo "".$value."
"; 84 | } 85 | } 86 | echo " 87 |
88 |
"; 89 | 90 | // Event Logs 91 | echo " 92 |
hMailserver Event Logs:
93 |
"; 94 | foreach($results_array as $value) { 95 | if (preg_match("/^hmailserver_events.*\.log/",$value)) { 96 | echo "".$value."
"; 97 | } 98 | } 99 | echo " 100 |
101 |
"; 102 | 103 | // SpamD Logs 104 | echo " 105 |
Spamassassin spamd Logs:
106 |
"; 107 | foreach($results_array as $value) { 108 | if (preg_match("/^spamd.*\.log/",$value)) { 109 | echo "".$value."
"; 110 | } 111 | } 112 | echo " 113 |
114 |
"; 115 | 116 | echo " 117 |
118 |
"; 119 | 120 | include_once("foot.php"); 121 | ?> -------------------------------------------------------------------------------- /functions_log.php: -------------------------------------------------------------------------------- 1 | ".$data[4].""); 47 | } 48 | 49 | // AUTH LOGIN decoder. 50 | // First we get a SENT: 334 VXNlcm5hbWU6 RECEIVED: AUTH LOGIN 51 | // The next RECEIVED: line contains login username which is e-mail address, base64 encoded. 52 | 53 | if (isset($datastore[$data[0] . $data[2]]) && strpos($data[5],'RECEIVED: ') !== false) { 54 | // We got it. 55 | $base64 = substr($data[5], strrpos($data[5], ' ') + 1, strlen($data[5])); 56 | $data[5] = 'RECEIVED: ' . base64_decode($base64) . ''; 57 | unset($datastore[$data[0] . $data[2]]); 58 | } else if (strpos($data[5], 'RECEIVED: AUTH LOGIN ') !== false && strlen($data[5]) > 21) { 59 | // Got singel line AUTH LOGIN? 60 | $base64 = substr($data[5], strrpos($data[5], ' ') + 1, strlen($data[5])); 61 | $data[5] = substr($data[5], 0, strrpos($data[5], ' ') + 1) .' ' . base64_decode($base64) . ''; 62 | } else if (strpos($data[5], 'SENT: 334 VXNlcm5hbWU6') !== false) { 63 | // Wait for it. 64 | $datastore[$data[0] . $data[2]] = true; 65 | } 66 | 67 | $events[$data[0] . $data[2]][1][] = array($data[3], $data[5]); 68 | } 69 | 70 | function parse_imap($data){ 71 | global $events; 72 | 73 | if (!isset($events[$data[0] . $data[2]])) { 74 | $events[$data[0] . $data[2]][0] = array($data[0], $data[2], "".$data[4].""); 75 | } 76 | 77 | $events[$data[0] . $data[2]][1][] = array($data[3], $data[5]); 78 | } 79 | 80 | function parse_error($data){ 81 | global $events; 82 | 83 | if (!isset($events[$data[0] . $data[2]])) { 84 | $events[$data[0] . $data[2]][0] = array($data[0]); 85 | } 86 | 87 | $events[$data[0] . $data[2]][1][] = array($data[2], $data[3]); 88 | } 89 | 90 | function parse_windef($line){ 91 | global $events; 92 | $line = cleanString($line); 93 | $line = cleanNonUTF8($line); 94 | $data = explode(" : ", $line); 95 | 96 | if (!isset($events[$data[2]])) { 97 | $events[$data[2]][0] = array($data[2]); 98 | } 99 | 100 | if (isset($data[4])) {$data4 = " : ".$data[4];} else {$data4 = "";} 101 | if (isset($data[5])) {$data5 = " : ".$data[5];} else {$data5 = "";} 102 | if (isset($data[6])) {$data6 = " : ".$data[6];} else {$data6 = "";} 103 | 104 | $events[$data[2]][1][] = array($data[0], $data[1]." : ", $data[3], $data4, $data5, $data6); 105 | } 106 | 107 | function events(){ 108 | global $events; 109 | $out = array(); 110 | foreach ($events as $data) { 111 | $out[] = $data; 112 | } 113 | if (empty($out)) $out = "No matched entries in the log file"; 114 | return $out; 115 | } 116 | 117 | ?> -------------------------------------------------------------------------------- /unsubscribe.php: -------------------------------------------------------------------------------- 1 | 1) { 20 | $exp = explode("?",$mailto); 21 | $mailtoAddress = $exp[0]; 22 | if (count(explode("&",$exp[1])) > 1) { 23 | if (preg_match("/^subject=/", explode("&",$exp[1])[0])) {$mailtoSubject = preg_replace("/subject=/","",explode("&",$exp[1])[0]);} 24 | if (preg_match("/^body=/", explode("&",$exp[1])[0])) {$mailtoBody = preg_replace("/body=/","",explode("&",$exp[1])[0]);} 25 | if (preg_match("/^subject=/", explode("&",$exp[1])[1])) {$mailtoSubject = preg_replace("/subject=/","",explode("&",$exp[1])[1]);} 26 | if (preg_match("/^body=/", explode("&",$exp[1])[1])) {$mailtoBody = preg_replace("/body=/","",explode("&",$exp[1])[1]);} 27 | } else { 28 | if (preg_match("/^subject=/", $exp[1])) {$mailtoSubject = preg_replace("/subject=/","",$exp[1]);} 29 | if (preg_match("/^body=/", $exp[1])) {$mailtoBody = preg_replace("/body=/","",$exp[1]);} 30 | } 31 | } else { 32 | $mailtoAddress = $mailto; 33 | } 34 | 35 | if (!isset($mailtoAddress)){$mailtoAddress="";} 36 | if (!isset($mailtoSubject)){$mailtoSubject="";} 37 | if (!isset($mailtoBody)){$mailtoBody="";} 38 | 39 | echo " 40 |

41 |
42 |

Unsubscriber

43 | The Unsubscriber will unsubscribe mailto: links scraped by the logger. A confirmation will be sent to the PostMaster. 44 |

45 |
46 |
Unsubscribee address:
".$from."
47 |
Message ID:
48 |
Unsubscribe mailto address:
".$mailtoAddress."
"; 49 | if ($mailtoSubject) { 50 | echo " 51 |
Unsubscribe subject:
".$mailtoSubject."
"; 52 | } 53 | if ($mailtoBody) { 54 | echo " 55 |
Unsubscribe body:
".$mailtoBody."
"; 56 | } 57 | echo " 58 |
59 |

"; 60 | 61 | if (isAccountLocal($from)) { 62 | if (!isset($_GET['submit'])) { 63 | echo " 64 | Do you want unsubscribe ".$from."? 65 |

66 | 67 | 68 | 69 | 70 |
"; 71 | } else { 72 | $unsub = sendUnsubscribeMessage($from, $mailtoAddress, $mailtoSubject, $mailtoBody, $msgid); 73 | if ($unsub) { 74 | echo " 75 | Unsubscribe message successfully sent"; 76 | } else { 77 | echo " 78 | ERROR sending unsubscribe message. Use Github issues for help."; 79 | } 80 | } 81 | } else { 82 | echo "Unsubscribee address NOT a local account! Cannot be unsubscribed!"; 83 | } 84 | 85 | echo " 86 |
"; 87 | 88 | include_once("foot.php"); 89 | ?> -------------------------------------------------------------------------------- /includes/old/dialTodayRejectJQ.php: -------------------------------------------------------------------------------- 1 | prepare(" 19 | SELECT 20 | ROUND(((COUNT(ip)) * 1.2), -2) AS dailymax, 21 | DATE(timestamp) AS daily 22 | FROM ".$Database['table_smtp']." a 23 | JOIN ".$Database['table_smtpa']." b ON a.id = b.id 24 | WHERE acc=0 AND DATE(timestamp) < DATE(NOW()) 25 | GROUP BY daily 26 | ORDER BY dailymax DESC 27 | LIMIT 1; 28 | "); 29 | $sqlmax->execute(); 30 | $gaugeMaxR = $sqlmax->fetchColumn(); 31 | 32 | //Set guage color marker points 33 | if (!$sqlmax->rowCount() > 0){$gaugeMaxR = 100;} 34 | $gauge100R = ($gaugeMaxR / 1.25); 35 | $gauge75R = ($gauge100R * 0.75); 36 | $gauge50R = ($gauge100R * 0.5); 37 | $gauge25R = ($gauge100R * 0.25); 38 | 39 | //Get current (today's) bans 40 | $sql = $pdo->prepare(" 41 | SELECT 42 | COUNT(ip) AS hits 43 | FROM ( 44 | SELECT * FROM (SELECT id AS ida, timestamp, ip FROM ".$Database['table_smtp'].") a 45 | JOIN (SELECT id AS idb, acc FROM ".$Database['table_smtpa'].") b ON a.ida = b.idb 46 | WHERE '".date('Y-m-d')." 00:00:00' <= timestamp AND acc=0 47 | ) AS A 48 | WHERE timestamp <= '".date('Y-m-d')." 23:59:59' AND acc=0; 49 | "); 50 | $sql->execute(); 51 | $hitsR = $sql->fetchColumn(); 52 | echo " 53 | "; 98 | ?> -------------------------------------------------------------------------------- /includes/old/dialTodayConnectJQ.php: -------------------------------------------------------------------------------- 1 | prepare(" 19 | SELECT 20 | ROUND(((COUNT(ip)) * 1.25), -2) AS dailymax, 21 | DATE(timestamp) AS daily 22 | FROM ".$Database['table_smtp']." a 23 | JOIN ".$Database['table_smtpa']." b ON a.id = b.id 24 | WHERE acc=1 AND reason='Client_Connection' AND DATE(timestamp) < DATE(NOW()) 25 | GROUP BY daily 26 | ORDER BY dailymax DESC 27 | LIMIT 1; 28 | "); 29 | $sqlmax->execute(); 30 | $gaugeMaxC = $sqlmax->fetchColumn(); 31 | 32 | //Set guage color marker points 33 | if (!$sqlmax->rowCount() > 0){$gaugeMaxC = 100;} 34 | $gauge100C = ($gaugeMaxC / 1.25); 35 | $gauge75C = ($gauge100C * 0.75); 36 | $gauge50C = ($gauge100C * 0.5); 37 | $gauge25C = ($gauge100C * 0.25); 38 | 39 | //Get current (today's) bans 40 | $sql = $pdo->prepare(" 41 | SELECT 42 | COUNT(ip) AS hits 43 | FROM ( 44 | SELECT * FROM (SELECT id AS ida, timestamp, ip FROM ".$Database['table_smtp'].") a 45 | JOIN (SELECT id AS idb, acc, reason FROM ".$Database['table_smtpa'].") b ON a.ida = b.idb 46 | WHERE '".date('Y-m-d')." 00:00:00' <= timestamp AND acc=1 AND reason='Client_Connection' 47 | ) AS x 48 | WHERE timestamp <= '".date('Y-m-d')." 23:59:59' AND acc=1 AND reason='Client_Connection'; 49 | "); 50 | $sql->execute(); 51 | $hitsC = $sql->fetchColumn(); 52 | echo " 53 | "; 98 | ?> -------------------------------------------------------------------------------- /logviewer.php: -------------------------------------------------------------------------------- 1 | 38 |

".$logfile."

"; 39 | 40 | if (preg_match("/^hmailserver_\d.*\.log$/",$logfile)) { 41 | 42 | echo " 43 |
44 | Show logs only for: 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
Filter results by: 57 | 58 | 59 | 60 | 61 |
"; 62 | } 63 | 64 | if (isset($LogType)) {$urlLogType = "&LogType=".$LogType;} else {$urlLogType = "";} 65 | echo " 66 |
67 |
68 | 69 |
70 | "; 71 | 72 | include_once("foot.php"); 73 | ?> -------------------------------------------------------------------------------- /tasks/hMSLogStatsCurrent.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | ╦ ╦╔╦╗╔═╗╦╦ ╔═╗╔═╗╦═╗╦ ╦╔═╗╦═╗ 3 | ╠═╣║║║╠═╣║║ ╚═╗║╣ ╠╦╝╚╗╔╝║╣ ╠╦╝ 4 | ╩ ╩╩ ╩╩ ╩╩╩═╝╚═╝╚═╝╩╚═ ╚╝ ╚═╝╩╚═ 5 | ╔═╗╦ ╦╔═╗╔═╗╦═╗ ╦ ╔═╗╔═╗ 6 | ╚═╗║ ║╠═╝║╣ ╠╦╝ ║ ║ ║║ ╦ 7 | ╚═╝╚═╝╩ ╚═╝╩╚═ ╩═╝╚═╝╚═╝ 8 | 9 | .SYNOPSIS 10 | 11 | 12 | .DESCRIPTION 13 | 14 | 15 | .FUNCTIONALITY 16 | 17 | 18 | .NOTES 19 | 20 | 21 | .EXAMPLE 22 | 23 | 24 | #> 25 | 26 | <# Include required files #> 27 | Try { 28 | .("$PSScriptRoot\hMSLogConfig.ps1") 29 | .("$PSScriptRoot\hMSLogFunctions.ps1") 30 | } 31 | Catch { 32 | Write-Output "$((Get-Date).ToString(`"yy/MM/dd HH:mm:ss.ff`")) : ERROR : Unable to load supporting PowerShell Scripts : $query `n$($Error[0])" | Out-File "$PSScriptRoot\PSError.log" -Append 33 | } 34 | 35 | <### START SCRIPT ###> 36 | 37 | Add-Type -AssemblyName System.Web 38 | 39 | <# Delete and recreate php data file #> 40 | $TempFolder = "$PSScriptRoot\Temp" 41 | If (-not(Test-Path $TempFolder)) {md $TempFolder} 42 | $StatsDataPHPTemp = "$TempFolder\statsCurrentDataTemp.php" 43 | If (Test-Path $StatsDataPHPTemp) {Remove-Item -Force -Path $StatsDataPHPTemp} 44 | New-Item $StatsDataPHPTemp -ItemType "file" 45 | Write-Output " 50 | $QueryDTC = " 51 | SELECT COUNT(ip) AS hits 52 | FROM hm_log_smtp 53 | WHERE DATE(timestamp) = DATE(NOW()) AND acc=1 AND reason='Client_Connection'; 54 | " 55 | MySQLQuery $QueryDTC | ForEach { 56 | $hitsC = $_.hits 57 | } 58 | Write-Output "`$hitsC = $hitsC;" | Out-File $StatsDataPHPTemp -Encoding ASCII -Append 59 | 60 | <# Today's Connections Dial - Rejections #> 61 | $QueryDTR = " 62 | SELECT COUNT(ip) AS hits 63 | FROM hm_log_smtp 64 | WHERE DATE(timestamp) = DATE(NOW()) AND acc=0; 65 | " 66 | MySQLQuery $QueryDTR | ForEach { 67 | $hitsR = $_.hits 68 | } 69 | Write-Output "`$hitsR = $hitsR;" | Out-File $StatsDataPHPTemp -Encoding ASCII -Append 70 | 71 | <# Today's Connections Dial - Messages #> 72 | $QueryDTM = "SELECT COUNT(*) AS count FROM hm_log_msg WHERE DATE(timestamp) = DATE(NOW());" 73 | MySQLQuery $QueryDTM | ForEach { 74 | $hitsM = $_.count 75 | } 76 | Write-Output "`$hitsM = $hitsM;" | Out-File $StatsDataPHPTemp -Encoding ASCII -Append 77 | 78 | <# Accepted Reasons Today bar chart #> 79 | $QueryART = " 80 | SELECT reason, COUNT(*) as numhits 81 | FROM hm_log_smtp 82 | WHERE acc=1 AND DATE(timestamp) = DATE(NOW()) 83 | GROUP BY reason 84 | ORDER BY reason ASC; 85 | " 86 | $dataArrART = @() 87 | $labelArrART = @() 88 | MySQLQuery $QueryART | ForEach { 89 | $dataArrART += $_.numhits 90 | $labelArrART += "'$($_.reason)'" 91 | } 92 | Write-Output "`$dataArrART = `"$($dataArrART -Join ",")`";" | Out-File $StatsDataPHPTemp -Encoding ASCII -Append 93 | Write-Output "`$labelArrART = `"$($labelArrART -Join ",")`";" | Out-File $StatsDataPHPTemp -Encoding ASCII -Append 94 | 95 | $QueryCountART = " 96 | SELECT COUNT(DISTINCT(reason)) as countacc 97 | FROM hm_log_smtp 98 | WHERE acc=1 AND DATE(timestamp) = DATE(NOW()); 99 | " 100 | MySQLQuery $QueryCountART | ForEach { 101 | $countAcc_rows += $_.countacc 102 | } 103 | Write-Output "`$countAcc_rows = $countAcc_rows;" | Out-File $StatsDataPHPTemp -Encoding ASCII -Append 104 | 105 | <# Rejected Reasons Today bar chart #> 106 | $QueryRRT = " 107 | SELECT reason, COUNT(*) as numhits 108 | FROM hm_log_smtp 109 | WHERE acc=0 AND DATE(timestamp) = DATE(NOW()) 110 | GROUP BY reason 111 | ORDER BY reason ASC; 112 | " 113 | $dataArrRRT = @() 114 | $labelArrRRT = @() 115 | MySQLQuery $QueryRRT | ForEach { 116 | $dataArrRRT += $_.numhits 117 | $labelArrRRT += "'$($_.reason)'" 118 | } 119 | Write-Output "`$dataArrRRT = `"$($dataArrRRT -Join ",")`";" | Out-File $StatsDataPHPTemp -Encoding ASCII -Append 120 | Write-Output "`$labelArrRRT = `"$($labelArrRRT -Join ",")`";" | Out-File $StatsDataPHPTemp -Encoding ASCII -Append 121 | 122 | $QueryCountRRT = " 123 | SELECT COUNT(DISTINCT(reason)) as countrej 124 | FROM hm_log_smtp 125 | WHERE acc=0 AND DATE(timestamp) = DATE(NOW()); 126 | " 127 | MySQLQuery $QueryCountRRT | ForEach { 128 | $countRej_rows += $_.countrej 129 | } 130 | Write-Output "`$countRej_rows = $countRej_rows;" | Out-File $StatsDataPHPTemp -Encoding ASCII -Append 131 | 132 | Write-Output "?>" | Out-File $StatsDataPHPTemp -Encoding ASCII -Append 133 | 134 | Copy-Item -Path $StatsDataPHPTemp -Destination "$wwwFolder\statsCurrentData.php" 135 | -------------------------------------------------------------------------------- /iframebayes.php: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 | hMailServer SQL Log 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | "; 33 | 34 | echo " 35 |
Train spamassassin bayes on file: ".$fn."


"; 36 | if ($fn) { 37 | if (file_exists($fn)) { 38 | if (!(isset($spam) || isset($ham))) { 39 | echo " 40 | Select training option:

41 |
42 | 43 | 44 | 45 | 46 |
"; 47 | } else { 48 | if (isset($spam)) {$spamham = "spam";} 49 | if (isset($ham)) {$spamham = "ham";} 50 | $hMS = hMSAuthenticate(); 51 | $SAHost = $hMS->Settings->AntiSpam->SpamAssassinHost; 52 | $SAPort = $hMS->Settings->AntiSpam->SpamAssassinPort; 53 | $script_command = "\"".$spamassassinPath."\spamc.exe\" -d \"$SAHost\" -p \"$SAPort\" -x -L ".$spamham." < \"".$fn."\""; 54 | try { 55 | $feedBayes = exec($script_command); 56 | if (preg_match("/Message\ssuccessfully\sun\/learned/",$feedBayes)) { 57 | echo " 58 | Message successfully fed to spamassassin for ".$spamham." training

"; 59 | } else { 60 | echo " 61 | Error: Output of spamc operation:

".$feedBayes."

"; 62 | } 63 | if (isset($spam)) { 64 | echo " 65 | "; 66 | } 67 | if (isset($ham)) { 68 | echo " 69 | "; 70 | } 71 | $sql = $pdo->prepare("UPDATE hm_log_msg SET spamadjusted = 1 WHERE id = ".$msgid.";"); 72 | $sql->execute(); 73 | 74 | } 75 | catch (Exception $e) { 76 | echo "ERROR! ".$e->getMessage(); 77 | } 78 | } 79 | } else { 80 | echo " 81 | ERROR - EML file could not be found"; 82 | } 83 | } else { 84 | echo " 85 | ERROR - EML filename not specified"; 86 | } 87 | 88 | echo " 89 | 119 | 120 | "; 121 | 122 | ?> -------------------------------------------------------------------------------- /unread.php: -------------------------------------------------------------------------------- 1 | 38 |

Unread Messages

39 | hMailServer database query to find un-flagged (unread) messages. This information gives an indication on actual user activity. 40 | 41 |
42 |

43 | 44 | 45 | 46 |
47 |
48 | 49 | 50 |
"; 51 | 52 | $offset = ($page-1) * $no_of_records_per_page; 53 | $total_pages_sql = $pdo->prepare(" 54 | SELECT COUNT(DISTINCT(hm_accounts.accountaddress)) AS count 55 | FROM hm_messages 56 | INNER JOIN hm_accounts ON hm_messages.messageaccountid = hm_accounts.accountid 57 | WHERE RIGHT(BIN(hm_messages.messageflags),1) = '0' AND accountactive = '1'".$search_SQL."; 58 | "); 59 | $total_pages_sql->execute(); 60 | $total_rows = $total_pages_sql->fetchColumn(); 61 | $total_pages = ceil($total_rows / $no_of_records_per_page); 62 | 63 | $sumUnread_sql = $pdo->prepare(" 64 | SELECT COUNT(*) AS count 65 | FROM hm_messages 66 | INNER JOIN hm_accounts ON hm_messages.messageaccountid = hm_accounts.accountid 67 | WHERE RIGHT(BIN(hm_messages.messageflags),1) = '0' AND accountactive = '1'".$search_SQL."; 68 | "); 69 | $sumUnread_sql->execute(); 70 | $sumUnread = $sumUnread_sql->fetchColumn(); 71 | 72 | $sql = $pdo->prepare(" 73 | SELECT hm_accounts.accountaddress AS address, COUNT(*) AS countunread 74 | FROM hm_messages 75 | INNER JOIN hm_accounts ON hm_messages.messageaccountid = hm_accounts.accountid 76 | WHERE RIGHT(BIN(hm_messages.messageflags),1) = '0' AND accountactive = '1'".$search_SQL." 77 | GROUP BY hm_accounts.accountaddress 78 | ORDER BY countunread DESC 79 | LIMIT ".$offset.", ".$no_of_records_per_page."; 80 | "); 81 | $sql->execute(); 82 | 83 | if ($search==""){ 84 | $search_res=""; 85 | } else { 86 | $search_res=" for account \"".$search."\""; 87 | } 88 | 89 | if ($total_pages < 2){ 90 | $pagination = ""; 91 | } else { 92 | $pagination = "(Page: ".number_format($page)." of ".number_format($total_pages).")"; 93 | } 94 | 95 | if ($total_rows == 1){$singular = '';} else {$singular= 's';} 96 | if ($total_rows == 0){ 97 | if ($search == "" && $reason == "" && $acc == ""){ 98 | echo "Please enter a search term"; 99 | } else { 100 | echo "No results ".$search_res; 101 | } 102 | } else { 103 | echo " 104 | Results ".$search_res.": ".number_format($sumUnread)." unread messages among ".number_format($total_rows)." account".$singular." ".$pagination."
105 |
106 |
107 |
108 |
Account Address
109 |
Unread Messages
110 |
"; 111 | 112 | while($row = $sql->fetch(PDO::FETCH_ASSOC)){ 113 | echo " 114 |
115 | 116 |
".number_format($row['countunread'])." 
117 |
"; 118 | } 119 | echo " 120 |
121 |
"; 122 | 123 | if ($search==""){$search_page = "";} else {$search_page = "&search=".$search;} 124 | 125 | if ($total_pages == 1){ 126 | echo ""; 127 | } else { 128 | echo " 129 | 130 | 138 | "; 139 | } 140 | } 141 | 142 | ?> 143 | 144 |
145 | 146 | -------------------------------------------------------------------------------- /iframemovetospam.php: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | hMailServer SQL Log 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | "; 32 | 33 | echo "
Move message to spam folder: ".$fn."


"; 34 | if ($fn) { 35 | if (file_exists($fn)) { 36 | $fnExpl = explode("\\",$fn); 37 | $fnCount = count($fnExpl); 38 | $fnFileName = $fnExpl[($fnCount-1)]; 39 | $acctDomain = $fnExpl[($fnCount-4)]; 40 | $acctAddress = $fnExpl[($fnCount-3)]."@".$fnExpl[($fnCount-4)]; 41 | echo "Filename: ".$fnFileName."
"; 42 | echo "Account Address: ".$acctAddress."
"; 43 | 44 | $hMS = hMSAuthenticate(); 45 | $hMSDomain = $hMS->Domains->ItemByName($acctDomain); 46 | $hMSAccount = $hMSDomain->Accounts->ItemByAddress($acctAddress); 47 | 48 | // <# Find junk folder for account and grab folder ID #> 49 | $junkFolderID = null; 50 | for ($IterateIMAPFolders = 0; $IterateIMAPFolders < $hMSAccount->IMAPFolders->Count; $IterateIMAPFolders++) { 51 | $hMSIMAPFolder = $hMSAccount->IMAPFolders->Item($IterateIMAPFolders); 52 | if (preg_match("/(spam|junk)/i", $hMSIMAPFolder->Name)) { 53 | $junkFolderID = $hMSIMAPFolder->ID; 54 | echo "
Identified Junk folder for account ".$acctAddress." as folder ID ".$junkFolderID." with folder name \"".$hMSIMAPFolder->Name."\"

"; 55 | } 56 | } 57 | 58 | // <# If junk folder found then copy message to junk folder and delete from original folder #> 59 | if ($junkFolderID) { 60 | $sql = $pdo->prepare("SELECT messageid, messagefolderid FROM hm_messages WHERE messagefilename = '".$fnFileName."';"); 61 | $sql->execute(); 62 | while($row = $sql->fetch(PDO::FETCH_ASSOC)){ 63 | $messageID = $row['messageid']; 64 | $messageFolderID = $row['messagefolderid']; 65 | } 66 | echo "Existing message ID: ".$messageID."
"; 67 | echo "Existing folder ID: ".$messageFolderID."
"; 68 | echo "Existing folder name: ".($hMSAccount->IMAPFolders->ItemByDBID($messageFolderID))->Name."

"; 69 | 70 | $Folder = $hMSAccount->IMAPFolders->ItemByDBID($messageFolderID); 71 | $Message = $Folder->Messages->ItemByDBID($messageID); 72 | 73 | if ($messageFolderID == $junkFolderID) { 74 | echo "
Message already exists in spam folder! Aborting operation!"; 75 | } else { 76 | 77 | echo "Moving message ".$fn." with ID ".$messageID." from folder \"".($hMSAccount->IMAPFolders->ItemByDBID($messageFolderID))->Name."\" to folder \"".($hMSAccount->IMAPFolders->ItemByDBID($junkFolderID))->Name."\"
"; 78 | 79 | try { 80 | $Message->Flag{1} = True; 81 | $Message->Save; 82 | $Message->Copy($junkFolderID); 83 | $Folder->Messages->DeleteByDBID($Message->ID); 84 | echo "
Successfully moved message to Junk folder.
"; 85 | } catch (Exception $ex) { 86 | echo "[ERROR] Could not copy or delete message with ID ".$Message->ID.""; 87 | echo "[ERROR] ".$ex.""; 88 | } 89 | } 90 | 91 | // <# If junk folder not found then create junk folder, copy message to junk folder and delete from original folder #> 92 | } else { 93 | echo "Junk folder not found - create new one then move message to it"; 94 | try { 95 | $hMSAccount->IMAPFolders->Add('Spam'); 96 | echo "Added folder \"Spam\" to account ".$hMSAccount->Address; 97 | $NewjunkFolderID = ($hMSAccount->IMAPFolders->ItemByName('Spam'))->ID; 98 | echo "Moving message with ID ".$Message->ID." to folder ".($hMSAccount->IMAPFolders->ItemByDBID($NewjunkFolderID))->Name; 99 | $Message->Flag{1} = True; 100 | $Message->Save; 101 | $Message->Copy($NewjunkFolderID); 102 | $Folder->Messages->DeleteByDBID($Message->ID); 103 | echo "
Successfully moved message to Junk folder.
"; 104 | } catch (Exception $ex) { 105 | echo "[ERROR] Could not copy or delete message with ID ".$Message->ID.""; 106 | echo "[ERROR] ".$ex.""; 107 | } 108 | } 109 | 110 | } else { 111 | echo "ERROR - EML file could not be found"; 112 | } 113 | 114 | } else { 115 | echo "ERROR - No filename found"; 116 | } 117 | 118 | 119 | echo " 120 | 121 | "; 122 | 123 | ?> -------------------------------------------------------------------------------- /iframemovetoinbox.php: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | hMailServer SQL Log 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | "; 32 | 33 | echo "
Move message to Inbox: ".$fn."


"; 34 | if ($fn) { 35 | if (file_exists($fn)) { 36 | $fnExpl = explode("\\",$fn); 37 | $fnCount = count($fnExpl); 38 | $fnFileName = $fnExpl[($fnCount-1)]; 39 | $acctDomain = $fnExpl[($fnCount-4)]; 40 | $acctAddress = $fnExpl[($fnCount-3)]."@".$fnExpl[($fnCount-4)]; 41 | echo "Filename: ".$fnFileName."
"; 42 | echo "Account Address: ".$acctAddress."
"; 43 | 44 | $hMS = hMSAuthenticate(); 45 | $hMSDomain = $hMS->Domains->ItemByName($acctDomain); 46 | $hMSAccount = $hMSDomain->Accounts->ItemByAddress($acctAddress); 47 | 48 | // <# Find inbox folder for account and grab folder ID #> 49 | $inboxFolderID = null; 50 | for ($IterateIMAPFolders = 0; $IterateIMAPFolders < $hMSAccount->IMAPFolders->Count; $IterateIMAPFolders++) { 51 | $hMSIMAPFolder = $hMSAccount->IMAPFolders->Item($IterateIMAPFolders); 52 | if (preg_match("/(inbox)/i", $hMSIMAPFolder->Name)) { 53 | $inboxFolderID = $hMSIMAPFolder->ID; 54 | echo "
Identified Inbox folder for account ".$acctAddress." as folder ID ".$inboxFolderID." with folder name \"".$hMSIMAPFolder->Name."\"

"; 55 | } 56 | } 57 | 58 | // <# If inbox folder found then copy message to inbox folder and delete from original folder #> 59 | if ($inboxFolderID) { 60 | $sql = $pdo->prepare("SELECT messageid, messagefolderid FROM hm_messages WHERE messagefilename = '".$fnFileName."';"); 61 | $sql->execute(); 62 | while($row = $sql->fetch(PDO::FETCH_ASSOC)){ 63 | $messageID = $row['messageid']; 64 | $messageFolderID = $row['messagefolderid']; 65 | } 66 | echo "Existing message ID: ".$messageID."
"; 67 | echo "Existing folder ID: ".$messageFolderID."
"; 68 | echo "Existing folder name: ".($hMSAccount->IMAPFolders->ItemByDBID($messageFolderID))->Name."

"; 69 | 70 | $Folder = $hMSAccount->IMAPFolders->ItemByDBID($messageFolderID); 71 | $Message = $Folder->Messages->ItemByDBID($messageID); 72 | 73 | if ($messageFolderID == $inboxFolderID) { 74 | echo "
Message already exists in Inbox folder! Aborting operation!"; 75 | } else { 76 | 77 | echo "Moving message ".$fn." with ID ".$messageID." from folder \"".($hMSAccount->IMAPFolders->ItemByDBID($messageFolderID))->Name."\" to folder \"".($hMSAccount->IMAPFolders->ItemByDBID($inboxFolderID))->Name."\"
"; 78 | 79 | try { 80 | // $Message->Flag{1} = True; 81 | $Message->Save; 82 | $Message->Copy($inboxFolderID); 83 | $Folder->Messages->DeleteByDBID($Message->ID); 84 | echo "
Successfully moved message to Inbox folder.
"; 85 | } catch (Exception $ex) { 86 | echo "[ERROR] Could not copy or delete message with ID ".$Message->ID.""; 87 | echo "[ERROR] ".$ex.""; 88 | } 89 | } 90 | 91 | // <# If junk folder not found then create junk folder, copy message to junk folder and delete from original folder #> 92 | } else { 93 | echo "Inbox folder not found!"; 94 | // try { 95 | // $hMSAccount->IMAPFolders->Add('Spam'); 96 | // echo "Added folder \"Spam\" to account ".$hMSAccount->Address; 97 | // $NewinboxFolderID = ($hMSAccount->IMAPFolders->ItemByName('Spam'))->ID; 98 | // echo "Moving message with ID ".$Message->ID." to folder ".($hMSAccount->IMAPFolders->ItemByDBID($NewinboxFolderID))->Name; 99 | // $Message->Flag{1} = True; 100 | // $Message->Save; 101 | // $Message->Copy($NewinboxFolderID); 102 | // $Folder->Messages->DeleteByDBID($Message->ID); 103 | // echo "
Successfully moved message to Junk folder.
"; 104 | // } catch (Exception $ex) { 105 | // echo "[ERROR] Could not copy or delete message with ID ".$Message->ID.""; 106 | // echo "[ERROR] ".$ex.""; 107 | // } 108 | } 109 | 110 | } else { 111 | echo "ERROR - EML file could not be found"; 112 | } 113 | 114 | } else { 115 | echo "ERROR - EML filename not specified"; 116 | } 117 | 118 | 119 | echo " 120 | 121 | "; 122 | 123 | ?> -------------------------------------------------------------------------------- /css/svgMap.min.css: -------------------------------------------------------------------------------- 1 | /*! svgMap | https://github.com/StephanWagner/svgMap | MIT License | Copyright Stephan Wagner | https://stephanwagner.me */ 2 | .svgMap-container,.svgMap-wrapper{position:relative}.svgMap-block-zoom-notice{position:absolute;z-index:2;top:100%;left:0;right:0;bottom:0;background:rgba(0,0,0,.8);pointer-events:none;opacity:0;color:#fff;transition:opacity 250ms}.svgMap-block-zoom-notice-active .svgMap-block-zoom-notice{pointer-events:all;top:0;opacity:1}.svgMap-block-zoom-notice>div{position:absolute;top:50%;left:0;right:0;text-align:center;padding:0 32px;transform:translateY(-50%);font-size:28px}@media (max-width:900px){.svgMap-block-zoom-notice>div{font-size:22px}}.svgMap-map-wrapper{position:relative;width:100%;padding-top:50%;overflow:hidden;background:#d9ecff;color:#111}.svgMap-map-wrapper *{box-sizing:border-box}.svgMap-map-wrapper :focus:not(:focus-visible){outline:0}.svgMap-map-wrapper .svgMap-map-image{display:block;position:absolute;top:0;left:0;width:100%;height:100%;margin:0}.svgMap-map-wrapper .svgMap-map-controls-wrapper{position:absolute;bottom:10px;left:10px;z-index:1;display:flex;overflow:hidden;border-radius:2px;box-shadow:0 0 0 2px rgba(0,0,0,.1)}.svgMap-map-wrapper .svgMap-map-controls-move,.svgMap-map-wrapper .svgMap-map-controls-zoom{display:flex;margin-right:5px;overflow:hidden;background:#fff}.svgMap-map-wrapper .svgMap-map-controls-move:last-child,.svgMap-map-wrapper .svgMap-map-controls-zoom:last-child{margin-right:0}.svgMap-map-wrapper .svgMap-control-button{background-color:transparent;border:none;border-radius:0;color:inherit;font:inherit;line-height:inherit;margin:0;padding:0;overflow:visible;text-transform:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;width:30px;height:30px;position:relative}.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:after,.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:before{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:#666;transition:background-color 250ms}.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:before{width:11px;height:3px}.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button.svgMap-zoom-reset-button::before{width:11px;height:11px;background:0 0;border:2px solid #666}@media (hover:hover){.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:hover:after,.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:hover:before{background:#111}}.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:active:after,.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:active:before{background:#111}.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button.svgMap-disabled:after,.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button.svgMap-disabled:before{background:#ccc}.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button.svgMap-zoom-reset-button.svgMap-disabled:before{border:2px solid #ccc;background:0 0}.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-in-button{margin:1px 0 1px 1px}.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-in-button:after{width:3px;height:11px}.svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-out-button{margin:1px 1px 1px 0}.svgMap-map-wrapper .svgMap-map-continent-controls-wrapper{position:absolute;top:10px;right:10px;z-index:1;display:flex;border-radius:2px;box-shadow:0 0 0 2px rgba(0,0,0,.1)}.svgMap-map-wrapper .svgMap-country{stroke:#fff;stroke-width:1;stroke-linejoin:round;vector-effect:non-scaling-stroke;transition:fill 250ms,stroke 250ms}.svgMap-map-wrapper .svgMap-country[data-link]{cursor:pointer}@media (hover:hover){.svgMap-map-wrapper .svgMap-country:hover{stroke:#333;stroke-width:1.5}}.svgMap-map-wrapper .svgMap-country.svgMap-active{stroke:#333;stroke-width:1.5}.svgMap-tooltip{box-shadow:0 0 3px rgba(0,0,0,.2);position:absolute;z-index:2;border-radius:2px;background:#fff;transform:translate(-50%,-100%);border-bottom:1px solid #000;display:none;pointer-events:none;min-width:60px}.svgMap-tooltip.svgMap-tooltip-flipped{transform:translate(-50%,0);border-bottom:0;border-top:1px solid #000}.svgMap-tooltip.svgMap-active{display:block}.svgMap-tooltip .svgMap-tooltip-content-container{position:relative;padding:10px 20px}.svgMap-tooltip .svgMap-tooltip-content-container .svgMap-tooltip-flag-container{text-align:center;margin:2px 0 5px}.svgMap-tooltip .svgMap-tooltip-content-container .svgMap-tooltip-flag-container.svgMap-tooltip-flag-container-emoji{font-size:50px;line-height:0;padding:25px 0 15px}.svgMap-tooltip .svgMap-tooltip-content-container .svgMap-tooltip-flag-container .svgMap-tooltip-flag{display:block;margin:auto;width:auto;height:32px;padding:2px;background:rgba(0,0,0,.15);border-radius:2px}.svgMap-tooltip .svgMap-tooltip-title{white-space:nowrap;font-size:18px;line-height:28px;padding:0 0 8px;text-align:center}.svgMap-tooltip .svgMap-tooltip-content{white-space:nowrap;text-align:center;font-size:14px;color:#777;margin:-5px 0 0}.svgMap-tooltip .svgMap-tooltip-content table{padding:0;border-spacing:0;margin:auto}.svgMap-tooltip .svgMap-tooltip-content table td{padding:2px 0;text-align:left}.svgMap-tooltip .svgMap-tooltip-content table td span{color:#111}.svgMap-tooltip .svgMap-tooltip-content table td:first-child{padding-right:10px;text-align:right}.svgMap-tooltip .svgMap-tooltip-content table td sup{vertical-align:baseline;position:relative;top:-5px}.svgMap-tooltip .svgMap-tooltip-content .svgMap-tooltip-no-data{padding:2px 0;color:#777;font-style:italic}.svgMap-tooltip .svgMap-tooltip-pointer{position:absolute;top:100%;left:50%;transform:translateX(-50%);overflow:hidden;height:10px;width:30px}.svgMap-tooltip .svgMap-tooltip-pointer:after{content:"";width:20px;height:20px;background:#fff;border:1px solid #000;position:absolute;bottom:6px;left:50%;transform:translateX(-50%) rotate(45deg)}.svgMap-tooltip.svgMap-tooltip-flipped .svgMap-tooltip-pointer{bottom:auto;top:-10px;transform:translateX(-50%) scaleY(-1)} -------------------------------------------------------------------------------- /iframeeml.php: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | MsgID: ".$msgid." 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | "; 30 | 31 | $fnArray = getMessageFilename($msgid); 32 | $addressFail = false; 33 | 34 | $hMS = hMSAuthenticate(); 35 | $dataFolder = $hMS->Settings->Directories->DataDirectory; 36 | 37 | if (is_array($fnArray) && count($fnArray)>0) { 38 | foreach ($fnArray as $fn) { 39 | preg_match('/(?<={)(\w{2})/',$fn,$matches); 40 | $fn2c = $matches[0]; 41 | $addresses=array(); 42 | $usersql = $pdo->prepare(" 43 | SELECT * FROM hm_log_msg a 44 | LEFT JOIN hm_log_attr b ON a.id = b.msgid 45 | WHERE a.id = ".$msgid."; 46 | "); 47 | $usersql->execute(); 48 | while($row = $usersql->fetch(PDO::FETCH_ASSOC)){ 49 | if (!in_array($row['envelopeFrom'], $addresses, true)){ 50 | array_push($addresses, $row['envelopeFrom']); 51 | } 52 | if (!in_array($row['envelopeTo'], $addresses, true)){ 53 | array_push($addresses, $envelopeTo = $row['envelopeTo']); 54 | } 55 | $plusAddress = getPlusAddress($row['envelopeTo']); 56 | if ($plusAddress) { 57 | if (!in_array($plusAddress, $addresses, true)){ 58 | array_push($addresses, $plusAddress); 59 | } 60 | } 61 | if (preg_match("/envelopeTo /",$row['item'])) { 62 | if (!in_array($row['value'], $addresses, true)){ 63 | array_push($addresses, $row['value']); 64 | } 65 | } 66 | if ($row['headerTo']) { 67 | $thx = explode(",",$row['headerTo']); 68 | $thxarr = array(); 69 | foreach ($thx as $thx_item) { 70 | preg_match('/(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/',$thx_item,$matches); 71 | if (isset($matches[0])){ 72 | if (!in_array($matches[0], $addresses, true)){ 73 | array_push($addresses, $matches[0]); 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | if(isset($envelopeTo)) { 81 | $forwardAddress = getForwardingAddress($envelopeTo); 82 | if ($forwardAddress) { 83 | if (!in_array($forwardAddress, $addresses, true)){ 84 | array_push($addresses, $forwardAddress); 85 | } 86 | } 87 | } 88 | 89 | if (isset($spamAccount)) { 90 | if (!in_array($spamAccount, $addresses, true)){ 91 | array_push($addresses, $spamAccount); 92 | } 93 | } 94 | 95 | // Loop through address array and see if they're local; if yes, try to find the message file 96 | for ($i = 0; $i < count($addresses); $i++) { 97 | if (isAccountLocal($addresses[$i])) { 98 | $domain = explode("@", $addresses[$i])[1]; 99 | $user = explode("@", $addresses[$i])[0]; 100 | $filename = $dataFolder."\\".$domain."\\".$user."\\".$fn2c."\\".$fn; 101 | if (file_exists($filename)) { 102 | 103 | $flag_sql = $pdo->prepare("SELECT messageflags FROM hm_messages WHERE messagefilename = '".$fn."';"); 104 | $flag_sql->execute(); 105 | $msgFlag = $flag_sql->fetchColumn(); 106 | 107 | echo " 108 |
109 | ".$filename."

"; 110 | if ($msgFlag){$mfnote = implode(" + ",get_bits($msgFlag));} else {$mfnote = "No Flags";} 111 | echo " 112 | Message Flags: ".$mfnote."

"; 113 | if ($useSpamassassin) { 114 | echo " 115 | Feed this message to Spamassassin for bayes training -OR- to test spam score:

116 | 117 | 118 | "; 119 | } 120 | echo " 121 |

"; 122 | $rawEML = fopen($filename, "r"); 123 | while(! feof($rawEML)) { 124 | $line = fgets($rawEML); 125 | echo "
".htmlentities($line)."
"; 126 | } 127 | $addressFail = false; 128 | echo " 129 | 152 | 153 | "; 154 | exit; 155 | // break; 156 | } else { 157 | $addressFail = true; 158 | } 159 | } else { 160 | $addressFail = true; 161 | } 162 | } 163 | } 164 | } else { 165 | $addressFail = true; 166 | } 167 | 168 | 169 | if ($addressFail) { 170 | echo " 171 | Raw EML file not found. It was likely deleted by spam rules or expunged by user or was not saved to sent messages folder."; 172 | } 173 | 174 | echo " 175 | 176 | "; 177 | 178 | 179 | ?> -------------------------------------------------------------------------------- /tasks/hMSLogSetupTasks.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | ╦ ╦╔╦╗╔═╗╦╦ ╔═╗╔═╗╦═╗╦ ╦╔═╗╦═╗ 3 | ╠═╣║║║╠═╣║║ ╚═╗║╣ ╠╦╝╚╗╔╝║╣ ╠╦╝ 4 | ╩ ╩╩ ╩╩ ╩╩╩═╝╚═╝╚═╝╩╚═ ╚╝ ╚═╝╩╚═ 5 | ╔═╗╦ ╦╔═╗╔═╗╦═╗ ╦ ╔═╗╔═╗ 6 | ╚═╗║ ║╠═╝║╣ ╠╦╝ ║ ║ ║║ ╦ 7 | ╚═╝╚═╝╩ ╚═╝╩╚═ ╩═╝╚═╝╚═╝ 8 | 9 | .SYNOPSIS 10 | Set up database and scheduled tasks for log website. 11 | 12 | .DESCRIPTION 13 | Set up database and scheduled tasks for log website. 14 | 15 | 16 | .FUNCTIONALITY 17 | 18 | 19 | .NOTES 20 | Make sure config file is properly filled with accurate information. 21 | 22 | .EXAMPLE 23 | 24 | 25 | #> 26 | 27 | <# Include required files #> 28 | Try { 29 | .("$PSScriptRoot\hMSLogConfig.ps1") 30 | .("$PSScriptRoot\hMSLogFunctions.ps1") 31 | } 32 | Catch { 33 | Write-Output "$((Get-Date).ToString(`"yy/MM/dd HH:mm:ss.ff`")) : ERROR : Unable to load supporting PowerShell Scripts : `n$($Error[0])" | Out-File "$PSScriptRoot\PSError.log" -Append 34 | } 35 | 36 | 37 | <### CREATE TABLES ###> 38 | 39 | $Query =" 40 | DROP TABLE IF EXISTS hm_log_attr; 41 | CREATE TABLE IF NOT EXISTS hm_log_attr ( 42 | id int(11) NOT NULL AUTO_INCREMENT, 43 | msgid int(11) NOT NULL DEFAULT 0, 44 | item tinytext DEFAULT NULL, 45 | value text DEFAULT NULL, 46 | PRIMARY KEY (id) 47 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 48 | " 49 | MySQLQuery $Query 50 | 51 | $Query =" 52 | DROP TABLE IF EXISTS hm_log_awstats; 53 | CREATE TABLE IF NOT EXISTS hm_log_awstats ( 54 | id int(11) NOT NULL AUTO_INCREMENT, 55 | timestamp datetime NOT NULL, 56 | sender tinytext NOT NULL, 57 | recipient tinytext NOT NULL, 58 | connsender tinytext NOT NULL, 59 | connrecipient tinytext NOT NULL, 60 | statuscode int(3) NOT NULL, 61 | PRIMARY KEY (id) 62 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 63 | " 64 | MySQLQuery $Query 65 | 66 | $Query =" 67 | DROP TABLE IF EXISTS hm_log_ip; 68 | CREATE TABLE IF NOT EXISTS hm_log_ip ( 69 | ipaddress varbinary(16) NOT NULL, 70 | lastport int(3) NOT NULL DEFAULT 0, 71 | country tinytext DEFAULT NULL, 72 | countrycode tinytext NOT NULL, 73 | hitsacc int(11) NOT NULL DEFAULT 1, 74 | hitsrej int(11) NOT NULL DEFAULT 0, 75 | lasthit datetime NOT NULL DEFAULT current_timestamp(), 76 | lastreason tinytext NOT NULL DEFAULT '0', 77 | lastacc tinyint(1) NOT NULL DEFAULT 0, 78 | lastevent tinytext DEFAULT NULL, 79 | PRIMARY KEY (ipaddress) 80 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 81 | " 82 | MySQLQuery $Query 83 | 84 | $Query =" 85 | DROP TABLE IF EXISTS hm_log_logon; 86 | CREATE TABLE IF NOT EXISTS hm_log_logon ( 87 | id int(11) NOT NULL AUTO_INCREMENT, 88 | timestamp datetime NOT NULL DEFAULT current_timestamp(), 89 | ip varbinary(16) NOT NULL, 90 | port int(4) NOT NULL, 91 | acc tinyint(1) NOT NULL, 92 | country tinytext NOT NULL, 93 | username tinytext NOT NULL, 94 | reason tinytext NOT NULL, 95 | PRIMARY KEY (id) 96 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 97 | " 98 | MySQLQuery $Query 99 | 100 | $Query =" 101 | DROP TABLE IF EXISTS hm_log_msg; 102 | CREATE TABLE IF NOT EXISTS hm_log_msg ( 103 | id mediumint(9) NOT NULL AUTO_INCREMENT, 104 | timestamp datetime NOT NULL DEFAULT current_timestamp(), 105 | envelopeFrom tinytext DEFAULT NULL, 106 | headerFrom tinytext DEFAULT NULL, 107 | envelopeTo tinytext DEFAULT NULL, 108 | headerTo text DEFAULT NULL, 109 | subject tinytext DEFAULT NULL, 110 | message mediumtext DEFAULT NULL, 111 | ip varbinary(16) NOT NULL, 112 | statuscode int(3) DEFAULT 600, 113 | PRIMARY KEY (id) 114 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 115 | " 116 | MySQLQuery $Query 117 | 118 | $Query =" 119 | DROP TABLE IF EXISTS hm_log_smtp; 120 | CREATE TABLE IF NOT EXISTS hm_log_smtp ( 121 | id int(11) NOT NULL AUTO_INCREMENT, 122 | timestamp timestamp NOT NULL DEFAULT current_timestamp(), 123 | ip varbinary(16) NOT NULL, 124 | port int(3) NOT NULL DEFAULT 0, 125 | acc tinyint(4) NOT NULL, 126 | event tinytext NOT NULL, 127 | reason tinytext NOT NULL, 128 | ptr tinytext DEFAULT NULL, 129 | helo tinytext DEFAULT NULL, 130 | country tinytext NOT NULL, 131 | msgid int(11) NOT NULL DEFAULT 0, 132 | PRIMARY KEY (id) 133 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 134 | " 135 | MySQLQuery $Query 136 | 137 | 138 | <### SCHEDULED TASKS ###> 139 | 140 | <# Log Handler - collects awstats, expires database records, etc. #> 141 | $TaskName = "hMS Log Handler" 142 | If (-not(TaskExists($TaskName))) { 143 | $Trigger = New-ScheduledTaskTrigger -Once -At "12/25/2022 0am" -RepetitionInterval (New-TimeSpan -Minute 1) -RepetitionDuration ((Get-Date).AddYears(25) - (Get-Date)) 144 | $Settings = New-ScheduledTaskSettingsSet 145 | $Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy Bypass -File `"$PSScriptRoot\hMSLogHandler.ps1`"" 146 | $Principal = New-ScheduledTaskPrincipal -UserID "SYSTEM" -LogonType ServiceAccount 147 | Register-ScheduledTask -TaskName $TaskName -Principal $Principal -Action $Action -Settings $Settings -Trigger $Trigger 148 | } 149 | 150 | <# Current Stats - collects data necessary for timely rendering of charts on stats page #> 151 | $TaskName = "hMS Log Current Stats" 152 | If (-not(TaskExists($TaskName))) { 153 | $Trigger = New-ScheduledTaskTrigger -Once -At "12/25/2022 0am" -RepetitionInterval (New-TimeSpan -Minute 1) -RepetitionDuration ((Get-Date).AddYears(25) - (Get-Date)) 154 | $Settings = New-ScheduledTaskSettingsSet 155 | $Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy Bypass -File `"$PSScriptRoot\hMSLogStatsCurrent.ps1`"" 156 | $Principal = New-ScheduledTaskPrincipal -UserID "SYSTEM" -LogonType ServiceAccount 157 | Register-ScheduledTask -TaskName $TaskName -Principal $Principal -Action $Action -Settings $Settings -Trigger $Trigger 158 | } 159 | 160 | <# Daily Stats - collects data necessary for timely rendering of charts on stats page #> 161 | $TaskName = "hMS Log Daily Stats" 162 | If (-not(TaskExists($TaskName))) { 163 | $Trigger = New-ScheduledTaskTrigger -Daily -At 12:01am 164 | $Settings = New-ScheduledTaskSettingsSet 165 | $Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy Bypass -File `"$PSScriptRoot\hMSLogStatsDaily.ps1`"" 166 | $Principal = New-ScheduledTaskPrincipal -UserID "SYSTEM" -LogonType ServiceAccount 167 | Register-ScheduledTask -TaskName $TaskName -Principal $Principal -Action $Action -Settings $Settings -Trigger $Trigger 168 | } 169 | -------------------------------------------------------------------------------- /iframelogviewer.php: -------------------------------------------------------------------------------- 1 |
".$LogTypes."

"; 21 | 22 | 23 | $hMS = hMSAuthenticate(); 24 | $Path = $hMS->Settings->Directories->LogDirectory; 25 | $Filename = $Path.'\\'.$logfile; 26 | 27 | echo " 28 | 29 | 30 | 31 | hMailServer SQL Log 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | "; 41 | 42 | if ((preg_match("/^hmailserver_\d.*\.log$/",$logfile)) && ($RawType)) { 43 | if (file_exists($Filename)) { 44 | $rawFile = fopen($Filename, "r"); 45 | while(! feof($rawFile)) { 46 | $line = fgets($rawFile); 47 | $line = cleanString($line); 48 | $line = cleanNonUTF8($line); 49 | echo "
".$line."
"; 50 | } 51 | } 52 | } elseif ((preg_match("/^hmailserver_\d.*\.log$/",$logfile)) && (!$RawType)) { 53 | 54 | $AllTypes = in_array('ALL', $Types); 55 | 56 | if (file_exists($Filename)) { 57 | $Filesize = filesize($Filename); 58 | $File = fopen($Filename, 'r'); 59 | 60 | if ($File) { 61 | while (($Line = fgets($File)) !== false) { 62 | $Unfiltered = $Line; 63 | $Filtered = $AllTypes ? $Unfiltered : filter_result_type($Unfiltered, $Types); 64 | if (!is_null($Filter)) { 65 | $Filtered = filter_result($Filtered, $Filter, false); 66 | $Filtered = preg_replace("/\w*?$Filter\w*/i", "{em}$0{/em}", $Filtered); 67 | } 68 | 69 | if (!is_null($Filtered)) parse($Filtered); 70 | } 71 | fclose($File); 72 | $out = events(); 73 | } else { 74 | $out = "Error opening log file"; 75 | } 76 | } else { 77 | $out = "Log file not found"; 78 | } 79 | 80 | // header('Content-Type: application/json'); 81 | // print_r($out); 82 | 83 | if (is_array($out) || is_object($out)) { 84 | foreach ($out as $header) { 85 | echo " 86 |
87 |
".implode(" - ",$header[0])."
88 |
"; 89 | foreach ($header[1] as $arrdata) { 90 | if (preg_match("/SMTPD|SMTPC|POP3D|POP3C|IMAPD/",$header[0][0])) { 91 | if (preg_match("/SENT/",$arrdata[1])) {$color = "rgba(255, 255, 0, .25) !important";} elseif (preg_match("/RECEIVED/",$arrdata[1])) {$color = "rgba(0, 255, 0, .25) !important";} else {$color = "none";} 92 | echo " 93 |
94 |
".$arrdata[0]."
95 |
96 |
97 |
".explode(": ",$arrdata[1])[0]."
98 |
:
99 |
100 |
101 |
".explode(": ",$arrdata[1])[1]."
102 |
103 |
104 |
"; 105 | } else { 106 | echo " 107 |
".$arrdata[0]."
108 |
".$arrdata[1]."
"; 109 | } 110 | echo " 111 |
"; 112 | } 113 | echo " 114 |
115 |
"; 116 | } 117 | } else { 118 | echo "No results found"; 119 | } 120 | 121 | } elseif (preg_match("/^WinDefAntiVirus.log$/",$logfile)) { 122 | 123 | if (file_exists($Filename)) { 124 | $Filesize = filesize($Filename); 125 | $File = fopen($Filename, 'r'); 126 | 127 | if ($File) { 128 | while (($Line = fgets($File)) !== false) { 129 | parse_windef($Line); 130 | } 131 | fclose($File); 132 | $out = events(); 133 | } else { 134 | $out = "Error opening log file"; 135 | } 136 | } else { 137 | $out = "Log file not found"; 138 | } 139 | 140 | if (is_array($out) || is_object($out)) { 141 | foreach (array_reverse($out) as $header) { 142 | echo " 143 |
144 |
".$header[0][0]."
145 |
"; 146 | foreach ($header[1] as $arrdata) { 147 | $notice = $arrdata[1]; 148 | $notice = preg_replace("/\[CLEAN\]/","$0",$notice); 149 | $notice = preg_replace("/\[VIRUS\]/","$0",$notice); 150 | $notice = preg_replace("/\[(NOFND|ERROR|FLOCK)\]/","$0",$notice); 151 | echo " 152 |
".$arrdata[0]."
153 |
".$notice." ".$arrdata[2]." ".$arrdata[3]." ".$arrdata[4]." ".$arrdata[5]."
154 |
"; 155 | } 156 | echo " 157 |
158 |
"; 159 | } 160 | } else { 161 | echo "No results found"; 162 | } 163 | 164 | 165 | } else { 166 | 167 | if (file_exists($Filename)) { 168 | $rawFile = fopen($Filename, "r"); 169 | while(! feof($rawFile)) { 170 | $line = fgets($rawFile); 171 | $line = cleanString($line); 172 | $line = cleanNonUTF8($line); 173 | 174 | $line = preg_replace("/(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3})/","$0",$line); 175 | $line = preg_replace("/\[CLEAN\]/","$0",$line); 176 | $line = preg_replace("/\[VIRUS\]/","$0",$line); 177 | $line = preg_replace("/\[(NOFND|ERROR|FLOCK)\]/","$0",$line); 178 | 179 | echo "
".$line."
"; 180 | } 181 | } 182 | } 183 | 184 | echo " 185 | 186 | "; 187 | 188 | ?> -------------------------------------------------------------------------------- /logsearch.php: -------------------------------------------------------------------------------- 1 | Settings->Directories->LogDirectory; 38 | 39 | $logDate_array = array(); 40 | $logFile_array = array(); 41 | $logType_array = array(); 42 | if (is_dir($logFolder)) { 43 | if ($handle = opendir($logFolder)) { 44 | while(($file = readdir($handle)) !== FALSE) { 45 | if (preg_match("/\.(".$log_ext.")$/",$file)) { 46 | $fileNoDate = preg_replace("/_(([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9]))\.(".$log_ext.")|\.(".$log_ext.")/","",$file); 47 | if (!in_array($fileNoDate, $logType_array, true)){ 48 | array_push($logType_array, $fileNoDate); 49 | } 50 | if ((preg_match("/^".$byType."_(([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9]))\.(".$log_ext.")|^".$byType."\.(".$log_ext.")/",$file)) || ($byType === "All Log Types")) { 51 | preg_match('/(([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9]))/',$file,$matches); 52 | if (isset($matches[0])){ 53 | if (!in_array($matches[0], $logDate_array, true)){ 54 | array_push($logDate_array, $matches[0]); 55 | } 56 | } 57 | } 58 | $logFile_array[] = $file; 59 | } 60 | } 61 | closedir($handle); 62 | } 63 | } 64 | rsort($logDate_array); 65 | 66 | 67 | 68 | echo " 69 |
70 |
71 | 81 | 91 | 92 | 93 | 94 |
"; 95 | 96 | if (empty($search)) { 97 | echo " 98 |
99 |
100 | No Search Term Provided 101 |
"; 102 | } else { 103 | $start = microtime(true); 104 | $highlight = "/\w*?".$search."\w*/i"; 105 | $results = array(); 106 | $logIterator = 0; 107 | $lineIterator = 0; 108 | $fileSize = 0; 109 | $fileCount = 0; 110 | $invalidRegEx = false; 111 | 112 | foreach ($logFile_array as $logFile) { 113 | $logFileTypeBase = preg_replace("/_(([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9]))\.(".$log_ext.")$|\.(".$log_ext.")$/","",$logFile); 114 | $logFileDateBase = preg_replace("/\.(".$log_ext.")$/","",$logFile); 115 | if ((($byType === $logFileTypeBase) || ($byType === "All Log Types")) && ((preg_match($logDateRegEx,$logFileDateBase)) || ($byDate === "All Dates"))) { 116 | $fileName = $logFolder.DIRECTORY_SEPARATOR.$logFile; 117 | $logLineIterator = 0; 118 | $lineCounter = 0; 119 | $linePosition = 1; 120 | $data = array(); 121 | 122 | if (file_exists($fileName)) { 123 | $fileSize = $fileSize + filesize($fileName); 124 | $encoding = detect_utf_encoding($fileName); 125 | $file = fopen($fileName, "r"); 126 | if ($file) { 127 | $fileCount++; 128 | while(!feof($file)) { 129 | $line = fgets($file); 130 | if (!is_null($encoding)) { 131 | if ($lineCounter > 0) { 132 | $line = preg_replace('/[^\x00-\x7F]/', '', $line); 133 | } 134 | if ($encoding == "UTF-16LE") { 135 | $line = mb_convert_encoding ($line, "UTF-8", "UTF-16"); 136 | } else { 137 | $line = mb_convert_encoding ($line, "UTF-8", $encoding); 138 | } 139 | $lineCounter++; 140 | } 141 | // if (preg_match("/".$search."/iu",$line)) { 142 | $match = @preg_match("/".$search."/iu", $line, $lineMatches); 143 | if ($match === false) { 144 | $err = preg_last_error(); 145 | if($err == PREG_INTERNAL_ERROR) { 146 | $invalidRegEx = true; 147 | break; 148 | } 149 | } else if ($match) { 150 | 151 | $line = cleanString($line); 152 | $line = cleanNonUTF8($line); 153 | $lineIterator++; 154 | $line = preg_replace($highlight, "$0", $line); 155 | 156 | if (!isset($results[$logIterator])) { 157 | $results[$logIterator][0] = array($logFile, $logLineIterator); 158 | } 159 | $results[$logIterator][0][1] = $logLineIterator + 1; 160 | $results[$logIterator][1][] = array($linePosition, $line); 161 | 162 | $logLineIterator++; 163 | } else { 164 | // do nothing 165 | } 166 | $linePosition++; 167 | } 168 | fclose($file); 169 | } else { 170 | echo " 171 |
172 |
173 | Error opening log file: ".$logFile." 174 |
"; 175 | } 176 | $logIterator++; 177 | } else { 178 | echo " 179 |
180 |
181 | Log file not found: ".$logFile." 182 |
"; 183 | } 184 | } 185 | } 186 | 187 | $end = microtime(true); 188 | $time = number_format(($end - $start), 2); 189 | 190 | if ($lineIterator === 0) { 191 | echo " 192 |
193 |
"; 194 | if ($invalidRegEx) { 195 | echo " 196 | Invalid Regular Expression Used In Search"; 197 | } else { 198 | echo " 199 | No Results"; 200 | } 201 | echo " 202 |
"; 203 | } else { 204 | echo " 205 |
".number_format($lineIterator)." results found among ".$fileCount." files totalling ".formatBytes($fileSize)." searched in ".$time." seconds

206 |
"; 207 | foreach ($results as $result) { 208 | echo " 209 |
".$result[0][0]." : ".$result[0][1]." Results
"; 210 | foreach ($result[1] as $lineresult) { 211 | echo " 212 |
213 |
Line ".number_format($lineresult[0]).".
214 |
".$lineresult[1]."
215 |
"; 216 | } 217 | echo "
"; 218 | } 219 | echo " 220 |
"; 221 | } 222 | } 223 | echo " 224 |
225 |
"; 226 | 227 | include_once("foot.php"); 228 | ?> -------------------------------------------------------------------------------- /msg.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT id FROM hm_log_msg WHERE id > ".$msgid." ORDER BY id ASC LIMIT 1;"); 18 | $nextid_sql->execute(); 19 | $nextid = $nextid_sql->fetchColumn(); 20 | if ($nextid) {$next = "Next Message";} else {$next = "Next Message";} 21 | 22 | $lastid_sql = $pdo->prepare("SELECT id FROM hm_log_msg WHERE id < ".$msgid." ORDER BY id DESC LIMIT 1;"); 23 | $lastid_sql->execute(); 24 | $lastid = $lastid_sql->fetchColumn(); 25 | if ($lastid) {$last = "Previous Message";} else {$last = "Previous Message";} 26 | 27 | echo " 28 |

29 |
30 |
31 |

Msg ID: ".$msgid."

32 |
33 |
34 |

".$last." | ".$next."

35 |
36 |
37 |
"; 38 | 39 | $sql = $pdo->prepare("SELECT * FROM hm_log_msg WHERE id = ".$msgid.";"); 40 | $sql->execute(); 41 | while($row = $sql->fetch(PDO::FETCH_ASSOC)){ 42 | $envelopeTo = $row['envelopeTo']; 43 | echo " 44 |
Subject:
".$row['subject']." 
45 |
Envelope From:
".$row['envelopeFrom']." 
46 |
From Header:
".htmlspecialchars($row['headerFrom'])." 
47 |
envelopeTo:
".$row['envelopeTo']." 
48 |
To Header:
".htmlspecialchars($row['headerTo'])." 
49 |
Status Code:
".getStatusCodeDescription($row['statuscode'])." 
"; 50 | } 51 | 52 | $awstatssql = $pdo->prepare(" 53 | SELECT * FROM (SELECT *, timestamp AS msgts, INET6_NTOA(ip) AS ipm, id AS amsgid FROM hm_log_msg) a 54 | LEFT JOIN (SELECT *, timestamp AS awsts FROM hm_log_awstats) b ON a.envelopeFrom = b.envelopeFrom AND a.envelopeTo = b.envelopeTo 55 | WHERE amsgid = ".$msgid." AND awsts < msgts + INTERVAL 30 SECOND AND awsts > msgts - INTERVAL 30 SECOND; 56 | "); 57 | $awstatssql->execute(); 58 | while($row = $awstatssql->fetch(PDO::FETCH_ASSOC)){ 59 | echo " 60 |
envelopeFrom Connection:
".$row['connsender']."
61 |
envelopeTo Connection:
".$row['connrecipient']."
"; 62 | } 63 | 64 | $countAttr_sql = $pdo->prepare("SELECT * FROM hm_log_attr WHERE msgid = ".$msgid.";"); 65 | $countAttr_sql->execute(); 66 | 67 | function geoip($matches) {return "".$matches[0]."";} 68 | 69 | while($rowAttr = $countAttr_sql->fetch(PDO::FETCH_ASSOC)){ 70 | $attrValue = nl2br(preg_replace('/ /',' ',$rowAttr['value'])); 71 | $attrValue = preg_replace_callback($regexIP,'geoip',$attrValue); 72 | if ($rowAttr['item']=="List-Unsubscribe") { 73 | echo " 74 |
".$rowAttr['item'].":
".nl2br(htmlspecialchars($rowAttr['value']))." [UNSUBSCRIBE]
"; 75 | } else { 76 | echo " 77 |
".$rowAttr['item'].":
".$attrValue."
"; 78 | } 79 | } 80 | 81 | echo " 82 |
83 |
"; 84 | 85 | $datasql = $pdo->prepare(" 86 | SELECT * FROM (SELECT *, timestamp AS msgts, INET6_NTOA(ip) AS ipm, id AS amsgid FROM hm_log_msg) m 87 | JOIN (SELECT *, timestamp AS smtpts, INET6_NTOA(ip) AS ipa FROM hm_log_smtp) b ON m.ipm = b.ipa 88 | WHERE amsgid = ".$msgid." AND smtpts < msgts + INTERVAL 60 SECOND AND smtpts > msgts - INTERVAL 60 SECOND AND port REGEXP '25|465|587' 89 | ORDER BY smtpts DESC; 90 | "); 91 | 92 | $datasql->execute(); 93 | 94 | echo " 95 |
96 |

SMTP Events:

97 |
98 |
99 |
100 |
Timestamp
101 |
IP
102 |
Port
103 |
Country
104 |
Event
105 |
Reason
106 |
HELO
107 |
PTR
108 |
Status
109 |
Msg ID
110 |
"; 111 | 112 | while($datarow = $datasql->fetch(PDO::FETCH_ASSOC)){ 113 | $ipSplit = explode(".",$datarow['ipa']); 114 | echo " 115 |
116 |
".date("y/m/d H:i:s", strtotime($datarow['timestamp']))." 
117 | 118 |
".$datarow['port']." 
119 | 120 |
".$datarow['event']." 
121 |
".$datarow['reason']." 
122 | 123 | "; 124 | if ($datarow['acc']==1) {$display_status="Accepted";} else {$display_status="Rejected";} 125 | echo " 126 |
".$display_status."
"; 127 | if (!$datarow['msgid']) { 128 | echo " 129 |
 
"; 130 | } else { 131 | echo " 132 | "; 133 | } 134 | echo " 135 |
"; 136 | } 137 | echo " 138 |
139 |
140 |
141 | 142 |
143 |

144 |
145 | 146 |
147 |
"; 148 | 149 | include_once("foot.php"); 150 | ?> -------------------------------------------------------------------------------- /messages.php: -------------------------------------------------------------------------------- 1 | 36 |

Messages

37 | The Messages Log contains logged message metadata. 38 | Click on the subject or ID to view the message. You can search by date or by metadata or message body content. 39 | Its recommended to periodically expire message data. Bolded rows = spam action taken. 40 |
41 |

"; 42 | echo " 43 | 53 | 54 | 55 | 56 | 57 | 58 |
59 |
60 | 61 | 62 |
"; 63 | 64 | if ($search==""){$search_SQL = "";} else {$search_SQL = " AND (envelopeFrom LIKE '%".$search."%' OR envelopeTo LIKE '%".$search."%' OR subject REGEXP '".$search."' OR message REGEXP '".$search."')";} 65 | if ($dateFrom==""){$dateFrom_SQL = "";} else {$dateFrom_SQL = " AND DATE(timestamp) >= '".$dateFrom."'";} 66 | if ($dateTo==""){$dateTo_SQL = "";} else {$dateTo_SQL = " AND DATE(timestamp) <= '".$dateTo."'";} 67 | if ($status==""){$status_SQL = "";} else {$status_SQL = " AND statuscode = '".$status."'";} 68 | if ($from==""){$from_SQL = "";} else {$from_SQL = " AND envelopeFrom = '".$from."'";} 69 | if ($to==""){$to_SQL = "";} else {$to_SQL = " AND envelopeTo = '".$to."'";} 70 | 71 | $offset = ($page-1) * $no_of_records_per_page; 72 | $total_pages_sql = $pdo->prepare(" 73 | SELECT COUNT(*) FROM hm_log_msg 74 | WHERE LENGTH(id) > 0 ".$search_SQL.$status_SQL.$dateFrom_SQL.$dateTo_SQL.$from_SQL.$to_SQL."; 75 | "); 76 | $total_pages_sql->execute(); 77 | $total_rows = $total_pages_sql->fetchColumn(); 78 | $total_pages = ceil($total_rows / $no_of_records_per_page); 79 | 80 | $sql = $pdo->prepare(" 81 | SELECT *, INET6_NTOA(ip) AS ipa FROM hm_log_msg 82 | WHERE LENGTH(id) > 0 ".$search_SQL.$status_SQL.$dateFrom_SQL.$dateTo_SQL.$from_SQL.$to_SQL." 83 | ORDER BY id DESC 84 | LIMIT ".$offset.", ".$no_of_records_per_page."; 85 | "); 86 | $sql->execute(); 87 | 88 | if ($search==""){ 89 | $search_res=""; 90 | } else { 91 | $search_res=" for search term \"".$search."\""; 92 | } 93 | 94 | if ($total_pages < 2){ 95 | $pagination = ""; 96 | } else { 97 | $pagination = "(Page: ".number_format($page)." of ".number_format($total_pages).")"; 98 | } 99 | 100 | if ($total_rows == 1){$singular = '';} else {$singular= 's';} 101 | if ($total_rows == 0){ 102 | if ($search == "" && $status == "" && $dateFrom == "" && $dateTo == ""){ 103 | echo "Please enter a search term"; 104 | } else { 105 | echo "No results ".$search_res; 106 | } 107 | } else { 108 | echo " 109 | Results ".$search_res.": ".number_format($total_rows)." Record".$singular." ".$pagination."
110 |
111 |
112 |
Timestamp
113 |
IP
114 |
From
115 |
To
116 |
Subject
117 |
Status
118 |
ID
119 |
"; 120 | 121 | while($row = $sql->fetch(PDO::FETCH_ASSOC)){ 122 | if ($row['spamadjusted'] == 1) {$spamadjusted = " spamadjusted";} else {$spamadjusted = "";} 123 | echo " 124 |
125 |
".date("y/m/d H:i:s", strtotime($row['timestamp']))."
126 | 127 | 128 | 129 | "; 130 | if ($row['statuscode'] != 250) { 131 | echo " 132 |
".$row['statuscode']."
"; 133 | } else { 134 | echo " 135 |
".$row['statuscode']."
"; 136 | } 137 | echo " 138 | 139 |
"; 140 | } 141 | echo " 142 |
"; // End table 143 | 144 | if ($search==""){$search_page = "";} else {$search_page = "&search=".$search;} 145 | if ($status==""){$status_page = "";} else {$status_page = "&status=".$status;} 146 | if ($dateFrom==""){$dateFrom_page = "";} else {$dateFrom_page = "&dateFrom=".$dateFrom;} 147 | if ($dateTo==""){$dateTo_page = "";} else {$dateTo_page = "&dateTo=".$dateTo;} 148 | 149 | if ($total_pages == 1){ 150 | echo ""; 151 | } else { 152 | echo " 153 | 154 | 162 | "; 163 | } 164 | } 165 | 166 | echo " 167 |
"; 168 | 169 | include_once("foot.php"); 170 | ?> 171 | -------------------------------------------------------------------------------- /css/svgMap.css: -------------------------------------------------------------------------------- 1 | /*! svgMap | https://github.com/StephanWagner/svgMap | MIT License | Copyright Stephan Wagner | https://stephanwagner.me */ 2 | .svgMap-wrapper, 3 | .svgMap-container { 4 | position: relative; 5 | } 6 | 7 | .svgMap-block-zoom-notice { 8 | position: absolute; 9 | z-index: 2; 10 | top: 100%; 11 | left: 0; 12 | right: 0; 13 | bottom: 0; 14 | background: rgba(0, 0, 0, 0.8); 15 | pointer-events: none; 16 | opacity: 0; 17 | color: #fff; 18 | transition: opacity 250ms; 19 | } 20 | .svgMap-block-zoom-notice-active .svgMap-block-zoom-notice { 21 | pointer-events: all; 22 | top: 0; 23 | opacity: 1; 24 | } 25 | .svgMap-block-zoom-notice > div { 26 | position: absolute; 27 | top: 50%; 28 | left: 0; 29 | right: 0; 30 | text-align: center; 31 | padding: 0 32px; 32 | transform: translateY(-50%); 33 | font-size: 28px; 34 | } 35 | @media (max-width: 900px) { 36 | .svgMap-block-zoom-notice > div { 37 | font-size: 22px; 38 | } 39 | } 40 | 41 | .svgMap-map-wrapper { 42 | position: relative; 43 | width: 100%; 44 | padding-top: 50%; 45 | overflow: hidden; 46 | background: #fff; 47 | color: #111; 48 | } 49 | .svgMap-map-wrapper * { 50 | box-sizing: border-box; 51 | } 52 | .svgMap-map-wrapper :focus:not(:focus-visible) { 53 | outline: 0; 54 | } 55 | .svgMap-map-wrapper .svgMap-map-image { 56 | display: block; 57 | position: absolute; 58 | top: 0; 59 | left: 0; 60 | width: 100%; 61 | height: 100%; 62 | margin: 0; 63 | } 64 | .svgMap-map-wrapper .svgMap-map-controls-wrapper { 65 | position: absolute; 66 | bottom: 10px; 67 | left: 10px; 68 | z-index: 1; 69 | display: flex; 70 | overflow: hidden; 71 | border-radius: 2px; 72 | box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1); 73 | } 74 | .svgMap-map-wrapper .svgMap-map-controls-zoom, 75 | .svgMap-map-wrapper .svgMap-map-controls-move { 76 | display: flex; 77 | margin-right: 5px; 78 | overflow: hidden; 79 | background: #fff; 80 | } 81 | .svgMap-map-wrapper .svgMap-map-controls-zoom:last-child, 82 | .svgMap-map-wrapper .svgMap-map-controls-move:last-child { 83 | margin-right: 0; 84 | } 85 | .svgMap-map-wrapper .svgMap-control-button { 86 | background-color: transparent; 87 | border: none; 88 | border-radius: 0; 89 | color: inherit; 90 | font: inherit; 91 | line-height: inherit; 92 | margin: 0; 93 | padding: 0; 94 | overflow: visible; 95 | text-transform: none; 96 | -webkit-appearance: none; 97 | -moz-appearance: none; 98 | appearance: none; 99 | cursor: pointer; 100 | width: 30px; 101 | height: 30px; 102 | position: relative; 103 | } 104 | .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:before, .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:after { 105 | content: ""; 106 | position: absolute; 107 | top: 50%; 108 | left: 50%; 109 | transform: translate(-50%, -50%); 110 | background: #666; 111 | transition: background-color 250ms; 112 | } 113 | .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:before { 114 | width: 11px; 115 | height: 3px; 116 | } 117 | .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button.svgMap-zoom-reset-button::before { 118 | width: 11px; 119 | height: 11px; 120 | background: none; 121 | border: 2px solid #666; 122 | } 123 | @media (hover: hover) { 124 | .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:hover:before, .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:hover:after { 125 | background: #111; 126 | } 127 | } 128 | .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:active:before, .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button:active:after { 129 | background: #111; 130 | } 131 | .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button.svgMap-disabled:before, .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button.svgMap-disabled:after { 132 | background: #ccc; 133 | } 134 | .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-button.svgMap-zoom-reset-button.svgMap-disabled:before { 135 | border: 2px solid #ccc; 136 | background: none; 137 | } 138 | .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-in-button { 139 | margin: 1px 0 1px 1px; 140 | } 141 | .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-in-button:after { 142 | width: 3px; 143 | height: 11px; 144 | } 145 | .svgMap-map-wrapper .svgMap-control-button.svgMap-zoom-out-button { 146 | margin: 1px 1px 1px 0; 147 | } 148 | .svgMap-map-wrapper .svgMap-map-continent-controls-wrapper { 149 | position: absolute; 150 | top: 10px; 151 | right: 10px; 152 | z-index: 1; 153 | display: flex; 154 | border-radius: 2px; 155 | box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1); 156 | } 157 | .svgMap-map-wrapper .svgMap-country { 158 | stroke: #fff; 159 | stroke-width: 1; 160 | stroke-linejoin: round; 161 | vector-effect: non-scaling-stroke; 162 | transition: fill 250ms, stroke 250ms; 163 | } 164 | .svgMap-map-wrapper .svgMap-country[data-link] { 165 | cursor: pointer; 166 | } 167 | @media (hover: hover) { 168 | .svgMap-map-wrapper .svgMap-country:hover { 169 | stroke: #333; 170 | stroke-width: 1.5; 171 | } 172 | } 173 | .svgMap-map-wrapper .svgMap-country.svgMap-active { 174 | stroke: #333; 175 | stroke-width: 1.5; 176 | } 177 | 178 | .svgMap-tooltip { 179 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); 180 | position: absolute; 181 | z-index: 2; 182 | border-radius: 2px; 183 | background: rgba(255,255,255,0.75); 184 | transform: translate(-50%, -100%); 185 | border-bottom: 1px solid #000; 186 | display: none; 187 | pointer-events: none; 188 | min-width: 60px; 189 | } 190 | .svgMap-tooltip.svgMap-tooltip-flipped { 191 | transform: translate(-50%, 0); 192 | border-bottom: 0; 193 | border-top: 1px solid #000; 194 | } 195 | .svgMap-tooltip.svgMap-active { 196 | display: block; 197 | } 198 | .svgMap-tooltip .svgMap-tooltip-content-container { 199 | position: relative; 200 | padding: 10px 20px; 201 | } 202 | .svgMap-tooltip .svgMap-tooltip-content-container .svgMap-tooltip-flag-container { 203 | text-align: center; 204 | margin: 2px 0 5px; 205 | } 206 | .svgMap-tooltip .svgMap-tooltip-content-container .svgMap-tooltip-flag-container.svgMap-tooltip-flag-container-emoji { 207 | font-size: 50px; 208 | line-height: 0; 209 | padding: 25px 0 15px; 210 | } 211 | .svgMap-tooltip .svgMap-tooltip-content-container .svgMap-tooltip-flag-container .svgMap-tooltip-flag { 212 | display: block; 213 | margin: auto; 214 | width: auto; 215 | height: 32px; 216 | padding: 2px; 217 | background: rgba(0, 0, 0, 0.15); 218 | border-radius: 2px; 219 | } 220 | .svgMap-tooltip .svgMap-tooltip-title { 221 | white-space: nowrap; 222 | font-size: 18px; 223 | line-height: 28px; 224 | padding: 0 0 8px; 225 | text-align: center; 226 | } 227 | .svgMap-tooltip .svgMap-tooltip-content { 228 | white-space: nowrap; 229 | text-align: center; 230 | font-size: 14px; 231 | font-weight: bold; 232 | color: #000; 233 | margin: -5px 0 0; 234 | } 235 | .svgMap-tooltip .svgMap-tooltip-content table { 236 | padding: 0; 237 | border-spacing: 0px; 238 | margin: auto; 239 | } 240 | .svgMap-tooltip .svgMap-tooltip-content table td { 241 | padding: 2px 0; 242 | text-align: left; 243 | } 244 | .svgMap-tooltip .svgMap-tooltip-content table td span { 245 | color: #111; 246 | } 247 | .svgMap-tooltip .svgMap-tooltip-content table td:first-child { 248 | padding-right: 10px; 249 | text-align: right; 250 | } 251 | .svgMap-tooltip .svgMap-tooltip-content table td sup { 252 | vertical-align: baseline; 253 | position: relative; 254 | top: -5px; 255 | } 256 | .svgMap-tooltip .svgMap-tooltip-content .svgMap-tooltip-no-data { 257 | padding: 2px 0; 258 | color: #777; 259 | font-style: italic; 260 | } 261 | .svgMap-tooltip .svgMap-tooltip-pointer { 262 | position: absolute; 263 | top: 100%; 264 | left: 50%; 265 | transform: translateX(-50%); 266 | overflow: hidden; 267 | height: 10px; 268 | width: 30px; 269 | } 270 | .svgMap-tooltip .svgMap-tooltip-pointer:after { 271 | content: ""; 272 | width: 20px; 273 | height: 20px; 274 | background: #fff; 275 | border: 1px solid #000; 276 | position: absolute; 277 | bottom: 6px; 278 | left: 50%; 279 | transform: translateX(-50%) rotate(45deg); 280 | } 281 | .svgMap-tooltip.svgMap-tooltip-flipped .svgMap-tooltip-pointer { 282 | bottom: auto; 283 | top: -10px; 284 | transform: translateX(-50%) scaleY(-1); 285 | } 286 | /*# sourceMappingURL=svgMap.css.map */ 287 | -------------------------------------------------------------------------------- /autoban.php: -------------------------------------------------------------------------------- 1 | "") { 25 | $search = $_GET['search']; 26 | $search_SQL = "AND (INET_NTOA(rangelowerip1) LIKE '%".$search."%' OR rangename LIKE '%".$search."%')"; 27 | $search_ph = $search; 28 | $search_page = "&search=".$search; 29 | } else { 30 | $search = ""; 31 | $search_SQL = ""; 32 | $search_ph = ""; 33 | $search_page = ""; 34 | } 35 | } else { 36 | $search = ""; 37 | $search_SQL = ""; 38 | $search_ph = ""; 39 | $search_page = ""; 40 | } 41 | if (isset($_GET['clear'])) { 42 | redirect("./autoban.php"); 43 | } 44 | 45 | if (isset($_GET['sort'])) { 46 | $sort_val = $_GET['sort']; 47 | $sort_page = "&sort=".$sort_val; 48 | if ($_GET['sort'] == "newest") {$sort_sql = "rangeexpirestime DESC"; $sort_ph = "↑ Expiration";} 49 | else if ($_GET['sort'] == "oldest") {$sort_sql = "rangeexpirestime ASC"; $sort_ph = "↓ Expiration";} 50 | else if ($_GET['sort'] == "priorityasc") {$sort_sql = "rangepriorityid ASC"; $sort_ph = "↑ Priority";} 51 | else if ($_GET['sort'] == "prioritydesc") {$sort_sql = "rangepriorityid DESC"; $sort_ph = "↓ Priority";} 52 | else if ($_GET['sort'] == "descriptionasc") {$sort_sql = "rangename ASC"; $sort_ph = "↑ Description";} 53 | else if ($_GET['sort'] == "descriptiondesc") {$sort_sql = "rangename DESC"; $sort_ph = "↓ Description";} 54 | else if ($_GET['sort'] == "ipasc") {$sort_sql = "rangelowerip1 ASC"; $sort_ph = "↑ IP";} 55 | else if ($_GET['sort'] == "ipdesc") {$sort_sql = "rangelowerip1 DESC"; $sort_ph = "↓ IP";} 56 | else {unset($_GET['sort']); $sort_sql = ""; $sort_ph = "Sort";} 57 | } else { 58 | $sort_val = ""; 59 | $sort_sql = ""; 60 | $sort_ph = "Sort"; 61 | $sort_page = ""; 62 | } 63 | if (isset($_GET['sort'])) { 64 | $orderby = "ORDER BY "; 65 | } else { 66 | $orderby = "ORDER BY rangeexpirestime DESC"; 67 | } 68 | if(isset($_POST['delete'])){ 69 | if(!empty($_POST['delete'])) { 70 | $sql_ip = "DELETE FROM hm_securityranges WHERE rangeid = '".$_POST['delete']."';"; 71 | $pdo->exec($sql_ip); 72 | } 73 | } 74 | 75 | echo " 76 |
77 |
78 |
79 | "; 90 | 91 | echo " 92 | 93 | 94 | 95 |
96 |
97 |
98 | 99 |
"; 100 | 101 | 102 | $offset = ($page-1) * $no_of_records_per_page; 103 | 104 | $total_pages_sql = $pdo->prepare(" 105 | SELECT Count( * ) AS count 106 | FROM hm_securityranges 107 | WHERE rangeexpires = 1 ".$search_SQL 108 | ); 109 | $total_pages_sql->execute(); 110 | $total_rows = $total_pages_sql->fetchColumn(); 111 | $total_pages = ceil($total_rows / $no_of_records_per_page); 112 | 113 | $sql_query = " 114 | SELECT *, INET_NTOA(rangelowerip1) AS ipaddress 115 | FROM hm_securityranges 116 | WHERE rangeexpires = 1 ".$search_SQL." 117 | ".$orderby.$sort_sql." 118 | LIMIT ".$offset.", ".$no_of_records_per_page; 119 | $sql = $pdo->prepare($sql_query); 120 | $sql->execute(); 121 | 122 | if ($search==""){ 123 | $search_res=""; 124 | } else { 125 | $search_res=" for \"".$search."\""; 126 | } 127 | 128 | if ($total_pages < 2){ 129 | $pagination = ""; 130 | } else { 131 | $pagination = "(Page: ".number_format($page)." of ".number_format($total_pages).")"; 132 | } 133 | 134 | if ($total_rows == 1){$singular = '';} else {$singular= 's';} 135 | if ($total_rows == 0){ 136 | if ($search == "" && $sort_val == ""){ 137 | echo "Please enter a search term"; 138 | } else { 139 | echo "No results ".$search_res; 140 | } 141 | } else { 142 | echo " 143 | Results ".$search_res.": ".number_format($total_rows)." Record".$singular." ".$pagination."
144 |
145 |
146 |
147 |
IP Address
148 |
Description
149 |
Priority
150 |
Expiration (minutes)
151 |
Delete
152 |
"; 153 | 154 | while($row = $sql->fetch(PDO::FETCH_ASSOC)){ 155 | $expiry = new DateTime($row['rangeexpirestime']); 156 | $timenow = new DateTime(date("Y-m-d H:i:s", time())); 157 | $expireTime = date_diff($timenow, $expiry, true); 158 | $minutes = $expireTime->days * 24 * 60; 159 | $minutes += $expireTime->h * 60; 160 | $minutes += $expireTime->i; 161 | echo " 162 |
163 | 164 |
".$row['rangename']." 
165 |
".$row['rangepriorityid']." 
166 |
".number_format($minutes)." 
167 |
168 | 169 |
170 |
"; 171 | } 172 | echo " 173 |
174 |
"; // End table 175 | 176 | if ($total_pages == 1){ 177 | echo ""; 178 | } else { 179 | echo " 180 | 181 | 189 | "; 190 | } 191 | } 192 | 193 | // JS autocomplete 194 | echo " 195 | 206 | 211 | "; 220 | 221 | ?> 222 | 223 |
224 | 225 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 24 |

Today's Activity:

25 |
26 |
27 |
28 | Connections
29 | "; 30 | include("./includes/dialTodayConnectCG.php"); 31 | echo " 32 | 33 |
34 |
35 |
36 |
37 | Rejections
38 | "; 39 | include("./includes/dialTodayRejectCG.php"); 40 | echo " 41 | 42 |
43 |
44 |
45 |
46 | 47 |
48 |
49 | Messages
50 | "; 51 | include("./includes/dialTodayMsgsCG.php"); 52 | echo " 53 | 54 |
55 |
56 |
57 | Red/Yellow border represents daily max drawn from available data. The Yellow Zone represents 75% to 100% of daily max. The Red Zone represents 100% to 125% of daily max. Therefore, if a dial is in the Red Zone, it is a new record daily high. 58 | "; 59 | 60 | // TODAY REASONS CHARTS 61 | 62 | echo " 63 |
64 |
65 |

Accepted Today:

66 |
67 | 68 |
69 | Today's client connections marked \"Accepted\" by reason (log scale). 70 |
71 |
72 |

Rejected Today:

73 |
74 | 75 |
76 | Today's client connections marked \"Rejected\" by reason (log scale). 77 |
78 |
79 |
"; 80 | 81 | include("./includes/chartTodayConnections.php"); 82 | include("./includes/chartTodayRejections.php"); 83 | 84 | // ALL-TIME PER DAY CONNECTION / REJECTION CHARTS 85 | 86 | echo " 87 |
88 |
89 |

SMTP Connections Per Day:

90 |
91 | 92 |
93 | Client connections to the server per day from available data. 94 |
95 |
96 |

SMTP Rejections Per Day:

97 |
98 | 99 |
100 | Counted reject reasons per day from available data. Reject reasons are not unique. There can be several or none for any given SMTP connection. 101 |
102 |
103 |
"; 104 | 105 | // ALL-TIME PER HOUR CONNECTION / REJECTION CHARTS 106 | 107 | echo " 108 |
109 |
110 |

Connections Averaged Per Hour:

111 |
112 | 113 |
114 | Client connections to the server averaged per hour from available data. 115 |
116 |
117 |

Rejections Averaged Per Hour:

118 |
119 | 120 |
121 | Counted reject reasons averaged per hour from available data. Reject reasons are not unique. There can be several or none for any given SMTP connection. 122 |
123 |
124 |
"; 125 | 126 | include("./includes/chartConnectionsPerDayCJS.php"); 127 | include("./includes/chartRejectionsPerDayCJS.php"); 128 | include("./includes/chartConnectionsPerHourCJS.php"); 129 | include("./includes/chartRejectionsPerHourCJS.php"); 130 | 131 | // Map 132 | 133 | echo " 134 |
135 |

Good Boy Map:

136 |
137 | Rejection ratio shows who's been good or bad. The higher the ratio of accepts to rejects shows darker, and vice verse. Darker red shows who behaves better."; 138 | include("./includes/mapData.php"); 139 | echo " 140 | 141 |
"; 142 | 143 | // envelopeFrom-envelopeTo Activity 144 | 145 | echo " 146 |
147 |
148 |

Most Active Senders

149 |
150 |
151 |
Envelope From
152 |
Count
153 |
Percent
154 |
155 | ".$EnvFromRows." 156 |
157 | Most active senders drawn from available data. 158 |
159 |
160 |

Most Active Recipients

161 |
162 |
163 |
Envelope To
164 |
Count
165 |
Percent
166 |
167 | ".$EnvToRows." 168 |
169 | Most active recipients drawn from available data. 170 |
171 |
172 |
"; 173 | 174 | // Reasons 175 | 176 | echo " 177 |
178 | 179 |
180 |

Log Reasons: Accepted

181 |
182 |
183 |
Reason
184 |
Count
185 |
Percent
186 |
187 | ".$ReasonAccRows." 188 |
189 | Accepted reasons enumerated drawn from available data. 190 |
191 |
192 |

Log Reasons: Rejected

193 |
194 |
195 |
Reason
196 |
Count
197 |
Percent
198 |
199 | ".$ReasonRejRows." 200 |
201 | Rejected reasons enumerated drawn from available data. 202 |
203 |
204 |
"; 205 | 206 | // TOP 10's 207 | 208 | echo " 209 |
210 |
211 |

Top 10 IPs:

212 |
213 |
214 |
IP Address
215 |
Country
216 |
Hits
217 |
Percent
218 |
219 | ".$TopTenIPsRows." 220 |
221 | Top 10 IP activity drawn from available data. 222 |
"; 223 | 224 | echo " 225 |
226 |

Top 10 countries:

227 |
228 |
229 |
Country
230 |
Hits
231 |
Percent
232 |
233 | ".$TopTenCountriesRows." 234 |
235 | Top 10 country activity drawn from available data. 236 |
237 |
238 |
"; 239 | 240 | include_once("foot.php"); 241 | ?> -------------------------------------------------------------------------------- /ids.php: -------------------------------------------------------------------------------- 1 | "; 28 | $search_page = "&search=".$search; 29 | } else { 30 | $search = ""; 31 | $search_SQL = ""; 32 | $search_ph = ""; 33 | // $search_hidden = ""; 34 | $search_page = ""; 35 | } 36 | if (isset($_GET['clear'])) { 37 | redirect("./ids.php"); 38 | } 39 | 40 | if (isset($_GET['sort1'])) { 41 | $sort1_val = $_GET['sort1']; 42 | $sort1_page = "&sort1=".$sort1_val; 43 | $sort1_hidden = ""; 44 | if ($_GET['sort1'] == "hitsasc") {$sort1_sql = "hits ASC"; $sort1_ph = "↑ Hits";} 45 | else if ($_GET['sort1'] == "hitsdesc") {$sort1_sql = "hits DESC"; $sort1_ph = "↓ Hits";} 46 | else if ($_GET['sort1'] == "newest") {$sort1_sql = "DATE(timestamp) ASC"; $sort1_ph = "↑ Date";} 47 | else if ($_GET['sort1'] == "oldest") {$sort1_sql = "DATE(timestamp) DESC"; $sort1_ph = "↓ Date";} 48 | else if ($_GET['sort1'] == "countryasc") {$sort1_sql = "trimcountry ASC"; $sort1_ph = "↑ Country";} 49 | else if ($_GET['sort1'] == "countrydesc") {$sort1_sql = "trimcountry DESC"; $sort1_ph = "↓ Country";} 50 | else if ($_GET['sort1'] == "ipasc") {$sort1_sql = "INET_ATON(ipaddress) ASC"; $sort1_ph = "↑ IP";} 51 | else if ($_GET['sort1'] == "ipdesc") {$sort1_sql = "INET_ATON(ipaddress) DESC"; $sort1_ph = "↓ IP";} 52 | else {unset($_GET['sort1']); $sort1_sql = ""; $sort1_ph = "Sort";} 53 | } else { 54 | $sort1_val = ""; 55 | $sort1_sql = ""; 56 | $sort1_ph = "Sort"; 57 | $sort1_page = ""; 58 | // $sort1_hidden = ""; 59 | } 60 | if (isset($_GET['sort2'])) { 61 | $sort2_val = $_GET['sort2']; 62 | $sort2_page = "&sort2=".$sort2_val; 63 | $sort2_hidden = ""; 64 | if ($_GET['sort2'] == "hitsasc") {$sort2_sql = ", hits ASC"; $sort2_ph = "↑ Hits";} 65 | else if ($_GET['sort2'] == "hitsdesc") {$sort2_sql = ", hits DESC"; $sort2_ph = "↓ Hits";} 66 | else if ($_GET['sort2'] == "newest") {$sort2_sql = ", timestamp ASC"; $sort2_ph = "↑ Date";} 67 | else if ($_GET['sort2'] == "oldest") {$sort2_sql = ", timestamp DESC"; $sort2_ph = "↓ Date";} 68 | else if ($_GET['sort2'] == "countryasc") {$sort2_sql = ", trimcountry ASC"; $sort2_ph = "↑ Country";} 69 | else if ($_GET['sort2'] == "countrydesc") {$sort2_sql = ", trimcountry DESC"; $sort2_ph = "↓ Country";} 70 | else if ($_GET['sort2'] == "ipasc") {$sort2_sql = ", INET_ATON(ipaddress) ASC"; $sort2_ph = "↑ IP";} 71 | else if ($_GET['sort2'] == "ipdesc") {$sort2_sql = ", INET_ATON(ipaddress) DESC"; $sort2_ph = "↓ IP";} 72 | else {unset($_GET['sort2']); $sort2_sql = ""; $sort2_ph = "Sort";} 73 | } else { 74 | $sort2_val = ""; 75 | $sort2_sql = ""; 76 | $sort2_ph = "Sort"; 77 | $sort2_page = ""; 78 | // $sort2_hidden = ""; 79 | } 80 | if ((isset($_GET['sort1'])) || (isset($_GET['sort2']))) { 81 | $orderby = "ORDER BY "; 82 | } else { 83 | $orderby = "ORDER BY timestamp DESC"; 84 | } 85 | if(isset($_POST['delete'])){ 86 | if(!empty($_POST['delete'])) { 87 | $sql_ip = "DELETE FROM hm_ids WHERE ipaddress = '".$_POST['delete']."';"; 88 | $pdo->exec($sql_ip); 89 | } 90 | } 91 | 92 | echo " 93 |
94 |
95 |
96 | "; 107 | 108 | if (isset($_GET['sort1'])) { 109 | echo " 110 | "; 121 | } 122 | echo " 123 | 124 | 125 | 126 |
127 |
128 |
129 | 130 |
"; 131 | 132 | 133 | $offset = ($page-1) * $no_of_records_per_page; 134 | 135 | $total_pages_sql = $pdo->prepare(" 136 | SELECT Count( * ) AS count 137 | FROM hm_ids 138 | ".$search_SQL." 139 | "); 140 | $total_pages_sql->execute(); 141 | $total_rows = $total_pages_sql->fetchColumn(); 142 | $total_pages = ceil($total_rows / $no_of_records_per_page); 143 | 144 | $sql_query = " 145 | SELECT 146 | TRIM(BOTH '\"' FROM country) AS trimcountry, 147 | country, 148 | ipaddress, 149 | timestamp, 150 | hits 151 | FROM hm_ids 152 | ".$search_SQL." 153 | ".$orderby.$sort1_sql.$sort2_sql." 154 | LIMIT ".$offset.", ".$no_of_records_per_page; 155 | $sql = $pdo->prepare($sql_query); 156 | $sql->execute(); 157 | 158 | if ($search==""){ 159 | $search_res=""; 160 | } else { 161 | $search_res=" for \"".$search."\""; 162 | } 163 | 164 | if ($total_pages < 2){ 165 | $pagination = ""; 166 | } else { 167 | $pagination = "(Page: ".number_format($page)." of ".number_format($total_pages).")"; 168 | } 169 | 170 | if ($total_rows == 1){$singular = '';} else {$singular= 's';} 171 | if ($total_rows == 0){ 172 | if ($search == "" && $sort1_val == "" && $sort2_val == ""){ 173 | echo "Please enter a search term"; 174 | } else { 175 | echo "No results ".$search_res; 176 | } 177 | } else { 178 | echo " 179 | Results ".$search_res.": ".number_format($total_rows)." Record".$singular." ".$pagination."
180 |
181 |
182 |
183 |
IP Address
184 |
Hits
185 |
Last
186 |
Country
187 |
Delete
188 |
"; 189 | 190 | while($row = $sql->fetch(PDO::FETCH_ASSOC)){ 191 | echo " 192 |
193 | 194 |
".number_format($row['hits'])."
195 |
".date("y/m/d H:i:s", strtotime($row['timestamp']))."
196 | 197 |
198 | 199 |
200 |
"; 201 | } 202 | echo " 203 |
204 |
"; // End table 205 | 206 | if ($total_pages == 1){ 207 | echo ""; 208 | } else { 209 | echo " 210 | 211 | 219 | "; 220 | } 221 | } 222 | 223 | // JS autocomplete 224 | echo " 225 | 236 | 241 | "; 250 | 251 | ?> 252 | 253 |
254 | 255 | -------------------------------------------------------------------------------- /css/stylesheet.css: -------------------------------------------------------------------------------- 1 | /* 2 | ╦ ╦╔╦╗╔═╗╦╦ ╔═╗╔═╗╦═╗╦ ╦╔═╗╦═╗ 3 | ╠═╣║║║╠═╣║║ ╚═╗║╣ ╠╦╝╚╗╔╝║╣ ╠╦╝ 4 | ╩ ╩╩ ╩╩ ╩╩╩═╝╚═╝╚═╝╩╚═ ╚╝ ╚═╝╩╚═ 5 | ╔═╗╦ ╦╔═╗╔═╗╦═╗ ╦ ╔═╗╔═╗ 6 | ╚═╗║ ║╠═╝║╣ ╠╦╝ ║ ║ ║║ ╦ 7 | ╚═╝╚═╝╩ ╚═╝╩╚═ ╩═╝╚═╝╚═╝ 8 | */ 9 | 10 | body { 11 | background: #fefefe; 12 | font-family: "Roboto"; 13 | font-size: 12pt; 14 | } 15 | 16 | .header { 17 | position: fixed; 18 | top: 0; 19 | left: 0; 20 | width: 100%; 21 | color: #000; 22 | background: #fefefe; 23 | z-index: 1; 24 | overflow: hidden; 25 | text-align:center; 26 | } 27 | 28 | .header h1 { 29 | font-size:25px; 30 | font-weight:normal; 31 | margin:0 auto; 32 | } 33 | 34 | .header h2 { 35 | font-size:15px; 36 | font-weight:normal; 37 | margin:0 auto; 38 | } 39 | 40 | .wrapper { 41 | /* max-width: 920px; */ 42 | position: relative; 43 | margin: 60px auto 30px auto; 44 | padding-top: 10px; 45 | } 46 | 47 | .clear { 48 | clear: both; 49 | } 50 | 51 | .banner { 52 | width: 100%; 53 | } 54 | 55 | .headlinks { 56 | max-width: 720px; 57 | min-width: 300px; 58 | position:relative; 59 | margin: 0px auto 10px auto; 60 | } 61 | 62 | .headlinks a:link, a:active, a:visited { 63 | color: red; 64 | text-decoration: underline; 65 | } 66 | 67 | .headlinks a:hover { 68 | color: red; 69 | text-decoration: none; 70 | } 71 | 72 | .headspacer { 73 | /* margin: 120px 0 0 0; */ 74 | /* padding: 10px; */ 75 | } 76 | 77 | .section { 78 | padding: 5px 0 15px 0; 79 | margin: 10px; 80 | display: block; 81 | /* border: 1px dashed #ccc; */ 82 | /* border-radius: 20px; */ 83 | } 84 | 85 | .section a:link, a:visited { 86 | color: black; 87 | text-decoration: none; 88 | } 89 | 90 | .section a:hover, a:active { 91 | color: red; 92 | text-decoration: underline; 93 | } 94 | 95 | .section h2 { 96 | font-size:16px; 97 | font-weight:bold; 98 | text-align:left; 99 | } 100 | 101 | .section h3 { 102 | font-size:25px; 103 | font-weight:bold; 104 | text-align:center; 105 | } 106 | 107 | .secleft { 108 | float: left; 109 | width: 49%; 110 | padding-right: 3px; 111 | } 112 | 113 | .secright { 114 | float: right; 115 | width: 49%; 116 | padding-left: 3px; 117 | } 118 | 119 | .secmsgleft { 120 | float: left; 121 | width: 250px; 122 | padding-right: 3px; 123 | } 124 | 125 | .secmsgright { 126 | margin-left: 260px; 127 | padding: 0 0 10px 0; 128 | font-family: consolas; 129 | word-wrap: break-word; 130 | } 131 | 132 | table.section { 133 | border-collapse: collapse; 134 | border: 1px solid black; 135 | border-spacing: 10px; 136 | width: 100%; 137 | font-size: 10pt; 138 | } 139 | 140 | table.section tr:nth-child(even) { 141 | background-color: #F8F8F8; 142 | padding: 4px; 143 | } 144 | 145 | table.section th, table.section td { 146 | border: 1px solid black; 147 | padding: 4px; 148 | word-wrap: break-word; 149 | } 150 | 151 | .footer { 152 | width: 100%; 153 | text-align: center; 154 | font-size: 8pt; 155 | } 156 | 157 | .nav { 158 | font-size: 0.8em; 159 | } 160 | 161 | .nav a:link, a:active, a:visited { 162 | /* color: red; */ 163 | text-decoration: underline; 164 | } 165 | 166 | .nav a:hover { 167 | /* color: red; */ 168 | text-decoration: none; 169 | } 170 | 171 | ul { 172 | list-style-type: none; 173 | padding: 0; 174 | } 175 | 176 | li { 177 | padding: 0 3px 0 0; 178 | display: inline; 179 | } 180 | 181 | .uriwidth { 182 | max-width: 350px; 183 | } 184 | 185 | .truncate{ 186 | display: block; 187 | max-width: 300px; 188 | white-space: nowrap; 189 | overflow: hidden; 190 | text-overflow: ellipsis; 191 | } 192 | 193 | /* ### TABLE CSS ### */ 194 | .div-table { 195 | display: table; 196 | width: 100%; 197 | font-size: 0.8em; 198 | border-left: 1px solid #ccc; 199 | border-bottom: 1px solid #ccc; 200 | } 201 | 202 | .div-table-row-header { 203 | display: table-row; 204 | font-weight: bold; 205 | text-align: center; 206 | } 207 | 208 | .div-table-row { 209 | display: table-row; 210 | } 211 | 212 | .div-table-row:nth-of-type(even) { 213 | background: #eee; 214 | border: 1px solid #ccc; 215 | } 216 | 217 | .div-table-col { 218 | display: table-cell; 219 | padding: 3px; 220 | border-right: 1px solid #ccc; 221 | border-top: 1px solid #ccc; 222 | } 223 | 224 | /* ### SIMPLE TABLE CSS ### */ 225 | .simple-div-table { 226 | display: table; 227 | /* width: 100%; */ 228 | font-size: 0.8em; 229 | border-left: 1px solid #ccc; 230 | border-bottom: 1px solid #ccc; 231 | } 232 | 233 | .simple-div-table-row-header { 234 | display: table-row; 235 | font-weight: bold; 236 | text-align: center; 237 | } 238 | 239 | .simple-div-table-row { 240 | display: table-row; 241 | } 242 | 243 | .simple-div-table-row:nth-of-type(even) { 244 | background: #eee; 245 | border: 1px solid #ccc; 246 | } 247 | 248 | .simple-div-table-col { 249 | display: table-cell; 250 | padding: 3px; 251 | border-right: 1px solid #ccc; 252 | border-top: 1px solid #ccc; 253 | } 254 | 255 | .center { 256 | text-align: center; 257 | } 258 | 259 | .right { 260 | text-align: right; 261 | } 262 | 263 | .left { 264 | text-align: left; 265 | } 266 | 267 | .highlight { 268 | background-color: yellow; 269 | font-weight: bold; 270 | } 271 | 272 | .spamadjusted { 273 | font-weight: bold; 274 | } 275 | 276 | .deadlink { 277 | max-width: 700px; 278 | border: 1px solid black; 279 | border-radius: 5px; 280 | padding: 10px;' 281 | white-space: pre; /* CSS 2.0 */ 282 | white-space: pre-wrap; /* CSS 2.1 */ 283 | white-space: pre-line; /* CSS 3.0 */ 284 | white-space: -pre-wrap; /* Opera 4-6 */ 285 | white-space: -o-pre-wrap; /* Opera 7 */ 286 | white-space: -moz-pre-wrap; /* Mozilla */ 287 | white-space: -hp-pre-wrap; /* HP Printers */ 288 | word-wrap: break-word; /* IE 5+ */ 289 | } 290 | 291 | .node { 292 | font-weight: normal; 293 | } 294 | 295 | .tooltip { 296 | position: relative; 297 | display: inline-block; 298 | border-bottom: 1px dotted black; 299 | } 300 | 301 | .tooltip .tooltiptext { 302 | visibility: hidden; 303 | width: 200px; 304 | height: 200px; 305 | background-color: #fff; 306 | color: #000; 307 | text-align: center; 308 | border: 1px dotted black; 309 | border-radius: 6px; 310 | padding: 5px 0; 311 | position: absolute; 312 | z-index: 1; 313 | bottom: 125%; 314 | left: 50%; 315 | margin-left: -100px; 316 | opacity: 0; 317 | transition: opacity 0.3s; 318 | } 319 | 320 | .tooltip .tooltiptext::after { 321 | content: ""; 322 | position: absolute; 323 | top: 100%; 324 | left: 50%; 325 | margin-left: -5px; 326 | border-width: 5px; 327 | border-style: solid; 328 | border-color: #555 transparent transparent transparent; 329 | } 330 | 331 | .tooltip:hover .tooltiptext { 332 | visibility: visible; 333 | opacity: 1; 334 | } 335 | 336 | .emlFileName { 337 | padding:5px; 338 | /* background-color:#ffffcc; */ 339 | border:1px solid black; 340 | border-radius:5px; 341 | font-family:consolas; 342 | font-size:.8em; 343 | font-weight:bold; 344 | } 345 | 346 | input[type=button], input[type=submit], .button { 347 | min-width:65px; 348 | } 349 | 350 | input[type=text], textarea, input[type=button], input[type=submit], .button { 351 | border: 1px solid black; 352 | border-radius:3px; 353 | } 354 | 355 | .chartcanvas { 356 | margin: auto; 357 | height: 150px; 358 | width: 90%; 359 | display: flex; 360 | justify-content: center; 361 | } 362 | 363 | .chartcanvas2 { 364 | margin: auto; 365 | width: 90%; 366 | display: flex; 367 | justify-content: center; 368 | } 369 | 370 | .resultsFrame {font-family: consolas;font-size: 12px;border:1px solid black;border-radius:5px;padding:10px;max-height:70vh;overflow:scroll;overflow-x:hidden;} 371 | .logline {display: block;padding-bottom: 0;line-break: auto;word-break: break-all;} 372 | .loglineleft {float:left;} 373 | .loglineright {margin-left:90px;} 374 | .logGroupContainer {padding-bottom: 10px;} 375 | .logGroupHeader {font-weight:bold;text-decoration-line: underline;text-decoration-style: dotted;text-decoration-thickness: 1px;background:rgba(102, 255, 102, 0.25)} 376 | 377 | @media only screen and (max-width: 629px) { 378 | .logline {padding-bottom: 10px;} 379 | 380 | .chartcanvas { 381 | height: 200px; 382 | width: 100%; 383 | } 384 | 385 | .chartcanvas2 { 386 | width: 100%; 387 | } 388 | 389 | .node { 390 | font-weight: bold; 391 | } 392 | 393 | .secleft { 394 | float: none; 395 | width: 100%; 396 | padding: 0 0 10px 0; 397 | text-align: left; 398 | } 399 | .secright { 400 | float: none ; 401 | width: 100% ; 402 | } 403 | 404 | .secmsgleft { 405 | float: none; 406 | width: 100%; 407 | padding-right: 0px; 408 | } 409 | 410 | .secmsgright { 411 | margin-left: 0px; 412 | padding: 0 0 10px 5px; 413 | white-space: pre; /* CSS 2.0 */ 414 | white-space: pre-wrap; /* CSS 2.1 */ 415 | white-space: pre-line; /* CSS 3.0 */ 416 | white-space: -pre-wrap; /* Opera 4-6 */ 417 | white-space: -o-pre-wrap; /* Opera 7 */ 418 | white-space: -moz-pre-wrap; /* Mozilla */ 419 | white-space: -hp-pre-wrap; /* HP Printers */ 420 | word-wrap: break-word; /* IE 5+ */ 421 | } 422 | 423 | /* ### TABLE CSS ### */ 424 | .div-table, .div-table-row-header, .div-table-row, .div-table-col { 425 | display: block; 426 | } 427 | 428 | .div-table { 429 | border: none; 430 | } 431 | 432 | /* Hide table headers (but not display: none;, for accessibility) */ 433 | .div-table-row-header { 434 | position: absolute; 435 | top: -9999px; 436 | left: -9999px; 437 | } 438 | 439 | .div-table-row { 440 | border: none; 441 | border-left: 1px solid #ccc; 442 | border-right: 1px solid #ccc; 443 | } 444 | 445 | .div-table-col { 446 | /* Behave like a "row" */ 447 | border: none; 448 | border-bottom: 1px solid #e6e6e6; 449 | position: relative; 450 | padding-left: 25%; 451 | text-align: left; 452 | white-space: pre; /* CSS 2.0 */ 453 | white-space: pre-wrap; /* CSS 2.1 */ 454 | white-space: pre-line; /* CSS 3.0 */ 455 | white-space: -pre-wrap; /* Opera 4-6 */ 456 | white-space: -o-pre-wrap; /* Opera 7 */ 457 | white-space: -moz-pre-wrap; /* Mozilla */ 458 | white-space: -hp-pre-wrap; /* HP Printers */ 459 | word-wrap: break-word; /* IE 5+ */ 460 | } 461 | 462 | .div-table-col:before { 463 | /* Now like a table header */ 464 | border: none; 465 | position: absolute; 466 | top: 3px; 467 | left: 6px; 468 | padding-right: 10px; 469 | /* Label the data */ 470 | content: attr(data-column); 471 | color: #000; 472 | font-weight: bold; 473 | } 474 | 475 | } 476 | -------------------------------------------------------------------------------- /js/chartjs-plugin-datalabels.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * chartjs-plugin-datalabels v2.2.0 3 | * https://chartjs-plugin-datalabels.netlify.app 4 | * (c) 2017-2022 chartjs-plugin-datalabels contributors 5 | * Released under the MIT license 6 | */ 7 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("chart.js/helpers"),require("chart.js")):"function"==typeof define&&define.amd?define(["chart.js/helpers","chart.js"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).ChartDataLabels=e(t.Chart.helpers,t.Chart)}(this,(function(t,e){"use strict";var r=function(){if("undefined"!=typeof window){if(window.devicePixelRatio)return window.devicePixelRatio;var t=window.screen;if(t)return(t.deviceXDPI||1)/(t.logicalXDPI||1)}return 1}(),a=function(e){var r,a=[];for(e=[].concat(e);e.length;)"string"==typeof(r=e.pop())?a.unshift.apply(a,r.split("\n")):Array.isArray(r)?e.push.apply(e,r):t.isNullOrUndef(e)||a.unshift(""+r);return a},o=function(t,e,r){var a,o=[].concat(e),n=o.length,i=t.font,l=0;for(t.font=r.string,a=0;ar.right&&(a|=2),er.bottom&&(a|=4),a}function u(t,e){var r,a,o=e.anchor,n=t;return e.clamp&&(n=function(t,e){for(var r,a,o,n=t.x0,i=t.y0,l=t.x1,u=t.y1,d=s(n,i,e),c=s(l,u,e);d|c&&!(d&c);)8&(r=d||c)?(a=n+(l-n)*(e.top-i)/(u-i),o=e.top):4&r?(a=n+(l-n)*(e.bottom-i)/(u-i),o=e.bottom):2&r?(o=i+(u-i)*(e.right-n)/(l-n),a=e.right):1&r&&(o=i+(u-i)*(e.left-n)/(l-n),a=e.left),r===d?d=s(n=a,i=o,e):c=s(l=a,u=o,e);return{x0:n,x1:l,y0:i,y1:u}}(n,e.area)),"start"===o?(r=n.x0,a=n.y0):"end"===o?(r=n.x1,a=n.y1):(r=(n.x0+n.x1)/2,a=(n.y0+n.y1)/2),function(t,e,r,a,o){switch(o){case"center":r=a=0;break;case"bottom":r=0,a=1;break;case"right":r=1,a=0;break;case"left":r=-1,a=0;break;case"top":r=0,a=-1;break;case"start":r=-r,a=-a;break;case"end":break;default:o*=Math.PI/180,r=Math.cos(o),a=Math.sin(o)}return{x:t,y:e,vx:r,vy:a}}(r,a,t.vx,t.vy,e.align)}var d=function(t,e){var r=(t.startAngle+t.endAngle)/2,a=Math.cos(r),o=Math.sin(r),n=t.innerRadius,i=t.outerRadius;return u({x0:t.x+a*n,y0:t.y+o*n,x1:t.x+a*i,y1:t.y+o*i,vx:a,vy:o},e)},c=function(t,e){var r=l(t,e.origin),a=r.x*t.options.radius,o=r.y*t.options.radius;return u({x0:t.x-a,y0:t.y-o,x1:t.x+a,y1:t.y+o,vx:r.x,vy:r.y},e)},h=function(t,e){var r=l(t,e.origin),a=t.x,o=t.y,n=0,i=0;return t.horizontal?(a=Math.min(t.x,t.base),n=Math.abs(t.base-t.x)):(o=Math.min(t.y,t.base),i=Math.abs(t.base-t.y)),u({x0:a,y0:o+i,x1:a+n,y1:o,vx:r.x,vy:r.y},e)},f=function(t,e){var r=l(t,e.origin);return u({x0:t.x,y0:t.y,x1:t.x+(t.width||0),y1:t.y+(t.height||0),vx:r.x,vy:r.y},e)},x=function(t){return Math.round(t*r)/r};function y(t,e){var r=e.chart.getDatasetMeta(e.datasetIndex).vScale;if(!r)return null;if(void 0!==r.xCenter&&void 0!==r.yCenter)return{x:r.xCenter,y:r.yCenter};var a=r.getBasePixel();return t.horizontal?{x:a,y:null}:{x:null,y:a}}function v(t,e,r){var a=r.backgroundColor,o=r.borderColor,n=r.borderWidth;(a||o&&n)&&(t.beginPath(),function(t,e,r,a,o,n){var i=Math.PI/2;if(n){var l=Math.min(n,o/2,a/2),s=e+l,u=r+l,d=e+a-l,c=r+o-l;t.moveTo(e,u),sr.x+r.w+2||t.y>r.y+r.h+2)},intersects:function(t){var e,r,a,o=this._points(),n=t._points(),i=[M(o[0],o[1]),M(o[0],o[3])];for(this._rotation!==t._rotation&&i.push(M(n[0],n[1]),M(n[0],n[3])),e=0;et.getProps([e],!0)[e]}),n=a.geometry(),i=$(l,a.model(),n),o._box.update(i,n,a.rotation()));(function(t,e){var r,a,o,n;for(r=t.length-1;r>=0;--r)for(o=t[r].$layout,a=r-1;a>=0&&o._visible;--a)(n=t[a].$layout)._visible&&o._box.intersects(n._box)&&e(o,n)})(t,(function(t,e){var r=t._hidable,a=e._hidable;r&&a||a?e._visible=!1:r&&(t._visible=!1)}))}(t)},lookup:function(t,e){var r,a;for(r=t.length-1;r>=0;--r)if((a=t[r].$layout)&&a._visible&&a._box.contains(e))return t[r];return null},draw:function(t,e){var r,a,o,n,i,l;for(r=0,a=e.length;r