예제 #1
0
 function createTicket($mid)
 {
     global $ost;
     if (!($mailinfo = $this->getHeaderInfo($mid))) {
         return false;
     }
     // TODO: If the content-type of the message is 'message/rfc822',
     // then this is a message with the forwarded message as the
     // attachment. Download the body and pass it along to the mail
     // parsing engine.
     $info = Mail_Parse::splitHeaders($mailinfo['header']);
     if (strtolower($info['Content-Type']) == 'message/rfc822') {
         if ($wrapped = $this->getPart($mid, 'message/rfc822')) {
             require_once INCLUDE_DIR . 'api.tickets.php';
             // Simulate piping the contents into the system
             $api = new TicketApiController();
             $parser = new EmailDataParser();
             if ($data = $parser->parse($wrapped)) {
                 return $api->processEmail($data);
             }
         }
         // If any of this fails, create the ticket as usual
     }
     //Is the email address banned?
     if ($mailinfo['email'] && TicketFilter::isBanned($mailinfo['email'])) {
         //We need to let admin know...
         $ost->logWarning(_S('Ticket denied'), sprintf(_S('Banned email — %s'), $mailinfo['email']), false);
         return true;
         //Report success (moved or delete)
     }
     // Parse MS TNEF emails
     if (($struct = imap_fetchstructure($this->mbox, $mid)) && ($attachments = $this->getAttachments($struct))) {
         foreach ($attachments as $i => $info) {
             if (0 === strcasecmp('application/ms-tnef', $info['type'])) {
                 try {
                     $data = $this->decode(imap_fetchbody($this->mbox, $mid, $info['index']), $info['encoding']);
                     $tnef = new TnefStreamParser($data);
                     $this->tnef = $tnef->getMessage();
                     // No longer considered an attachment
                     unset($attachments[$i]);
                     // There should only be one of these
                     break;
                 } catch (TnefException $ex) {
                     // Noop -- winmail.dat remains an attachment
                 }
             }
         }
     }
     $vars = $mailinfo;
     $vars['name'] = $mailinfo['name'];
     $vars['subject'] = $mailinfo['subject'] ?: '[No Subject]';
     $vars['emailId'] = $mailinfo['emailId'] ?: $this->getEmailId();
     $vars['to-email-id'] = $mailinfo['emailId'] ?: 0;
     $vars['flags'] = new ArrayObject();
     if ($this->isBounceNotice($mid)) {
         // Fetch the original References and assign to 'references'
         if ($headers = $this->getOriginalMessageHeaders($mid)) {
             $vars['references'] = $headers['references'];
             $vars['in-reply-to'] = @$headers['in-reply-to'] ?: null;
         }
         // Fetch deliver status report
         $vars['message'] = $this->getDeliveryStatusMessage($mid) ?: $this->getBody($mid);
         $vars['thread-type'] = 'N';
         $vars['flags']['bounce'] = true;
     } else {
         $vars['message'] = $this->getBody($mid);
         $vars['flags']['bounce'] = TicketFilter::isBounce($info);
     }
     //Missing FROM name  - use email address.
     if (!$vars['name']) {
         list($vars['name']) = explode('@', $vars['email']);
     }
     if ($ost->getConfig()->useEmailPriority()) {
         $vars['priorityId'] = $this->getPriority($mid);
     }
     $ticket = null;
     $newticket = true;
     $errors = array();
     $seen = false;
     // Use the settings on the thread entry on the ticket details
     // form to validate the attachments in the email
     $tform = TicketForm::objects()->one()->getForm();
     $messageField = $tform->getField('message');
     $fileField = $messageField->getWidget()->getAttachments();
     // Fetch attachments if any.
     if ($messageField->isAttachmentsEnabled()) {
         // Include TNEF attachments in the attachments list
         if ($this->tnef) {
             foreach ($this->tnef->attachments as $at) {
                 $attachments[] = array('cid' => @$at->AttachContentId ?: false, 'data' => $at, 'size' => @$at->DataSize ?: null, 'type' => @$at->AttachMimeTag ?: false, 'name' => $at->getName());
             }
         }
         $vars['attachments'] = array();
         foreach ($attachments as $a) {
             $file = array('name' => $a['name'], 'type' => $a['type']);
             if (@$a['data'] instanceof TnefAttachment) {
                 $file['data'] = $a['data']->getData();
             } else {
                 // only fetch the body if necessary
                 $self = $this;
                 $file['data'] = function () use($self, $mid, $a) {
                     return $self->decode(imap_fetchbody($self->mbox, $mid, $a['index']), $a['encoding']);
                 };
             }
             // Include the Content-Id if specified (for inline images)
             $file['cid'] = isset($a['cid']) ? $a['cid'] : false;
             // Validate and save immediately
             try {
                 $file['id'] = $fileField->uploadAttachment($file);
             } catch (FileUploadError $ex) {
                 $file['error'] = $file['name'] . ': ' . $ex->getMessage();
             }
             $vars['attachments'][] = $file;
         }
     }
     // Allow signal handlers to interact with the message decoding
     Signal::send('mail.processed', $this, $vars);
     $seen = false;
     if (($thread = ThreadEntry::lookupByEmailHeaders($vars, $seen)) && ($t = $thread->getTicket()) && ($vars['staffId'] || !$t->isClosed() || $t->isReopenable()) && ($message = $thread->postEmail($vars))) {
         if (!$message instanceof ThreadEntry) {
             // Email has been processed previously
             return $message;
         }
         $ticket = $message->getTicket();
     } elseif ($seen) {
         // Already processed, but for some reason (like rejection), no
         // thread item was created. Ignore the email
         return true;
     } elseif ($ticket = Ticket::create($vars, $errors, 'Email')) {
         $message = $ticket->getLastMessage();
     } else {
         //Report success if the email was absolutely rejected.
         if (isset($errors['errno']) && $errors['errno'] == 403) {
             // Never process this email again!
             ThreadEntry::logEmailHeaders(0, $vars['mid']);
             return true;
         }
         // Log an error to the system logs
         $mailbox = Email::lookup($vars['emailId']);
         $ost->logError(_S('Mail Processing Exception'), sprintf(_S("Mailbox: %s | Error(s): %s"), $mailbox->getEmail(), print_r($errors, true)), false);
         // Indicate failure of mail processing
         return null;
     }
     return $ticket;
 }
예제 #2
0
 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;
 }
예제 #3
0
    function _build($lang) {
        list($code, $zip) = $this->_request("download/$lang.zip");

        if ($code !== 200)
            $this->fail('Language is not available'."\n");

        $temp = tempnam('/tmp', 'osticket-cli');
        $f = fopen($temp, 'w');
        fwrite($f, $zip);
        fclose($f);
        $zip = new ZipArchive();
        $zip->open($temp);
        unlink($temp);

        $lang = str_replace('-','_',$lang);
        @unlink(I18N_DIR."$lang.phar");
        $phar = new Phar(I18N_DIR."$lang.phar");
        $phar->startBuffering();

        $po_file = false;

        for ($i=0; $i<$zip->numFiles; $i++) {
            $info = $zip->statIndex($i);
            $contents = $zip->getFromIndex($i);
            if (!$contents)
                continue;
            if (strpos($info['name'], '/messages.po') !== false) {
                $po_file = $contents;
                // Don't add the PO file as-is to the PHAR file
                continue;
            }
            $phar->addFromString($info['name'], $contents);
        }

        // TODO: Add i18n extras (like fonts)
        // Redactor language pack
        //
        $langs = array($lang);
        if (strpos($lang, '_') !== false) {
            @list($short) = explode('_', $lang);
            $langs[] = $short;
        }
        foreach ($langs as $l) {
            list($code, $js) = $this->_http_get(
                'http://imperavi.com/webdownload/redactor/lang/?lang='
                .strtolower($l));
            if ($code == 200 && ($js != 'File not found')) {
                $phar->addFromString('js/redactor.js', $js);
                break;
            }
        }
        if ($code != 200)
            $this->stderr->write($lang . ": Unable to fetch Redactor language file\n");

        // JQuery UI Datepicker
        // http://jquery-ui.googlecode.com/svn/tags/latest/ui/i18n/jquery.ui.datepicker-de.js
        foreach ($langs as $l) {
            list($code, $js) = $this->_http_get(
                'http://jquery-ui.googlecode.com/svn/tags/latest/ui/i18n/jquery.ui.datepicker-'
                    .str_replace('_','-',$l).'.js');
            // If locale-specific version is not available, use the base
            // language version (de if de_CH is not available)
            if ($code == 200)
                break;
        }
        if ($code == 200)
            $phar->addFromString('js/jquery.ui.datepicker.js', $js);
        else
            $this->stderr->write(str_replace('_','-',$lang)
                .": Unable to fetch jQuery UI Datepicker locale file\n");

        // True type fonts for PDF printing
        $langs = (include I18N_DIR . 'langs.php');
        $info = $langs[$lang];
        if (isset($info['fonts'])) {
            foreach ($info['fonts'] as $name => $types) {
                foreach ($types as $code => $fullname) {
                    list($ttf, $url) = $fullname;
                    if (!$url)
                        continue;
                    list($code, $file) = $this->_http_get($url);
                    if ($code == 200)
                        $phar->addFromString('fonts/'.$ttf, $file);
                    else
                        $this->stderr->write(
                            "*** Unable to fetch $url\n");
                }
            }
        }

        // Add in the messages.mo.php file
        if ($po_file) {
            $pipes = array();
            $msgfmt = proc_open('msgfmt -o- -',
                array(0=>array('pipe','r'), 1=>array('pipe','w')),
                $pipes);
            if (is_resource($msgfmt)) {
                fwrite($pipes[0], $po_file);
                fclose($pipes[0]);
                $mo_input = fopen('php://temp', 'r+b');
                fwrite($mo_input, stream_get_contents($pipes[1]));
                rewind($mo_input);
                require_once INCLUDE_DIR . 'class.translation.php';
                $mo = Translation::buildHashFile($mo_input, false, true);
                $phar->addFromString('LC_MESSAGES/messages.mo.php', $mo);
                fclose($mo_input);
            }
        }

        // Add in translation of javascript strings
        $phrases = array();
        if ($mo && ($js = $this->__getAllJsPhrases())) {
            $mo = (eval (substr($mo, 5))); # Chop off <?php
            foreach ($js as $c) {
                foreach ($c['forms'] as $f) {
                    $phrases[$f] = @$mo[$f] ?: $f;
                }
            }
            $phar->addFromString(
                'js/osticket-strings.js',
                sprintf('(function($){$.oststrings=%s;})(jQuery);',
                    JsonDataEncoder::encode($phrases))
            );
        }

        // Include a manifest
        include_once INCLUDE_DIR . 'class.mailfetch.php';

        $po_header = Mail_Parse::splitHeaders($mo['']);
        $info = array(
            'Build-Date' => date(DATE_RFC822),
            'Build-Version' => trim(`git describe`),
            'Language' => $po_header['Language'],
            #'Phrases' =>
            #'Translated' =>
            #'Approved' =>
            'Id' => 'lang:' . $lang,
            'Last-Revision' => $po_header['PO-Revision-Date'],
            'Version' => (int)(strtotime($po_header['PO-Revision-Date']) / 10000),
        );
        $phar->addFromString(
            'MANIFEST.php',
            sprintf('<?php return %s;', var_export($info, true)));

        // TODO: Sign files

        // Use a very small stub
        $phar->setStub('<?php __HALT_COMPILER();');
        $phar->setSignatureAlgorithm(Phar::SHA1);
        $phar->stopBuffering();
    }
예제 #4
0
 static function isBounce($headers)
 {
     if ($headers && !is_array($headers)) {
         $headers = Mail_Parse::splitHeaders($headers);
     }
     $bounce_headers = array('From' => array('stripos', array('MAILER-DAEMON', '<>'), null, false), 'Subject' => array('stripos', array('DELIVERY FAILURE', 'DELIVERY STATUS', 'UNDELIVERABLE:', 'Undelivered Mail Returned'), 0), 'Return-Path' => array('strcmp', array('<>'), 0), 'Content-Type' => array('stripos', array('report-type=delivery-status'), null, false), 'X-Failed-Recipients' => array('strpos', array('@'), null, false));
     foreach ($bounce_headers as $header => $find) {
         if (!isset($headers[$header])) {
             continue;
         }
         @(list($func, $searches, $pos, $neg) = $find);
         if (!($value = $headers[$header]) || !is_array($searches)) {
             continue;
         }
         foreach ($searches as $f) {
             $result = call_user_func($func, $value, $f);
             if ($pos === null && $result !== $neg or $result === $pos) {
                 return true;
             }
         }
     }
     return false;
 }
예제 #5
0
 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 getEmailHeaderArray()
 {
     require_once INCLUDE_DIR . 'class.mailparse.php';
     if (!isset($this->ht['@headers'])) {
         $this->ht['@headers'] = Mail_Parse::splitHeaders($this->ht['headers']);
     }
     return $this->ht['@headers'];
 }
예제 #7
0
 function getEmailHeaders()
 {
     require_once INCLUDE_DIR . 'class.mailparse.php';
     $sql = 'SELECT headers FROM ' . TICKET_EMAIL_INFO_TABLE . ' WHERE message_id=' . $this->getId();
     $headers = db_result(db_query($sql));
     return Mail_Parse::splitHeaders($headers);
 }
예제 #8
0
 function isAutoBounce($headers)
 {
     if ($headers && !is_array($headers)) {
         $headers = Mail_Parse::splitHeaders($headers);
     }
     $bounce_headers = array('From' => array('<MAILER-DAEMON@MAILER-DAEMON>', 'MAILER-DAEMON', '<>'), 'Subject' => array('DELIVERY FAILURE', 'DELIVERY STATUS', 'UNDELIVERABLE:'));
     foreach ($bounce_headers as $header => $find) {
         if (!isset($headers[$header])) {
             continue;
         }
         $value = strtoupper($headers[$header]);
         if (is_array($find)) {
             foreach ($find as $f) {
                 if (strpos($value, $f) === 0) {
                     return true;
                 }
             }
         } elseif (strpos($value, $find) === 0) {
             return true;
         }
     }
     return false;
 }