Page MenuHomePhorge

No OneTemporary

diff --git a/resources/ircbot/example_config.json b/resources/ircbot/example_config.json
index e909689e13..a310b74bb4 100644
--- a/resources/ircbot/example_config.json
+++ b/resources/ircbot/example_config.json
@@ -1,11 +1,15 @@
{
"server" : "irc.freenode.net",
"port" : 6667,
"nick" : "phabot",
"join" : [
"#phabot-test"
],
"handlers" : [
"PhabricatorIRCProtocolHandler"
- ]
+ ],
+
+ "conduit.uri" : null,
+ "conduit.user" : null,
+ "conduit.cert" : null
}
diff --git a/src/applications/people/controller/edit/PhabricatorPeopleEditController.php b/src/applications/people/controller/edit/PhabricatorPeopleEditController.php
index 97d4290ebe..fba33053cb 100644
--- a/src/applications/people/controller/edit/PhabricatorPeopleEditController.php
+++ b/src/applications/people/controller/edit/PhabricatorPeopleEditController.php
@@ -1,379 +1,426 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PhabricatorPeopleEditController extends PhabricatorPeopleController {
public function shouldRequireAdmin() {
return true;
}
private $id;
private $view;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
$this->view = idx($data, 'view');
}
public function processRequest() {
$request = $this->getRequest();
$admin = $request->getUser();
if ($this->id) {
$user = id(new PhabricatorUser())->load($this->id);
if (!$user) {
return new Aphront404Response();
}
} else {
$user = new PhabricatorUser();
}
$views = array(
'basic' => 'Basic Information',
'password' => 'Reset Password',
'role' => 'Edit Role',
+ 'cert' => 'Conduit Certificate',
);
if (!$user->getID()) {
$view = 'basic';
} else if (isset($views[$this->view])) {
$view = $this->view;
} else {
$view = 'basic';
}
$content = array();
if ($request->getStr('saved')) {
$notice = new AphrontErrorView();
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$notice->setTitle('Changed Saved');
$notice->appendChild('<p>Your changes were saved.</p>');
$content[] = $notice;
}
switch ($view) {
case 'basic':
$response = $this->processBasicRequest($user);
break;
case 'password':
$response = $this->processPasswordRequest($user);
break;
case 'role':
$response = $this->processRoleRequest($user);
break;
+ case 'cert':
+ $response = $this->processCertificateRequest($user);
+ break;
}
if ($response instanceof AphrontResponse) {
return $response;
}
$content[] = $response;
if ($user->getID()) {
$side_nav = new AphrontSideNavView();
$side_nav->appendChild($content);
foreach ($views as $key => $name) {
$side_nav->addNavItem(
phutil_render_tag(
'a',
array(
'href' => '/people/edit/'.$user->getID().'/'.$key.'/',
'class' => ($key == $view)
? 'aphront-side-nav-selected'
: null,
),
phutil_escape_html($name)));
}
$content = $side_nav;
}
return $this->buildStandardPageResponse(
$content,
array(
'title' => 'Edit User',
));
}
private function processBasicRequest(PhabricatorUser $user) {
$request = $this->getRequest();
$admin = $request->getUser();
$e_username = true;
$e_realname = true;
$e_email = true;
$errors = array();
$request = $this->getRequest();
if ($request->isFormPost()) {
if (!$user->getID()) {
$user->setUsername($request->getStr('username'));
}
$user->setRealName($request->getStr('realname'));
$user->setEmail($request->getStr('email'));
if (!strlen($user->getUsername())) {
$errors[] = "Username is required.";
$e_username = 'Required';
} else if (!preg_match('/^[a-z0-9]+$/', $user->getUsername())) {
$errors[] = "Username must consist of only numbers and letters.";
$e_username = 'Invalid';
} else {
$e_username = null;
}
if (!strlen($user->getRealName())) {
$errors[] = 'Real name is required.';
$e_realname = 'Required';
} else {
$e_realname = null;
}
if (!strlen($user->getEmail())) {
$errors[] = 'Email is required.';
$e_email = 'Required';
} else {
$e_email = null;
}
if (!$errors) {
try {
$user->save();
$response = id(new AphrontRedirectResponse())
->setURI('/people/edit/'.$user->getID().'/?saved=true');
return $response;
} catch (AphrontQueryDuplicateKeyException $ex) {
$errors[] = 'Username and email must be unique.';
$same_username = id(new PhabricatorUser())
->loadOneWhere('username = %s', $user->getUsername());
$same_email = id(new PhabricatorUser())
->loadOneWhere('email = %s', $user->getEmail());
if ($same_username) {
$e_username = 'Duplicate';
}
if ($same_email) {
$e_email = 'Duplicate';
}
}
}
}
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle('Form Errors')
->setErrors($errors);
}
$form = new AphrontFormView();
$form->setUser($admin);
if ($user->getID()) {
$form->setAction('/people/edit/'.$user->getID().'/');
} else {
$form->setAction('/people/edit/');
}
if ($user->getID()) {
$is_immutable = true;
} else {
$is_immutable = false;
}
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Username')
->setName('username')
->setValue($user->getUsername())
->setError($e_username)
->setDisabled($is_immutable)
->setCaption('Usernames are permanent and can not be changed later!'))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Real Name')
->setName('realname')
->setValue($user->getRealName())
->setError($e_realname))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Email')
->setName('email')
->setValue($user->getEmail())
->setError($e_email))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save')
->addCancelButton('/people/'));
$panel = new AphrontPanelView();
if ($user->getID()) {
$panel->setHeader('Edit User');
} else {
$panel->setHeader('Create New User');
}
$panel->appendChild($form);
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
return array($error_view, $panel);
}
private function processPasswordRequest(PhabricatorUser $user) {
$request = $this->getRequest();
$admin = $request->getUser();
$e_password = true;
$errors = array();
if ($request->isFormPost()) {
if (strlen($request->getStr('password'))) {
$user->setPassword($request->getStr('password'));
$e_password = null;
} else {
$errors[] = 'Password is required.';
$e_password = 'Required';
}
if (!$errors) {
$user->save();
return id(new AphrontRedirectResponse())
->setURI($request->getRequestURI()->alter('saved', 'true'));
}
}
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle('Form Errors')
->setErrors($errors);
}
$form = id(new AphrontFormView())
->setUser($admin)
->setAction($request->getRequestURI()->alter('saved', null))
->appendChild(
'<p class="aphront-form-instructions">Submitting this form will '.
'change this user\'s password. They will no longer be able to login '.
'with their old password.</p>')
->appendChild(
id(new AphrontFormTextControl())
->setLabel('New Password')
->setName('password')
->setError($e_password))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Reset Password'));
$panel = new AphrontPanelView();
$panel->setHeader('Reset Password');
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$panel->appendChild($form);
return array($error_view, $panel);
}
private function processRoleRequest(PhabricatorUser $user) {
$request = $this->getRequest();
$admin = $request->getUser();
$is_self = ($user->getID() == $admin->getID());
$errors = array();
if ($request->isFormPost()) {
if ($is_self) {
$errors[] = "You can not edit your own role.";
} else {
$user->setIsAdmin($request->getInt('is_admin'));
$user->setIsDisabled($request->getInt('is_disabled'));
$user->setIsSystemAgent($request->getInt('is_agent'));
}
if (!$errors) {
$user->save();
return id(new AphrontRedirectResponse())
->setURI($request->getRequestURI()->alter('saved', 'true'));
}
}
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle('Form Errors')
->setErrors($errors);
}
$form = id(new AphrontFormView())
->setUser($admin)
->setAction($request->getRequestURI()->alter('saved', null));
if ($is_self) {
$form->appendChild(
'<p class="aphront-form-instructions">NOTE: You can not edit your own '.
'role.</p>');
}
$form
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'is_admin',
1,
'Admin: wields absolute power.',
$user->getIsAdmin())
->setDisabled($is_self))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'is_disabled',
1,
'Disabled: can not login.',
$user->getIsDisabled())
->setDisabled($is_self))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'is_agent',
1,
'Agent: system agent (robot).',
$user->getIsSystemAgent())
->setDisabled($is_self));
if (!$is_self) {
$form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Edit Role'));
}
$panel = new AphrontPanelView();
$panel->setHeader('Edit Role');
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
$panel->appendChild($form);
return array($error_view, $panel);
}
+ private function processCertificateRequest($user) {
+ $request = $this->getRequest();
+ $admin = $request->getUser();
+
+
+ $form = new AphrontFormView();
+ $form
+ ->setUser($admin)
+ ->setAction($request->getRequestURI())
+ ->appendChild(
+ '<p class="aphront-form-instructions">You can use this certificate '.
+ 'to write scripts or bots which interface with Phabricator over '.
+ 'Conduit.</p>');
+
+ if ($user->getIsSystemAgent()) {
+ $form
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setLabel('Username')
+ ->setValue($user->getUsername()))
+ ->appendChild(
+ id(new AphrontFormTextAreaControl())
+ ->setLabel('Certificate')
+ ->setValue($user->getConduitCertificate()));
+ } else {
+ $form->appendChild(
+ id(new AphrontFormStaticControl())
+ ->setLabel('Certificate')
+ ->setValue(
+ 'You may only view the certificates for System Agents. Mark '.
+ 'this account as a "system agent" in the "Edit Role" tab to '.
+ 'view the corresponding certificate.'));
+ }
+
+ $panel = new AphrontPanelView();
+ $panel->setHeader('Conduit Certificate');
+ $panel->setWidth(AphrontPanelView::WIDTH_FORM);
+
+ $panel->appendChild($form);
+
+ return array($panel);
+ }
+
}
diff --git a/src/applications/people/controller/edit/__init__.php b/src/applications/people/controller/edit/__init__.php
index b689acf53d..742bbef230 100644
--- a/src/applications/people/controller/edit/__init__.php
+++ b/src/applications/people/controller/edit/__init__.php
@@ -1,25 +1,27 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/people/controller/base');
phutil_require_module('phabricator', 'applications/people/storage/user');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/checkbox');
+phutil_require_module('phabricator', 'view/form/control/static');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/form/control/text');
+phutil_require_module('phabricator', 'view/form/control/textarea');
phutil_require_module('phabricator', 'view/form/error');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/layout/sidenav');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorPeopleEditController.php');
diff --git a/src/infrastructure/daemon/irc/bot/PhabricatorIRCBot.php b/src/infrastructure/daemon/irc/bot/PhabricatorIRCBot.php
index 8d5db14d92..605ead6ebc 100644
--- a/src/infrastructure/daemon/irc/bot/PhabricatorIRCBot.php
+++ b/src/infrastructure/daemon/irc/bot/PhabricatorIRCBot.php
@@ -1,193 +1,227 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Simple IRC bot which runs as a Phabricator daemon. Although this bot is
* somewhat useful, it is also intended to serve as a demo of how to write
* "system agents" which communicate with Phabricator over Conduit, so you can
* script system interactions and integrate with other systems.
*
* NOTE: This is super janky and experimental right now.
*
* @group irc
*/
final class PhabricatorIRCBot extends PhabricatorDaemon {
private $socket;
private $handlers;
private $writeBuffer;
private $readBuffer;
+ private $conduit;
+
public function run() {
$argv = $this->getArgv();
if (count($argv) !== 1) {
throw new Exception("usage: PhabricatorIRCBot <json_config_file>");
}
$json_raw = Filesystem::readFile($argv[0]);
$config = json_decode($json_raw, true);
if (!is_array($config)) {
throw new Exception("File '{$argv[0]}' is not valid JSON!");
}
$server = idx($config, 'server');
$port = idx($config, 'port', 6667);
$join = idx($config, 'join', array());
$handlers = idx($config, 'handlers', array());
$nick = idx($config, 'nick', 'phabot');
if (!preg_match('/^[A-Za-z0-9_]+$/', $nick)) {
throw new Exception(
"Nickname '{$nick}' is invalid, must be alphanumeric!");
}
if (!$join) {
throw new Exception("No channels to 'join' in config!");
}
foreach ($handlers as $handler) {
$obj = newv($handler, array($this));
$this->handlers[] = $obj;
}
+ $conduit_uri = idx($config, 'conduit.uri');
+ if ($conduit_uri) {
+ $conduit_user = idx($config, 'conduit.user');
+ $conduit_cert = idx($config, 'conduit.cert');
+
+ $conduit = new ConduitClient($conduit_uri);
+ $response = $conduit->callMethodSynchronous(
+ 'conduit.connect',
+ array(
+ 'client' => 'PhabricatorIRCBot',
+ 'clientVersion' => '1.0',
+ 'clientDescription' => php_uname('n').':'.$nick,
+ 'user' => $conduit_user,
+ 'certificate' => $conduit_cert,
+ ));
+
+ $this->conduit = $conduit;
+ }
+
$errno = null;
$error = null;
$socket = fsockopen($server, $port, $errno, $error);
if (!$socket) {
throw new Exception("Failed to connect, #{$errno}: {$error}");
}
$ok = stream_set_blocking($socket, false);
if (!$ok) {
throw new Exception("Failed to set stream nonblocking.");
}
$this->socket = $socket;
$this->writeCommand('USER', "{$nick} 0 * :{$nick}");
$this->writeCommand('NICK', "{$nick}");
foreach ($join as $channel) {
$this->writeCommand('JOIN', "{$channel}");
}
$this->runSelectLoop();
}
private function runSelectLoop() {
do {
$read = array($this->socket);
if (strlen($this->writeBuffer)) {
$write = array($this->socket);
} else {
$write = array();
}
$except = array();
$ok = @stream_select($read, $write, $except, $timeout_sec = 1);
if ($ok === false) {
throw new Exception(
"socket_select() failed: ".socket_strerror(socket_last_error()));
}
if ($read) {
do {
$data = fread($this->socket, 4096);
if ($data === false) {
throw new Exception("fread() failed!");
} else {
$this->debugLog(true, $data);
$this->readBuffer .= $data;
}
} while (strlen($data));
}
if ($write) {
do {
$len = fwrite($this->socket, $this->writeBuffer);
if ($len === false) {
throw new Exception("fwrite() failed!");
} else {
$this->debugLog(false, substr($this->writeBuffer, 0, $len));
$this->writeBuffer = substr($this->writeBuffer, $len);
}
} while (strlen($this->writeBuffer));
}
- $this->processReadBuffer();
+ do {
+ $routed_message = $this->processReadBuffer();
+ } while ($routed_message);
} while (true);
}
private function write($message) {
$this->writeBuffer .= $message;
return $this;
}
public function writeCommand($command, $message) {
return $this->write($command.' '.$message."\r\n");
}
private function processReadBuffer() {
$until = strpos($this->readBuffer, "\r\n");
if ($until === false) {
- return;
+ return false;
}
$message = substr($this->readBuffer, 0, $until);
$this->readBuffer = substr($this->readBuffer, $until + 2);
$pattern =
'/^'.
'(?:(?P<sender>:(\S+)) )?'. // This may not be present.
'(?P<command>[A-Z0-9]+) '.
'(?P<data>.*)'.
'$/';
$matches = null;
if (!preg_match($pattern, $message, $matches)) {
throw new Exception("Unexpected message from server: {$message}");
}
$irc_message = new PhabricatorIRCMessage(
idx($matches, 'sender'),
$matches['command'],
$matches['data']);
$this->routeMessage($irc_message);
+
+ return true;
}
private function routeMessage(PhabricatorIRCMessage $message) {
foreach ($this->handlers as $handler) {
$handler->receiveMessage($message);
}
}
public function __destroy() {
$this->write("QUIT Goodbye.\r\n");
fclose($this->socket);
}
private function debugLog($is_read, $message) {
echo $is_read ? '<<< ' : '>>> ';
echo addcslashes($message, "\0..\37\177..\377");
echo "\n";
}
+ public function getConduit() {
+ if (empty($this->conduit)) {
+ throw new Exception(
+ "This bot is not configured with a Conduit uplink. Set 'conduit.uri', ".
+ "'conduit.user' and 'conduit.cert' in the configuration to connect.");
+ }
+ return $this->conduit;
+ }
+
}
diff --git a/src/infrastructure/daemon/irc/bot/__init__.php b/src/infrastructure/daemon/irc/bot/__init__.php
index 2bad43e7d8..9c84970a76 100644
--- a/src/infrastructure/daemon/irc/bot/__init__.php
+++ b/src/infrastructure/daemon/irc/bot/__init__.php
@@ -1,16 +1,17 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'infrastructure/daemon/base');
phutil_require_module('phabricator', 'infrastructure/daemon/irc/message');
+phutil_require_module('phutil', 'conduit/client');
phutil_require_module('phutil', 'filesystem');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorIRCBot.php');
diff --git a/src/infrastructure/daemon/irc/handler/base/PhabricatorIRCHandler.php b/src/infrastructure/daemon/irc/handler/base/PhabricatorIRCHandler.php
index 79d81302c1..755824273e 100644
--- a/src/infrastructure/daemon/irc/handler/base/PhabricatorIRCHandler.php
+++ b/src/infrastructure/daemon/irc/handler/base/PhabricatorIRCHandler.php
@@ -1,40 +1,44 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Responds to IRC messages. You plug a bunch of these into a
* @{class:PhabricatorIRCBot} to give it special behavior.
*
* @group irc
*/
abstract class PhabricatorIRCHandler {
private $bot;
final public function __construct(PhabricatorIRCBot $irc_bot) {
$this->bot = $irc_bot;
}
final protected function write($command, $message) {
$this->bot->writeCommand($command, $message);
return $this;
}
+ final protected function getConduit() {
+ return $this->bot->getConduit();
+ }
+
abstract public function receiveMessage(PhabricatorIRCMessage $message);
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 20:27 (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1128438
Default Alt Text
(23 KB)

Event Timeline