blob: ddc5d955fd46dc493abdb5f2f2948196b29ed16b [file] [log] [blame]
<?php
/*******************************************************************************
* Copyright (c) 2013, 2014 Eclipse Foundation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wayne Beaton (Eclipse Foundation)- initial API and implementation
*******************************************************************************/
class BugzillaClient {
var $host;
var $cookieFile;
var $loggedIn = false;
/**
* This is the pattern that we look for to verify that the
* attempt to login as succeeded. Since we're parsing web pages,
* we look for an occurance of "Log out" (assuming that we can
* only log out if we have previously logged in).
*
* @var string
*/
var $loggedInPattern = '/<a href="index.cgi\?logout=1">Log(&nbsp;| )out<\/a>/';
var $bugCreatedPattern = '/Bug (\d+)+ Submitted/';
var $attachmentCreatedPattern = '/Attachment (\d+) added to Bug (\d+)/';
function __construct($host) {
$this->host = $host;
$this->cookieFile = tempnam(null, 'bugzilla');
}
function isLoggedIn() {
return $this->loggedIn;
}
function login($user, $password) {
if ($this->isLoggedIn()) return;
$query = new BugzillaPostQuery($this, "index.cgi");
$result = $query
->setData('Bugzilla_login', $user)
->setData('Bugzilla_password', $password)
->setData('GoAheadAndLogIn', '1')
->execute();
/*
* If the value that we get back from the server contains the
* "loggedInPattern", then we were successful. Otherwise, obviously,
* we were not successful. In this event, extract the error message
* from the title and pass it back as the message in an exception.
*/
if(!preg_match($this->loggedInPattern, $result->contents)) {
if (preg_match('/<title>(.*)<\/title>/', $result->contents, $matches)) {
$message = $matches[1];
} else $message = 'Unknown error';
throw new BugzillaInvalidCredentialsException($message);
}
$this->loggedIn = true;
return $this;
}
function logout() {
// TODO Should we throw an exception?
if (!$this->isLoggedIn()) return;
$query = new BugzillaPostQuery($this, "index.cgi");
$query
->setData('logout', '1')
->execute();
$this->loggedIn = false;
}
function startBug($product, $component, $summary) {
return new BugBuilder($this, $product, $component, $summary);
}
}
class BugzillaInvalidCredentialsException extends Exception {}
class BugBuilder {
var $client;
var $product;
var $component;
var $summary;
var $body = array();
var $cc = array();
function __construct($client, $product, $component, $summary) {
$this->client = $client;
$this->product = $product;
$this->component = $component;
$this->summary = $summary;
}
function addParagraph($paragraph) {
$this->body[] = $paragraph;
return $this;
}
function addCC($email) {
$this->cc[] = $email;
return $this;
}
function create() {
// Recent versions of Bugzilla require a token when creating
// a bug. Attempt to get the token.
// FIXME Only do this if the Bugzilla version requires it.
$query = new BugzillaGetQuery($this->client, 'enter_bug.cgi');
$queryFormResult = $query->execute();
$query = new BugzillaPostQuery($this->client, 'post_bug.cgi');
$result = $query
->setData('form_name', 'enter_bug')
->setData('token', $queryFormResult->getToken())
->setData('product', $this->product)
->setData('component', $this->component)
->setData('cc', join(',', $this->cc))
->setData('short_desc', $this->summary)
->setData('comment', join('\n\n', $this->body))
->setData('op_sys', 'All')
->setData('rep_platform', 'All')
->setData('bug_severity', 'normal')
->setData('priority', 'Normal')
->setData('version', 'unspecified')
->execute();
if (preg_match($this->client->bugCreatedPattern, $result->contents, $matches)) {
return new Bug($this->client, $matches[1]);
}
if (preg_match('/<title>(.*)<\/title>/ims', $result->contents, $matches)) {
$message = preg_replace('/\s+/', ' ', $matches[1]);
} else $message = 'Unknown error';
// TODO Make custom exception
throw new Exception($message);
}
}
class AttachmentBuilder {
var $bug;
var $file;
var $description = 'A file';
function __construct($bug) {
$this->bug = $bug;
}
function setFile($file) {
$this->file = $file;
return $this;
}
function setDescription($description) {
$this->description = $description;
return $this;
}
function create() {
$query = new BugzillaGetQuery($this->bug->client, 'attachment.cgi');
$queryFormResult = $query
->setData('bugid', $this->bug->id)
->setData('action', 'enter')
->execute();
$query = new BugzillaPostQuery($this->bug->client, 'attachment.cgi');
$result = $query
->setData('form_name', 'enter_bug')
->setData('token', $queryFormResult->getToken())
->setData('bugid', $this->bug->id)
->setData('action', 'insert')
->setData('data', "@$this->file")
->setData('description', $this->description)
->setData('contenttypemethod', 'autodetect')
->execute();
if (preg_match($this->bug->client->attachmentCreatedPattern, $result->contents, $matches)) {
return new Attachment($this->bug, $matches[1]);
}
if (preg_match('/<title>(.*)<\/title>/ims', $result->contents, $matches)) {
$message = preg_replace('/\s+/', ' ', $matches[1]);
} else $message = 'Unknown error';
// TODO Make custom exception
throw new Exception($message);
}
}
class Bug {
var $client;
var $id;
function __construct($client, $id) {
$this->client = $client;
$this->id = $id;
}
function getId() {
return $this->id;
}
function startAttachment() {
return new AttachmentBuilder($this);
}
}
class Attachment {
var $bug;
var $id;
function __construct($bug, $id) {
$this->bug = $bug;
$this->id = $id;
}
}
/**
* Instances of this class are used to build and execute a POST query
* against Bugzilla.
*
* @internal
*/
class BugzillaPostQuery {
var $client;
var $resource;
var $parameters = array();
function __construct($client, $resource) {
$this->client = $client;
$this->resource = $resource;
}
function setData($key, $value) {
$this->parameters[$key] = $value;
return $this;
}
function getUrl() {
return $this->client->host . '/' . $this->resource;
}
function getDefaultOptions() {
return array(
CURLOPT_USERAGENT => "Eclipse",
CURLOPT_COOKIEFILE => $this->client->cookieFile,
CURLOPT_COOKIEJAR => $this->client->cookieFile,
// CURLOPT_VERBOSE => 1,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $this->getUrl()
);
}
function getOptions() {
$options = $this->getDefaultOptions();
$options[CURLOPT_POST] = TRUE;
$options[CURLOPT_POSTFIELDS] = $this->parameters;
return $options;
}
function execute() {
$curl = curl_init($this->client->host);
curl_setopt_array($curl, $this->getOptions());
$contents = curl_exec($curl);
curl_close($curl);
return new QueryResult($contents);
}
}
class QueryResult {
var $contents;
function __construct($contents) {
$this->contents = $contents;
}
function getToken() {
if (preg_match('/<input type="hidden" name="token" value="(\w+)">/', $this->contents, $matches))
return $matches[1];
return null;
}
}
class BugzillaGetQuery extends BugzillaPostQuery {
function getUrl() {
return parent::getUrl() . '?' . $this->getParameters();
}
function getOptions() {
$options = $this->getDefaultOptions();
$options[CURLOPT_HTTPGET] = TRUE;
return $options;
}
function getParameters() {
$parameters = array();
foreach($this->parameters as $key => $value) $parameters[] = "$key=$value";
return join('&', $parameters);
}
}
?>