static function lookupByToken($token) { //Expecting well formatted token see getAuthToken routine for details. $matches = array(); if (!preg_match(static::$token_regex, $token, $matches)) { return null; } //Unpack the user and ticket ids $matches += unpack('Vuid/Vtid', Base32::decode(strtolower(substr($matches['hash'], 0, 13)))); $user = null; switch ($matches['type']) { case 'c': //Collaborator c if (($user = Collaborator::lookup($matches['uid'])) && $user->getTicketId() != $matches['tid']) { $user = null; } break; case 'o': //Ticket owner if ($ticket = Ticket::lookup($matches['tid'])) { if (($user = $ticket->getOwner()) && $user->getId() != $matches['uid']) { $user = null; } } break; } if (!$user || !$user instanceof TicketUser || strcasecmp($user->getAuthToken($matches['algo']), $token)) { return false; } return $user; }
function viewCollaborator($cid) { global $thisstaff; if (!($collaborator = Collaborator::lookup($cid)) || !($ticket = $collaborator->getTicket()) || !$ticket->checkStaffAccess($thisstaff)) { Http::response(404, 'Unknown collaborator'); } return self::_collaborator($collaborator); }
function _getTicketUser($ticket, $user) { // Ticket owner? if ($ticket->getUserId() == $user->getId()) { $user = $ticket->getOwner(); } elseif (!($user = Collaborator::lookup(array('userId' => $user->getId(), 'ticketId' => $ticket->getId())))) { return false; } //Bro, we don't know you! return $user; }
function changeOwner($user) { global $thisstaff; if (!$user || $user->getId() == $this->getOwnerId() || !$thisstaff->canEditTickets()) { return false; } $sql = 'UPDATE ' . TICKET_TABLE . ' SET updated = NOW() ' . ', user_id = ' . db_input($user->getId()) . ' WHERE ticket_id = ' . db_input($this->getId()); if (!db_query($sql) || !db_affected_rows()) { return false; } $this->ht['user_id'] = $user->getId(); $this->user = null; $this->collaborators = null; $this->recipients = null; //Log an internal note $note = sprintf(_S('%s changed ticket ownership to %s'), $thisstaff->getName(), $user->getName()); //Remove the new owner from list of collaborators $c = Collaborator::lookup(array('userId' => $user->getId(), 'ticketId' => $this->getId())); if ($c && $c->remove()) { $note .= ' ' . _S('(removed as collaborator)'); } $this->logNote('Ticket ownership changed', $note); return true; }
static function lookup($criteria) { $id = is_numeric($criteria) ? $criteria : self::getIdByInfo($criteria); return $id && ($c = new Collaborator($id)) && $c->getId() == $id ? $c : null; }
/** * Parameters: * mailinfo (hash<String>) email header information. Must include keys * - "mid" => Message-Id header of incoming mail * - "in-reply-to" => Message-Id the email is a direct response to * - "references" => List of Message-Id's the email is in response * - "subject" => Find external ticket number in the subject line * * seen (by-ref:bool) a flag that will be set if the message-id was * positively found, indicating that the message-id has been * previously seen. This is useful if no thread-id is associated * with the email (if it was rejected for instance). */ function lookupByEmailHeaders(&$mailinfo, &$seen = false) { // Search for messages using the References header, then the // in-reply-to header $search = 'SELECT thread_id, email_mid FROM ' . TICKET_EMAIL_INFO_TABLE . ' WHERE email_mid=%s ORDER BY thread_id DESC'; if (list($id, $mid) = db_fetch_row(db_query(sprintf($search, db_input($mailinfo['mid']))))) { $seen = true; return ThreadEntry::lookup($id); } foreach (array('in-reply-to', 'references') as $header) { $matches = array(); if (!isset($mailinfo[$header]) || !$mailinfo[$header]) { continue; } elseif (!preg_match_all('/<[^>@]+@[^>]+>/', $mailinfo[$header], $matches)) { continue; } // The References header will have the most recent message-id // (parent) on the far right. // @see rfc 1036, section 2.2.5 // @see http://www.jwz.org/doc/threading.html $thread = null; foreach (array_reverse($matches[0]) as $mid) { //Try to determine if it's a reply to a tagged email. $ref = null; if (strpos($mid, '+')) { list($left, $right) = explode('@', $mid); list($left, $ref) = explode('+', $left); $mid = "{$left}@{$right}"; } $res = db_query(sprintf($search, db_input($mid))); while (list($id) = db_fetch_row($res)) { if (!($t = ThreadEntry::lookup($id))) { continue; } // Capture the first match thread item if (!$thread) { $thread = $t; } // We found a match - see if we can ID the user. // XXX: Check access of ref is enough? if ($ref && ($uid = $t->getUIDFromEmailReference($ref))) { if ($ref[0] == 's') { //staff $mailinfo['staffId'] = $uid; } else { // user or collaborator. $mailinfo['userId'] = $uid; } // Best possible case — found the thread and the // user return $t; } } } // Second best case — found a thread but couldn't identify the // user from the header. Return the first thread entry matched if ($thread) { return $thread; } } // Search for ticket by the [#123456] in the subject line // This is the last resort - emails must match to avoid message // injection by third-party. $subject = $mailinfo['subject']; $match = array(); if ($subject && $mailinfo['email'] && preg_match("/\\b#(\\S+)/u", $subject, $match) && ($ticket = Ticket::lookupByNumber($match[1])) && ($user = User::lookup(array('emails__address' => $mailinfo['email'])))) { //We have a valid ticket and user if ($ticket->getUserId() == $user->getId() || ($c = Collaborator::lookup(array('userId' => $user->getId(), 'ticketId' => $ticket->getId())))) { $mailinfo['userId'] = $user->getId(); return $ticket->getLastMessage(); } } // Search for the message-id token in the body if (preg_match('`(?:data-mid="|Ref-Mid: )([^"\\s]*)(?:$|")`', $mailinfo['message'], $match)) { if ($thread = ThreadEntry::lookupByRefMessageId($match[1], $mailinfo['email'])) { return $thread; } } return null; }