function alertAdmin($subject, $message, $log = false) { global $cfg; //Set admin's email address if (!$cfg || !($to = $cfg->getAdminEmail())) { $to = ADMIN_EMAIL; } //Try getting the alert email. $email = null; if ($cfg && !($email = $cfg->getAlertEmail())) { $email = $cfg->getDefaultEmail(); } //will take the default email. if ($email) { $email->send($to, $subject, $message); } else { //no luck - try the system mail. Email::sendmail($to, $subject, $message, sprintf('"osTicket Alerts"<%s>', $to)); } //log the alert? Watch out for loops here. if ($log && is_object($cfg)) { //if $cfg is not set then it means we don't have DB connection. Sys::log(LOG_CRIT, $subject, $message, false); //Log the enter...and make sure no alerts are resent. } }
function api_exit($code, $msg = '') { global $remotehost, $cfg; if ($code != EX_SUCCESS) { //Error occured... $_SESSION['api']['errors'] += 1; $_SESSION['api']['time'] = time(); Sys::log(LOG_WARNING, "API error - code #{$code}", $msg); //echo "API Error:.$msg"; } if ($remotehost) { switch ($code) { case EX_SUCCESS: Http::response(200, $code, 'text/plain'); break; case EX_UNAVAILABLE: Http::response(405, $code, 'text/plain'); break; case EX_NOPERM: Http::response(403, $code, 'text/plain'); break; case EX_DATAERR: case EX_NOINPUT: default: Http::response(416, $code, 'text/plain'); } } exit($code); }
function encrypt($text, $salt) { //if mcrypt extension is not installed--simply return unencryted text and log a warning. if (!function_exists('mcrypt_encrypt') || !function_exists('mcrypt_decrypt')) { $msg = 'Cryptography extension mcrypt is not enabled or installed. IMAP/POP passwords are being stored as plain text in database.'; Sys::log(LOG_WARN, 'mcrypt missing', $msg); return $text; } return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)))); }
function encrypt($text, $salt) { //if mcrypt extension is not installed--simply return unencryted text and log a warning. if (!function_exists('mcrypt_encrypt') || !function_exists('mcrypt_decrypt')) { $msg = 'A extensão de criptografia mcrypt não está habilitada ou instalada. As senhas de IMAP/POP estão sendo armazenadas como texto sem formatação no banco de dados.'; Sys::log(LOG_WARN, 'mcrypt missing', $msg); return $text; } return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)))); }
function encrypt($text, $salt) { //if mcrypt extension is not installed--simply return unencryted text and log a warning. if (!function_exists('mcrypt_encrypt') || !function_exists('mcrypt_decrypt')) { $msg = 'Extensión mcrypt criptografíca no está habilitada o instalada. Contraseñas de IMAP/POP se almacenan como texto sin formato en la base de datos.'; Sys::log(LOG_WARN, 'Falta mcrypt', $msg); return $text; } return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)))); }
function db_query($query, $database = "", $conn = "") { global $cfg; if ($conn) { /* connection is provided*/ $response = $database ? mysql_db_query($database, $query, $conn) : mysql_query($query, $conn); } else { $response = $database ? mysql_db_query($database, $query) : mysql_query($query); } if (!$response) { //error reporting $alert = '[' . $query . ']' . "\n\n" . db_error(); Sys::log(LOG_ALERT, 'DB Error #' . db_errno(), $alert, $cfg && $cfg->alertONSQLError()); //echo $msg; #uncomment during debuging or dev. } return $response; }
<?php /********************************************************************* logout.php Destroy users session. Copyright (c) 2012-2014 Katak Support http://www.katak-support.com/ Released under the GNU General Public License WITHOUT ANY WARRANTY. Derived from osTicket v1.6 by Peter Rotich. See LICENSE.TXT for details. $Id: $ **********************************************************************/ require_once 'user.inc.php'; //Log logout info... $msg = sprintf("%s/%s " . _("logged out"), $_SESSION['_user']['userID'], $_SESSION['_user']['key']); Sys::log(LOG_DEBUG, 'User logout', $msg, $_SESSION['_user']['userID']); //We are checking to make sure the user is logged in before a logout to avoid session reset tricks on excess logins $_SESSION['_user'] = array(); session_unset(); session_destroy(); header('Location: index.php'); require 'index.php';
$_SESSION['daylight'] = $user->observeDaylight(); Sys::log(LOG_DEBUG, 'Inicio de sesión de Staff', sprintf("%s Identificado como [%s]", $user->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug. //Redirect to the original destination. (make sure it is not redirecting to login page.) $dest = $dest && (!strstr($dest, 'login.php') && !strstr($dest, 'ajax.php')) ? $dest : 'index.php'; session_write_close(); session_regenerate_id(); @header("Location: {$dest}"); require_once 'index.php'; //Just incase header is messed up. exit; } //If we get to this point we know the login failed. $_SESSION['_staff']['strikes'] += 1; if (!$errors && $_SESSION['_staff']['strikes'] > $cfg->getStaffMaxLogins()) { $msg = 'Acceso Denegado'; $errors['err'] = '¿Olvidaste tus datos de conexión?. Contacta con el Administrador'; $_SESSION['_staff']['laststrike'] = time(); $alert = 'Exceso de intentos de conexión por un miembro del personal' . "\n" . 'Nombre de usuario: ' . $_POST['username'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'TIME: ' . date('M j, Y, g:i a T') . "\n\n" . 'Intentos #' . $_SESSION['_staff']['strikes'] . "\n" . 'Tiempo de espera: ' . $cfg->getStaffLoginTimeout() / 60 . " Minutos \n\n"; Sys::log(LOG_ALERT, 'Excesivos intentos de conexión (Miembro del Staff)', $alert, $cfg->alertONLoginError()); } elseif ($_SESSION['_staff']['strikes'] % 2 == 0) { //Log every other failed login attempt as a warning. $alert = 'Nombre de usuario: ' . $_POST['username'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'Hora: ' . date('M j, Y, g:i a T') . "\n\n" . 'Intentos #' . $_SESSION['_staff']['strikes']; Sys::log(LOG_WARNING, 'Intento de inicio de sesión fallido (Miembro del Staff)', $alert); } } define("OSTSCPINC", TRUE); //Make includes happy! $login_err = $_POST ? true : false; //error displayed only on post include_once INCLUDE_DIR . 'staff/login.tpl.php';
function getMsgTemplate($name) { //TODO: Don't preload - do ondemand fetch! $tpl = array(); switch (strtolower($name)) { case 'ticket_autoresp': $tpl = array('subj' => $this->ht['ticket_autoresp_subj'], 'body' => $this->ht['ticket_autoresp_body']); break; case 'msg_autoresp': $tpl = array('subj' => $this->ht['message_autoresp_subj'], 'body' => $this->ht['message_autoresp_body']); break; case 'ticket_notice': $tpl = array('subj' => $this->ht['ticket_notice_subj'], 'body' => $this->ht['ticket_notice_body']); break; case 'overlimit_notice': $tpl = array('subj' => $this->ht['ticket_overlimit_subj'], 'body' => $this->ht['ticket_overlimit_body']); break; case 'ticket_reply': $tpl = array('subj' => $this->ht['ticket_reply_subj'], 'body' => $this->ht['ticket_reply_body']); break; case 'ticket_alert': $tpl = array('subj' => $this->ht['ticket_alert_subj'], 'body' => $this->ht['ticket_alert_body']); break; case 'msg_alert': $tpl = array('subj' => $this->ht['message_alert_subj'], 'body' => $this->ht['message_alert_body']); break; case 'note_alert': $tpl = array('subj' => $this->ht['note_alert_subj'], 'body' => $this->ht['note_alert_body']); break; case 'assigned_alert': $tpl = array('subj' => $this->ht['assigned_alert_subj'], 'body' => $this->ht['assigned_alert_body']); break; case 'transfer_alert': $tpl = array('subj' => $this->ht['transfer_alert_subj'], 'body' => $this->ht['transfer_alert_body']); break; case 'overdue_alert': $tpl = array('subj' => $this->ht['ticket_overdue_subj'], 'body' => $this->ht['ticket_overdue_body']); break; default: Sys::log(LOG_WARNING, 'Template Fetch Error', "Unable to fetch '{$name}' template - id #" . $this->getId()); $tpl = array(); } return $tpl; }
<?php /********************************************************************* cron.php File to handle cron job calls (local and remote). Peter Rotich <*****@*****.**> Copyright (c) 2006-2010 osTicket http://www.osticket.com Released under the GNU General Public License WITHOUT ANY WARRANTY. See LICENSE.TXT for details. vim: expandtab sw=4 ts=4 sts=4: $Id: $ **********************************************************************/ @chdir(realpath(dirname(__FILE__)) . '/'); //Change dir. require 'api.inc.php'; require_once INCLUDE_DIR . 'class.cron.php'; Cron::run(); Sys::log(LOG_DEBUG, 'Cron Job', 'Cron Job Externo ejecutado [' . $_SERVER['REMOTE_ADDR'] . ']');
$_SESSION['TZ_OFFSET'] = $cfg->getTZoffset(); $_SESSION['daylight'] = $cfg->observeDaylightSaving(); //Log login info... $msg = sprintf("%s/%s logged in [%s]", $ticket->getEmail(), $ticket->getExtId(), $_SERVER['REMOTE_ADDR']); Sys::log(LOG_DEBUG, 'User login', $msg); //Redirect tickets.php session_write_close(); session_regenerate_id(); @header("Location: tickets.php"); require_once 'tickets.php'; //Just incase. of header already sent error. exit; } } //If we get to this point we know the login failed. $_SESSION['_client']['strikes'] += 1; if (!$errors && $_SESSION['_client']['strikes'] > $cfg->getClientMaxLogins()) { $loginmsg = 'Access Denied'; $errors['err'] = 'Forgot your login info? Please <a href="open.php">open a new ticket</a>.'; $_SESSION['_client']['laststrike'] = time(); $alert = 'Excessive login attempts by a client?' . "\n" . 'Email: ' . $_POST['lemail'] . "\n" . 'Ticket#: ' . $_POST['lticket'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'Time:' . date('M j, Y, g:i a T') . "\n\n" . 'Attempts #' . $_SESSION['_client']['strikes']; Sys::log(LOG_ALERT, 'Excessive login attempts (client)', $alert, $cfg->alertONLoginError()); } elseif ($_SESSION['_client']['strikes'] % 2 == 0) { //Log every other failed login attempt as a warning. $alert = 'Email: ' . $_POST['lemail'] . "\n" . 'Ticket #: ' . $_POST['lticket'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'TIME: ' . date('M j, Y, g:i a T') . "\n\n" . 'Attempts #' . $_SESSION['_client']['strikes']; Sys::log(LOG_WARNING, 'Failed login attempt (client)', $alert); } } require CLIENTINC_DIR . 'header.inc.php'; require CLIENTINC_DIR . 'login.inc.php'; require CLIENTINC_DIR . 'footer.inc.php';
<?php /********************************************************************* logout.php Log out staff Destroy the session and redirect to login.php Peter Rotich <*****@*****.**> Copyright (c) 2006-2010 osTicket http://www.osticket.com Released under the GNU General Public License WITHOUT ANY WARRANTY. See LICENSE.TXT for details. vim: expandtab sw=4 ts=4 sts=4: $Id: $ **********************************************************************/ require 'staff.inc.php'; Sys::log(LOG_DEBUG, 'Staff logout', sprintf("%s logged out [%s]", $thisuser->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug. $_SESSION['_staff'] = array(); session_unset(); session_destroy(); @header('Location: login.php'); require 'login.php';
<?php /********************************************************************* cron.php File to handle cron job calls (local and remote). Peter Rotich <*****@*****.**> Copyright (c) 2006-2010 osTicket http://www.osticket.com Released under the GNU General Public License WITHOUT ANY WARRANTY. See LICENSE.TXT for details. vim: expandtab sw=4 ts=4 sts=4: $Id: cron.php,v 1.1.2.1 2009/08/17 18:25:57 carlos.delfino Exp $ **********************************************************************/ @chdir(realpath(dirname(__FILE__)) . '/'); //Change dir. require 'api.inc.php'; require_once INCLUDE_DIR . 'class.cron.php'; Cron::run(); Sys::log(LOG_DEBUG, 'Cron Job', 'External cron job executed [' . $_SERVER['REMOTE_ADDR'] . ']');
ignore_user_abort(1); //Leave me a lone bro! @set_time_limit(0); //useless when safe_mode is on $data = sprintf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%", 71, 73, 70, 56, 57, 97, 1, 0, 1, 0, 128, 255, 0, 192, 192, 192, 0, 0, 0, 33, 249, 4, 1, 0, 0, 0, 0, 44, 0, 0, 0, 0, 1, 0, 1, 0, 0, 2, 2, 68, 1, 0, 59); $datasize = strlen($data); header('Content-type: image/gif'); header('Cache-Control: no-cache, must-revalidate'); header("Content-Length: {$datasize}"); header('Connection: Close'); print $data; ob_start(); //Keep the image output clean. Hide our dirt. //TODO: Make cron DB based to allow for better time limits. Direct calls for now sucks big time. //We DON'T want to spawn cron on every page load...we record the lastcroncall on the session per user $sec = time() - $_SESSION['lastcroncall']; if ($sec > 180) { //user can call cron once every 3 minutes. require_once INCLUDE_DIR . 'class.cron.php'; Cron::TicketMonitor(); //Age tickets: We're going to age tickets ever regardless of cron settings. if ($cfg && $cfg->enableAutoCron()) { //ONLY fetch tickets if autocron is enabled! Cron::MailFetcher(); //Fetch mail. Sys::log(LOG_DEBUG, 'Autocron', 'cron job executed [' . $thisuser->getUserName() . ']'); } $_SESSION['lastcroncall'] = time(); } $output = ob_get_contents(); ob_end_clean();
$_SESSION['TZ_OFFSET'] = $cfg->getTZoffset(); $_SESSION['daylight'] = $cfg->observeDaylightSaving(); //Log login info... $msg = sprintf("%s/%s logado em [%s]", $ticket->getEmail(), $ticket->getExtId(), $_SERVER['REMOTE_ADDR']); Sys::log(LOG_DEBUG, 'Usu�rio de login', $msg); //Redirect tickets.php session_write_close(); session_regenerate_id(); @header("Location: tickets.php"); require_once 'tickets.php'; //Just incase. of header already sent error. exit; } } //If we get to this point we know the login failed. $_SESSION['_client']['strikes'] += 1; if (!$errors && $_SESSION['_client']['strikes'] > $cfg->getClientMaxLogins()) { $loginmsg = 'Acesso negado'; $errors['err'] = 'Esqueceu suas informa��es de login? Por favor, <a href="open.php">abra um novo ticket</a>.'; $_SESSION['_client']['laststrike'] = time(); $alert = 'Tentativas excessivas de login por um cliente?' . "\n" . 'E-mail: ' . $_POST['lemail'] . "\n" . 'Ticket#: ' . $_POST['lticket'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'Hora:' . date('M j, Y, g:i a T') . "\n\n" . 'Tentativas #' . $_SESSION['_client']['strikes']; Sys::log(LOG_ALERT, 'Tentativas excessivas de login (cliente)', $alert, $cfg->alertONLoginError()); } elseif ($_SESSION['_client']['strikes'] % 2 == 0) { //Log every other failed login attempt as a warning. $alert = 'E-mail: ' . $_POST['lemail'] . "\n" . 'Ticket #: ' . $_POST['lticket'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'Hora: ' . date('M j, Y, g:i a T') . "\n\n" . 'Tentativas #' . $_SESSION['_client']['strikes']; Sys::log(LOG_WARNING, 'Falha de tentativa de login (cliente)', $alert); } } require CLIENTINC_DIR . 'header.inc.php'; require CLIENTINC_DIR . 'login.inc.php'; require CLIENTINC_DIR . 'footer.inc.php';
<?php /********************************************************************* logout.php Log out staff Destroy the session and redirect to login.php Peter Rotich <*****@*****.**> Copyright (c) 2006-2010 osTicket http://www.osticket.com Released under the GNU General Public License WITHOUT ANY WARRANTY. See LICENSE.TXT for details. vim: expandtab sw=4 ts=4 sts=4: $Id: $ **********************************************************************/ require 'staff.inc.php'; Sys::log(LOG_DEBUG, 'Atendente desconectado', sprintf("%s desconectado [%s]", $thisuser->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug. $_SESSION['_staff'] = array(); session_unset(); session_destroy(); @header('Location: login.php'); require 'login.php';
<?php /********************************************************************* logout.php Log out staff Destroy the session and redirect to the staff entry page. Copyright (c) 2012-2013 Katak Support http://www.katak-support.com/ Released under the GNU General Public License WITHOUT ANY WARRANTY. Derived from osTicket by Peter Rotich. See LICENSE.TXT for details. $Id: $ **********************************************************************/ require 'staff.inc.php'; Sys::log(LOG_DEBUG, 'Staff logout', sprintf("%s " . _('logged out'), $thisuser->getUserName()), $thisuser->getUserName()); //Debug. $_SESSION['_staff'] = array(); session_unset(); session_destroy(); @header('Location: index.php'); require 'index.php';
<?php /********************************************************************* logout.php Log out staff Destroy the session and redirect to login.php Peter Rotich <*****@*****.**> Copyright (c) 2006-2010 osTicket http://www.osticket.com Released under the GNU General Public License WITHOUT ANY WARRANTY. See LICENSE.TXT for details. vim: expandtab sw=4 ts=4 sts=4: $Id: $ **********************************************************************/ require 'staff.inc.php'; Sys::log(LOG_DEBUG, 'Desconexión Miembro del Staff ', sprintf("%s Desconectado [%s]", $thisuser->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug. $_SESSION['_staff'] = array(); session_unset(); session_destroy(); @header('Location: login.php'); require 'login.php';
function create($vars, &$errors, $origin, $autorespond = true, $alertstaff = true) { global $cfg, $thisclient, $_FILES; //Make sure the email is not banned if ($vars['email'] && EmailFilter::isBanned($vars['email'])) { $errors['err'] = 'Ticket denied. Error #403'; Sys::log(LOG_WARNING, 'Ticket denied', 'Banned email - ' . $vars['email']); return 0; } $id = 0; $fields = array(); $fields['name'] = array('type' => 'string', 'required' => 1, 'error' => 'Name required'); $fields['email'] = array('type' => 'email', 'required' => 1, 'error' => 'Valid email required'); $fields['subject'] = array('type' => 'string', 'required' => 1, 'error' => 'Subject required'); $fields['message'] = array('type' => 'text', 'required' => 1, 'error' => 'Message required'); switch (strtolower($origin)) { case 'web': $fields['topicId'] = array('type' => 'int', 'required' => 1, 'error' => 'Select help topic'); break; case 'staff': $fields['deptId'] = array('type' => 'int', 'required' => 1, 'error' => 'Dept. required'); $fields['topicId'] = array('type' => 'int', 'required' => 1, 'error' => 'Topic required'); $fields['duedate'] = array('type' => 'date', 'required' => 0, 'error' => 'Invalid date - must be MM/DD/YY'); case 'api': $fields['source'] = array('type' => 'string', 'required' => 1, 'error' => 'Indicate source'); break; case 'email': $fields['emailId'] = array('type' => 'int', 'required' => 1, 'error' => 'Email unknown'); break; default: # TODO: Return error message $errors['origin'] = 'Invalid origin given'; } $fields['pri'] = array('type' => 'int', 'required' => 0, 'error' => 'Invalid Priority'); $fields['phone'] = array('type' => 'phone', 'required' => 0, 'error' => 'Valid phone # required'); if (!Validator::process($fields, $vars, $errors) && !$errors['err']) { $errors['err'] = 'Missing or invalid data - check the errors and try again'; } //Make sure phone extension is valid if ($vars['phone_ext']) { if (!is_numeric($vars['phone_ext']) && !$errors['phone']) { $errors['phone'] = 'Invalid phone ext.'; } elseif (!$vars['phone']) { //make sure they just didn't enter ext without phone # XXX: reconsider allowing! $errors['phone'] = 'Phone number required'; } } //Make sure the due date is valid if ($vars['duedate']) { if (!$vars['time'] || strpos($vars['time'], ':') === false) { $errors['time'] = 'Select time'; } elseif (strtotime($vars['duedate'] . ' ' . $vars['time']) === false) { $errors['duedate'] = 'Invalid duedate'; } elseif (strtotime($vars['duedate'] . ' ' . $vars['time']) <= time()) { $errors['duedate'] = 'Due date must be in the future'; } } //check attachment..if any is set ...only set on webbased tickets.. //XXX:?? Create ticket anyway and simply drop the attachments?? We're already doing so with emails. if ($_FILES['attachment']['name'] && $cfg->allowOnlineAttachments()) { if (!$cfg->canUploadFileType($_FILES['attachment']['name'])) { $errors['attachment'] = 'Invalid file type [ ' . Format::htmlchars($_FILES['attachment']['name']) . ' ]'; } elseif ($_FILES['attachment']['size'] > $cfg->getMaxFileSize()) { $errors['attachment'] = 'File is too big. Max ' . $cfg->getMaxFileSize() . ' bytes allowed'; } } # Perform email filter actions on the new ticket arguments XXX: Move filter to the top and check for reject... if (!$errors && ($ef = new EmailFilter($vars))) { $ef->apply($vars); } # Some things will need to be unpacked back into the scope of this # function if (isset($vars['autorespond'])) { $autorespond = $vars['autorespond']; } //check ticket limits..if limit set is >0 //TODO: Base ticket limits on SLA... XXX: move it elsewhere?? if ($vars['email'] && !$errors && $cfg->getMaxOpenTickets() > 0 && strcasecmp($origin, 'staff')) { $openTickets = Ticket::getOpenTicketsByEmail($vars['email']); if ($openTickets >= $cfg->getMaxOpenTickets()) { $errors['err'] = "You've reached the maximum open tickets allowed."; //Send the notice only once (when the limit is reached) incase of autoresponders at client end. if ($cfg->getMaxOpenTickets() == $openTickets && $cfg->sendOverlimitNotice()) { if ($vars['deptId']) { $dept = Dept::lookup($vars['deptId']); } if (!$dept || !($tpl = $dept->getTemplate())) { $tpl = $cfg->getDefaultTemplate(); } if (!$dept || !($email = $dept->getAutoRespEmail())) { $email = $cfg->getDefaultEmail(); } if ($tpl && ($msg = $tpl->getOverlimitMsgTemplate()) && $email) { $body = str_replace('%name', $vars['name'], $msg['body']); $body = str_replace('%email', $vars['email'], $msg['body']); $body = str_replace('%url', $cfg->getBaseUrl(), $body); $body = str_replace('%signature', $dept && $dept->isPublic() ? $dept->getSignature() : '', $body); $email->send($vars['email'], $msg['subj'], $body); } //Log + Alert admin...this might be spammy (no option to disable)...but it is helpful..I think. $msg = 'Support ticket request denied for ' . $vars['email'] . "\n" . 'Open ticket:' . $openTickets . "\n" . 'Max Allowed:' . $cfg->getMaxOpenTickets() . "\n\nNotice only sent once"; Sys::log(LOG_CRIT, 'Overlimit Notice', $msg); } } } //Any error above is fatal. if ($errors) { return 0; } // OK...just do it. $deptId = $vars['deptId']; //pre-selected Dept if any. $priorityId = $vars['pri']; $source = ucfirst($vars['source']); $topic = NULL; // Intenal mapping magic...see if we need to overwrite anything if (isset($vars['topicId']) && ($topic = Topic::lookup($vars['topicId']))) { //Ticket created via web by user/or staff $deptId = $deptId ? $deptId : $topic->getDeptId(); $priorityId = $priorityId ? $priorityId : $topic->getPriorityId(); if ($autorespond) { $autorespond = $topic->autoRespond(); } $source = $vars['source'] ? $vars['source'] : 'Web'; } elseif ($vars['emailId'] && !$vars['deptId'] && ($email = Email::lookup($vars['emailId']))) { //Emailed Tickets $deptId = $email->getDeptId(); $priorityId = $priorityId ? $priorityId : $email->getPriorityId(); if ($autorespond) { $autorespond = $email->autoRespond(); } $email = null; $source = 'Email'; } elseif ($vars['deptId']) { //Opened by staff. $deptId = $vars['deptId']; $source = ucfirst($vars['source']); } //Last minute checks $priorityId = $priorityId ? $priorityId : $cfg->getDefaultPriorityId(); $deptId = $deptId ? $deptId : $cfg->getDefaultDeptId(); $topicId = $vars['topicId'] ? $vars['topicId'] : 0; $ipaddress = $vars['ip'] ? $vars['ip'] : $_SERVER['REMOTE_ADDR']; //We are ready son...hold on to the rails. $extId = Ticket::genExtRandID(); $sql = 'INSERT INTO ' . TICKET_TABLE . ' SET created=NOW() ' . ' ,lastmessage= NOW()' . ' ,ticketID=' . db_input($extId) . ' ,dept_id=' . db_input($deptId) . ' ,topic_id=' . db_input($topicId) . ' ,priority_id=' . db_input($priorityId) . ' ,email=' . db_input($vars['email']) . ' ,name=' . db_input(Format::striptags($vars['name'])) . ' ,subject=' . db_input(Format::striptags($vars['subject'])) . ' ,phone="' . db_input($vars['phone'], false) . '"' . ' ,phone_ext=' . db_input($vars['phone_ext'] ? $vars['phone_ext'] : '') . ' ,ip_address=' . db_input($ipaddress) . ' ,source=' . db_input($source); //Make sure the origin is staff - avoid firebug hack! if ($vars['duedate'] && !strcasecmp($origin, 'staff')) { $sql .= ' ,duedate=' . db_input(date('Y-m-d G:i', Misc::dbtime($vars['duedate'] . ' ' . $vars['time']))); } if (!db_query($sql) || !($id = db_insert_id()) || !($ticket = Ticket::lookup($id))) { return null; } /* -------------------- POST CREATE ------------------------ */ $dept = $ticket->getDept(); if (!$cfg->useRandomIds()) { //Sequential ticketIDs support really..really suck arse. $extId = $id; //To make things really easy we are going to use autoincrement ticket_id. db_query('UPDATE ' . TICKET_TABLE . ' SET ticketID=' . db_input($extId) . ' WHERE ticket_id=' . $id . ' LIMIT 1'); //TODO: RETHING what happens if this fails?? [At the moment on failure random ID is used...making stuff usable] } //post the message. $msgid = $ticket->postMessage($vars['message'], $source, $vars['mid'], $vars['header'], true); //TODO: recover from postMessage error?? //Upload attachments...web based. - XXX: Assumes user uploaded attachments!! XXX: move it to client interface. if ($_FILES['attachment']['name'] && $cfg->allowOnlineAttachments() && $msgid) { if (!$cfg->allowAttachmentsOnlogin() || $cfg->allowAttachmentsOnlogin() && ($thisuser && $thisuser->isValid())) { $ticket->uploadAttachment($_FILES['attachment'], $msgid, 'M'); } } // Configure service-level-agreement for this ticket $ticket->selectSLAId($vars['slaId']); //Auto assign staff or team - auto assignment based on filter rules. if ($vars['staffId'] && !$vars['assignId']) { $ticket->assignToStaff($vars['staffId'], 'auto-assignment'); } if ($vars['teamId'] && !$vars['assignId']) { $ticket->assignToTeam($vars['teamId'], 'auto-assignment'); } /********** double check auto-response ************/ //Overwrite auto responder if the FROM email is one of the internal emails...loop control. if ($autorespond && Email::getIdByEmail($ticket->getEmail())) { $autorespond = false; } if ($autorespond && $dept && !$dept->autoRespONNewTicket()) { $autorespond = false; } # Messages that are clearly auto-responses from email systems should # not have a return 'ping' message if ($autorespond && $vars['header'] && EmailFilter::isAutoResponse(Mail_Parse::splitHeaders($vars['header']))) { $autorespond = false; } //Don't auto respond to mailer daemons. if ($autorespond && (strpos(strtolower($vars['email']), 'mailer-daemon@') !== false || strpos(strtolower($vars['email']), 'postmaster@') !== false)) { $autorespond = false; } /***** See if we need to send some alerts ****/ $ticket->onNewTicket($vars['message'], $autorespond, $alertstaff); return $ticket; }
$_SESSION['daylight'] = $user->observeDaylight(); Sys::log(LOG_DEBUG, 'Staff login', sprintf("%s logged in [%s]", $user->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug. //Redirect to the original destination. (make sure it is not redirecting to login page.) $dest = $dest && (!strstr($dest, 'login.php') && !strstr($dest, 'ajax.php')) ? $dest : 'index.php'; session_write_close(); session_regenerate_id(); @header("Location: {$dest}"); require_once 'index.php'; //Just incase header is messed up. exit; } //If we get to this point we know the login failed. $_SESSION['_staff']['strikes'] += 1; if (!$errors && $_SESSION['_staff']['strikes'] > $cfg->getStaffMaxLogins()) { $msg = 'Access Denied'; $errors['err'] = 'Forgot your login info? Contact IT Dept.'; $_SESSION['_staff']['laststrike'] = time(); $alert = 'Excessive login attempts by a staff member?' . "\n" . 'Username: '******'username'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'TIME: ' . date('M j, Y, g:i a T') . "\n\n" . 'Attempts #' . $_SESSION['_staff']['strikes'] . "\n" . 'Timeout: ' . $cfg->getStaffLoginTimeout() / 60 . " minutes \n\n"; Sys::log(LOG_ALERT, 'Excessive login attempts (staff)', $alert, $cfg->alertONLoginError()); } elseif ($_SESSION['_staff']['strikes'] % 2 == 0) { //Log every other failed login attempt as a warning. $alert = 'Username: '******'username'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'TIME: ' . date('M j, Y, g:i a T') . "\n\n" . 'Attempts #' . $_SESSION['_staff']['strikes']; Sys::log(LOG_WARNING, 'Failed login attempt (staff)', $alert); } } define("OSTSCPINC", TRUE); //Make includes happy! $login_err = $_POST ? true : false; //error displayed only on post include_once INCLUDE_DIR . 'staff/login.tpl.php';
$errors['vpassword'] = '******'; } if (!$errors) { if (!$thisuser->check_passwd($_POST['password'])) { $errors['password'] = '******'; } elseif (strcmp($_POST['npassword'], $_POST['vpassword'])) { $errors['npassword'] = $errors['vpassword'] = '******'t match'; } elseif (!strcasecmp($_POST['password'], $_POST['npassword'])) { $errors['npassword'] = '******'; } } if (!$errors) { $sql = 'UPDATE ' . STAFF_TABLE . ' SET updated=NOW() ' . ',change_passwd=0, passwd=' . db_input(MD5($_POST['npassword'])) . ' WHERE staff_id=' . db_input($thisuser->getId()); if (db_query($sql) && db_affected_rows()) { $msg = 'Password Changed Successfully'; Sys::log(LOG_ALERT, 'Excessive login attempts (staff)', $alert, $cfg->alertONLoginError()); } else { $errors['err'] = 'Unable to complete password change. Internal error.'; } } break; case 'info': //Update profile info if (!$_POST['firstname']) { $errors['firstname'] = 'First name required'; } if (!$_POST['lastname']) { $errors['lastname'] = 'Last name required'; } if (!$_POST['email'] || !Validator::is_email($_POST['email'])) { $errors['email'] = 'Valid email required';
$_SESSION['daylight'] = $user->observeDaylight(); Sys::log(LOG_DEBUG, 'Staff login', sprintf('%s ' . _('logged in'), $user->getUserName()), $user->getUserName()); //Debug //Redirect to the original destination. (make sure it is not redirecting to login page.) $dest = $dest && (!strstr($dest, 'login.php') && !strstr($dest, 'ajax.php')) ? $dest : 'index.php'; session_write_close(); session_regenerate_id(); @header("Location: {$dest}"); require_once 'index.php'; //Just incase header is messed up. exit; } //If we get to this point we know the login failed. $_SESSION['_staff']['strikes'] += 1; if (!$errors && $_SESSION['_staff']['strikes'] > $cfg->getStaffMaxLogins()) { $msg = _('Access Denied'); $errors['err'] = _('Forgot your login info? Contact IT Dept.'); $_SESSION['_staff']['laststrike'] = time(); $alert = _('Excessive login attempts by a staff member') . "\n\n" . _('Username') . ": " . $_POST['username'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . _('Time') . ': ' . date('M j, Y, g:i a T') . "\n\n" . _('Attempts No.') . ' ' . $_SESSION['_staff']['strikes'] . "\n" . 'Timeout: ' . $cfg->getStaffLoginTimeout() / 60 . " " . _("minutes") . " \n"; Sys::log(LOG_ALERT, 'Excessive login attempts (staff)', $alert, $_POST['username'], $cfg->alertONLoginError()); } elseif ($_SESSION['_staff']['strikes'] % 2 == 0) { //Log every other failed login attempt as a warning. $alert = _('Failed login attempts by a staff member') . "\n\n" . _('Username') . ": " . $_POST['username'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . _('Time') . ": " . date('M j, Y, g:i a T') . "\n" . _('Attempts No.') . ' ' . $_SESSION['_staff']['strikes']; Sys::log(LOG_WARNING, 'Failed login attempt (staff)', $alert, $_POST['username']); } } define("KTKADMININC", TRUE); //Make includes happy! $login_err = $_POST ? true : false; //error displayed only on post include_once INCLUDE_DIR . 'staff/login.tpl.php';
function create($var, &$errors, $origin, $autorespond = true, $alertstaff = true) { global $cfg, $thisclient, $_FILES; /* Coders never code so fully and joyfully as when they do it for free - Peter Rotich */ $id = 0; $fields = array(); $fields['name'] = array('type' => 'string', 'required' => 1, 'error' => 'Name required'); $fields['email'] = array('type' => 'email', 'required' => 1, 'error' => 'Valid email required'); $fields['subject'] = array('type' => 'string', 'required' => 1, 'error' => 'Subject required'); $fields['message'] = array('type' => 'text', 'required' => 1, 'error' => 'Message required'); if (strcasecmp($origin, 'web') == 0) { //Help topic only applicable on web tickets. $fields['topicId'] = array('type' => 'int', 'required' => 1, 'error' => 'Select help topic'); } elseif (strcasecmp($origin, 'staff') == 0) { //tickets created by staff...e.g on callins. $fields['deptId'] = array('type' => 'int', 'required' => 1, 'error' => 'Dept. required'); $fields['source'] = array('type' => 'string', 'required' => 1, 'error' => 'Indicate source'); $fields['duedate'] = array('type' => 'date', 'required' => 0, 'error' => 'Invalid date - must be MM/DD/YY'); } else { //Incoming emails $fields['emailId'] = array('type' => 'int', 'required' => 1, 'error' => 'Email unknown'); } $fields['pri'] = array('type' => 'int', 'required' => 0, 'error' => 'Invalid Priority'); $fields['phone'] = array('type' => 'phone', 'required' => 0, 'error' => 'Valid phone # required'); $validate = new Validator($fields); if (!$validate->validate($var)) { $errors = array_merge($errors, $validate->errors()); } //Make sure the email is not banned if (!$errors && BanList::isbanned($var['email'])) { $errors['err'] = 'Ticket denied. Error #403'; //We don't want to tell the user the real reason...Psssst. Sys::log(LOG_WARNING, 'Ticket denied', 'Banned email - ' . $var['email']); //We need to let admin know which email got banned. } if (!$errors && $thisclient && strcasecmp($thisclient->getEmail(), $var['email'])) { $errors['email'] = 'Email mismatch.'; } //Make sure phone extension is valid if ($var['phone_ext']) { if (!is_numeric($var['phone_ext']) && !$errors['phone']) { $errors['phone'] = 'Invalid phone ext.'; } elseif (!$var['phone']) { //make sure they just didn't enter ext without phone # $errors['phone'] = 'Phone number required'; } } //Make sure the due date is valid if ($var['duedate']) { if (!$var['time'] || strpos($var['time'], ':') === false) { $errors['time'] = 'Select time'; } elseif (strtotime($var['duedate'] . ' ' . $var['time']) === false) { $errors['duedate'] = 'Invalid duedate'; } elseif (strtotime($var['duedate'] . ' ' . $var['time']) <= time()) { $errors['duedate'] = 'Due date must be in the future'; } } //check attachment..if any is set ...only set on webbased tickets.. if ($_FILES['attachment']['name'] && $cfg->allowOnlineAttachments()) { if (!$cfg->canUploadFileType($_FILES['attachment']['name'])) { $errors['attachment'] = 'Invalid file type [ ' . Format::htmlchars($_FILES['attachment']['name']) . ' ]'; } elseif ($_FILES['attachment']['size'] > $cfg->getMaxFileSize()) { $errors['attachment'] = 'File is too big. Max ' . $cfg->getMaxFileSize() . ' bytes allowed'; } } //check ticket limits..if limit set is >0 //TODO: Base ticket limits on SLA... if ($var['email'] && !$errors && $cfg->getMaxOpenTickets() > 0 && strcasecmp($origin, 'staff')) { $openTickets = Ticket::getOpenTicketsByEmail($var['email']); if ($openTickets >= $cfg->getMaxOpenTickets()) { $errors['err'] = "You've reached the maximum open tickets allowed."; //Send the notice only once (when the limit is reached) incase of autoresponders at client end. if ($cfg->getMaxOpenTickets() == $openTickets && $cfg->sendOverlimitNotice()) { if ($var['deptId']) { $dept = new Dept($var['deptId']); } if (!$dept || !($tplId = $dept->getTemplateId())) { $tplId = $cfg->getDefaultTemplateId(); } $sql = 'SELECT ticket_overlimit_subj,ticket_overlimit_body FROM ' . EMAIL_TEMPLATE_TABLE . ' WHERE cfg_id=' . db_input($cfg->getId()) . ' AND tpl_id=' . db_input($tplId); $resp = db_query($sql); if (db_num_rows($resp) && (list($subj, $body) = db_fetch_row($resp))) { $body = str_replace("%name", $var['name'], $body); $body = str_replace("%email", $var['email'], $body); $body = str_replace("%url", $cfg->getBaseUrl(), $body); $body = str_replace('%signature', $dept && $dept->isPublic() ? $dept->getSignature() : '', $body); if (!$dept || !($email = $dept->getAutoRespEmail())) { $email = $cfg->getDefaultEmail(); } if ($email) { $email->send($var['email'], $subj, $body); } } //Alert admin...this might be spammy (no option to disable)...but it is helpful..I think. $msg = 'Support ticket request denied for ' . $var['email'] . "\n" . 'Open ticket:' . $openTickets . "\n" . 'Max Allowed:' . $cfg->getMaxOpenTickets() . "\n\nNotice only sent once"; Sys::alertAdmin('Overlimit Notice', $msg); } } } //Any error above is fatal. if ($errors) { return 0; } // OK...just do it. $deptId = $var['deptId']; //pre-selected Dept if any. $priorityId = $var['pri']; $source = ucfirst($var['source']); $topic = NULL; // Intenal mapping magic...see if we need to overwrite anything if (isset($var['topicId'])) { //Ticket created via web by user/or staff if ($var['topicId'] && ($topic = new Topic($var['topicId'])) && $topic->getId()) { $deptId = $deptId ? $deptId : $topic->getDeptId(); $priorityId = $priorityId ? $priorityId : $topic->getPriorityId(); $topicDesc = $topic->getName(); if ($autorespond) { $autorespond = $topic->autoRespond(); } } $source = $var['source'] ? $var['source'] : 'Web'; } elseif ($var['emailId'] && !$var['deptId']) { //Emailed Tickets $email = new Email($var['emailId']); if ($email && $email->getId()) { $deptId = $email->getDeptId(); $priorityId = $priorityId ? $priorityId : $email->getPriorityId(); if ($autorespond) { $autorespond = $email->autoRespond(); } } $email = null; $source = 'Email'; } elseif ($var['deptId']) { //Opened by staff. $deptId = $var['deptId']; $source = ucfirst($var['source']); } //Don't auto respond to mailer daemons. if (strpos(strtolower($var['email']), 'mailer-daemon@') !== false || strpos(strtolower($var['email']), 'postmaster@') !== false) { $autorespond = false; } //Last minute checks $priorityId = $priorityId ? $priorityId : $cfg->getDefaultPriorityId(); $deptId = $deptId ? $deptId : $cfg->getDefaultDeptId(); $topicId = $var['topicId'] ? $var['topicId'] : 0; $ipaddress = $var['ip'] ? $var['ip'] : $_SERVER['REMOTE_ADDR']; //We are ready son...hold on to the rails. $extId = Ticket::genExtRandID(); $sql = 'INSERT INTO ' . TICKET_TABLE . ' SET created=NOW() ' . ',ticketID=' . db_input($extId) . ',dept_id=' . db_input($deptId) . ',topic_id=' . db_input($topicId) . ',priority_id=' . db_input($priorityId) . ',email=' . db_input($var['email']) . ',name=' . db_input(Format::striptags($var['name'])) . ',subject=' . db_input(Format::striptags($var['subject'])) . ',helptopic=' . db_input(Format::striptags($topicDesc)) . ',phone="' . db_input($var['phone'], false) . '"' . ',phone_ext=' . db_input($var['phone_ext'] ? $var['phone_ext'] : '') . ',ip_address=' . db_input($ipaddress) . ',source=' . db_input($source); //Make sure the origin is staff - avoid firebug hack! if ($var['duedate'] && !strcasecmp($origin, 'staff')) { $sql .= ',duedate=' . db_input(date('Y-m-d G:i', Misc::dbtime($var['duedate'] . ' ' . $var['time']))); } //echo $sql; $ticket = null; //return $ticket; if (db_query($sql) && ($id = db_insert_id())) { if (!$cfg->useRandomIds()) { //Sequential ticketIDs support really..really suck arse. $extId = $id; //To make things really easy we are going to use autoincrement ticket_id. db_query('UPDATE ' . TICKET_TABLE . ' SET ticketID=' . db_input($extId) . ' WHERE ticket_id=' . $id); //TODO: RETHING what happens if this fails?? [At the moment on failure random ID is used...making stuff usable] } //Load newly created ticket. $ticket = new Ticket($id); //post the message. $msgid = $ticket->postMessage($var['message'], $source, $var['mid'], $var['header'], true); //TODO: recover from postMessage error?? //Upload attachments...web based. if ($_FILES['attachment']['name'] && $cfg->allowOnlineAttachments() && $msgid) { if (!$cfg->allowAttachmentsOnlogin() || $cfg->allowAttachmentsOnlogin() && ($thisclient && $thisclient->isValid())) { $ticket->uploadAttachment($_FILES['attachment'], $msgid, 'M'); //TODO: recover from upload issues? } } $dept = $ticket->getDept(); if (!$dept || !($tplId = $dept->getTemplateId())) { $tplId = $cfg->getDefaultTemplateId(); } //Overwrite auto responder if the FROM email is one of the internal emails...loop control. if ($autorespond && Email::getIdByEmail($ticket->getEmail())) { $autorespond = false; } //SEND OUT NEW TICKET AUTORESP && ALERTS. //New Ticket AutoResponse.. if ($autorespond && $cfg->autoRespONNewTicket() && $dept->autoRespONNewTicket()) { $sql = 'SELECT ticket_autoresp_subj,ticket_autoresp_body FROM ' . EMAIL_TEMPLATE_TABLE . ' WHERE cfg_id=' . db_input($cfg->getId()) . ' AND tpl_id=' . db_input($tplId); if (($resp = db_query($sql)) && db_num_rows($resp) && (list($subj, $body) = db_fetch_row($resp))) { $body = $ticket->replaceTemplateVars($body); $subj = $ticket->replaceTemplateVars($subj); $body = str_replace('%message', $var['issue'] ? $var['issue'] : $var['message'], $body); $body = str_replace('%signature', $dept && $dept->isPublic() ? $dept->getSignature() : '', $body); if (!$dept || !($email = $dept->getAutoRespEmail())) { $email = $cfg->getDefaultEmail(); } if ($email) { //Reply separator tag. if ($cfg->stripQuotedReply() && ($tag = $cfg->getReplySeparator())) { $body = "\n{$tag}\n\n" . $body; } $email->send($ticket->getEmail(), $subj, $body); } } else { Sys::log(LOG_WARNING, 'Template Fetch Error', "Unable to fetch autoresponse template #{$tplId}"); } } //If enabled...send alert to staff (New Ticket Alert) if ($alertstaff && $cfg->alertONNewTicket() && is_object($ticket)) { $sql = 'SELECT ticket_alert_subj,ticket_alert_body FROM ' . EMAIL_TEMPLATE_TABLE . ' WHERE cfg_id=' . db_input($cfg->getId()) . ' AND tpl_id=' . db_input($tplId); if (($resp = db_query($sql)) && db_num_rows($resp) && (list($subj, $body) = db_fetch_row($resp))) { $body = $ticket->replaceTemplateVars($body); $subj = $ticket->replaceTemplateVars($subj); $body = str_replace('%message', $var['issue'] ? $var['issue'] : $var['message'], $body); if (!($email = $cfg->getAlertEmail())) { $email = $cfg->getDefaultEmail(); } if ($email && $email->getId()) { $sentlist = array(); //Admin Alert. if ($cfg->alertAdminONNewTicket()) { $alert = str_replace("%staff", 'Admin', $body); $email->send($cfg->getAdminEmail(), $subj, $alert); $sentlist[] = $cfg->getAdminEmail(); } //get the list $recipients = array(); //Dept. Manager if ($cfg->alertDeptManagerONNewTicket()) { $recipients[] = $dept->getManager(); } //Staff members if ($cfg->alertDeptMembersONNewTicket()) { $sql = 'SELECT staff_id FROM ' . STAFF_TABLE . ' WHERE onvacation=0 AND dept_id=' . db_input($dept->getId()); if (($users = db_query($sql)) && db_num_rows($users)) { while (list($id) = db_fetch_row($users)) { $recipients[] = new Staff($id); } } } foreach ($recipients as $k => $staff) { if (!$staff || !is_object($staff) || !$staff->isAvailable()) { continue; } if (in_array($staff->getEmail(), $sentlist)) { continue; } //avoid duplicate emails. $alert = str_replace("%staff", $staff->getFirstName(), $body); $email->send($staff->getEmail(), $subj, $alert); $sentlist[] = $staff->getEmail(); } } } else { Sys::log(LOG_WARNING, 'Template Fetch Error', "Unable to fetch 'new ticket' alert template #{$tplId}"); } } } return $ticket; }
function fetchMail() { global $cfg; if (!$cfg->canFetchMail()) { return; } //We require imap ext to fetch emails via IMAP/POP3 if (!function_exists('imap_open')) { $msg = 'PHP debe ser compilado con la extensión IMAP habilitada para IMAP/POP3 fetch(captura de correo) para que funcione'; Sys::log(LOG_WARN, 'Error de captura de correo', $msg); return; } $MAX_ERRORS = 5; //Max errors before we start delayed fetch attempts - hardcoded for now. $sql = ' SELECT email_id,mail_host,mail_port,mail_protocol,mail_encryption,mail_delete,mail_errors,userid,userpass FROM ' . EMAIL_TABLE . ' WHERE mail_active=1 AND (mail_errors<=' . $MAX_ERRORS . ' OR (TIME_TO_SEC(TIMEDIFF(NOW(),mail_lasterror))>5*60) )' . ' AND (mail_lastfetch IS NULL OR TIME_TO_SEC(TIMEDIFF(NOW(),mail_lastfetch))>mail_fetchfreq*60) '; //echo $sql; if (!($accounts = db_query($sql)) || !db_num_rows($accounts)) { return; } //TODO: Lock the table here?? while ($row = db_fetch_array($accounts)) { $fetcher = new MailFetcher($row['userid'], Misc::decrypt($row['userpass'], SECRET_SALT), $row['mail_host'], $row['mail_port'], $row['mail_protocol'], $row['mail_encryption']); if ($fetcher->connect()) { $fetcher->fetchTickets($row['email_id'], $row['mail_fetchmax'], $row['mail_delete'] ? true : false); $fetcher->close(); db_query('UPDATE ' . EMAIL_TABLE . ' SET mail_errors=0, mail_lastfetch=NOW() WHERE email_id=' . db_input($row['email_id'])); } else { $errors = $row['mail_errors'] + 1; db_query('UPDATE ' . EMAIL_TABLE . ' SET mail_errors=mail_errors+1, mail_lasterror=NOW() WHERE email_id=' . db_input($row['email_id'])); if ($errors >= $MAX_ERRORS) { //We've reached the MAX consecutive errors...will attempt logins at delayed intervals $msg = "\nEl sistema está teniendo problemas para obtener los correos electrónicos de la cuenta de correo siguiente: \n" . "\nUsuario: " . $row['userid'] . "\nHost: " . $row['mail_host'] . "\nError: " . $fetcher->getLastError() . "\n\n " . $errors . ' errores consecutivos. Máximo de ' . $MAX_ERRORS . ' permitidos' . "\n\n Esto podría ser una cuestión relacionada con la conexión al host. Siguiente intento en aprox. 10 min"; Sys::alertAdmin('Alerta de fallo en la captura de correo', $msg, true); } } } }
function fetchMail() { global $cfg; if (!$cfg->canFetchMail()) { return; } //We require imap ext to fetch emails via IMAP/POP3 if (!function_exists('imap_open')) { $msg = 'PHP deve ser compilado com extensão IMAP habilitado para buscar a trabalhar IMAP/POP3!'; Sys::log(LOG_WARN, 'Erro em buscar o email', $msg); return; } $MAX_ERRORS = 5; //Max errors before we start delayed fetch attempts - hardcoded for now. $sql = ' SELECT email_id,mail_host,mail_port,mail_protocol,mail_encryption,mail_delete,mail_errors,userid,userpass FROM ' . EMAIL_TABLE . ' WHERE mail_active=1 AND (mail_errors<=' . $MAX_ERRORS . ' OR (TIME_TO_SEC(TIMEDIFF(NOW(),mail_lasterror))>5*60) )' . ' AND (mail_lastfetch IS NULL OR TIME_TO_SEC(TIMEDIFF(NOW(),mail_lastfetch))>mail_fetchfreq*60) '; //echo $sql; if (!($accounts = db_query($sql)) || !db_num_rows($accounts)) { return; } //TODO: Lock the table here?? while ($row = db_fetch_array($accounts)) { $fetcher = new MailFetcher($row['userid'], Misc::decrypt($row['userpass'], SECRET_SALT), $row['mail_host'], $row['mail_port'], $row['mail_protocol'], $row['mail_encryption']); if ($fetcher->connect()) { $fetcher->fetchTickets($row['email_id'], $row['mail_fetchmax'], $row['mail_delete'] ? true : false); $fetcher->close(); db_query('UPDATE ' . EMAIL_TABLE . ' SET mail_errors=0, mail_lastfetch=NOW() WHERE email_id=' . db_input($row['email_id'])); } else { $errors = $row['mail_errors'] + 1; db_query('UPDATE ' . EMAIL_TABLE . ' SET mail_errors=mail_errors+1, mail_lasterror=NOW() WHERE email_id=' . db_input($row['email_id'])); if ($errors >= $MAX_ERRORS) { //We've reached the MAX consecutive errors...will attempt logins at delayed intervals $msg = "\nO sistema está tendo problemas para coletar e-mails da conta do e-mail seguinte: \n" . "\nUsuário: " . $row['userid'] . "\nHost: " . $row['mail_host'] . "\nErro: " . $fetcher->getLastError() . "\n\n " . $errors . ' erros consecutivos. Máximo de ' . $MAX_ERRORS . ' permitido' . "\n\n Isso pode ser problemas de conexão relacionados ao hospedeiro. Próxima tentativa de login em aprox. 10 minutos"; Sys::alertAdmin('Alerta de falha na busca por email', $msg, true); } } } }
function create($vars, &$errors, $origin, $autorespond = true, $alertstaff = true) { global $cfg, $thisclient, $_FILES; //Check for 403 if ($vars['email'] && Validator::is_email($vars['email'])) { //Make sure the email address is not banned if (EmailFilter::isBanned($vars['email'])) { $errors['err'] = 'Ticket denied. Error #403'; Sys::log(LOG_WARNING, 'Ticket denied', 'Banned email - ' . $vars['email']); return 0; } //Make sure the open ticket limit hasn't been reached. (LOOP CONTROL) if ($cfg->getMaxOpenTickets() > 0 && strcasecmp($origin, 'staff') && ($client = Client::lookupByEmail($vars['email'])) && ($openTickets = $client->getNumOpenTickets()) && $opentickets >= $cfg->getMaxOpenTickets()) { $errors['err'] = "You've reached the maximum open tickets allowed."; Sys::log(LOG_WARNING, 'Ticket denied -' . $vars['email'], sprintf('Max open tickets (%d) reached for %s ', $cfg->getMaxOpenTickets(), $vars['email'])); return 0; } } // Make sure email contents should not be rejected if (($email_filter = new EmailFilter($vars)) && ($filter = $email_filter->shouldReject())) { $errors['err'] = 'Ticket denied. Error #403'; Sys::log(LOG_WARNING, 'Ticket denied', sprintf('Banned email - %s by filter "%s"', $vars['email'], $filter->getName())); return 0; } $id = 0; $fields = array(); $fields['name'] = array('type' => 'string', 'required' => 1, 'error' => 'Name required'); $fields['email'] = array('type' => 'email', 'required' => 1, 'error' => 'Valid email required'); $fields['subject'] = array('type' => 'string', 'required' => 1, 'error' => 'Subject required'); $fields['message'] = array('type' => 'text', 'required' => 1, 'error' => 'Message required'); switch (strtolower($origin)) { case 'web': $fields['topicId'] = array('type' => 'int', 'required' => 1, 'error' => 'Select help topic'); break; case 'staff': $fields['deptId'] = array('type' => 'int', 'required' => 1, 'error' => 'Dept. required'); $fields['topicId'] = array('type' => 'int', 'required' => 1, 'error' => 'Topic required'); $fields['duedate'] = array('type' => 'date', 'required' => 0, 'error' => 'Invalid date - must be MM/DD/YY'); case 'api': $fields['source'] = array('type' => 'string', 'required' => 1, 'error' => 'Indicate source'); break; case 'email': $fields['emailId'] = array('type' => 'int', 'required' => 1, 'error' => 'Email unknown'); break; default: # TODO: Return error message $errors['err'] = $errors['origin'] = 'Invalid origin given'; } $fields['priorityId'] = array('type' => 'int', 'required' => 0, 'error' => 'Invalid Priority'); $fields['phone'] = array('type' => 'phone', 'required' => 0, 'error' => 'Valid phone # required'); if (!Validator::process($fields, $vars, $errors) && !$errors['err']) { $errors['err'] = 'Missing or invalid data - check the errors and try again'; } //Make sure phone extension is valid if ($vars['phone_ext']) { if (!is_numeric($vars['phone_ext']) && !$errors['phone']) { $errors['phone'] = 'Invalid phone ext.'; } elseif (!$vars['phone']) { //make sure they just didn't enter ext without phone # XXX: reconsider allowing! $errors['phone'] = 'Phone number required'; } } //Make sure the due date is valid if ($vars['duedate']) { if (!$vars['time'] || strpos($vars['time'], ':') === false) { $errors['time'] = 'Select time'; } elseif (strtotime($vars['duedate'] . ' ' . $vars['time']) === false) { $errors['duedate'] = 'Invalid duedate'; } elseif (strtotime($vars['duedate'] . ' ' . $vars['time']) <= time()) { $errors['duedate'] = 'Due date must be in the future'; } } # Perform email filter actions on the new ticket arguments XXX: Move filter to the top and check for reject... if (!$errors && $email_filter) { $email_filter->apply($vars); } # Some things will need to be unpacked back into the scope of this # function if (isset($vars['autorespond'])) { $autorespond = $vars['autorespond']; } //Any error above is fatal. if ($errors) { return 0; } // OK...just do it. $deptId = $vars['deptId']; //pre-selected Dept if any. $priorityId = $vars['priorityId']; $source = ucfirst($vars['source']); $topic = NULL; // Intenal mapping magic...see if we need to overwrite anything if (isset($vars['topicId']) && ($topic = Topic::lookup($vars['topicId']))) { //Ticket created via web by user/or staff $deptId = $deptId ? $deptId : $topic->getDeptId(); $priorityId = $priorityId ? $priorityId : $topic->getPriorityId(); if ($autorespond) { $autorespond = $topic->autoRespond(); } $source = $vars['source'] ? $vars['source'] : 'Web'; } elseif ($vars['emailId'] && !$vars['deptId'] && ($email = Email::lookup($vars['emailId']))) { //Emailed Tickets $deptId = $email->getDeptId(); $priorityId = $priorityId ? $priorityId : $email->getPriorityId(); if ($autorespond) { $autorespond = $email->autoRespond(); } $email = null; $source = 'Email'; } elseif ($vars['deptId']) { //Opened by staff. $deptId = $vars['deptId']; $source = ucfirst($vars['source']); } //Last minute checks $priorityId = $priorityId ? $priorityId : $cfg->getDefaultPriorityId(); $deptId = $deptId ? $deptId : $cfg->getDefaultDeptId(); $topicId = $vars['topicId'] ? $vars['topicId'] : 0; $ipaddress = $vars['ip'] ? $vars['ip'] : $_SERVER['REMOTE_ADDR']; //We are ready son...hold on to the rails. $extId = Ticket::genExtRandID(); $sql = 'INSERT INTO ' . TICKET_TABLE . ' SET created=NOW() ' . ' ,lastmessage= NOW()' . ' ,ticketID=' . db_input($extId) . ' ,dept_id=' . db_input($deptId) . ' ,topic_id=' . db_input($topicId) . ' ,priority_id=' . db_input($priorityId) . ' ,email=' . db_input($vars['email']) . ' ,name=' . db_input(Format::striptags($vars['name'])) . ' ,subject=' . db_input(Format::striptags($vars['subject'])) . ' ,phone="' . db_input($vars['phone'], false) . '"' . ' ,phone_ext=' . db_input($vars['phone_ext'] ? $vars['phone_ext'] : '') . ' ,ip_address=' . db_input($ipaddress) . ' ,source=' . db_input($source); //Make sure the origin is staff - avoid firebug hack! if ($vars['duedate'] && !strcasecmp($origin, 'staff')) { $sql .= ' ,duedate=' . db_input(date('Y-m-d G:i', Misc::dbtime($vars['duedate'] . ' ' . $vars['time']))); } if (!db_query($sql) || !($id = db_insert_id()) || !($ticket = Ticket::lookup($id))) { return null; } /* -------------------- POST CREATE ------------------------ */ $dept = $ticket->getDept(); if (!$cfg->useRandomIds()) { //Sequential ticketIDs support really..really suck arse. $extId = $id; //To make things really easy we are going to use autoincrement ticket_id. db_query('UPDATE ' . TICKET_TABLE . ' SET ticketID=' . db_input($extId) . ' WHERE ticket_id=' . $id . ' LIMIT 1'); //TODO: RETHING what happens if this fails?? [At the moment on failure random ID is used...making stuff usable] } //post the message. $msgid = $ticket->postMessage($vars['message'], $source, $vars['mid'], $vars['header'], true); // Configure service-level-agreement for this ticket $ticket->selectSLAId($vars['slaId']); //Auto assign staff or team - auto assignment based on filter rules. if ($vars['staffId'] && !$vars['assignId']) { $ticket->assignToStaff($vars['staffId'], 'auto-assignment'); } if ($vars['teamId'] && !$vars['assignId']) { $ticket->assignToTeam($vars['teamId'], 'auto-assignment'); } /********** double check auto-response ************/ //Overwrite auto responder if the FROM email is one of the internal emails...loop control. if ($autorespond && Email::getIdByEmail($ticket->getEmail())) { $autorespond = false; } if ($autorespond && $dept && !$dept->autoRespONNewTicket()) { $autorespond = false; } # Messages that are clearly auto-responses from email systems should # not have a return 'ping' message if ($autorespond && $vars['header'] && EmailFilter::isAutoResponse(Mail_Parse::splitHeaders($vars['header']))) { $autorespond = false; } //Don't auto respond to mailer daemons. if ($autorespond && (strpos(strtolower($vars['email']), 'mailer-daemon@') !== false || strpos(strtolower($vars['email']), 'postmaster@') !== false)) { $autorespond = false; } /***** See if we need to send some alerts ****/ $ticket->onNewTicket($vars['message'], $autorespond, $alertstaff); /************ check if the user JUST reached the max. open tickets limit **********/ if ($cfg->getMaxOpenTickets() > 0 && ($client = $ticket->getClient()) && $client->getNumOpenTickets() == $cfg->getMaxOpenTickets()) { $ticket->onOpenLimit($autorespond && strcasecmp($origin, 'staff')); } /* Phew! ... time for tea (KETEPA) */ return $ticket; }
function send($to, $subject, $message, $attachments = null, $options = null) { global $cfg; //Get SMTP info IF enabled! $smtp = array(); if ($this->isSMTPEnabled() && ($info = $this->getSMTPInfo())) { //is SMTP enabled for the current email? $smtp = $info; } elseif ($cfg && ($email = $cfg->getDefaultSMTPEmail()) && $email->isSMTPEnabled()) { //What about global SMTP setting? if ($email->allowSpoofing() && ($info = $email->getSMTPInfo())) { //If spoofing is allowed..then continue. $smtp = $info; } elseif ($email->getId() != $this->getId()) { //No spoofing allowed. Send it via the default SMTP email. return $email->send($to, $subject, $message, $attachments, $options); } } //Get the goodies require_once 'Mail.php'; // PEAR Mail package require_once 'Mail/mime.php'; // PEAR Mail_Mime packge //do some cleanup $eol = "\n"; $to = preg_replace("/(\r\n|\r|\n)/s", '', trim($to)); $subject = stripslashes(preg_replace("/(\r\n|\r|\n)/s", '', trim($subject))); $body = stripslashes(preg_replace("/(\r\n|\r)/s", "\n", trim($message))); $fromname = $this->getName(); $from = sprintf('"%s"<%s>', $fromname ? $fromname : $this->getEmail(), $this->getEmail()); $headers = array('From' => $from, 'To' => $to, 'Subject' => $subject, 'Date' => date('D,d M Y H:i:s O'), 'Message-ID' => '<' . Misc::randCode(6) . '' . time() . '-' . $this->getEmail() . '>', 'X-Mailer' => 'osTicket v1.7', 'Content-Type' => 'text/html; charset="UTF-8"'); $mime = new Mail_mime(); $mime->setTXTBody($body); //XXX: Attachments if ($attachments) { foreach ($attachments as $attachment) { if ($attachment['file_id'] && ($file = AttachmentFile::lookup($attachment['file_id']))) { $mime->addAttachment($file->getData(), $file->getType(), $file->getName(), false); } elseif ($attachment['file'] && file_exists($attachment['file']) && is_readable($attachment['file'])) { $mime->addAttachment($attachment['file'], $attachment['type'], $attachment['name']); } } } $options = array('head_encoding' => 'quoted-printable', 'text_encoding' => 'quoted-printable', 'html_encoding' => 'base64', 'html_charset' => 'utf-8', 'text_charset' => 'utf-8'); //encode the body $body = $mime->get($options); //encode the headers. $headers = $mime->headers($headers); if ($smtp) { //Send via SMTP $mail = mail::factory('smtp', array('host' => $smtp['host'], 'port' => $smtp['port'], 'auth' => $smtp['auth'] ? true : false, 'username' => $smtp['username'], 'password' => $smtp['password'], 'timeout' => 20, 'debug' => false)); $result = $mail->send($to, $headers, $body); if (!PEAR::isError($result)) { return true; } $alert = sprintf("Unable to email via %s:%d [%s]\n\n%s\n", $smtp['host'], $smtp['port'], $smtp['username'], $result->getMessage()); Sys::log(LOG_ALERT, 'SMTP Error', $alert, false); //print_r($result); } //No SMTP or it failed....use php's native mail function. $mail = mail::factory('mail'); return PEAR::isError($mail->send($to, $headers, $body)) ? false : true; }
$_SESSION['daylight'] = $user->observeDaylight(); Sys::log(LOG_DEBUG, 'Staff login', sprintf("%s logged in [%s]", $user->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug. //Redirect to the original destination. (make sure it is not redirecting to login page.) $dest = $dest && (!strstr($dest, 'login.php') && !strstr($dest, 'ajax.php')) ? $dest : 'index.php'; session_write_close(); session_regenerate_id(); @header("Location: {$dest}"); require_once 'index.php'; //Just incase header is messed up. exit; } //If we get to this point we know the login failed. $_SESSION['_staff']['strikes'] += 1; if (!$errors && $_SESSION['_staff']['strikes'] > $cfg->getStaffMaxLogins()) { $msg = 'Acesso Negado'; $errors['err'] = 'Esqueceu seu login? Entre em contato com Departamento de TI'; $_SESSION['_staff']['laststrike'] = time(); $alert = 'Excesso de tentativas de login por um membro da equipe?' . "\n" . 'Username: '******'username'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'TIME: ' . date('M j, Y, g:i a T') . "\n\n" . 'Attempts #' . $_SESSION['_staff']['strikes'] . "\n" . 'Timeout: ' . $cfg->getStaffLoginTimeout() / 60 . " minutes \n\n"; Sys::log(LOG_ALERT, 'Excessive login attempts (staff)', $alert, $cfg->alertONLoginError()); } elseif ($_SESSION['_staff']['strikes'] % 2 == 0) { //Log every other failed login attempt as a warning. $alert = 'Username: '******'username'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'TIME: ' . date('M j, Y, g:i a T') . "\n\n" . 'Attempts #' . $_SESSION['_staff']['strikes']; Sys::log(LOG_WARNING, 'Tentativa de login falhou (atendente)', $alert); } } define("OSTSCPINC", TRUE); //Make includes happy! $login_err = $_POST ? true : false; //error displayed only on post include_once INCLUDE_DIR . 'staff/login.tpl.php';
function fetchMail(){ global $cfg; if(!$cfg->canFetchMail()) return; //We require imap ext to fetch emails via IMAP/POP3 if(!function_exists('imap_open')) { $msg='PHP must be compiled with IMAP extension enabled for IMAP/POP3 fetch to work!'; Sys::log(LOG_WARN,'Mail Fetch Error',$msg); return; } $MAX_ERRORS=5; //Max errors before we start delayed fetch attempts - hardcoded for now. $sql=' SELECT email_id,mail_host,mail_port,mail_protocol,mail_encryption,mail_delete,mail_errors,userid,userpass FROM '.EMAIL_TABLE. ' WHERE mail_active=1 AND (mail_errors<='.$MAX_ERRORS.' OR (TIME_TO_SEC(TIMEDIFF(NOW(),mail_lasterror))>5*60) )'. ' AND (mail_lastfetch IS NULL OR TIME_TO_SEC(TIMEDIFF(NOW(),mail_lastfetch))>mail_fetchfreq*60) '; //echo $sql; if(!($accounts=db_query($sql)) || !db_num_rows($accounts)) return; //TODO: Lock the table here?? while($row=db_fetch_array($accounts)) { $fetcher = new MailFetcher($row['userid'],Misc::decrypt($row['userpass'],SECRET_SALT), $row['mail_host'],$row['mail_port'],$row['mail_protocol'],$row['mail_encryption']); if($fetcher->connect()){ $fetcher->fetchTickets($row['email_id'],$row['mail_fetchmax'],$row['mail_delete']?true:false); $fetcher->close(); db_query('UPDATE '.EMAIL_TABLE.' SET mail_errors=0, mail_lastfetch=NOW() WHERE email_id='.db_input($row['email_id'])); }else{ $errors=$row['mail_errors']+1; db_query('UPDATE '.EMAIL_TABLE.' SET mail_errors=mail_errors+1, mail_lasterror=NOW() WHERE email_id='.db_input($row['email_id'])); if($errors>=$MAX_ERRORS){ //We've reached the MAX consecutive errors...will attempt logins at delayed intervals $msg="\nThe system is having trouble fetching emails from the following mail account: \n". "\nUser: "******"\nHost: ".$row['mail_host']. "\nError: ".$fetcher->getLastError(). "\n\n ".$errors.' consecutive errors. Maximum of '.$MAX_ERRORS. ' allowed'. "\n\n This could be connection issues related to the host. Next delayed login attempt in aprox. 10 minutes"; Sys::alertAdmin('Mail Fetch Failure Alert',$msg,true); } } } }
function login($username, $passwd, &$errors, $strike = true) { global $cfg; if ($_SESSION['_staff']['laststrike']) { if (time() - $_SESSION['_staff']['laststrike'] < $cfg->getStaffLoginTimeout()) { $errors['err'] = 'You\'ve reached maximum failed login attempts allowed.'; } else { //Timeout is over. //Reset the counter for next round of attempts after the timeout. $_SESSION['_staff']['laststrike'] = null; $_SESSION['_staff']['strikes'] = 0; } } if (!$errors && ($user = new StaffSession($username)) && $user->getId() && $user->check_passwd($passwd)) { //update last login && password reset stuff. $sql = 'UPDATE ' . STAFF_TABLE . ' SET lastlogin=NOW() '; if ($user->isPasswdResetDue() && !$user->isAdmin()) { $sql .= ',change_passwd=1'; } $sql .= ' WHERE staff_id=' . db_input($user->getId()); db_query($sql); //Now set session crap and lets roll baby! $_SESSION['_staff'] = array(); //clear. $_SESSION['_staff']['userID'] = $username; $user->refreshSession(); //set the hash. $_SESSION['TZ_OFFSET'] = $user->getTZoffset(); $_SESSION['daylight'] = $user->observeDaylight(); Sys::log(LOG_DEBUG, 'Staff login', sprintf("%s logged in [%s]", $user->getUserName(), $_SERVER['REMOTE_ADDR'])); //Debug. $sid = session_id(); //Current ID session_regenerate_id(TRUE); //Destroy old session ID - needed for PHP version < 5.1.0 TODO: remove when we move to php 5.3 as min. requirement. if ($session && is_object($session) && $sid) { $session->destroy($sid); } session_write_close(); return $user; } //If we get to this point we know the login failed. $_SESSION['_staff']['strikes'] += 1; if (!$errors && $_SESSION['_staff']['strikes'] > $cfg->getStaffMaxLogins()) { $errors['err'] = 'Forgot your login info? Contact Admin.'; $_SESSION['_staff']['laststrike'] = time(); $alert = 'Excessive login attempts by a staff member?' . "\n" . 'Username: '******'username'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'TIME: ' . date('M j, Y, g:i a T') . "\n\n" . 'Attempts #' . $_SESSION['_staff']['strikes'] . "\n" . 'Timeout: ' . $cfg->getStaffLoginTimeout() / 60 . " minutes \n\n"; Sys::log(LOG_ALERT, 'Excessive login attempts (' . $_POST['username'] . ')', $alert, $cfg->alertONLoginError()); } elseif ($_SESSION['_staff']['strikes'] % 2 == 0) { //Log every other failed login attempt as a warning. $alert = 'Username: '******'username'] . "\n" . 'IP: ' . $_SERVER['REMOTE_ADDR'] . "\n" . 'TIME: ' . date('M j, Y, g:i a T') . "\n\n" . 'Attempts #' . $_SESSION['_staff']['strikes']; Sys::log(LOG_WARNING, 'Failed staff login attempt (' . $_POST['username'] . ')', $alert); } return false; }