blob: f12a1298390b2ccb31791ecb9a3121cd11d2d051 [file] [log] [blame]
<?php
/*******************************************************************************
* Copyright (c) 2013 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 $user;
var $password;
var $cookies = array();
/**
* 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>/';
function __construct($host, $user, $password) {
$this->host = $host;
$this->user = $user;
$this->password = $password;
}
function isLoggedIn() {
// TODO Is this enough?
return count($this->cookies);
}
function login() {
if ($this->isLoggedIn()) return $this;
$query = new BugzillaPostQuery($this, "index.cgi");
$query
->setData('Bugzilla_login', $this->user)
->setData('Bugzilla_password', $this->password)
->setData('GoAheadAndLogIn', '1')
->execute();
$contents = $query->getContents();
/*
* 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, $contents)) {
$message = 'Unknown error';
if (preg_match('/<title>(.*)<\/title>/', $contents, $matches)) {
$message = $matches[1];
}
throw new BugzillaInvalidCredentialsException($message);
}
return $this;
}
function logout() {
// TODO Should we throw an exception?
if (!$this->isLoggedIn()) return;
$query = new BugzillaGetQuery($this, "index.cgi");
$query
->setData('logout', '1')
->execute();
unset($this->cookies);
}
function startBug($product, $component, $summary) {
$this->login();
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() {
$query = new BugzillaGetQuery($this->client, 'enter_bug.cgi');
$query->execute();
//echo($query->getContents());
if (!preg_match('/<input type="hidden" name="token" value="(\w+)">/', $query->getContents(), $matches))
throw new Exception('Can\'t get token.');
$token = $matches[1];
$query = new BugzillaPostQuery($this->client, 'post_bug.cgi');
$query
->setData('form_name', 'enter_bug')
->setData('token', $token)
->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('/Bug (\d)+ Submitted/', $query->getContents(), $matches)) {
return new Bug($matches[1]);
}
$message = 'Unknown error';
if (preg_match('/<title>(.*)<\/title>/ims', $query->getContents(), $matches)) {
$message = preg_replace('/\s+/', ' ', $matches[1]);
}
// TODO Make custom exception
throw new Exception($message);
}
}
class Bug {
var $id;
function __construct($id) {
$this->id = $id;
}
function getId() {
return $this->id;
}
}
/**
* Instances of this class are used to build and execute a POST query
* against Bugzilla. As a side-effect of the execution, any cookies
* that found in the response header are applied to the BugzillaClient
* instance and used in any subsequent calls.
* @internal
*/
class BugzillaPostQuery {
var $client;
var $resource;
var $parameters = array();
var $contents;
var $headers;
function __construct($client, $resource) {
$this->client = $client;
$this->resource = $resource;
}
function setData($key, $value) {
$this->parameters[] = $key . '=' . urlencode($value);
return $this;
}
/**
* Assemble the request headers. Minimally, we have a content type.
* If the client has cookies, add them as well.
*
* @internal
*/
function getRequestHeaders() {
$headers = array('Content-type: application/x-www-form-urlencoded');
$cookies = array();
foreach($this->client->cookies as $key => $value) $cookies[] = "$key=$value";
if ($cookies) $headers[] = "Cookie: " . join('; ', $cookies);
//return $headers;
return join("\r\n", $headers) . "\r\n";
}
function getParameters() {
return join('&', $this->parameters);
}
function getOptions() {
return array(
'http'=>array(
'method' => 'POST',
'header' => $this->getRequestHeaders(),
'content' => $this->getParameters()
)
);
}
function getUrl() {
return $this->client->host . '/' . $this->resource;
}
function execute() {
$options = $this->getOptions();
$streamcontext = stream_context_create($options);
if (!$in = @fopen($this->getUrl(), 'r', false, $streamcontext)) {
$error = error_get_last();
throw new Exception($error['message']);
}
// TODO handle possible failures.
$this->contents = stream_get_contents($in);
$this->headers = $http_response_header;
fclose($in);
$this->fetchAndStoreCookies($this->headers);
return $this;
}
function getContents() {
return $this->contents;
}
function getHeaders() {
return $this->headers;
}
/**
* Fetch the cookies from the response header and store them directly
* into the client.
*
* @param string[] $header
*/
private function fetchAndStoreCookies($headers) {
$cookies = array();
foreach( $headers as $header ) {
if( preg_match( "/Set-Cookie: ([^=]+)=([^;]+);/", $header, $matches )) {
$this->client->cookies[$matches[1]] = $matches[2];
}
}
}
}
class BugzillaGetQuery extends BugzillaPostQuery {
function getOptions() {
return array(
'http'=>array(
'method' => 'GET',
'header' => $this->getRequestHeaders()
)
);
}
function getUrl() {
return parent::getUrl() . '?' . $this->getParameters();
}
}
?>