Esempio n. 1
0
 function getPriority()
 {
     return Mail_Parse::parsePriority($this->getHeader());
 }
Esempio n. 2
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();
    }
Esempio n. 3
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;
 }
Esempio n. 4
0
 function parse($stream)
 {
     global $cfg;
     $contents = '';
     if (is_resource($stream)) {
         while (!feof($stream)) {
             $contents .= fread($stream, 8192);
         }
     } else {
         $contents = $stream;
     }
     $parser = new Mail_Parse($contents);
     if (!$parser->decode()) {
         //Decode...returns false on decoding errors
         return $this->err('Email parse failed [' . $parser->getError() . ']');
     }
     $data = array();
     $data['emailId'] = 0;
     $data['recipients'] = array();
     $data['subject'] = $parser->getSubject();
     $data['header'] = $parser->getHeader();
     $data['mid'] = $parser->getMessageId();
     $data['priorityId'] = $parser->getPriority();
     $data['flags'] = new ArrayObject();
     //FROM address: who sent the email.
     if ($fromlist = $parser->getFromAddressList()) {
         $from = $fromlist[0];
         //Default.
         foreach ($fromlist as $fromobj) {
             if (!Validator::is_email($fromobj->mailbox . '@' . $fromobj->host)) {
                 continue;
             }
             $from = $fromobj;
             break;
         }
         $data['email'] = $from->mailbox . '@' . $from->host;
         $data['name'] = trim($from->personal, '"');
         if ($from->comment && $from->comment[0]) {
             $data['name'] .= ' (' . $from->comment[0] . ')';
         }
         //Use email address as name  when FROM address doesn't  have a name.
         if (!$data['name'] && $data['email']) {
             $data['name'] = $data['email'];
         }
     }
     /* Scan through the list of addressees (via To, Cc, and Delivered-To headers), and identify
      * how the mail arrived at the system. One of the mails should be in the system email list.
      * The recipient list (without the Delivered-To addressees) will be made available to the
      * ticket filtering system. However, addresses in the Delivered-To header should never be
      * considered for the collaborator list.
      */
     $tolist = array();
     if ($to = $parser->getToAddressList()) {
         $tolist['to'] = $to;
     }
     if ($cc = $parser->getCcAddressList()) {
         $tolist['cc'] = $cc;
     }
     if ($dt = $parser->getDeliveredToAddressList()) {
         $tolist['delivered-to'] = $dt;
     }
     foreach ($tolist as $source => $list) {
         foreach ($list as $addr) {
             if (!($emailId = Email::getIdByEmail(strtolower($addr->mailbox) . '@' . $addr->host))) {
                 //Skip virtual Delivered-To addresses
                 if ($source == 'delivered-to') {
                     continue;
                 }
                 $data['recipients'][] = array('source' => sprintf(_S("Email (%s)"), $source), 'name' => trim(@$addr->personal, '"'), 'email' => strtolower($addr->mailbox) . '@' . $addr->host);
             } elseif (!$data['emailId']) {
                 $data['emailId'] = $emailId;
             }
         }
     }
     /*
      * In the event that the mail was delivered to the system although none of the system
      * mail addresses are in the addressee lists, be careful not to include the addressee
      * in the collaborator list. Therefore, the delivered-to addressees should be flagged so they
      * are not added to the collaborator list in the ticket creation process.
      */
     if ($tolist['delivered-to']) {
         foreach ($tolist['delivered-to'] as $addr) {
             foreach ($data['recipients'] as $i => $r) {
                 if (strcasecmp($r['email'], $addr->mailbox . '@' . $addr->host) === 0) {
                     $data['recipients'][$i]['source'] = 'delivered-to';
                 }
             }
         }
     }
     //maybe we got BCC'ed??
     if (!$data['emailId']) {
         $emailId = 0;
         if ($bcc = $parser->getBccAddressList()) {
             foreach ($bcc as $addr) {
                 if ($emailId = Email::getIdByEmail($addr->mailbox . '@' . $addr->host)) {
                     break;
                 }
             }
         }
         $data['emailId'] = $emailId;
     }
     if ($parser->isBounceNotice()) {
         // Fetch the original References and assign to 'references'
         if ($headers = $parser->getOriginalMessageHeaders()) {
             $data['references'] = $headers['references'];
             $data['in-reply-to'] = @$headers['in-reply-to'] ?: null;
         }
         // Fetch deliver status report
         $data['message'] = $parser->getDeliveryStatusMessage() ?: $parser->getBody();
         $data['thread-type'] = 'N';
         $data['flags']['bounce'] = true;
     } else {
         // Typical email
         $data['message'] = $parser->getBody();
         $data['in-reply-to'] = @$parser->struct->headers['in-reply-to'];
         $data['references'] = @$parser->struct->headers['references'];
         $data['flags']['bounce'] = TicketFilter::isBounce($data['header']);
     }
     $data['to-email-id'] = $data['emailId'];
     if ($replyto = $parser->getReplyTo()) {
         $replyto = $replyto[0];
         $data['reply-to'] = $replyto->mailbox . '@' . $replyto->host;
         if ($replyto->personal) {
             $data['reply-to-name'] = trim($replyto->personal, " \t\n\r\v\"");
         }
     }
     $data['attachments'] = $parser->getAttachments();
     return $data;
 }
Esempio n. 5
0
ini_set('memory_limit', '256M');
//The concern here is having enough mem for emails with attachments.
require 'api.inc.php';
require_once INCLUDE_DIR . 'class.mailparse.php';
require_once INCLUDE_DIR . 'class.email.php';
//Make sure piping is enabled!
if (!$cfg->isEmailPipingEnabled()) {
    api_exit(EX_UNAVAILABLE, 'Email piping not enabled - check MTA settings.');
}
//Get the input
$data = isset($_SERVER['HTTP_HOST']) ? file_get_contents('php://input') : file_get_contents('php://stdin');
if (empty($data)) {
    api_exit(EX_NOINPUT, 'No data');
}
//Parse the email.
$parser = new Mail_Parse($data);
if (!$parser->decode()) {
    //Decode...returns false on decoding errors
    api_exit(EX_DATAERR, 'Email parse failed [' . $parser->getError() . "]\n\n" . $data);
}
//Check from address. make sure it is not a banned address.
$fromlist = $parser->getFromAddressList();
//Check for parsing errors on FROM address.
if (!$fromlist || PEAR::isError($fromlist)) {
    api_exit(EX_DATAERR, 'Invalid FROM address [' . $fromlist ? $fromlist->getMessage() : '' . "]\n\n" . $data);
}
$from = $fromlist[0];
//Default.
foreach ($fromlist as $fromobj) {
    if (!Validator::is_email($fromobj->mailbox . '@' . $fromobj->host)) {
        continue;
Esempio n. 6
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;
 }
Esempio n. 7
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;
 }
Esempio n. 8
0
require_once INCLUDE_DIR . 'class.mailparse.php';
require_once INCLUDE_DIR . 'class.email.php';
ini_set('memory_limit', '256M');
//The concern here is having enough mem for emails with attachments.
//ini_set('allow_url_include', 1); //Allow file get contents.
//Make sure piping is enabled!
if (!$cfg->enableEmailPiping()) {
    api_exit(EX_UNAVAILABLE, 'Services not available!');
}
//Get the input
$data = isset($_SERVER['HTTP_HOST']) ? file_get_contents('php://input') : file_get_contents('php://stdin');
if (empty($data)) {
    api_exit(EX_NOINPUT, 'No data');
}
//Parse the email.
$parser = new Mail_Parse($data);
if (!$parser->decode()) {
    //Decode...returns false on decoding errors
    api_exit(EX_NOINPUT, 'Email parse failed [' . $parser->getError() . "]\n\n" . $data);
}
//Check from address. make sure it is not a banned address.
$fromlist = $parser->getFromAddressList();
//Check for parsing errors on FROM address.
if (!$fromlist || PEAR::isError($fromlist)) {
    api_exit(EX_DATAERR, 'Invalid FROM address [' . $fromlist->getMessage() . "]\n\n" . $data);
}
//Try to figure out the email associated with the message.
$deptId = 0;
$tolist = $parser->getToAddressList();
foreach ($tolist as $toaddr) {
    if ($emailId = Email::getIdByEmail($toaddr->mailbox . '@' . $toaddr->host)) {
Esempio n. 9
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'];
 }
Esempio n. 11
0
 /**
  * Retrieve a list of all the recients of this message if the message
  * was received via email.
  *
  * Returns:
  * (array<RFC_822>) list of recipients parsed with the Mail/RFC822
  * address parsing utility. Returns an empty array if the message was
  * not received via email.
  */
 function getAllEmailRecipients()
 {
     $headers = self::getEmailHeaderArray();
     $recipients = array();
     if (!$headers) {
         return $recipients;
     }
     foreach (array('To', 'Cc') as $H) {
         if (!isset($headers[$H])) {
             continue;
         }
         if (!($all = Mail_Parse::parseAddressList($headers[$H]))) {
             continue;
         }
         $recipients = array_merge($recipients, $all);
     }
     return $recipients;
 }
Esempio n. 12
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);
 }
Esempio n. 13
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;
 }
Esempio n. 14
0
 function parse($stream)
 {
     global $cfg;
     $contents = '';
     if (is_resource($stream)) {
         while (!feof($stream)) {
             $contents .= fread($stream, 8192);
         }
     } else {
         $contents = $stream;
     }
     $parser = new Mail_Parse($contents);
     if (!$parser->decode()) {
         //Decode...returns false on decoding errors
         return $this->err('Email parse failed [' . $parser->getError() . ']');
     }
     $data = array();
     //FROM address: who sent the email.
     if (($fromlist = $parser->getFromAddressList()) && !PEAR::isError($fromlist)) {
         $from = $fromlist[0];
         //Default.
         foreach ($fromlist as $fromobj) {
             if (!Validator::is_email($fromobj->mailbox . '@' . $fromobj->host)) {
                 continue;
             }
             $from = $fromobj;
             break;
         }
         $data['email'] = $from->mailbox . '@' . $from->host;
         $data['name'] = trim($from->personal, '"');
         if ($from->comment && $from->comment[0]) {
             $data['name'] .= ' (' . $from->comment[0] . ')';
         }
         //Use email address as name  when FROM address doesn't  have a name.
         if (!$data['name'] && $data['email']) {
             $data['name'] = $data['email'];
         }
     }
     //TO Address:Try to figure out the email address... associated with the incoming email.
     $emailId = 0;
     if ($tolist = $parser->getToAddressList()) {
         foreach ($tolist as $toaddr) {
             if ($emailId = Email::getIdByEmail($toaddr->mailbox . '@' . $toaddr->host)) {
                 break;
             }
         }
     }
     //maybe we got CC'ed??
     if (!$emailId && ($cclist = $parser->getCcAddressList())) {
         foreach ($cclist as $ccaddr) {
             if ($emailId = Email::getIdByEmail($ccaddr->mailbox . '@' . $ccaddr->host)) {
                 break;
             }
         }
     }
     $data['subject'] = $parser->getSubject();
     $data['message'] = Format::stripEmptyLines($parser->getBody());
     $data['header'] = $parser->getHeader();
     $data['mid'] = $parser->getMessageId();
     $data['priorityId'] = $parser->getPriority();
     $data['emailId'] = $emailId;
     $data['in-reply-to'] = $parser->struct->headers['in-reply-to'];
     $data['references'] = $parser->struct->headers['references'];
     if (($replyto = $parser->getReplyTo()) && !PEAR::isError($replyto)) {
         $replyto = $replyto[0];
         $data['reply-to'] = $replyto->mailbox . '@' . $replyto->host;
         if ($replyto->personal) {
             $data['reply-to-name'] = trim($replyto->personal, " \t\n\r\v\"");
         }
     }
     if ($cfg && $cfg->allowEmailAttachments()) {
         $data['attachments'] = $parser->getAttachments();
     }
     return $data;
 }