Example #1
0
 function downloadFileAction(DevblocksHttpRequest $request)
 {
     $umsession = UmPortalHelper::getSession();
     $stack = $request->path;
     if (null == ($active_user = $umsession->getProperty('sc_login', null))) {
         return;
     }
     // Attachment ID + display name
     @($ticket_mask = array_shift($stack));
     @($hash = array_shift($stack));
     @($display_name = array_shift($stack));
     if (empty($ticket_mask) || empty($hash) || empty($display_name)) {
         return;
     }
     if (null == ($ticket_id = DAO_Ticket::getTicketIdByMask($ticket_mask))) {
         return;
     }
     // Load attachments by ticket mask
     list($attachments) = DAO_Attachment::search(array(SearchFields_Attachment::TICKET_MASK => new DevblocksSearchCriteria(SearchFields_Attachment::TICKET_MASK, '=', $ticket_mask)), -1, 0, null, null, false);
     $attachment = null;
     if (is_array($attachments)) {
         foreach ($attachments as $possible_file) {
             // Compare the hash
             $fingerprint = md5($possible_file[SearchFields_Attachment::ID] . $possible_file[SearchFields_Attachment::MESSAGE_ID] . $possible_file[SearchFields_Attachment::DISPLAY_NAME]);
             if (0 == strcmp($fingerprint, $hash)) {
                 if (null == ($attachment = DAO_Attachment::get($possible_file[SearchFields_Attachment::ID]))) {
                     return;
                 }
                 break;
             }
         }
     }
     // No hit (bad hash)
     if (null == $attachment) {
         return;
     }
     // Load requesters
     if (null == ($requesters = DAO_Ticket::getRequestersByTicket($ticket_id))) {
         return;
     }
     // Security: Make sure the active user is a requester on the proper ticket
     if (!isset($requesters[$active_user->id])) {
         return;
     }
     // Set headers
     header("Expires: Mon, 26 Nov 1962 00:00:00 GMT\n");
     header("Last-Modified: " . gmdate("D,d M YH:i:s") . " GMT\n");
     header("Cache-control: private\n");
     header("Pragma: no-cache\n");
     header("Content-Type: " . $attachment->mime_type . "\n");
     header("Content-transfer-encoding: binary\n");
     header("Content-Length: " . $attachment->getFileSize() . "\n");
     // Dump contents
     echo $attachment->getFileContents();
     exit;
 }
Example #2
0
 private function _handleImportTicket($xml)
 {
     $settings = CerberusSettings::getInstance();
     $logger = DevblocksPlatform::getConsoleLog();
     $workers = DAO_Worker::getAll();
     static $email_to_worker_id = null;
     static $group_name_to_id = null;
     static $bucket_name_to_id = null;
     // Hash Workers so we can ID their incoming tickets
     if (null == $email_to_worker_id) {
         $email_to_worker_id = array();
         if (is_array($workers)) {
             foreach ($workers as $worker) {
                 /* @var $worker CerberusWorker */
                 $email_to_worker_id[strtolower($worker->email)] = intval($worker->id);
             }
         }
     }
     // Hash Group names
     if (null == $group_name_to_id) {
         $groups = DAO_Group::getAll();
         $group_name_to_id = array();
         if (is_array($groups)) {
             foreach ($groups as $group) {
                 $group_name_to_id[strtolower($group->name)] = intval($group->id);
             }
         }
     }
     // Hash Bucket names
     if (null == $bucket_name_to_id) {
         $buckets = DAO_Bucket::getAll();
         $bucket_name_to_id = array();
         if (is_array($buckets)) {
             foreach ($buckets as $bucket) {
                 /* @var $bucket CerberusCategory */
                 // Hash by team ID and bucket name
                 $hash = md5($bucket->team_id . strtolower($bucket->name));
                 $bucket_to_id[$hash] = intval($bucket->id);
             }
         }
     }
     $sMask = (string) $xml->mask;
     $sSubject = substr((string) $xml->subject, 0, 255);
     $sGroup = (string) $xml->group;
     $sBucket = (string) $xml->bucket;
     $iCreatedDate = (int) $xml->created_date;
     $iUpdatedDate = (int) $xml->updated_date;
     $isWaiting = (int) $xml->is_waiting;
     $isClosed = (int) $xml->is_closed;
     if (empty($sMask)) {
         $sMask = CerberusApplication::generateTicketMask();
     }
     // Find the destination Group + Bucket (or create them)
     if (empty($sGroup)) {
         $iDestGroupId = 0;
         if (null != ($iDestGroup = DAO_Group::getDefaultGroup())) {
             $iDestGroupId = $iDestGroup->id;
         }
     } elseif (null == ($iDestGroupId = @$group_name_to_id[strtolower($sGroup)])) {
         $iDestGroupId = DAO_Group::createTeam(array(DAO_Group::TEAM_NAME => $sGroup));
         // Give all superusers manager access to this new group
         if (is_array($workers)) {
             foreach ($workers as $worker) {
                 if ($worker->is_superuser) {
                     DAO_Group::setTeamMember($iDestGroupId, $worker->id, true);
                 }
             }
         }
         // Rehash
         DAO_Group::getAll(true);
         $group_name_to_id[strtolower($sGroup)] = $iDestGroupId;
     }
     if (empty($sBucket)) {
         $iDestBucketId = 0;
         // Inbox
     } elseif (null == ($iDestBucketId = @$bucket_name_to_id[md5($iDestGroupId . strtolower($sBucket))])) {
         $iDestBucketId = DAO_Bucket::create($sBucket, $iDestGroupId);
         // Rehash
         DAO_Bucket::getAll(true);
         $bucket_name_to_id[strtolower($sBucket)] = $iDestBucketId;
     }
     // Xpath the first and last "from" out of "/ticket/messages/message/headers/from"
     $aMessageNodes = $xml->xpath("/ticket/messages/message");
     $iNumMessages = count($aMessageNodes);
     @($eFirstMessage = reset($aMessageNodes));
     if (is_null($eFirstMessage)) {
         $logger->warn('[Importer] Ticket ' . $sMask . " doesn't have any messages.  Skipping.");
         return false;
     }
     if (is_null($eFirstMessage->headers) || is_null($eFirstMessage->headers->from)) {
         $logger->warn('[Importer] Ticket ' . $sMask . " first message doesn't provide a sender address.");
         return false;
     }
     $sFirstWrote = self::_parseRfcAddressList($eFirstMessage->headers->from, true);
     if (null == ($firstWroteInst = CerberusApplication::hashLookupAddress($sFirstWrote, true))) {
         $logger->warn('[Importer] Ticket ' . $sMask . " - Invalid sender adddress: " . $sFirstWrote);
         return false;
     }
     $eLastMessage = end($aMessageNodes);
     if (is_null($eLastMessage)) {
         $logger->warn('[Importer] Ticket ' . $sMask . " doesn't have any messages.  Skipping.");
         return false;
     }
     if (is_null($eLastMessage->headers) || is_null($eLastMessage->headers->from)) {
         $logger->warn('[Importer] Ticket ' . $sMask . " last message doesn't provide a sender address.");
         return false;
     }
     $sLastWrote = self::_parseRfcAddressList($eLastMessage->headers->from, true);
     if (null == ($lastWroteInst = CerberusApplication::hashLookupAddress($sLastWrote, true))) {
         $logger->warn('[Importer] Ticket ' . $sMask . ' last message has an invalid sender address: ' . $sLastWrote);
         return false;
     }
     // Last action code + last worker
     $sLastActionCode = CerberusTicketActionCode::TICKET_OPENED;
     $iLastWorkerId = 0;
     if ($iNumMessages > 1) {
         if (null != @($iLastWorkerId = $email_to_worker_id[strtolower($lastWroteInst->email)])) {
             $sLastActionCode = CerberusTicketActionCode::TICKET_WORKER_REPLY;
         } else {
             $sLastActionCode = CerberusTicketActionCode::TICKET_CUSTOMER_REPLY;
             $iLastWorkerId = 0;
         }
     }
     // Dupe check by ticket mask
     if (null != DAO_Ticket::getTicketByMask($sMask)) {
         $logger->warn("[Importer] Ticket mask '" . $sMask . "' already exists.  Making it unique.");
         $uniqueness = 1;
         $origMask = $sMask;
         // Append new uniqueness to the ticket mask:  LLL-NNNNN-NNN-1, LLL-NNNNN-NNN-2, ...
         do {
             $sMask = $origMask . '-' . ++$uniqueness;
         } while (null != DAO_Ticket::getTicketIdByMask($sMask));
         $logger->info("[Importer] The unique mask for '" . $origMask . "' is now '" . $sMask . "'");
     }
     // Create ticket
     $fields = array(DAO_Ticket::MASK => $sMask, DAO_Ticket::SUBJECT => $sSubject, DAO_Ticket::IS_WAITING => $isWaiting, DAO_Ticket::IS_CLOSED => $isClosed, DAO_Ticket::FIRST_WROTE_ID => intval($firstWroteInst->id), DAO_Ticket::LAST_WROTE_ID => intval($lastWroteInst->id), DAO_Ticket::CREATED_DATE => $iCreatedDate, DAO_Ticket::UPDATED_DATE => $iUpdatedDate, DAO_Ticket::TEAM_ID => intval($iDestGroupId), DAO_Ticket::CATEGORY_ID => intval($iDestBucketId), DAO_Ticket::LAST_ACTION_CODE => $sLastActionCode, DAO_Ticket::LAST_WORKER_ID => intval($iLastWorkerId));
     $ticket_id = DAO_Ticket::createTicket($fields);
     //		echo "Ticket: ",$ticket_id,"<BR>";
     //		print_r($fields);
     // Create requesters
     if (!is_null($xml->requesters)) {
         foreach ($xml->requesters->address as $eAddress) {
             /* @var $eAddress SimpleXMLElement */
             $sRequesterAddy = (string) $eAddress;
             // [TODO] RFC822
             // Insert requesters
             if (null == ($requesterAddyInst = CerberusApplication::hashLookupAddress($sRequesterAddy, true))) {
                 $logger->warn('[Importer] Ticket ' . $sMask . ' - Ignoring malformed requester: ' . $sRequesterAddy);
                 continue;
             }
             DAO_Ticket::createRequester($requesterAddyInst->id, $ticket_id);
         }
     }
     // Create messages
     $is_first = true;
     if (!is_null($xml->messages)) {
         foreach ($xml->messages->message as $eMessage) {
             /* @var $eMessage SimpleXMLElement */
             $eHeaders =& $eMessage->headers;
             /* @var $eHeaders SimpleXMLElement */
             $sMsgFrom = (string) $eHeaders->from;
             $sMsgDate = (string) $eHeaders->date;
             $sMsgFrom = self::_parseRfcAddressList($sMsgFrom, true);
             if (NULL == $sMsgFrom) {
                 $logger->warn('[Importer] Ticket ' . $sMask . ' - Invalid message sender: ' . $sMsgFrom . ' (skipping)');
                 continue;
             }
             if (null == ($msgFromInst = CerberusApplication::hashLookupAddress($sMsgFrom, true))) {
                 $logger->warn('[Importer] Ticket ' . $sMask . ' - Invalid message sender: ' . $sMsgFrom . ' (skipping)');
                 continue;
             }
             @($msgWorkerId = intval($email_to_worker_id[strtolower($msgFromInst->email)]));
             //			$logger->info('Checking if '.$msgFromInst->email.' is a worker');
             $fields = array(DAO_Message::TICKET_ID => $ticket_id, DAO_Message::CREATED_DATE => strtotime($sMsgDate), DAO_Message::ADDRESS_ID => $msgFromInst->id, DAO_Message::IS_OUTGOING => !empty($msgWorkerId) ? 1 : 0, DAO_Message::WORKER_ID => !empty($msgWorkerId) ? $msgWorkerId : 0);
             $email_id = DAO_Message::create($fields);
             // First thread
             if ($is_first) {
                 DAO_Ticket::updateTicket($ticket_id, array(DAO_Ticket::FIRST_MESSAGE_ID => $email_id));
                 $is_first = false;
             }
             // Create attachments
             if (!is_null($eMessage->attachments)) {
                 foreach ($eMessage->attachments->attachment as $eAttachment) {
                     /* @var $eAttachment SimpleXMLElement */
                     $sFileName = (string) $eAttachment->name;
                     $sMimeType = (string) $eAttachment->mimetype;
                     $sFileSize = (int) $eAttachment->size;
                     $sFileContentB64 = (string) $eAttachment->content;
                     // [TODO] This could be a little smarter about detecting extensions
                     if (empty($sMimeType)) {
                         $sMimeType = "application/octet-stream";
                     }
                     $sFileContent = base64_decode($sFileContentB64);
                     unset($sFileContentB64);
                     $fields = array(DAO_Attachment::MESSAGE_ID => $email_id, DAO_Attachment::DISPLAY_NAME => $sFileName, DAO_Attachment::FILE_SIZE => intval($sFileSize), DAO_Attachment::FILEPATH => '', DAO_Attachment::MIME_TYPE => $sMimeType);
                     $file_id = DAO_Attachment::create($fields);
                     // Write file to disk using ID (Model)
                     $file_path = Model_Attachment::saveToFile($file_id, $sFileContent);
                     unset($sFileContent);
                     // Update attachment table
                     DAO_Attachment::update($file_id, array(DAO_Attachment::FILEPATH => $file_path));
                 }
             }
             // Create message content
             $sMessageContentB64 = (string) $eMessage->content;
             $sMessageContent = base64_decode($sMessageContentB64);
             // Content-type specific handling
             if (isset($eMessage->content['content-type'])) {
                 // do we have a content-type?
                 if (strtolower($eMessage->content['content-type']) == 'html') {
                     // html?
                     // Force to plaintext part
                     $sMessageContent = CerberusApplication::stripHTML($sMessageContent);
                 }
             }
             unset($sMessageContentB64);
             DAO_MessageContent::create($email_id, $sMessageContent);
             unset($sMessageContent);
             // Headers
             foreach ($eHeaders->children() as $eHeader) {
                 /* @var $eHeader SimpleXMLElement */
                 DAO_MessageHeader::create($email_id, $eHeader->getName(), (string) $eHeader);
             }
         }
     }
     // Create comments
     if (!is_null($xml->comments)) {
         foreach ($xml->comments->comment as $eComment) {
             /* @var $eMessage SimpleXMLElement */
             $iCommentDate = (int) $eComment->created_date;
             $sCommentAuthor = (string) $eComment->author;
             // [TODO] Address Hash Lookup
             $sCommentTextB64 = (string) $eComment->content;
             $sCommentText = base64_decode($sCommentTextB64);
             unset($sCommentTextB64);
             $commentAuthorInst = CerberusApplication::hashLookupAddress($sCommentAuthor, true);
             // [TODO] Sanity checking
             $fields = array(DAO_TicketComment::TICKET_ID => intval($ticket_id), DAO_TicketComment::CREATED => intval($iCommentDate), DAO_TicketComment::ADDRESS_ID => intval($commentAuthorInst->id), DAO_TicketComment::COMMENT => $sCommentText);
             $comment_id = DAO_TicketComment::create($fields);
             unset($sCommentText);
         }
     }
     $logger->info('[Importer] Imported ticket #' . $ticket_id);
     return true;
 }
Example #3
0
 /**
  * Looks up a ticket ID by the provided mask using a revolving cache.
  * This is useful if you need to translate several ticket masks into 
  * IDs where there may be a lot of redundancy (batches in the e-mail 
  * parser, etc.)
  * 
  * @param string $mask The ticket mask to look up
  * @return integer The ticket id, or NULL if not found
  *  
  * @todo [JAS]: Move this to a global cache/hash registry 
  */
 public static function hashLookupTicketIdByMask($mask)
 {
     static $hash_mask_to_id = array();
     static $hash_hits = array();
     static $hash_size = 0;
     if (isset($hash_mask_to_id[$mask])) {
         $return = $hash_mask_to_id[$mask];
         @($hash_hits[$mask] = intval($hash_hits[$mask]) + 1);
         $hash_size++;
         // [JAS]: if our hash grows past our limit, crop hits array + intersect keys
         if ($hash_size > 250) {
             arsort($hash_hits);
             $hash_hits = array_slice($hash_hits, 0, 100, true);
             $hash_mask_to_id = array_intersect_key($hash_mask_to_id, $hash_hits);
             $hash_size = count($hash_mask_to_id);
         }
         return $return;
     }
     $ticket_id = DAO_Ticket::getTicketIdByMask($mask);
     if (!empty($ticket_id)) {
         $hash_mask_to_id[$mask] = $ticket_id;
     }
     return $ticket_id;
 }
Example #4
0
 function browseAction()
 {
     $translate = DevblocksPlatform::getTranslationService();
     $visit = CerberusApplication::getVisit();
     /* @var $visit CerberusVisit */
     $request = DevblocksPlatform::getHttpRequest();
     $stack = $request->path;
     array_shift($stack);
     // display
     array_shift($stack);
     // browse
     @($id = array_shift($stack));
     // [JAS]: Mask
     if (!is_numeric($id)) {
         $id = DAO_Ticket::getTicketIdByMask($id);
     }
     $ticket = DAO_Ticket::getTicket($id);
     if (empty($ticket)) {
         echo "<H1>" . $translate->_('display.invalid_ticket') . "</H1>";
         return;
     }
     // Display series support (inherited paging from Display)
     @($view_id = array_shift($stack));
     if (!empty($view_id)) {
         $view = C4_AbstractViewLoader::getView('', $view_id);
         // Restrict to the active worker's groups
         $active_worker = CerberusApplication::getActiveWorker();
         $memberships = $active_worker->getMemberships();
         $view->params['tmp'] = new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_TEAM_ID, 'in', array_keys($memberships));
         $range = 250;
         // how far to jump ahead of the current page
         $block_size = 250;
         $page = floor($view->renderPage * $view->renderLimit / $block_size);
         $index = array();
         $found = false;
         $full = false;
         do {
             list($series, $null) = DAO_Ticket::search(array(SearchFields_Ticket::TICKET_MASK), $view->params, $block_size, $page, $view->renderSortBy, $view->renderSortAsc, false);
             // Index by mask
             foreach ($series as $idx => $val) {
                 // Find our match before we index anything
                 if (!$found && $idx == $id) {
                     $found = true;
                 } elseif (!$found) {
                     // Only keep a max of X things behind our match, reserve the most room ahead
                     if (count($index) == 20) {
                         array_shift($index);
                     }
                 }
                 $index[] = $val[SearchFields_Ticket::TICKET_MASK];
                 // Stop if we fill up our desired rows
                 if (count($index) == $range) {
                     $full = true;
                     break;
                 }
             }
             $page++;
         } while (!empty($series) && !$full);
         $series_info = array('title' => $view->name, 'total' => count($index), 'series' => $index);
         $visit->set('ch_display_series', $series_info);
     }
     DevblocksPlatform::redirect(new DevblocksHttpResponse(array('display', $ticket->mask)));
 }
Example #5
0
 function render()
 {
     $tpl = DevblocksPlatform::getTemplateService();
     $response = DevblocksPlatform::getHttpResponse();
     @($ticket_id = $response->path[2]);
     @($page_type = DevblocksPlatform::importGPC($_REQUEST['page_type'], 'string', 'reply'));
     $message_id = $response->path[3];
     if (empty($ticket_id)) {
         $session = DevblocksPlatform::getSessionService();
         $visit = $session->getVisit();
         return;
     }
     if (!is_numeric($ticket_id)) {
         $ticket_id = DAO_Ticket::getTicketIdByMask($ticket_id);
     }
     $ticket = DAO_Ticket::getTicket($ticket_id);
     $tpl->assign('ticket', $ticket);
     $tpl->assign('ticket_id', $ticket_id);
     $tpl->assign('message_id', $message_id);
     $tpl->assign('page_type', $page_type);
     if (0 == strcasecmp($message_id, 'full')) {
         $tpl->display('file:' . dirname(__FILE__) . '/templates/display.tpl');
     } else {
         $message = DAO_Ticket::getMessage($message_id);
         if (empty($message)) {
             $message = array_pop($ticket->getMessages());
         }
         $tpl->assign('message', $message);
         $tpl->display('file:' . dirname(__FILE__) . '/templates/display_brief.tpl');
     }
 }
Example #6
0
 protected function getAction($path, $keychain)
 {
     if (Model_WebapiKey::ACL_NONE == intval(@$keychain->rights['acl_tickets'])) {
         $this->_error("Action not permitted.");
     }
     // Single GET
     if (1 == count($path) && is_numeric($path[0])) {
         $this->_getIdAction($path);
     }
     // Actions
     $value = array_shift($path);
     switch ($value) {
         case 'list':
             $this->_getListAction($path);
             break;
         default:
             if (($id = DAO_Ticket::getTicketIdByMask($value)) != null) {
                 $this->_getIdAction(array($id));
             }
             break;
     }
 }