-
Jared Hancock authored
Previously, form sections were grouped into form sets for reusability. This patch drops the form sets and makes form sections the new "forms". Eventually a section-header field will be added that technically does not add any dynamic data to the form, but allows for the same feature as having a form set with multiple sections.
Jared Hancock authoredPreviously, form sections were grouped into form sets for reusability. This patch drops the form sets and makes form sections the new "forms". Eventually a section-header field will be added that technically does not add any dynamic data to the form, but allows for the same feature as having a form set with multiple sections.
api.tickets.php 6.71 KiB
<?php
include_once INCLUDE_DIR.'class.api.php';
include_once INCLUDE_DIR.'class.ticket.php';
class TicketApiController extends ApiController {
# Supported arguments -- anything else is an error. These items will be
# inspected _after_ the fixup() method of the ApiXxxDataParser classes
# so that all supported input formats should be supported
function getRequestStructure($format, $data=null) {
$supported = array(
"alert", "autorespond", "source", "topicId",
"attachments" => array("*" =>
array("name", "type", "data", "encoding")
),
"message", "ip", "priorityId"
);
# Fetch dynamic form field names for the given help topic and add
# the names to the supported request structure
if (isset($data['topicId'])) {
$topic=Topic::lookup($data['topicId']);
$form=DynamicForm::lookup($topic->ht['form_id']);
foreach ($form->getFields() as $field)
$supported[] = $field->get('name');
}
if(!strcasecmp($format, 'email')) {
$supported = array_merge($supported, array('header', 'mid',
'emailId', 'ticketId', 'reply-to', 'reply-to-name',
'in-reply-to', 'references'));
$supported['attachments']['*'][] = 'cid';
}
return $supported;
}
/*
Validate data - overwrites parent's validator for additional validations.
*/
function validate(&$data, $format) {
global $ost;
//Call parent to Validate the structure
if(!parent::validate($data, $format))
$this->exerr(400, 'Unexpected or invalid data received');
//Nuke attachments IF API files are not allowed.
if(!$ost->getConfig()->allowAPIAttachments())
$data['attachments'] = array();
//Validate attachments: Do error checking... soft fail - set the error and pass on the request.
if($data['attachments'] && is_array($data['attachments'])) {
foreach($data['attachments'] as &$attachment) {
if(!$ost->isFileTypeAllowed($attachment))
$attachment['error'] = 'Invalid file type (ext) for '.Format::htmlchars($attachment['name']);
elseif ($attachment['encoding'] && !strcasecmp($attachment['encoding'], 'base64')) {
if(!($attachment['data'] = base64_decode($attachment['data'], true)))
$attachment['error'] = sprintf('%s: Poorly encoded base64 data', Format::htmlchars($attachment['name']));
}
}
unset($attachment);
}
return true;
}
function create($format) {
if(!($key=$this->requireApiKey()) || !$key->canCreateTickets())
return $this->exerr(401, 'API key not authorized');
$ticket = null;
if(!strcasecmp($format, 'email')) {
# Handle remote piped emails - could be a reply...etc.
$ticket = $this->processEmail();
} else {
# Parse request body
$ticket = $this->createTicket($this->getRequest($format));
}
if(!$ticket)
return $this->exerr(500, "Unable to create new ticket: unknown error");
$this->response(201, $ticket->getExtId());
}
/* private helper functions */
function createTicket($data) {
# Pull off some meta-data
$alert = $data['alert'] ? $data['alert'] : true;
$autorespond = $data['autorespond'] ? $data['autorespond'] : true;
$data['source'] = $data['source'] ? $data['source'] : 'API';
# Create the ticket with the data (attempt to anyway)
$errors = array();
$topic=Topic::lookup($data['topicId']);
$form=DynamicForm::lookup($topic->ht['form_id'])->instanciate();
# Collect name, email address, and subject for banning and such
foreach ($form->getFields() as $field) {
$fname = $field->get('name');
if ($fname && isset($data[$fname]))
$field->value = $data[$fname];
}
if (!$form->isValid())
$errors = array_merge($errors, $form->errors());
$ticket = Ticket::create($data, $errors, $data['source'], $autorespond, $alert);
# Return errors (?)
if (count($errors)) {
if(isset($errors['errno']) && $errors['errno'] == 403)
return $this->exerr(403, 'Ticket denied');
else
return $this->exerr(
400,
"Unable to create new ticket: validation errors:\n"
.Format::array_implode(": ", "\n", $errors)
);
} elseif (!$ticket) {
return $this->exerr(500, "Unable to create new ticket: unknown error");
}
# Save dynamic forms
foreach ($forms as $f) {
$f->set('ticket_id', $ticket->getId());
$f->save();
}
return $ticket;
}
function processEmail() {
$data = $this->getEmailRequest();
if($data['ticketId'] && ($ticket=Ticket::lookup($data['ticketId']))) {
if(($msgid=$ticket->postMessage($data, 'Email')))
return $ticket;
}
if (($thread = ThreadEntry::lookupByEmailHeaders($data))
&& $thread->postEmail($data)) {
return $thread->getTicket();
}
return $this->createTicket($data);
}
}
//Local email piping controller - no API key required!
class PipeApiController extends TicketApiController {
//Overwrite grandparent's (ApiController) response method.
function response($code, $resp) {
//Use postfix exit codes - instead of HTTP
switch($code) {
case 201: //Success
$exitcode = 0;
break;
case 400:
$exitcode = 66;
break;
case 401: /* permission denied */
case 403:
$exitcode = 77;
break;
case 415:
case 416:
case 417:
case 501:
$exitcode = 65;
break;
case 503:
$exitcode = 69;
break;
case 500: //Server error.
default: //Temp (unknown) failure - retry
$exitcode = 75;
}
//echo "$code ($exitcode):$resp";
//We're simply exiting - MTA will take care of the rest based on exit code!
exit($exitcode);
}
function process() {
$pipe = new PipeApiController();
if(($ticket=$pipe->processEmail()))
return $pipe->response(201, $ticket->getNumber());
return $pipe->exerr(416, 'Request failed - retry again!');
}
}
?>