| <?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( | )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(); |
| } |
| } |
| |
| ?> |