]> git.datanom.net - qtadmin.git/commitdiff
initial release
authorMichael Rasmussen <mir@datanom.net>
Sun, 31 May 2015 12:50:20 +0000 (14:50 +0200)
committerMichael Rasmussen <mir@datanom.net>
Sun, 31 May 2015 12:50:20 +0000 (14:50 +0200)
16 files changed:
auth.php [new file with mode: 0644]
config.php [new file with mode: 0644]
css/auth.css [new file with mode: 0644]
css/styles.css [new file with mode: 0644]
index.php [new file with mode: 0644]
js/timer.js [new file with mode: 0644]
lib/db_factory.php [new file with mode: 0644]
lib/db_implementation.php [new file with mode: 0644]
lib/db_mysqli.inc.php [new file with mode: 0644]
lib/utils.inc.php [new file with mode: 0644]
mail_report.php [new file with mode: 0644]
message_view.php [new file with mode: 0644]
pics/delete.png [new file with mode: 0644]
pics/release.png [new file with mode: 0644]
quarantine.php [new file with mode: 0644]
show_headers.php [new file with mode: 0644]

diff --git a/auth.php b/auth.php
new file mode 100644 (file)
index 0000000..120f84b
--- /dev/null
+++ b/auth.php
@@ -0,0 +1,87 @@
+<?php
+/* vim: set ts=4 tw=0 sw=4 noet: */
+       require_once 'config.php';
+       require_once $CFG->root . 'lib/utils.inc.php';
+
+       $auth_url = $_SERVER['PHP_SELF'] . '?op=login';
+       $login = $_SERVER['PHP_SELF'];
+       $login_form = <<<LF
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+               <title>Login</title>
+               <link rel="stylesheet" href="css/auth.css">
+       </head>
+       <body>
+               <div class="container">
+                       <div id="login-form">
+                               <h3>Login</h3>
+                               <fieldset>
+                                       <form action="$auth_url" method="post">
+                                               <input type="email" name="user" required value="Email" onBlur="if(this.value=='')this.value='Email'" onFocus="if(this.value=='Email')this.value='' "/>
+                                               <input type="password" name="password" required value="Password" onBlur="if(this.value=='')this.value='Password'" onFocus="if(this.value=='Password')this.value='' "/>
+                                               <input type="submit" value="Login"/>
+<!--                                           <footer class="clearfix">
+                                                       <p><span class="info">?</span><a href="#">Forgot Password</a></p>
+                                               </footer>-->
+                                       </form>
+                               </fieldset>
+                       </div> <!-- end login-form -->
+                       <p class="footer">Powered by <a href="https://qtadmin.datanom.net">
+                       QtAdmin</a>. &copy; 2015 by Michael Rasmussen</p>
+               </div>
+       </body>
+</html>
+LF;
+
+    $login_error = <<<LE
+<DOCTYPE html>
+<html>
+    <head>
+        <meta charset="UTF-8">
+        <title>Error</title>
+        <link rel="stylesheet" href="css/auth.css">
+    </head>
+    <body>
+        <div class="container">
+            <div id="greeting">
+                <h3>ERROR</h3>
+                <fieldset>
+                    <p><span class="error">__ERROR__<span></p>
+                    <p><a href="$login">Login</a></p>
+                </fieldset>
+            </div> <!-- end login-form -->
+            <p class="footer">Powered by <a href="https://qtadmin.datanom.net">
+            QtAdmin</a>. &copy; 2015 by Michael Rasmussen</p>
+        </div>
+    </body>
+</html>
+LE;
+
+       $util = Utils::getInstance();
+       if (isset($_GET['op']))
+               $action = $_GET['op'];
+       else
+               $action = 'default';
+
+       if ($action == 'logout') {
+               // logout
+               $util->logout();
+               echo $login_form;
+       } else if ($action == 'login') {
+               // login
+               $user = $_POST['user'];
+               $password = $_POST['password'];
+               if ($util->login($user, $password)) {
+                       header('Location: index.php');
+               } else {
+                       $error = $util->getLoginStatus();
+                       $error = str_replace('__ERROR__', $error, $login_error);
+                       echo $error;
+               }
+       } else {
+               echo $login_form;
+       }                       
+?>
+
diff --git a/config.php b/config.php
new file mode 100644 (file)
index 0000000..2380794
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/* vim: set ts=4 tw=0 sw=4 noet: */
+$CFG = new stdClass;
+
+// Amavis
+$CFG->amavisd_db_host = "127.0.0.1";
+$CFG->amavisd_db_port = 3306;
+$CFG->amavisd_db_name = "amavisd";
+$CFG->amavisd_db_user = "amavisd";
+$CFG->amavisd_db_password = "suFt3oEEmmXTjFq1bjTXRNIPXhF41a";
+$CFG->amavis_policy_port = 9998;
+// LDAP
+$CFG->ldap_dsn =  "ldap://127.0.0.1:389";
+$CFG->ldap_base_dn = "o=domains,dc=datanom,dc=net";
+
+$CFG->root = '/usr/share/quarantine-admin/';
+$CFG->wwwroot = '/qtadmin/';
+
+// HTTP_AUTH or LDAP
+$CFG->auth_method = 'LDAP';
+// If HTTP_AUTH is chosen configure admin user here
+//$CFG->admin_user = 'some_admin';
+
+$CFG->db_driver = 'mysqli';
+$DB = null;
+
+// Session timeout in minuts
+// Default timeout is 20 minuts
+$CFG->session_timeout = 60;
+
+?>
diff --git a/css/auth.css b/css/auth.css
new file mode 100644 (file)
index 0000000..0494280
--- /dev/null
@@ -0,0 +1,198 @@
+/* ---------- GENERAL ---------- */
+* {
+  box-sizing: border-box;
+}
+*:before, *:after {
+  box-sizing: border-box;
+}
+
+body {
+  background: #eaeaea;
+  color: #999;
+  font: 400 16px/1.5em sans-serif;
+  margin: 0;
+}
+
+h3 {
+  margin: 0;
+}
+
+a {
+  color: #999;
+  text-decoration: none;
+}
+
+a:hover {
+  color: #1dabb8;
+}
+
+fieldset {
+  border: none;
+  margin: 0;
+}
+
+input {
+  border: none;
+  font-family: inherit;
+  font-size: inherit;
+  margin: 0;
+  -webkit-appearance: none;
+}
+
+input:focus {
+  outline: none;
+}
+
+input[type="submit"] {
+  cursor: pointer;
+}
+
+.clearfix {
+  *zoom: 1;
+}
+.clearfix:before, .clearfix:after {
+  content: ' ';
+  display: table;
+}
+.clearfix:after {
+  clear: both;
+}
+
+.container {
+  left: 50%;
+  position: fixed;
+  top: 50%;
+  -webkit-transform: translate(-50%, -50%);
+      -ms-transform: translate(-50%, -50%);
+          transform: translate(-50%, -50%);
+}
+
+.footer {
+  text-align: center;
+  margin-top: 10px;
+  margin-bottom: 10px;
+  font-size: 0.7em;
+  background: #ECECAC;
+  color: black;
+}
+
+/* ---------- LOGIN-FORM ---------- */
+#login-form {
+  width: 300px;
+}
+
+#login-form h3 {
+  background-color: #282830;
+  border-radius: 5px 5px 0 0;
+  color: #fff;
+  font-size: 14px;
+  padding: 20px;
+  text-align: center;
+  text-transform: uppercase;
+}
+
+#login-form fieldset {
+  background: #fff;
+  border-radius: 0 0 5px 5px;
+  padding: 20px;
+  position: relative;
+}
+
+#login-form fieldset:before {
+  background-color: #fff;
+  content: "";
+  height: 8px;
+  left: 50%;
+  margin: -4px 0 0 -4px;
+  position: absolute;
+  top: 0;
+  -webkit-transform: rotate(45deg);
+  -ms-transform: rotate(45deg);
+  transform: rotate(45deg);
+  width: 8px;
+}
+
+#login-form input {
+  font-size: 14px;
+}
+
+#login-form input[type="email"],
+#login-form input[type="password"] {
+  border: 1px solid #dcdcdc;
+  padding: 12px 10px;
+  width: 100%;
+}
+
+#login-form input[type="email"] {
+  border-radius: 3px 3px 0 0;
+}
+
+#login-form input[type="password"] {
+  border-top: none;
+  border-radius: 0px 0px 3px 3px;
+}
+
+#login-form input[type="submit"] {
+  background: #1dabb8;
+  border-radius: 3px;
+  color: #fff;
+  float: right;
+  font-weight: bold;
+  margin-top: 20px;
+  padding: 12px 20px;
+}
+
+#login-form input[type="submit"]:hover {
+  background: #198d98;
+}
+
+#login-form footer {
+  font-size: 12px;
+  margin-top: 16px;
+}
+
+.info {
+  background: #e5e5e5;
+  border-radius: 50%;
+  display: inline-block;
+  height: 20px;
+  line-height: 20px;
+  margin: 0 10px 0 0;
+  text-align: center;
+  width: 20px;
+}
+
+/* ---------- GREETING ---------- */
+#greeting {
+  width: 300px;
+}
+
+#greeting fieldset {
+  background: #fff;
+  border-radius: 0 0 5px 5px;
+  padding: 20px;
+  position: relative;
+}
+
+#greeting h3 {
+  background-color: #282830;
+  border-radius: 5px 5px 0 0;
+  color: #fff;
+  font-size: 14px;
+  padding: 20px;
+  text-align: center;
+  text-transform: uppercase;
+}
+
+#greeting p {
+  text-align: center;
+  color: navy;
+}
+
+#greeting a {
+  color: green;
+}
+
+.error {
+  color: red;
+}
diff --git a/css/styles.css b/css/styles.css
new file mode 100644 (file)
index 0000000..6d1951a
--- /dev/null
@@ -0,0 +1,133 @@
+/* vim: set ts=4 tw=0 sw=4 noet: */
+.h1 {
+       text-align: center;
+       color: navy;
+}
+
+.user {
+       float: right;
+       font-family: Verdana, Arial, Helvetica, sans-serif;
+       font-size: 0.8em;
+       margin-bottom: 10px;
+}
+
+.bg_odd {
+       background: lightgray;
+}
+
+.bg_even {
+       background: white;
+}
+
+table {
+       border-collapse: collapse;
+       width: 100%;
+}
+
+table, th, td {
+       border: 1px solid green;
+}
+
+th {
+       background: navy;
+       color: white;
+       font-weight: bold;
+}
+
+td {
+       padding: 2px 6px;
+}
+
+body {
+       font-family: Verdana, Arial, Helvetica, sans-serif;
+       font-size: 0.9em;
+       color: #3D658C;
+       background: white;
+}
+
+.total-rows {
+       color: red;
+       font-size: 0.8em;
+}
+
+.page-nav {
+       float: right;
+       margin-top: 5px;
+}
+
+.mail-purge {
+       margin-top: 5px;
+}
+
+.footer {
+       text-align: center;
+       margin-top: 20px;
+       margin-bottom: 10px;
+       font-size: 0.7em;
+       background: #ECECAC;
+       color: black;
+}
+
+.whitefooter {
+       color: green;
+}
+
+img {
+       border: 0;
+}
+
+.nav-img {
+       width: 24px;
+       height: 24px;
+}
+
+.nav-action {
+       text-align: center;
+       width: 53px;
+}
+
+.label {
+       color: black;
+       background: lightgray;
+       font-weight: bold;
+       width: 10%;
+}
+
+.value {
+       color: black;
+       background: white;
+       font-size: 0.8em;
+}
+
+.ruler {
+       color: white;
+       background: black;
+       font-size: 1.4em;
+       font-weight: bold;
+       text-align: center;
+}
+
+.button-menu {
+       width: 100%;
+       border: 0;
+       margin-bottom: 10px;
+}
+
+.button {
+       text-align: center;
+       border: 0 solid white;
+       background: white;
+       width: 25%;
+}
+
+input.btn-input {
+       width: 150px;
+}
+
+.time {
+       float: right;
+       color: white;
+       background: green;
+       font-size: 0.7em;
+}
+
diff --git a/index.php b/index.php
new file mode 100644 (file)
index 0000000..9bbd08d
--- /dev/null
+++ b/index.php
@@ -0,0 +1,101 @@
+<?php
+/* vim: set ts=4 tw=0 sw=4 noet: */
+       require_once 'config.php';
+       require_once $CFG->root . 'lib/db_factory.php';
+       require_once $CFG->root . 'lib/utils.inc.php';
+
+       $util = Utils::getInstance();
+       unset($_SESSION['mailInfo']);
+       
+       if ($util->isLoggedIn()) {
+               if (isset($_GET['rowsperpage'])) {
+                       $rowsPerPage = $_GET['rowsperpage'];
+               } else {
+                       $rowsPerPage = 20;
+               }
+
+               $pageNum = 1;
+               if (isset($_GET['page'])) {
+                       $pageNum = $_GET['page'];
+               }
+
+               $offset = ($pageNum - 1) * $rowsPerPage;
+
+               $util->setHeading('Quarantine Administration');
+               echo $util->getHeader();
+               echo $util->getHeading();
+
+               echo "<span class=\"user\">{$util->getUser()} 
+                       <a href=\"auth.php?op=logout\">Logout</a></span>";
+               $which = ($util->isAdmin() == true) ? 'all' : $util->getUser();
+               $rows = $DB->getQMails($offset, $rowsPerPage, $which);
+               $numrows = $DB->numRows();
+               echo "<span class=\"total-rows\">$numrows quarantined mail(s)</span>";
+               echo "<table><tr>";
+        echo "<table><tr><th>Received</th><th>Cause</th>";
+        echo "<th>Sender</th><th>Recipient</th><th>Subject</th><th>Action</th>";
+               echo "</tr>";
+
+               $mailInfo = array();
+               $i = 0;
+               foreach ($rows as $row) {
+                       if ($i % 2)
+                               echo '<tr class="bg_odd">';
+                       else
+                               echo '<tr class="bg_even">';
+                       $id = $row->mail_id;
+                       $mailInfo[$id] = serialize($row);
+                       $url = urlencode($id);
+                       $recipient = "<a href=\"mail_report.php?id=$url\">{$row->recipient}</a>";
+                       $action = "<a href=\"quarantine.php?id=$url&op=release\">";
+                       $action .= "<img class=\"nav-img\" src=\"pics/release.png\" alt=\"Release\" /></a>";
+                       $action .= "&nbsp;<a href=\"quarantine.php?id=$url&op=delete\">";
+                       $action .= "<img class=\"nav-img\" src=\"pics/delete.png\" alt=\"Delete\" /></a>";
+                       $sender = $row->sender;
+                       $received = strftime("%c", $row->time_iso);
+                       $quaratinefor = $util->convertContent($row->quaratinefor);
+                       $subject = $row->subject;
+            echo "<td>$received</td><td class=\"nav-action\">".
+                "$quaratinefor</td><td>$sender</td><td>$recipient</td>".
+                "<td>$subject</td><td class=\"nav-action\">$action</td></tr>";
+                       $i++;
+               }
+               $_SESSION['mailInfo'] = $mailInfo;
+               echo "</table>";
+
+               $maxPage = ceil($numrows/$rowsPerPage);
+               $self = $_SERVER['PHP_SELF'];
+               
+               if ($pageNum > 1) {
+                       $page  = $pageNum - 1;
+                       $prev = " <a href=\"$self?page=$page&rowsperpage=$rowsPerPage\" 
+                               class='whitefooter'>[Prev]</a>";
+                       $first = " <a href=\"$self?page=1&rowsperpage=$rowsPerPage\" 
+                               class='whitefooter'>[First Page]</a> ";
+               } else {
+                       $prev  = '&nbsp;'; // we're on page one, don't print previous link
+                       $first = '&nbsp;'; // nor the first page link
+               }
+               
+               if ($pageNum < $maxPage) {
+                       $page = $pageNum + 1;
+                       $next = " <a href=\"$self?page=$page&rowsperpage=$rowsPerPage\" 
+                               class='whitefooter'>[Next]</a>";
+                       $last = "<a href=\"$self?page=$maxPage&rowsperpage=$rowsPerPage\" 
+                               class='whitefooter'>[Last Page]</a> ";
+               } else {
+                       $next = '&nbsp;'; // we're on the last page, don't print next link
+                       $last = '&nbsp;'; // nor the last page link
+               }
+               $marked = $DB->getMarked($which);
+               $_SESSION['marked'] = serialize($marked);
+               echo '<input class="mail-purge" type="button" value="Purge Mails ('.
+                         count($marked).')" onclick="javascript: location.href=\'quarantine.php?op=purge\'"/>';
+               echo "<p class=\"page-nav\">$first$prev Showing page $pageNum of 
+                         $maxPage pages $next$last</p>";
+
+               echo $util->getFooter();
+       } else {
+               header('Location: auth.php');
+       }
+?>
diff --git a/js/timer.js b/js/timer.js
new file mode 100644 (file)
index 0000000..a7e9c1d
--- /dev/null
@@ -0,0 +1,20 @@
+/* vim: set ts=4 tw=0 sw=4 noet: */
+var timerRef=setInterval(function(){myTimer()},1000);
+var timeStart = new Date().getTime() + timeout;
+
+function myTimer() {
+       var elapsed = new Date().getTime();
+       var expire = timeStart - elapsed;
+       var timeLeft = Math.round(((expire % 86400000) % 3600000) / 60000);
+
+       if (timeLeft < 0) {
+               timeLeft = 'Expired';
+               document.getElementById("time").style.background = "red";
+               clearInterval(timerRef);
+       } else {
+               timeLeft += 1;
+               timeLeft = '< ' + timeLeft + ' min';
+       }
+
+    document.getElementById("timer").innerHTML = timeLeft;
+}
diff --git a/lib/db_factory.php b/lib/db_factory.php
new file mode 100644 (file)
index 0000000..68b1ee5
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/* vim: set ts=4 tw=0 sw=4 noet: */
+if (isset($CFG->db_driver)) {
+       switch ($CFG->db_driver) {
+               case 'mysqli':
+                       require_once $CFG->root . 'lib/db_mysqli.inc.php';
+                       $DB = DBMysqli::getInstance();
+                       break;
+               default:
+                       die ('Unknow database driver: ' . $CFG->db_driver);
+       }
+} else {
+       die ('Missing config file');
+}
+?>
diff --git a/lib/db_implementation.php b/lib/db_implementation.php
new file mode 100644 (file)
index 0000000..c35754f
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+/* vim: set ts=4 tw=0 sw=4 noet: */
+abstract class DBImpl {
+
+       private function __clone() {}
+
+       abstract protected static function getInstance();
+       abstract protected function getQMails($offset = -1, $rowsPerPage = -1, $recipient = 'all');
+       abstract protected function getMarked($recipient = 'all');
+       abstract protected function numRows();
+       abstract protected function getMail($id);
+       abstract protected function setCharset($charset = 'utf8');
+       abstract protected function update($sql);       
+}
diff --git a/lib/db_mysqli.inc.php b/lib/db_mysqli.inc.php
new file mode 100644 (file)
index 0000000..e128d2f
--- /dev/null
@@ -0,0 +1,151 @@
+<?php
+/* vim: set ts=4 tw=0 sw=4 noet: */
+require_once $CFG->root .'lib/db_implementation.php';
+
+class DBMysqli extends DBImpl {
+
+       private static $_instance = null;
+       private $con = null;
+       private $charset = 'utf8';
+       private $numRows = -1;
+
+       private function __construct() {
+               $this->connect();
+       }
+
+       function __destruct() {
+               if ($this->con) {
+                       $this->con->close();
+                       $this->con = null;
+               }
+       }
+
+       private function __clone() {}
+
+       public static function getInstance() {
+               if (!is_object(self::$_instance)) {
+                       self::$_instance = new DBMysqli();
+               }
+               return self::$_instance; 
+       }
+
+       private function connect() {
+               global $CFG;
+
+               if (!$this->con) {
+                       $this->con = new mysqli($CFG->amavisd_db_host, $CFG->amavisd_db_user,
+                                         $CFG->amavisd_db_password, $CFG->amavisd_db_name,
+                                         $CFG->amavisd_db_port);
+                       if ($this->con->connect_error) {
+                               die ('Connect error ('.$this->con->connect_errno.') '.
+                                       $this->con->connect_error);
+                       }
+               }
+       }
+       
+       public function setCharset($charset = 'utf8') {
+               $this->charset = $charset;
+       }
+
+       private function changeCharset() {
+               if (!$this->con->set_charset($this->charset)) {
+                       printf("Error loading character set %s: %s\n", $this->charset, $this->con->error);
+               }
+       }
+
+       public function update($sql) {
+               $success = true;
+
+               if (! is_array($sql)) {
+                       $sql = array($sql);
+               }
+
+               $this->con->autocommit(false);
+               foreach ($sql as $query) {
+                       $this->con->query($query) ? null : $success = false;            
+               }
+               $success ? $this->con->commit() : $this->con->rollback();
+               $this->con->autocommit(true);
+
+               return $success;
+       }
+
+       function getMarked($recipient = 'all') {
+               $all = array();
+
+               $query = "select distinct m.mail_id as id from quarantine q, msgrcpt m, msgs s, maddr r ";
+               $query .= "where m.mail_id = q.mail_id and q.mail_id = s.mail_id and m.rid = r.id ";
+               $query .= "and (m.rs = 'R' or m.rs = 'D')";
+
+        if ($recipient != 'all') {
+            $query .= " and email = '$recipient'";
+        }
+
+        if ($result = $this->con->query($query)) {
+            while ($row = $result->fetch_object()) {
+               $all[] = $row->id;
+                       }
+        }
+
+               return $all;
+       }
+       
+       public function getQMails($offset = -1, $rowsPerPage = -1, $recipient = 'all') {
+               $row = array();
+               $this->changeCharset();
+               $query = "SELECT DISTINCT quarantine.mail_id, secret_id, rs, bspam_level, ";
+               $query .= "(UNIX_TIMESTAMP(time_iso) + (3600 * 2)) AS time_iso, ";
+               $query .= "SUBSTRING(sender.email,1,35) AS sender, ";
+               $query .= "SUBSTRING(recipient.email,1,28) AS recipient, size, msgrcpt.content ";
+               $query .= "AS quaratinefor, SUBSTRING( subject, 1, 25) AS subject FROM ";
+               $query .= "`quarantine` LEFT JOIN msgrcpt ON msgrcpt.mail_id = quarantine.mail_id ";
+               $query .= "LEFT JOIN msgs ON msgs.mail_id = quarantine.mail_id LEFT JOIN maddr AS ";
+               $query .= "recipient ON msgrcpt.rid = recipient.id LEFT JOIN maddr AS sender ON ";
+               $query .= "msgs.sid = sender.id WHERE msgrcpt.rs != 'R' AND msgrcpt.rs != 'D'";
+               
+               if ($recipient != 'all') {
+                       $query .= " and recipient.email = '$recipient'";
+               }
+               
+               if ($offset >= 0 && $rowsPerPage >= 0) {
+                       $result = $this->con->query($query, MYSQLI_STORE_RESULT);
+                       if ($result) {
+                               $this->numRows = $result->num_rows;
+                               $result->free();
+                       } else {
+                               $this->numRows = 0;
+                       }
+                       $query .= " ORDER BY time_iso DESC LIMIT $offset, $rowsPerPage";
+               }
+       
+               if ($result = $this->con->query($query, MYSQLI_USE_RESULT)) {
+                       if ($this->numRows < 0)
+                               $this->numRows = $result->num_rows;
+                       while ($obj = $result->fetch_object()) {
+                               $row[] = $obj;
+                       }
+                       $result->free();
+               }
+               
+               return $row;
+       }
+
+       public function numRows() {
+               return $this->numRows;
+       }
+
+       public function getMail($id) {
+               $row = null;
+
+               $this->changeCharset();
+               $query = "SELECT * FROM quarantine WHERE mail_id = '$id'";
+                if ($result = $this->con->query($query, MYSQLI_USE_RESULT)) {
+                        $row = $result->fetch_object();
+                }
+
+               return $row;
+       }
+
+}
+
+?>
diff --git a/lib/utils.inc.php b/lib/utils.inc.php
new file mode 100644 (file)
index 0000000..ec842b8
--- /dev/null
@@ -0,0 +1,232 @@
+<?php
+/* vim: set ts=4 tw=0 sw=4 noet: */
+require_once $CFG->root .'config.php';
+
+class Utils {
+
+       private static $_instance = null;
+       private $server;
+       private $user;
+       private $is_admin;
+       private $loginStatus;
+       private $header = '<!DOCTYPE html>
+<html>
+<head>
+       <meta charset="utf-8">
+       <link rel="stylesheet" href="css/styles.css">
+       <script>
+               var timeout = __TIMEOUT__;
+       </script>
+       <script src="__ROOT__js/timer.js"></script>
+       <title>__TITLE__</title>
+</head>
+<body>';
+       private $footer = '<p class="footer">Powered by <a href="https://qtadmin.datanom.net">
+                       QtAdmin</a>. &copy; 2015 by Michael Rasmussen</p></body></html>';
+       private $heading = '<p id="time" class="time">Session timeout: 
+                       <span id="timer"></span></p><h1 class="h1">__TITLE__</h1>';
+
+       private function __construct() {
+               global $CFG;
+
+               $this->server = $_SERVER;
+               session_start();
+                
+               $this->user = null;
+               $this->is_admin = false;
+               $this->loginStatus = 'Not logged in';
+
+               if (isset($_SESSION['user'])) {
+                       $this->user = $_SESSION['user'];
+                       $this->loginStatus = 'OK';
+                       $this->is_admin = $_SESSION['is_admin'];
+               } else {
+                       if ($CFG->auth_method == 'HTTP_AUTH') {
+                               if (isset($this->server['PHP_AUTH_USER'])) {
+                                       $this->user = $this->server['PHP_AUTH_USER'];
+                                       $this->loginStatus = 'OK';
+                                       if ($CFG->admin_user == $this->user)
+                                               $this->is_admin = true;
+                               }
+                       }
+               }
+               $_SESSION['user'] = $this->user;
+               $_SESSION['is_admin'] = $this->is_admin;
+       }
+
+       private function __clone() {}
+
+       public static function getInstance() {
+               global $CFG;
+
+               if (!is_object(self::$_instance)) {
+                       self::$_instance = new Utils();
+               }
+               // Session timeout handler
+               if ('' == session_id())
+                       session_start();
+               if (isset($CFG->session_timeout)) {
+                       $timeout = $CFG->session_timeout * 60;
+               } else {
+                       $timeout = 20 * 60;
+               }
+               
+               if (ini_get('session.gc_maxlifetime') != $timeout)
+                       ini_set('session.gc_maxlifetime', $timeout);
+               if (ini_get('session.cookie_lifetime') != $timeout)
+                       ini_set('session.cookie_lifetime', $timeout);
+               $time = $_SERVER['REQUEST_TIME'];
+               if (isset($_SESSION['LAST_ACTIVITY']) && ($time - $_SESSION['LAST_ACTIVITY']) >= $timeout) {
+                       session_unset();
+                       session_destroy();
+                       session_start();
+                       self::$_instance->user = null;
+                       self::$_instance->is_admin = false;
+               }
+               $_SESSION['LAST_ACTIVITY'] = $time;
+
+               return self::$_instance; 
+       }
+
+       public function logout() {
+               $_SESSION = array();
+               if (ini_get('session.use_cookies')) {
+                       $params = session_get_cookie_params();
+                       setcookie(session_name(), '', time() - 42000,
+                               $params['path'], $params['domain'],
+                               $params['secure'], $params['httponly']);
+               }
+               session_unset();
+               session_destroy();
+               $this->user = null;
+               $this->is_admin = false;
+       }
+
+       public function isAdmin() {
+               //file_put_contents('/tmp/login.txt', var_export($this, true));
+               return $this->is_admin;
+       }
+
+       public function login($user, $pw) {
+               global $CFG;
+               $result = false;
+
+               unset($_SESSION['user']);
+               unset($_SESSION['is_admin']);
+               $this->user = null;
+               $this->is_admin = false;
+               
+               $p = explode('@', $user);
+               if (count($p) != 2) {
+                       $this->loginStatus = 'Bad username';
+                       return false;
+               }
+               $domain = $p[1];
+               $dn = "mail=$user,ou=Users,domainName=$domain,$CFG->ldap_base_dn";
+        $filter = "(&(objectclass=mailUser)(accountStatus=active)(mail=$user))";
+        $ds = @ldap_connect($CFG->ldap_dsn);
+        if ($ds) {
+                       @ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
+            $r = @ldap_bind($ds, $dn, $pw);
+            if ($r) {
+                $sr = @ldap_search($ds, $CFG->ldap_base_dn, $filter, array('mail','domainglobaladmin'));
+                $info = @ldap_get_entries($ds, $sr); // array
+                if ($info['count'] > 0) {
+                                       $_SESSION['user'] = $user;
+                               $this->user = $user;
+                                       $result = true;
+                                       $this->loginStatus = 'OK';
+                                       $admin = 'NO';
+                                       if (isset($info[0]['domainglobaladmin'])) {
+                                               $admin = $info[0]['domainglobaladmin'][0];
+                                               $admin = strtoupper($admin);
+                                       } 
+                                       $this->is_admin = ($admin == 'YES') ? true : false;
+                                       $_SESSION['is_admin'] = $this->is_admin;
+                } else {
+                    $this->loginStatus = 'Login failed';
+                }
+            } else {
+                $this->loginStatus = ldap_error($ds);
+            }
+            @ldap_close($ds);
+        } else {
+            $this->loginStatus = 'Connect to LDAP server failed';
+        }
+
+               return $result;
+       }
+
+       public function getLoginStatus() {
+               return $this->loginStatus;
+       }
+
+       public function isLoggedIn() {
+               global $CFG;
+               $loggedIn = false;
+
+               if ($this->user) {
+                       $loggedIn = true;
+               } else if (isset($_SESSION['user'])) {
+                       $this->user = $_SESSION['user'];
+                       $loggedIn = true;
+               } else {
+                       if ($CFG->auth_method == 'HTTP_AUTH') {
+                               if (isset($this->server['PHP_AUTH_USER'])) {
+                                       $this->user = $this->server['PHP_AUTH_USER'];
+                                       $loggedIn = true;
+                               }
+                       }
+               }
+
+               return $loggedIn;
+       }
+
+       public function getUser() {
+               $this->isLoggedIn();
+               return $this->user;
+       }
+
+       public function getHeader() {
+               return $this->header;
+       }
+
+        public function getFooter() {
+                return $this->footer;
+        }
+
+        public function getHeading() {
+                return $this->heading;
+        }
+
+        public function setHeading($heading) {
+                       global $CFG;
+
+                       $timeout = $CFG->session_timeout * 60 * 1000;
+            $this->heading = str_replace('__TITLE__', $heading, $this->heading);
+                       $this->header = str_replace('__TITLE__', $heading, $this->header);
+                       $this->header = str_replace('__ROOT__', $CFG->wwwroot, $this->header);
+                       $this->header = str_replace('__TIMEOUT__', $timeout, $this->header);
+        }
+
+               public function convertContent($code) {
+                       $table = array(
+                               'V' => 'Virus',
+                               'B' => 'Banned',
+                               'U' => 'Unchecked',
+                               'S' => 'Spam',
+                               'Y' => 'Spammy',
+                               'M' => 'Bad Mime',
+                               'H' => 'Bad Header',
+                               'O' => 'Over sized',
+                               'T' => 'MTA err',
+                               'C' => 'Clean'
+                       );
+                       
+                       $string = $table[$code];
+                       if (empty($string))
+                               $string = 'Unknown';
+
+                       return $string;
+               }
+}
diff --git a/mail_report.php b/mail_report.php
new file mode 100644 (file)
index 0000000..e40f229
--- /dev/null
@@ -0,0 +1,142 @@
+<?php
+/* vim: set ts=4 tw=0 sw=4 noet: */
+       require_once 'Mail/mimeDecode.php';
+       require_once 'config.php';
+       require_once $CFG->root . 'lib/db_factory.php';
+       require_once $CFG->root . 'lib/utils.inc.php';
+
+       $util = Utils::getInstance();
+       $loggedIn = $util->isLoggedIn();
+       if ($loggedIn && isset($_GET['id'])) {
+               $util->setHeading('Spam Report');
+               echo $util->getHeader();
+               echo $util->getHeading();
+
+               $id = $_GET['id'];
+               $mail = unserialize($_SESSION['mailInfo'][$id]);
+
+               $row = $DB->getMail($id);
+               $string = $row->mail_text;
+               $sa_tests = null;
+               $params['include_bodies'] = false;
+               $params['decode_bodies']  = true;
+               $params['decode_headers'] = true;
+               $params['input']          = $string;
+               $params['crlf']           = "\r\n";
+               
+               $structure = Mail_mimeDecode::decode($params);
+               $headers = $structure->headers;
+               $rows = array();
+               foreach ($headers as $header => $value) {
+                       if ($header == 'x-spam-status') {
+                               if (preg_match('/^(.*),\s*score=([\d\.]+).*tests=\[(.*)\].*/',
+                                       $value, $matches)) {
+                                       $sa_tests = array('x-spam-status' => $matches[1],
+                                               'score' => $matches[2], 'tests' => $matches[3]);
+                               }
+                       } else if ($header == 'x-spam-flag') {
+                $value = str_replace("<", "&lt;", $value);
+                               $value = str_replace(">", "&gt;", $value);
+                $rows['spam'] = "<tr><td class=\"label\">Spam</td><td class=\"value\">$value</td></tr>"; 
+            } else if ($header == 'x-spam-score') {
+                $value = str_replace("<", "&lt;", $value);
+                               $value = str_replace(">", "&gt;", $value);
+                $rows['score'] = "<tr><td class=\"label\">Spam Score</td><td class=\"value\">$value</td></tr>";
+            } else if ($header == 'x-spam-level') {
+                $value = str_replace("<", "&lt;", $value);
+                               $value = str_replace(">", "&gt;", $value);
+                $rows['level'] = "<tr><td class=\"label\">Spam Level</td><td class=\"value\">$value</td></tr>";
+            } else if ($header == 'date') {
+                $value = str_replace("<", "&lt;", $value);
+                               $value = str_replace(">", "&gt;", $value);
+                $rows['date'] = "<tr><td class=\"label\">Date</td><td class=\"value\">$value</td></tr>";
+            } else if ($header == 'from') {
+                $value = str_replace("<", "&lt;", $value);
+                               $value = str_replace(">", "&gt;", $value);
+                $rows['from'] = "<tr><td class=\"label\">From</td><td class=\"value\">$value</td></tr>";
+            } else if ($header == 'reply-to') {
+                $value = str_replace("<", "&lt;", $value);
+                               $value = str_replace(">", "&gt;", $value);
+                $rows['reply-to'] = "<tr><td class=\"label\">Reply-to</td><td class=\"value\">$value</td></tr>";
+            } else if ($header == 'to') {
+                $value = str_replace("<", "&lt;", $value);
+                               $value = str_replace(">", "&gt;", $value);
+                $rows['to'] = "<tr><td class=\"label\">To</td><td class=\"value\">$value</td></tr>";
+            } else if ($header == 'subject') {
+                $value = str_replace("<", "&lt;", $value);
+                               $value = str_replace(">", "&gt;", $value);
+                $rows['subject'] = "<tr><td class=\"label\">Subject</td><td class=\"value\">$value</td></tr>";
+            } else if ($header == 'received') {
+                               $headers = '';
+                               foreach ($value as $val) {
+                    $val = str_replace("<", "&lt;", $val);
+                                       $val = str_replace(">", "&gt;", $val);
+                    $headers .= ($headers == '') ? "$val" : "<br/><br/>$val";
+                }
+                $rows['headers'] = "<tr><td class=\"label\">Headers</td><td class=\"value\">$headers</td></tr>";
+                       }
+               }
+               $bayes = array();
+               if ($sa_tests) {
+                       $sa_tests['tests'] = str_replace(" ","",$sa_tests['tests']);
+                       $sa_rules = explode(",",$sa_tests['tests']);
+                       $sa_count = count($sa_rules);
+                       for ($i = 0; $i < $sa_count; $i++) {
+                               $sa_rule = explode("=", $sa_rules[$i]);
+                               $bayes[$i] = "<tr><td class=\"label\">$sa_rule[0]</td><td class=\"value\">$sa_rule[1]</td></tr>";
+                       }
+                       $bayes['total'] = "<tr><td class=\"label\">TOTAL</td><td class=\"value\">".$sa_tests['score']."</td></tr>";
+               } else {
+                       $bayes['total'] = "<tr><td class=\"label\">TOTAL</td><td class=\"value\">-</td></tr>";
+               }
+               $plain = "?id=$id&format=plain";
+               $html = "?id=$id&format=html";
+               echo '<p class="ruler">Message ID : ' . $mail->mail_id . '</p>';
+               echo '<table class="button-menu">';
+               echo '<tr class="button-row">';
+               echo '<td class="button"><input class="btn-input" type="button" value="Return" 
+                         onclick="javascript: history.back();"/></td>';
+               echo '<td class="button"><input class="btn-input" type="button" value="View HTML"
+                        onclick="javascript: window.location.href=\'message_view.php'.$html.'\'"/></td>';
+               echo '<td class="button"><input class="btn-input" type="button" value="View Plain Text" 
+                        onclick="javascript: window.location.href=\'message_view.php'.$plain.'\'"/></td>';
+               echo '<td class="button"><input class="btn-input" type="button" value="View Full Headers"
+                        onclick="javascript: window.location.href=\'show_headers.php?id='.$id.'\'"/></td>';
+               echo '</tr></table>';
+               echo '<table>';
+               if (isset($rows['spam']))
+                       echo $rows['spam'];
+               if (isset($rows['score']))
+                       echo $rows['score'];
+               if (isset($rows['level']))
+                       echo $rows['level'];
+               if (isset($rows['headers']))
+                       echo $rows['headers'];
+               if (isset($rows['date']))
+                       echo $rows['date'];
+               if (isset($rows['from']))
+                       echo $rows['from'];
+               if (isset($rows['reply-to']))
+                       echo $rows['reply-to'];
+               if (isset($rows['to']))
+                       echo $rows['to'];
+               if (isset($rows['subject']))
+                       echo $rows['subject'];
+               echo '</table>';
+               echo '<p class="ruler">Spamassassin Report</p>';
+               echo '<table><tr><th>Rule</th><th>Score</th></tr>';
+               foreach ($bayes as $key => $row) {
+                       if ($key != 'total') {
+                               echo $row;
+                       }
+               }
+               echo $bayes['total'];
+               echo '</table>';
+               echo $util->getFooter();
+       } else if ($loggedIn) {
+               header('Location: index.php');
+       } else {
+               header('Location: auth.php');
+       }
+
+?>
diff --git a/message_view.php b/message_view.php
new file mode 100644 (file)
index 0000000..03cced9
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+/* vim: set ts=4 tw=0 sw=4 noet: */
+       require_once 'Mail/mimeDecode.php';
+       require_once 'config.php';
+       require_once $CFG->root . 'lib/db_factory.php';
+       require_once $CFG->root . 'lib/utils.inc.php';
+
+       $util = Utils::getInstance();
+       $loggedIn = $util->isLoggedIn();
+       if ($loggedIn && isset($_GET['id'])) {
+        $id = $_GET['id'];
+        $id = urldecode($id);
+
+               $util->setHeading("Message ID : $id");
+               echo $util->getHeader();
+               echo $util->getHeading();
+
+               $mail = unserialize($_SESSION['mailInfo'][$id]);
+
+               $row = $DB->getMail($id);
+               $string = $row->mail_text;
+               $sa_tests = null;
+               $params['include_bodies'] = true;
+               $params['decode_bodies']  = true;
+               $params['decode_headers'] = true;
+               $params['input']          = $string;
+               $params['crlf']           = "\r\n";
+               
+               $structure = Mail_mimeDecode::decode($params);
+               $headers = $structure->headers;
+
+        echo '<table class="button-menu">';
+        echo '<tr class="button-row">';
+        echo '<td class="button"><input class="btn-input" type="button" value="Return" 
+              onclick="javascript: history.back();"/></td>';
+        echo '</tr></table>';
+
+               echo '<table><tr>';
+               $from = $headers['from'];
+               $from = str_replace("<", "&lt;", $from);
+               $from = str_replace(">", "&gt;", $from);
+               echo "<tr><td class=\"label\">From</td><td class=\"value\">$from</td></tr>";
+               $to = $headers['to'];
+               $to = str_replace("<", "&lt;", $to);
+               $to = str_replace(">", "&gt;", $to);
+               echo "<tr><td class=\"label\">To</td><td class=\"value\">$to</td></tr>";
+               $date = $headers['date'];
+               $date = str_replace("<", "&lt;", $date);
+               $date = str_replace(">", "&gt;", $date);
+               echo "<tr><td class=\"label\">Date</td><td class=\"value\">$date</td></tr>";
+               echo "<tr><td class=\"label\">Subject</td><td class=\"value\">".$headers['subject']."</td></tr>";
+               echo '<tr><td class="label">Body</td><td class="value">';
+               if (isset($structure->parts)) {
+                       foreach ($structure->parts as $part) {
+                               if ($_GET['format'] == 'html') {
+                                       if ($part->ctype_primary=="text" and $part->ctype_secondary=="html") {
+                                               $bodytext = str_replace("\n", " ",$part->body);
+                                               $bodytext = str_replace("<body>", "", $bodytext);
+                                               $bodytext = str_replace("</body>", "", $bodytext);
+                                                $bodytext = str_replace("<head>", "", $bodytext);
+                                                $bodytext = str_replace("</head>", "", $bodytext);
+                                               $bodytext = str_replace("<html>", "", $bodytext);
+                                               $bodytext = str_replace("</html>", "", $bodytext);
+                                               echo $bodytext;
+                                       }
+                               } else {
+                                       if ($part->ctype_primary=="text" and $part->ctype_secondary=="plain") {
+                                               $bodytext = str_replace("\n", "<br />",$part->body);
+                                               echo $bodytext;
+                                       }
+                               }
+                       }
+               } else {
+                       if ($_GET['format'] == 'html') {
+                               $bodytext = str_replace("\n", " ",$structure->body);
+                               $bodytext = str_replace("<body>", "", $bodytext);
+                               $bodytext = str_replace("</body>", "", $bodytext);
+                                $bodytext = str_replace("<head>", "", $bodytext);
+                                $bodytext = str_replace("</head>", "", $bodytext);
+
+                               $bodytext = str_replace("<html>", "", $bodytext);
+                               $bodytext = str_replace("</html>", "", $bodytext);
+                               echo $bodytext;
+                       } else {
+                               $bodytext = $structure->body;           
+                               $bodytext = wordwrap($bodytext, 90, "<br/>");
+                               echo $bodytext;
+                       }
+               }
+               echo '</td></tr></table>';
+               echo $util->getFooter();
+    } else if ($loggedIn) {
+        header('Location: index.php');
+    } else {
+        header('Location: auth.php');
+    }
+
+?>
+
diff --git a/pics/delete.png b/pics/delete.png
new file mode 100644 (file)
index 0000000..31150da
Binary files /dev/null and b/pics/delete.png differ
diff --git a/pics/release.png b/pics/release.png
new file mode 100644 (file)
index 0000000..6e2541c
Binary files /dev/null and b/pics/release.png differ
diff --git a/quarantine.php b/quarantine.php
new file mode 100644 (file)
index 0000000..be89417
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/* vim: set ts=4 tw=0 sw=4 noet: */
+       require_once 'config.php';
+       require_once $CFG->root . 'lib/db_factory.php';
+       require_once $CFG->root . 'lib/utils.inc.php';
+
+       function error($error) {
+               $util = Utils::getInstance();
+               $util->setHeading("Error");
+               echo $util->getHeader();
+               echo $util->getHeading();
+               echo "<p style=\"color: red;\">$error</p>";
+               echo '<a href="index.php">Return</a>';
+               echo $util->getFooter();
+       }
+
+       $util = Utils::getInstance();
+       $loggedIn = $util->isLoggedIn();
+       $request = isset($_GET['op']) ? $_GET['op'] : '';
+       if ($loggedIn && isset($_GET['id'])) {
+        $mail_id = urldecode($_GET['id']);
+               $mail = unserialize($_SESSION['mailInfo']["$mail_id"]);
+               $secret_id = $mail->secret_id;
+               $recipient = $mail->recipient;
+
+               $query = array();
+               if ($request == 'release') {
+                       $amavisserver = $CFG->amavisd_db_host;
+                       $policy_port = $CFG->amavis_policy_port;
+
+                       $fp = fsockopen($amavisserver, $policy_port, $errno, $errstr, 30);
+                       if (!$fp) {
+                               error("$errstr ($errno)");
+                               exit;
+                       }
+                       $out = "request=" . $request . "\r\n";
+                       $out .= "mail_id=" . $mail_id . "\r\n";
+                       $out .= "recipient=" . $recipient . "\r\n";
+                       $out .= "secret_id=" . $secret_id . "\r\n\r\n";
+                       fwrite($fp, $out);
+                       $response = fread($fp, 8192);
+                       fclose($fp);
+                       $response = urldecode($response);
+                       if (! preg_match("/^setreply=250\s+([\d\.]+)\s+(.*)/", $response, $matches)) {
+                               error("Request to release failed [$out][$response]");
+                               exit;
+                       }
+                       if ($matches[1] != '2.0.0') {
+                               error($matches[2]);
+                               exit;
+                       }
+
+                       $query[] = "UPDATE msgrcpt SET rs = 'R' WHERE mail_id = '$mail_id'";
+               } else if ($request == 'delete') {
+                       $query[] = "UPDATE msgrcpt SET rs = 'D' WHERE mail_id = '$mail_id'";
+               } else {
+                       error("Unknown operation [$request]");
+                       exit;
+               }
+               $success = $DB->update($query);
+               if (! $success) {
+                       error("Message not released, contact administrator [$query]");
+                       exit;
+               }
+               header('Location: index.php');
+    } else if ($loggedIn && $request == 'purge') {
+               $marked = unserialize($_SESSION['marked']);
+               unset($_SESSION['marked']);
+               $query = array();
+               $error = array();
+               foreach ($marked as $mail_id) {
+            $query[] = "delete from msgs where mail_id = '$mail_id'";
+            $query[] = "delete from msgrcpt where mail_id = '$mail_id'";
+            $query[] = "delete from quarantine where mail_id = '$mail_id'";
+               $success = $DB->update($query);
+               if (! $success) {
+               $error[] = $mail_id;
+                       }
+               }
+               if (count($error) > 0) {
+                       $str = implode(', ', $error);
+               error("The following messages was not purged [$str], contact administrator");
+               exit;
+               }
+               header('Location: index.php');
+       } else if ($loggedIn) {
+        header('Location: index.php');
+    } else {
+        header('Location: auth.php');
+    }
+
+?>
+
diff --git a/show_headers.php b/show_headers.php
new file mode 100644 (file)
index 0000000..0c0197c
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/* vim: set ts=4 tw=0 sw=4 noet: */
+       require_once 'Mail/mimeDecode.php';
+       require_once 'config.php';
+       require_once $CFG->root . 'lib/db_factory.php';
+       require_once $CFG->root . 'lib/utils.inc.php';
+
+       $util = Utils::getInstance();
+       $loggedIn = $util->isLoggedIn();
+       if ($loggedIn && isset($_GET['id'])) {
+               $util->setHeading('Full Headers Report');
+               echo $util->getHeader();
+               echo $util->getHeading();
+
+               $id = $_GET['id'];
+               $mail = unserialize($_SESSION['mailInfo'][$id]);
+
+               $row = $DB->getMail($id);
+               $string = $row->mail_text;
+               $sa_tests = null;
+               $params['include_bodies'] = false;
+               $params['decode_bodies']  = true;
+               $params['decode_headers'] = true;
+               $params['input']          = $string;
+               $params['crlf']           = "\r\n";
+               
+               $structure = Mail_mimeDecode::decode($params);
+               $headers = $structure->headers;
+               $output = '<table><tr><th>Header</th><th>Value</th></tr>';
+               foreach ($headers as $header => $value) {
+                       if (is_array($value)) {
+                               $text = '';
+                               foreach ($value as $val) {
+                                       if ($text == '') {
+                                               $text = $val;
+                                       } else {
+                                               $text .= "<br/><br/>$val";
+                                       }
+                               }
+                       } else {
+                               $text = $value;
+                       }
+                       $output .= "<tr><td class=\"label\">$header</td><td class=\"value\">$text</td></tr>";
+               }
+               $output .= '</table>';
+               echo '<table class="button-menu">';
+        echo '<tr class="button-row">';
+        echo '<td class="button"><input class="btn-input" type="button" value="Return" 
+              onclick="javascript: history.back();"/></td>';
+               echo '</tr></table>';
+               echo $output;
+               echo $util->getFooter();
+       } else if ($loggedIn) {
+               header('Location: index.php');
+       } else {
+               header('Location: auth.php');
+       }
+
+?>
This page took 0.128583 seconds and 5 git commands to generate.