Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php
index bc200e7078..a4d5fdd568 100644
--- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php
+++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php
@@ -1,755 +1,755 @@
<?php
final class PhabricatorConduitAPIController
extends PhabricatorConduitController {
public function shouldRequireLogin() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$method = $request->getURIData('method');
$time_start = microtime(true);
$api_request = null;
$method_implementation = null;
$log = new PhabricatorConduitMethodCallLog();
$log->setMethod($method);
$metadata = array();
$multimeter = MultimeterControl::getInstance();
if ($multimeter) {
$multimeter->setEventContext('api.'.$method);
}
try {
list($metadata, $params, $strictly_typed) = $this->decodeConduitParams(
$request,
$method);
$call = new ConduitCall($method, $params, $strictly_typed);
$method_implementation = $call->getMethodImplementation();
$result = null;
// TODO: The relationship between ConduitAPIRequest and ConduitCall is a
// little odd here and could probably be improved. Specifically, the
// APIRequest is a sub-object of the Call, which does not parallel the
// role of AphrontRequest (which is an indepenent object).
// In particular, the setUser() and getUser() existing independently on
// the Call and APIRequest is very awkward.
$api_request = $call->getAPIRequest();
$allow_unguarded_writes = false;
$auth_error = null;
$conduit_username = '-';
if ($call->shouldRequireAuthentication()) {
$auth_error = $this->authenticateUser($api_request, $metadata, $method);
// If we've explicitly authenticated the user here and either done
// CSRF validation or are using a non-web authentication mechanism.
$allow_unguarded_writes = true;
if ($auth_error === null) {
$conduit_user = $api_request->getUser();
if ($conduit_user && $conduit_user->getPHID()) {
$conduit_username = $conduit_user->getUsername();
}
$call->setUser($api_request->getUser());
}
}
$access_log = PhabricatorAccessLog::getLog();
if ($access_log) {
$access_log->setData(
array(
'u' => $conduit_username,
'm' => $method,
));
}
if ($call->shouldAllowUnguardedWrites()) {
$allow_unguarded_writes = true;
}
if ($auth_error === null) {
if ($allow_unguarded_writes) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
}
try {
$result = $call->execute();
$error_code = null;
$error_info = null;
} catch (ConduitException $ex) {
$result = null;
$error_code = $ex->getMessage();
if ($ex->getErrorDescription()) {
$error_info = $ex->getErrorDescription();
} else {
$error_info = $call->getErrorDescription($error_code);
}
}
if ($allow_unguarded_writes) {
unset($unguarded);
}
} else {
list($error_code, $error_info) = $auth_error;
}
} catch (Exception $ex) {
$result = null;
if ($ex instanceof ConduitException) {
$error_code = 'ERR-CONDUIT-CALL';
} else {
$error_code = 'ERR-CONDUIT-CORE';
// See T13581. When a Conduit method raises an uncaught exception
// other than a "ConduitException", log it.
phlog($ex);
}
$error_info = $ex->getMessage();
}
$log
->setCallerPHID(
isset($conduit_user)
? $conduit_user->getPHID()
: null)
->setError((string)$error_code)
->setDuration(phutil_microseconds_since($time_start));
if (!PhabricatorEnv::isReadOnly()) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$log->save();
unset($unguarded);
}
$response = id(new ConduitAPIResponse())
->setResult($result)
->setErrorCode($error_code)
->setErrorInfo($error_info);
switch ($request->getStr('output')) {
case 'human':
return $this->buildHumanReadableResponse(
$method,
$api_request,
$response->toDictionary(),
$method_implementation);
case 'json':
default:
$response = id(new AphrontJSONResponse())
->setAddJSONShield(false)
->setContent($response->toDictionary());
$capabilities = $this->getConduitCapabilities();
if ($capabilities) {
$capabilities = implode(' ', $capabilities);
$response->addHeader('X-Conduit-Capabilities', $capabilities);
}
return $response;
}
}
/**
* Authenticate the client making the request to a Phabricator user account.
*
* @param ConduitAPIRequest Request being executed.
* @param dict Request metadata.
* @return null|pair Null to indicate successful authentication, or
* an error code and error message pair.
*/
private function authenticateUser(
ConduitAPIRequest $api_request,
array $metadata,
$method) {
$request = $this->getRequest();
if ($request->getUser()->getPHID()) {
$request->validateCSRF();
return $this->validateAuthenticatedUser(
$api_request,
$request->getUser());
}
$auth_type = idx($metadata, 'auth.type');
if ($auth_type === ConduitClient::AUTH_ASYMMETRIC) {
$host = idx($metadata, 'auth.host');
if (!$host) {
return array(
'ERR-INVALID-AUTH',
pht(
'Request is missing required "%s" parameter.',
'auth.host'),
);
}
// TODO: Validate that we are the host!
$raw_key = idx($metadata, 'auth.key');
$public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_key);
$ssl_public_key = $public_key->toPKCS8();
// First, verify the signature.
try {
$protocol_data = $metadata;
ConduitClient::verifySignature(
$method,
$api_request->getAllParameters(),
$protocol_data,
$ssl_public_key);
} catch (Exception $ex) {
return array(
'ERR-INVALID-AUTH',
pht(
'Signature verification failure. %s',
$ex->getMessage()),
);
}
// If the signature is valid, find the user or device which is
// associated with this public key.
$stored_key = id(new PhabricatorAuthSSHKeyQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withKeys(array($public_key))
->withIsActive(true)
->executeOne();
if (!$stored_key) {
$key_summary = id(new PhutilUTF8StringTruncator())
->setMaximumBytes(64)
->truncateString($raw_key);
return array(
'ERR-INVALID-AUTH',
pht(
'No user or device is associated with the public key "%s".',
$key_summary),
);
}
$object = $stored_key->getObject();
if ($object instanceof PhabricatorUser) {
$user = $object;
} else {
if ($object->isDisabled()) {
return array(
'ERR-INVALID-AUTH',
pht(
'The key which signed this request is associated with a '.
'disabled device ("%s").',
$object->getName()),
);
}
if (!$stored_key->getIsTrusted()) {
return array(
'ERR-INVALID-AUTH',
pht(
'The key which signed this request is not trusted. Only '.
'trusted keys can be used to sign API calls.'),
);
}
if (!PhabricatorEnv::isClusterRemoteAddress()) {
return array(
'ERR-INVALID-AUTH',
pht(
- 'This request originates from outside of the Phabricator '.
- 'cluster address range. Requests signed with trusted '.
- 'device keys must originate from within the cluster.'),
+ 'This request originates from outside of the cluster address '.
+ 'range. Requests signed with trusted device keys must '.
+ 'originate from within the cluster.'),
);
}
$user = PhabricatorUser::getOmnipotentUser();
// Flag this as an intracluster request.
$api_request->setIsClusterRequest(true);
}
return $this->validateAuthenticatedUser(
$api_request,
$user);
} else if ($auth_type === null) {
// No specified authentication type, continue with other authentication
// methods below.
} else {
return array(
'ERR-INVALID-AUTH',
pht(
'Provided "%s" ("%s") is not recognized.',
'auth.type',
$auth_type),
);
}
$token_string = idx($metadata, 'token');
if (strlen($token_string)) {
if (strlen($token_string) != 32) {
return array(
'ERR-INVALID-AUTH',
pht(
'API token "%s" has the wrong length. API tokens should be '.
'32 characters long.',
$token_string),
);
}
$type = head(explode('-', $token_string));
$valid_types = PhabricatorConduitToken::getAllTokenTypes();
$valid_types = array_fuse($valid_types);
if (empty($valid_types[$type])) {
return array(
'ERR-INVALID-AUTH',
pht(
'API token "%s" has the wrong format. API tokens should be '.
'32 characters long and begin with one of these prefixes: %s.',
$token_string,
implode(', ', $valid_types)),
);
}
$token = id(new PhabricatorConduitTokenQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withTokens(array($token_string))
->withExpired(false)
->executeOne();
if (!$token) {
$token = id(new PhabricatorConduitTokenQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withTokens(array($token_string))
->withExpired(true)
->executeOne();
if ($token) {
return array(
'ERR-INVALID-AUTH',
pht(
'API token "%s" was previously valid, but has expired.',
$token_string),
);
} else {
return array(
'ERR-INVALID-AUTH',
pht(
'API token "%s" is not valid.',
$token_string),
);
}
}
// If this is a "cli-" token, it expires shortly after it is generated
// by default. Once it is actually used, we extend its lifetime and make
// it permanent. This allows stray tokens to get cleaned up automatically
// if they aren't being used.
if ($token->getTokenType() == PhabricatorConduitToken::TYPE_COMMANDLINE) {
if ($token->getExpires()) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$token->setExpires(null);
$token->save();
unset($unguarded);
}
}
// If this is a "clr-" token, Phabricator must be configured in cluster
// mode and the remote address must be a cluster node.
if ($token->getTokenType() == PhabricatorConduitToken::TYPE_CLUSTER) {
if (!PhabricatorEnv::isClusterRemoteAddress()) {
return array(
'ERR-INVALID-AUTH',
pht(
- 'This request originates from outside of the Phabricator '.
- 'cluster address range. Requests signed with cluster API '.
- 'tokens must originate from within the cluster.'),
+ 'This request originates from outside of the cluster address '.
+ 'range. Requests signed with cluster API tokens must '.
+ 'originate from within the cluster.'),
);
}
// Flag this as an intracluster request.
$api_request->setIsClusterRequest(true);
}
$user = $token->getObject();
if (!($user instanceof PhabricatorUser)) {
return array(
'ERR-INVALID-AUTH',
pht('API token is not associated with a valid user.'),
);
}
return $this->validateAuthenticatedUser(
$api_request,
$user);
}
$access_token = idx($metadata, 'access_token');
if ($access_token) {
$token = id(new PhabricatorOAuthServerAccessToken())
->loadOneWhere('token = %s', $access_token);
if (!$token) {
return array(
'ERR-INVALID-AUTH',
pht('Access token does not exist.'),
);
}
$oauth_server = new PhabricatorOAuthServer();
$authorization = $oauth_server->authorizeToken($token);
if (!$authorization) {
return array(
'ERR-INVALID-AUTH',
pht('Access token is invalid or expired.'),
);
}
$user = id(new PhabricatorPeopleQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($token->getUserPHID()))
->executeOne();
if (!$user) {
return array(
'ERR-INVALID-AUTH',
pht('Access token is for invalid user.'),
);
}
$ok = $this->authorizeOAuthMethodAccess($authorization, $method);
if (!$ok) {
return array(
'ERR-OAUTH-ACCESS',
pht('You do not have authorization to call this method.'),
);
}
$api_request->setOAuthToken($token);
return $this->validateAuthenticatedUser(
$api_request,
$user);
}
// For intracluster requests, use a public user if no authentication
// information is provided. We could do this safely for any request,
// but making the API fully public means there's no way to disable badly
// behaved clients.
if (PhabricatorEnv::isClusterRemoteAddress()) {
if (PhabricatorEnv::getEnvConfig('policy.allow-public')) {
$api_request->setIsClusterRequest(true);
$user = new PhabricatorUser();
return $this->validateAuthenticatedUser(
$api_request,
$user);
}
}
// Handle sessionless auth.
// TODO: This is super messy.
// TODO: Remove this in favor of token-based auth.
if (isset($metadata['authUser'])) {
$user = id(new PhabricatorUser())->loadOneWhere(
'userName = %s',
$metadata['authUser']);
if (!$user) {
return array(
'ERR-INVALID-AUTH',
pht('Authentication is invalid.'),
);
}
$token = idx($metadata, 'authToken');
$signature = idx($metadata, 'authSignature');
$certificate = $user->getConduitCertificate();
$hash = sha1($token.$certificate);
if (!phutil_hashes_are_identical($hash, $signature)) {
return array(
'ERR-INVALID-AUTH',
pht('Authentication is invalid.'),
);
}
return $this->validateAuthenticatedUser(
$api_request,
$user);
}
// Handle session-based auth.
// TODO: Remove this in favor of token-based auth.
$session_key = idx($metadata, 'sessionKey');
if (!$session_key) {
return array(
'ERR-INVALID-SESSION',
pht('Session key is not present.'),
);
}
$user = id(new PhabricatorAuthSessionEngine())
->loadUserForSession(PhabricatorAuthSession::TYPE_CONDUIT, $session_key);
if (!$user) {
return array(
'ERR-INVALID-SESSION',
pht('Session key is invalid.'),
);
}
return $this->validateAuthenticatedUser(
$api_request,
$user);
}
private function validateAuthenticatedUser(
ConduitAPIRequest $request,
PhabricatorUser $user) {
if (!$user->canEstablishAPISessions()) {
return array(
'ERR-INVALID-AUTH',
pht('User account is not permitted to use the API.'),
);
}
$request->setUser($user);
id(new PhabricatorAuthSessionEngine())
->willServeRequestForUser($user);
return null;
}
private function buildHumanReadableResponse(
$method,
ConduitAPIRequest $request = null,
$result = null,
ConduitAPIMethod $method_implementation = null) {
$param_rows = array();
$param_rows[] = array('Method', $this->renderAPIValue($method));
if ($request) {
foreach ($request->getAllParameters() as $key => $value) {
$param_rows[] = array(
$key,
$this->renderAPIValue($value),
);
}
}
$param_table = new AphrontTableView($param_rows);
$param_table->setColumnClasses(
array(
'header',
'wide',
));
$result_rows = array();
foreach ($result as $key => $value) {
$result_rows[] = array(
$key,
$this->renderAPIValue($value),
);
}
$result_table = new AphrontTableView($result_rows);
$result_table->setColumnClasses(
array(
'header',
'wide',
));
$param_panel = id(new PHUIObjectBoxView())
->setHeaderText(pht('Method Parameters'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($param_table);
$result_panel = id(new PHUIObjectBoxView())
->setHeaderText(pht('Method Result'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($result_table);
$method_uri = $this->getApplicationURI('method/'.$method.'/');
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($method, $method_uri)
->addTextCrumb(pht('Call'))
->setBorder(true);
$example_panel = null;
if ($request && $method_implementation) {
$params = $request->getAllParameters();
$example_panel = $this->renderExampleBox(
$method_implementation,
$params);
}
$title = pht('Method Call Result');
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-exchange');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter(array(
$param_panel,
$result_panel,
$example_panel,
));
$title = pht('Method Call Result');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function renderAPIValue($value) {
$json = new PhutilJSON();
if (is_array($value)) {
$value = $json->encodeFormatted($value);
}
$value = phutil_tag(
'pre',
array('style' => 'white-space: pre-wrap;'),
$value);
return $value;
}
private function decodeConduitParams(
AphrontRequest $request,
$method) {
$content_type = $request->getHTTPHeader('Content-Type');
if ($content_type == 'application/json') {
throw new Exception(
pht('Use form-encoded data to submit parameters to Conduit endpoints. '.
'Sending a JSON-encoded body and setting \'Content-Type\': '.
'\'application/json\' is not currently supported.'));
}
// Look for parameters from the Conduit API Console, which are encoded
// as HTTP POST parameters in an array, e.g.:
//
// params[name]=value&params[name2]=value2
//
// The fields are individually JSON encoded, since we require users to
// enter JSON so that we avoid type ambiguity.
$params = $request->getArr('params', null);
if ($params !== null) {
foreach ($params as $key => $value) {
if ($value == '') {
// Interpret empty string null (e.g., the user didn't type anything
// into the box).
$value = 'null';
}
$decoded_value = json_decode($value, true);
if ($decoded_value === null && strtolower($value) != 'null') {
// When json_decode() fails, it returns null. This almost certainly
// indicates that a user was using the web UI and didn't put quotes
// around a string value. We can either do what we think they meant
// (treat it as a string) or fail. For now, err on the side of
// caution and fail. In the future, if we make the Conduit API
// actually do type checking, it might be reasonable to treat it as
// a string if the parameter type is string.
throw new Exception(
pht(
"The value for parameter '%s' is not valid JSON. All ".
"parameters must be encoded as JSON values, including strings ".
"(which means you need to surround them in double quotes). ".
"Check your syntax. Value was: %s.",
$key,
$value));
}
$params[$key] = $decoded_value;
}
$metadata = idx($params, '__conduit__', array());
unset($params['__conduit__']);
return array($metadata, $params, true);
}
// Otherwise, look for a single parameter called 'params' which has the
// entire param dictionary JSON encoded.
$params_json = $request->getStr('params');
if (strlen($params_json)) {
$params = null;
try {
$params = phutil_json_decode($params_json);
} catch (PhutilJSONParserException $ex) {
throw new PhutilProxyException(
pht(
"Invalid parameter information was passed to method '%s'.",
$method),
$ex);
}
$metadata = idx($params, '__conduit__', array());
unset($params['__conduit__']);
return array($metadata, $params, true);
}
// If we do not have `params`, assume this is a simple HTTP request with
// HTTP key-value pairs.
$params = array();
$metadata = array();
foreach ($request->getPassthroughRequestData() as $key => $value) {
$meta_key = ConduitAPIMethod::getParameterMetadataKey($key);
if ($meta_key !== null) {
$metadata[$meta_key] = $value;
} else {
$params[$key] = $value;
}
}
return array($metadata, $params, false);
}
private function authorizeOAuthMethodAccess(
PhabricatorOAuthClientAuthorization $authorization,
$method_name) {
$method = ConduitAPIMethod::getConduitMethod($method_name);
if (!$method) {
return false;
}
$required_scope = $method->getRequiredScope();
switch ($required_scope) {
case ConduitAPIMethod::SCOPE_ALWAYS:
return true;
case ConduitAPIMethod::SCOPE_NEVER:
return false;
}
$authorization_scope = $authorization->getScope();
if (!empty($authorization_scope[$required_scope])) {
return true;
}
return false;
}
private function getConduitCapabilities() {
$capabilities = array();
if (AphrontRequestStream::supportsGzip()) {
$capabilities[] = 'gzip';
}
return $capabilities;
}
}
diff --git a/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php b/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php
index 7550f92210..d002ea5157 100644
--- a/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php
+++ b/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php
@@ -1,108 +1,108 @@
<?php
final class PhabricatorConduitTokenEditController
extends PhabricatorConduitController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
if ($id) {
$token = id(new PhabricatorConduitTokenQuery())
->setViewer($viewer)
->withIDs(array($id))
->withExpired(false)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$token) {
return new Aphront404Response();
}
$object = $token->getObject();
$is_new = false;
$title = pht('View API Token');
} else {
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($request->getStr('objectPHID')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$object) {
return new Aphront404Response();
}
$token = PhabricatorConduitToken::initializeNewToken(
$object->getPHID(),
PhabricatorConduitToken::TYPE_STANDARD);
$is_new = true;
$title = pht('Generate API Token');
$submit_button = pht('Generate Token');
}
$panel_uri = id(new PhabricatorConduitTokensSettingsPanel())
->setViewer($viewer)
->setUser($object)
->getPanelURI();
id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
$viewer,
$request,
$panel_uri);
if ($request->isFormPost()) {
$token->save();
if ($is_new) {
$token_uri = '/conduit/token/edit/'.$token->getID().'/';
} else {
$token_uri = $panel_uri;
}
return id(new AphrontRedirectResponse())->setURI($token_uri);
}
$dialog = $this->newDialog()
->setTitle($title)
->addHiddenInput('objectPHID', $object->getPHID());
if ($is_new) {
$dialog
->appendParagraph(pht('Generate a new API token?'))
->addSubmitButton($submit_button)
->addCancelButton($panel_uri);
} else {
$form = id(new AphrontFormView())
->setUser($viewer);
if ($token->getTokenType() === PhabricatorConduitToken::TYPE_CLUSTER) {
$dialog->appendChild(
pht(
- 'This token is automatically generated by Phabricator, and used '.
- 'to make requests between nodes in a Phabricator cluster. You '.
- 'can not use this token in external applications.'));
+ 'This token is automatically generated, and used to make '.
+ 'requests between nodes in a cluster. You can not use this '.
+ 'token in external applications.'));
} else {
$form->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Token'))
->setValue($token->getToken()));
}
$dialog
->appendForm($form)
->addCancelButton($panel_uri, pht('Done'));
}
return $dialog;
}
}
diff --git a/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php b/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php
index 8119deb860..9b02dca353 100644
--- a/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php
+++ b/src/applications/conduit/method/ConduitConnectConduitAPIMethod.php
@@ -1,155 +1,155 @@
<?php
final class ConduitConnectConduitAPIMethod extends ConduitAPIMethod {
public function getAPIMethodName() {
return 'conduit.connect';
}
public function shouldRequireAuthentication() {
return false;
}
public function shouldAllowUnguardedWrites() {
return true;
}
public function getMethodDescription() {
return pht('Connect a session-based client.');
}
protected function defineParamTypes() {
return array(
'client' => 'required string',
'clientVersion' => 'required int',
'clientDescription' => 'optional string',
'user' => 'optional string',
'authToken' => 'optional int',
'authSignature' => 'optional string',
'host' => 'deprecated',
);
}
protected function defineReturnType() {
return 'dict<string, any>';
}
protected function defineErrorTypes() {
return array(
'ERR-BAD-VERSION' => pht(
'Client/server version mismatch. Upgrade your server or downgrade '.
'your client.'),
'NEW-ARC-VERSION' => pht(
'Client/server version mismatch. Upgrade your client.'),
'ERR-UNKNOWN-CLIENT' => pht('Client is unknown.'),
'ERR-INVALID-USER' => pht(
'The username you are attempting to authenticate with is not valid.'),
'ERR-INVALID-CERTIFICATE' => pht(
'Your authentication certificate for this server is invalid.'),
'ERR-INVALID-TOKEN' => pht(
"The challenge token you are authenticating with is outside of the ".
"allowed time range. Either your system clock is out of whack or ".
"you're executing a replay attack."),
'ERR-NO-CERTIFICATE' => pht('This server requires authentication.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$client = $request->getValue('client');
$client_version = (int)$request->getValue('clientVersion');
$client_description = (string)$request->getValue('clientDescription');
$client_description = id(new PhutilUTF8StringTruncator())
->setMaximumBytes(255)
->truncateString($client_description);
$username = (string)$request->getValue('user');
switch ($client) {
case 'arc':
$server_version = 6;
$supported_versions = array(
$server_version => true,
// Client version 5 introduced "user.query" call
4 => true,
// Client version 6 introduced "diffusion.getlintmessages" call
5 => true,
);
if (empty($supported_versions[$client_version])) {
if ($server_version < $client_version) {
$ex = new ConduitException('ERR-BAD-VERSION');
$ex->setErrorDescription(
pht(
"Your '%s' client version is '%d', which is newer than the ".
- "server version, '%d'. Upgrade your Phabricator install.",
+ "server version, '%d'. Upgrade your server.",
'arc',
$client_version,
$server_version));
} else {
$ex = new ConduitException('NEW-ARC-VERSION');
$ex->setErrorDescription(
pht(
'A new version of arc is available! You need to upgrade '.
'to connect to this server (you are running version '.
'%d, the server is running version %d).',
$client_version,
$server_version));
}
throw $ex;
}
break;
default:
// Allow new clients by default.
break;
}
$token = $request->getValue('authToken');
$signature = $request->getValue('authSignature');
$user = id(new PhabricatorUser())->loadOneWhere('username = %s', $username);
if (!$user) {
throw new ConduitException('ERR-INVALID-USER');
}
$session_key = null;
if ($token && $signature) {
$threshold = 60 * 15;
$now = time();
if (abs($token - $now) > $threshold) {
throw id(new ConduitException('ERR-INVALID-TOKEN'))
->setErrorDescription(
pht(
'The request you submitted is signed with a timestamp, but that '.
'timestamp is not within %s of the current time. The '.
'signed timestamp is %s (%s), and the current server time is '.
'%s (%s). This is a difference of %s seconds, but the '.
'timestamp must differ from the server time by no more than '.
'%s seconds. Your client or server clock may not be set '.
'correctly.',
phutil_format_relative_time($threshold),
$token,
date('r', $token),
$now,
date('r', $now),
($token - $now),
$threshold));
}
$valid = sha1($token.$user->getConduitCertificate());
if (!phutil_hashes_are_identical($valid, $signature)) {
throw new ConduitException('ERR-INVALID-CERTIFICATE');
}
$session_key = id(new PhabricatorAuthSessionEngine())->establishSession(
PhabricatorAuthSession::TYPE_CONDUIT,
$user->getPHID(),
$partial = false);
} else {
throw new ConduitException('ERR-NO-CERTIFICATE');
}
return array(
'connectionID' => mt_rand(),
'sessionKey' => $session_key,
'userPHID' => $user->getPHID(),
);
}
}
diff --git a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php
index 9d244401e6..06ba536dfe 100644
--- a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php
+++ b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php
@@ -1,192 +1,192 @@
<?php
final class PhabricatorConduitSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Conduit Methods');
}
public function getApplicationClassName() {
return 'PhabricatorConduitApplication';
}
public function canUseInPanelContext() {
return false;
}
public function getPageSize(PhabricatorSavedQuery $saved) {
return PHP_INT_MAX - 1;
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter('isStable', $request->getStr('isStable'));
$saved->setParameter('isUnstable', $request->getStr('isUnstable'));
$saved->setParameter('isDeprecated', $request->getStr('isDeprecated'));
$saved->setParameter('nameContains', $request->getStr('nameContains'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorConduitMethodQuery());
$query->withIsStable($saved->getParameter('isStable'));
$query->withIsUnstable($saved->getParameter('isUnstable'));
$query->withIsDeprecated($saved->getParameter('isDeprecated'));
$query->withIsInternal(false);
$contains = $saved->getParameter('nameContains');
if (strlen($contains)) {
$query->withNameContains($contains);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name Contains'))
->setName('nameContains')
->setValue($saved->getParameter('nameContains')));
$is_stable = $saved->getParameter('isStable');
$is_unstable = $saved->getParameter('isUnstable');
$is_deprecated = $saved->getParameter('isDeprecated');
$form
->appendChild(
id(new AphrontFormCheckboxControl())
->setLabel('Stability')
->addCheckbox(
'isStable',
1,
hsprintf(
'<strong>%s</strong>: %s',
pht('Stable Methods'),
pht('Show established API methods with stable interfaces.')),
$is_stable)
->addCheckbox(
'isUnstable',
1,
hsprintf(
'<strong>%s</strong>: %s',
pht('Unstable Methods'),
pht('Show new methods which are subject to change.')),
$is_unstable)
->addCheckbox(
'isDeprecated',
1,
hsprintf(
'<strong>%s</strong>: %s',
pht('Deprecated Methods'),
pht(
'Show old methods which will be deleted in a future '.
- 'version of Phabricator.')),
+ 'version of this software.')),
$is_deprecated));
}
protected function getURI($path) {
return '/conduit/'.$path;
}
protected function getBuiltinQueryNames() {
return array(
'modern' => pht('Modern Methods'),
'all' => pht('All Methods'),
);
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'modern':
return $query
->setParameter('isStable', true)
->setParameter('isUnstable', true);
case 'all':
return $query
->setParameter('isStable', true)
->setParameter('isUnstable', true)
->setParameter('isDeprecated', true);
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $methods,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($methods, 'ConduitAPIMethod');
$viewer = $this->requireViewer();
$out = array();
$last = null;
$list = null;
foreach ($methods as $method) {
$app = $method->getApplicationName();
if ($app !== $last) {
$last = $app;
if ($list) {
$out[] = $list;
}
$list = id(new PHUIObjectItemListView());
$list->setHeader($app);
$app_object = $method->getApplication();
if ($app_object) {
$app_name = $app_object->getName();
} else {
$app_name = $app;
}
}
$method_name = $method->getAPIMethodName();
$item = id(new PHUIObjectItemView())
->setHeader($method_name)
->setHref($this->getApplicationURI('method/'.$method_name.'/'))
->addAttribute($method->getMethodSummary());
switch ($method->getMethodStatus()) {
case ConduitAPIMethod::METHOD_STATUS_STABLE:
break;
case ConduitAPIMethod::METHOD_STATUS_UNSTABLE:
$item->addIcon('fa-warning', pht('Unstable'));
$item->setStatusIcon('fa-warning yellow');
break;
case ConduitAPIMethod::METHOD_STATUS_DEPRECATED:
$item->addIcon('fa-warning', pht('Deprecated'));
$item->setStatusIcon('fa-warning red');
break;
case ConduitAPIMethod::METHOD_STATUS_FROZEN:
$item->addIcon('fa-archive', pht('Frozen'));
$item->setStatusIcon('fa-archive grey');
break;
}
$list->addItem($item);
}
if ($list) {
$out[] = $list;
}
$result = new PhabricatorApplicationSearchResultView();
$result->setContent($out);
return $result;
}
}
diff --git a/src/applications/config/check/PhabricatorBaseURISetupCheck.php b/src/applications/config/check/PhabricatorBaseURISetupCheck.php
index bf3ac0b071..92e46641d7 100644
--- a/src/applications/config/check/PhabricatorBaseURISetupCheck.php
+++ b/src/applications/config/check/PhabricatorBaseURISetupCheck.php
@@ -1,104 +1,104 @@
<?php
final class PhabricatorBaseURISetupCheck extends PhabricatorSetupCheck {
public function getDefaultGroup() {
return self::GROUP_IMPORTANT;
}
protected function executeChecks() {
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
$host_header = AphrontRequest::getHTTPHeader('Host');
if (strpos($host_header, '.') === false) {
if (!strlen(trim($host_header))) {
$name = pht('No "Host" Header');
$summary = pht('No "Host" header present in request.');
$message = pht(
'This request did not include a "Host" header. This may mean that '.
'your webserver (like nginx or apache) is misconfigured so the '.
- '"Host" header is not making it to Phabricator, or that you are '.
+ '"Host" header is not making it to this software, or that you are '.
'making a raw request without a "Host" header using a tool or '.
'library.'.
"\n\n".
'If you are using a web browser, check your webserver '.
'configuration. If you are using a tool or library, check how the '.
'request is being constructed.'.
"\n\n".
'It is also possible (but very unlikely) that some other network '.
'device (like a load balancer) is stripping the header.'.
"\n\n".
'Requests must include a valid "Host" header.');
} else {
$name = pht('Bad "Host" Header');
$summary = pht('Request has bad "Host" header.');
$message = pht(
'This request included an invalid "Host" header, with value "%s". '.
'Host headers must contain a dot ("."), like "example.com". This '.
'is required for some browsers to be able to set cookies.'.
"\n\n".
'This may mean the base URI is configured incorrectly. You must '.
- 'serve Phabricator from a base URI with a dot (like '.
- '"https://phabricator.mycompany.com"), not a bare domain '.
- '(like "https://phabricator/"). If you are trying to use a bare '.
+ 'serve this software from a base URI with a dot (like '.
+ '"https://devtools.example.com"), not a bare domain '.
+ '(like "https://devtools/"). If you are trying to use a bare '.
'domain, change your configuration to use a full domain with a dot '.
'in it instead.'.
"\n\n".
'This might also mean that your webserver (or some other network '.
'device, like a load balancer) is mangling the "Host" header, or '.
'you are using a tool or library to issue a request manually and '.
'setting the wrong "Host" header.'.
"\n\n".
'Requests must include a valid "Host" header.',
$host_header);
}
$this
->newIssue('request.host')
->setName($name)
->setSummary($summary)
->setMessage($message)
->setIsFatal(true);
}
if ($base_uri) {
return;
}
$base_uri_guess = PhabricatorEnv::getRequestBaseURI();
$summary = pht(
'The base URI for this install is not configured. Many major features '.
'will not work properly until you configure it.');
$message = pht(
'The base URI for this install is not configured, and major features '.
'will not work properly until you configure it.'.
"\n\n".
'You should set the base URI to the URI you will use to access '.
- 'Phabricator, like "http://phabricator.example.com/".'.
+ 'this server, like "http://devtools.example.com/".'.
"\n\n".
'Include the protocol (http or https), domain name, and port number if '.
'you are using a port other than 80 (http) or 443 (https).'.
"\n\n".
'Based on this request, it appears that the correct setting is:'.
"\n\n".
'%s'.
"\n\n".
'To configure the base URI, run the command shown below.',
$base_uri_guess);
$this
->newIssue('config.phabricator.base-uri')
->setShortName(pht('No Base URI'))
->setName(pht('Base URI Not Configured'))
->setSummary($summary)
->setMessage($message)
->addCommand(
hsprintf(
- '<tt>phabricator/ $</tt> %s',
+ '<tt>$</tt> %s',
csprintf(
'./bin/config set phabricator.base-uri %s',
$base_uri_guess)));
}
}
diff --git a/src/applications/config/check/PhabricatorBinariesSetupCheck.php b/src/applications/config/check/PhabricatorBinariesSetupCheck.php
index 5b8f93346c..b87282fcc7 100644
--- a/src/applications/config/check/PhabricatorBinariesSetupCheck.php
+++ b/src/applications/config/check/PhabricatorBinariesSetupCheck.php
@@ -1,257 +1,256 @@
<?php
final class PhabricatorBinariesSetupCheck extends PhabricatorSetupCheck {
public function getDefaultGroup() {
return self::GROUP_OTHER;
}
protected function executeChecks() {
if (phutil_is_windows()) {
$bin_name = 'where';
} else {
$bin_name = 'which';
}
if (!Filesystem::binaryExists($bin_name)) {
$message = pht(
- "Without '%s', Phabricator can not test for the availability ".
+ "Without '%s', this software can not test for the availability ".
"of other binaries.",
$bin_name);
$this->raiseWarning($bin_name, $message);
// We need to return here if we can't find the 'which' / 'where' binary
// because the other tests won't be valid.
return;
}
if (!Filesystem::binaryExists('diff')) {
$message = pht(
- "Without '%s', Phabricator will not be able to generate or render ".
+ "Without '%s', this software will not be able to generate or render ".
"diffs in multiple applications.",
'diff');
$this->raiseWarning('diff', $message);
} else {
$tmp_a = new TempFile();
$tmp_b = new TempFile();
$tmp_c = new TempFile();
Filesystem::writeFile($tmp_a, 'A');
Filesystem::writeFile($tmp_b, 'A');
Filesystem::writeFile($tmp_c, 'B');
list($err) = exec_manual('diff %s %s', $tmp_a, $tmp_b);
if ($err) {
$this->newIssue('bin.diff.same')
->setName(pht("Unexpected '%s' Behavior", 'diff'))
->setMessage(
pht(
"The '%s' binary on this system has unexpected behavior: ".
"it was expected to exit without an error code when passed ".
"identical files, but exited with code %d.",
'diff',
$err));
}
list($err) = exec_manual('diff %s %s', $tmp_a, $tmp_c);
if (!$err) {
$this->newIssue('bin.diff.diff')
->setName(pht("Unexpected 'diff' Behavior"))
->setMessage(
pht(
"The '%s' binary on this system has unexpected behavior: ".
"it was expected to exit with a nonzero error code when passed ".
"differing files, but did not.",
'diff'));
}
}
$table = new PhabricatorRepository();
$vcses = queryfx_all(
$table->establishConnection('r'),
'SELECT DISTINCT versionControlSystem FROM %T',
$table->getTableName());
foreach ($vcses as $vcs) {
switch ($vcs['versionControlSystem']) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$binary = 'git';
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$binary = 'svn';
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$binary = 'hg';
break;
default:
$binary = null;
break;
}
if (!$binary) {
continue;
}
if (!Filesystem::binaryExists($binary)) {
$message = pht(
'You have at least one repository configured which uses this '.
'version control system. It will not work without the VCS binary.');
$this->raiseWarning($binary, $message);
continue;
}
$version = PhutilBinaryAnalyzer::getForBinary($binary)
->getBinaryVersion();
switch ($vcs['versionControlSystem']) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$bad_versions = array();
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$bad_versions = array(
// We need 1.5 for "--depth", see T7228.
'< 1.5' => pht(
'The minimum supported version of Subversion is 1.5, which '.
'was released in 2008.'),
'= 1.7.1' => pht(
'This version of Subversion has a bug where `%s` does not work '.
'for files added in rN (Subversion issue #2873), fixed in 1.7.2.',
'svn diff -c N'),
);
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$bad_versions = array(
// We need 2.4 for utilizing `{p1node}` keyword in templates, see
// D21679 and D21681.
'< 2.4' => pht(
'The minimum supported version of Mercurial is 2.4, which was '.
'released in 2012.'),
);
break;
}
if ($version === null) {
$this->raiseUnknownVersionWarning($binary);
} else {
$version_details = array();
foreach ($bad_versions as $spec => $details) {
list($operator, $bad_version) = explode(' ', $spec, 2);
$is_bad = version_compare($version, $bad_version, $operator);
if ($is_bad) {
$version_details[] = pht(
'(%s%s) %s',
$operator,
$bad_version,
$details);
}
}
if ($version_details) {
$this->raiseBadVersionWarning(
$binary,
$version,
$version_details);
}
}
}
}
private function raiseWarning($bin, $message) {
if (phutil_is_windows()) {
$preamble = pht(
"The '%s' binary could not be found. Set the webserver's %s ".
"environmental variable to include the directory where it resides, or ".
- "add that directory to '%s' in the Phabricator configuration.",
+ "add that directory to '%s' in configuration.",
$bin,
'PATH',
'environment.append-paths');
} else {
$preamble = pht(
"The '%s' binary could not be found. Symlink it into '%s', or set the ".
"webserver's %s environmental variable to include the directory where ".
- "it resides, or add that directory to '%s' in the Phabricator ".
- "configuration.",
+ "it resides, or add that directory to '%s' in configuration.",
$bin,
- 'phabricator/support/bin/',
+ 'support/bin/',
'PATH',
'environment.append-paths');
}
$this->newIssue('bin.'.$bin)
->setShortName(pht("'%s' Missing", $bin))
->setName(pht("Missing '%s' Binary", $bin))
->setSummary(
pht("The '%s' binary could not be located or executed.", $bin))
->setMessage($preamble.' '.$message)
->addPhabricatorConfig('environment.append-paths');
}
private function raiseUnknownVersionWarning($binary) {
$summary = pht(
'Unable to determine the version number of "%s".',
$binary);
$message = pht(
'Unable to determine the version number of "%s". Usually, this means '.
- 'the program changed its version format string recently and Phabricator '.
- 'does not know how to parse the new one yet, but might indicate that '.
- 'you have a very old (or broken) binary.'.
+ 'the program changed its version format string recently and this '.
+ 'software does not know how to parse the new one yet, but might '.
+ 'indicate that you have a very old (or broken) binary.'.
"\n\n".
'Because we can not determine the version number, checks against '.
'minimum and known-bad versions will be skipped, so we might fail '.
'to detect an incompatible binary.'.
"\n\n".
- 'You may be able to resolve this issue by updating Phabricator, since '.
- 'a newer version of Phabricator is likely to be able to parse the '.
+ 'You may be able to resolve this issue by updating this server, since '.
+ 'a newer version of the software is likely to be able to parse the '.
'newer version string.'.
"\n\n".
- 'If updating Phabricator does not fix this, you can report the issue '.
+ 'If updating the software does not fix this, you can report the issue '.
'to the upstream so we can adjust the parser.'.
"\n\n".
'If you are confident you have a recent version of "%s" installed and '.
'working correctly, it is usually safe to ignore this warning.',
$binary,
$binary);
$this->newIssue('bin.'.$binary.'.unknown-version')
->setShortName(pht("Unknown '%s' Version", $binary))
->setName(pht("Unknown '%s' Version", $binary))
->setSummary($summary)
->setMessage($message)
->addLink(
PhabricatorEnv::getDoclink('Contributing Bug Reports'),
pht('Report this Issue to the Upstream'));
}
private function raiseBadVersionWarning($binary, $version, array $problems) {
$summary = pht(
'This server has a known bad version of "%s".',
$binary);
$message = array();
$message[] = pht(
'This server has a known bad version of "%s" installed ("%s"). This '.
'version is not supported, or contains important bugs or security '.
'vulnerabilities which are fixed in a newer version.',
$binary,
$version);
$message[] = pht('You should upgrade this software.');
$message[] = pht('The known issues with this old version are:');
foreach ($problems as $problem) {
$message[] = $problem;
}
$message = implode("\n\n", $message);
$this->newIssue("bin.{$binary}.bad-version")
->setName(pht('Unsupported/Insecure "%s" Version', $binary))
->setSummary($summary)
->setMessage($message);
}
}
diff --git a/src/applications/config/check/PhabricatorDatabaseSetupCheck.php b/src/applications/config/check/PhabricatorDatabaseSetupCheck.php
index e48e22196e..a5989f37b3 100644
--- a/src/applications/config/check/PhabricatorDatabaseSetupCheck.php
+++ b/src/applications/config/check/PhabricatorDatabaseSetupCheck.php
@@ -1,242 +1,242 @@
<?php
final class PhabricatorDatabaseSetupCheck extends PhabricatorSetupCheck {
public function getDefaultGroup() {
return self::GROUP_IMPORTANT;
}
public function getExecutionOrder() {
// This must run after basic PHP checks, but before most other checks.
return 500;
}
protected function executeChecks() {
$host = PhabricatorEnv::getEnvConfig('mysql.host');
$matches = null;
if (preg_match('/^([^:]+):(\d+)$/', $host, $matches)) {
$host = $matches[1];
$port = $matches[2];
$this->newIssue('storage.mysql.hostport')
->setName(pht('Deprecated mysql.host Format'))
->setSummary(
pht(
'Move port information from `%s` to `%s` in your config.',
'mysql.host',
'mysql.port'))
->setMessage(
pht(
'Your `%s` configuration contains a port number, but this usage '.
'is deprecated. Instead, put the port number in `%s`.',
'mysql.host',
'mysql.port'))
->addPhabricatorConfig('mysql.host')
->addPhabricatorConfig('mysql.port')
->addCommand(
hsprintf(
- '<tt>phabricator/ $</tt> ./bin/config set mysql.host %s',
+ '<tt>$</tt> ./bin/config set mysql.host %s',
$host))
->addCommand(
hsprintf(
- '<tt>phabricator/ $</tt> ./bin/config set mysql.port %s',
+ '<tt>$</tt> ./bin/config set mysql.port %s',
$port));
}
$refs = PhabricatorDatabaseRef::queryAll();
$refs = mpull($refs, null, 'getRefKey');
// Test if we can connect to each database first. If we can not connect
// to a particular database, we only raise a warning: this allows new web
// nodes to start during a disaster, when some databases may be correctly
// configured but not reachable.
$connect_map = array();
$any_connection = false;
foreach ($refs as $ref_key => $ref) {
$conn_raw = $ref->newManagementConnection();
try {
queryfx($conn_raw, 'SELECT 1');
$database_exception = null;
$any_connection = true;
} catch (AphrontInvalidCredentialsQueryException $ex) {
$database_exception = $ex;
} catch (AphrontConnectionQueryException $ex) {
$database_exception = $ex;
}
if ($database_exception) {
$connect_map[$ref_key] = $database_exception;
unset($refs[$ref_key]);
}
}
if ($connect_map) {
// This is only a fatal error if we could not connect to anything. If
// possible, we still want to start if some database hosts can not be
// reached.
$is_fatal = !$any_connection;
foreach ($connect_map as $ref_key => $database_exception) {
$issue = PhabricatorSetupIssue::newDatabaseConnectionIssue(
$database_exception,
$is_fatal);
$this->addIssue($issue);
}
}
foreach ($refs as $ref_key => $ref) {
if ($this->executeRefChecks($ref)) {
return;
}
}
}
private function executeRefChecks(PhabricatorDatabaseRef $ref) {
$conn_raw = $ref->newManagementConnection();
$ref_key = $ref->getRefKey();
$engines = queryfx_all($conn_raw, 'SHOW ENGINES');
$engines = ipull($engines, 'Support', 'Engine');
$innodb = idx($engines, 'InnoDB');
if ($innodb != 'YES' && $innodb != 'DEFAULT') {
$message = pht(
'The "InnoDB" engine is not available in MySQL (on host "%s"). '.
'Enable InnoDB in your MySQL configuration.'.
"\n\n".
'(If you aleady created tables, MySQL incorrectly used some other '.
'engine to create them. You need to convert them or drop and '.
'reinitialize them.)',
$ref_key);
$this->newIssue('mysql.innodb')
->setName(pht('MySQL InnoDB Engine Not Available'))
->setMessage($message)
->setIsFatal(true);
return true;
}
$namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace');
$databases = queryfx_all($conn_raw, 'SHOW DATABASES');
$databases = ipull($databases, 'Database', 'Database');
if (empty($databases[$namespace.'_meta_data'])) {
$message = pht(
'Run the storage upgrade script to setup databases (host "%s" has '.
'not been initialized).',
$ref_key);
$this->newIssue('storage.upgrade')
->setName(pht('Setup MySQL Schema'))
->setMessage($message)
->setIsFatal(true)
- ->addCommand(hsprintf('<tt>phabricator/ $</tt> ./bin/storage upgrade'));
+ ->addCommand(hsprintf('<tt>$</tt> ./bin/storage upgrade'));
return true;
}
$conn_meta = $ref->newApplicationConnection(
$namespace.'_meta_data');
$applied = queryfx_all($conn_meta, 'SELECT patch FROM patch_status');
$applied = ipull($applied, 'patch', 'patch');
$all = PhabricatorSQLPatchList::buildAllPatches();
$diff = array_diff_key($all, $applied);
if ($diff) {
$message = pht(
'Run the storage upgrade script to upgrade databases (host "%s" is '.
'out of date). Missing patches: %s.',
$ref_key,
implode(', ', array_keys($diff)));
$this->newIssue('storage.patch')
->setName(pht('Upgrade MySQL Schema'))
->setIsFatal(true)
->setMessage($message)
->addCommand(
- hsprintf('<tt>phabricator/ $</tt> ./bin/storage upgrade'));
+ hsprintf('<tt>$</tt> ./bin/storage upgrade'));
return true;
}
// NOTE: It's possible that replication is broken but we have not been
// granted permission to "SHOW SLAVE STATUS" so we can't figure it out.
// We allow this kind of configuration and survive these checks, trusting
// that operations knows what they're doing. This issue is shown on the
// "Database Servers" console.
switch ($ref->getReplicaStatus()) {
case PhabricatorDatabaseRef::REPLICATION_MASTER_REPLICA:
$message = pht(
'Database host "%s" is configured as a master, but is replicating '.
'another host. This is dangerous and can mangle or destroy data. '.
'Only replicas should be replicating. Stop replication on the '.
- 'host or reconfigure Phabricator.',
+ 'host or adjust configuration.',
$ref->getRefKey());
$this->newIssue('db.master.replicating')
->setName(pht('Replicating Master'))
->setIsFatal(true)
->setMessage($message);
return true;
case PhabricatorDatabaseRef::REPLICATION_REPLICA_NONE:
case PhabricatorDatabaseRef::REPLICATION_NOT_REPLICATING:
if (!$ref->getIsMaster()) {
$message = pht(
'Database replica "%s" is listed as a replica, but is not '.
'currently replicating. You are vulnerable to data loss if '.
'the master fails.',
$ref->getRefKey());
// This isn't a fatal because it can normally only put data at risk,
// not actually do anything destructive or unrecoverable.
$this->newIssue('db.replica.not-replicating')
->setName(pht('Nonreplicating Replica'))
->setMessage($message);
}
break;
}
// If we have more than one master, we require that the cluster database
// configuration written to each database node is exactly the same as the
// one we are running with.
$masters = PhabricatorDatabaseRef::getAllMasterDatabaseRefs();
if (count($masters) > 1) {
$state_actual = queryfx_one(
$conn_meta,
'SELECT stateValue FROM %T WHERE stateKey = %s',
PhabricatorStorageManagementAPI::TABLE_HOSTSTATE,
'cluster.databases');
if ($state_actual) {
$state_actual = $state_actual['stateValue'];
}
$state_expect = $ref->getPartitionStateForCommit();
if ($state_expect !== $state_actual) {
$message = pht(
'Database host "%s" has a configured cluster state which disagrees '.
'with the state on this host ("%s"). Run `bin/storage partition` '.
'to commit local state to the cluster. This host may have started '.
'with an out-of-date configuration.',
$ref->getRefKey(),
php_uname('n'));
$this->newIssue('db.state.desync')
->setName(pht('Cluster Configuration Out of Sync'))
->setMessage($message)
->setIsFatal(true);
return true;
}
}
}
}
diff --git a/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php b/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php
index cd29ecdc78..8466c5a6c6 100644
--- a/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php
+++ b/src/applications/config/check/PhabricatorElasticsearchSetupCheck.php
@@ -1,88 +1,88 @@
<?php
final class PhabricatorElasticsearchSetupCheck extends PhabricatorSetupCheck {
public function getDefaultGroup() {
return self::GROUP_OTHER;
}
protected function executeChecks() {
// TODO: Avoid fataling if we don't have a master database configured
// but have the MySQL search index configured. See T12965.
if (PhabricatorEnv::isReadOnly()) {
return;
}
$services = PhabricatorSearchService::getAllServices();
foreach ($services as $service) {
try {
$host = $service->getAnyHostForRole('read');
} catch (PhabricatorClusterNoHostForRoleException $e) {
// ignore the error
continue;
}
if ($host instanceof PhabricatorElasticsearchHost) {
$index_exists = null;
$index_sane = null;
try {
$engine = $host->getEngine();
$index_exists = $engine->indexExists();
if ($index_exists) {
$index_sane = $engine->indexIsSane();
}
} catch (Exception $ex) {
$summary = pht('Elasticsearch is not reachable as configured.');
$message = pht(
- 'Elasticsearch is configured (with the %s setting) but Phabricator'.
- ' encountered an exception when trying to test the index.'.
+ 'Elasticsearch is configured (with the %s setting) but an '.
+ 'exception was encountered when trying to test the index.'.
"\n\n".
'%s',
phutil_tag('tt', array(), 'cluster.search'),
phutil_tag('pre', array(), $ex->getMessage()));
$this->newIssue('elastic.misconfigured')
->setName(pht('Elasticsearch Misconfigured'))
->setSummary($summary)
->setMessage($message)
->addRelatedPhabricatorConfig('cluster.search');
return;
}
if (!$index_exists) {
$summary = pht(
'You enabled Elasticsearch but the index does not exist.');
$message = pht(
'You likely enabled cluster.search without creating the '.
'index. Use the following command to create a new index.');
$this
->newIssue('elastic.missing-index')
->setName(pht('Elasticsearch Index Not Found'))
->addCommand('./bin/search init')
->setSummary($summary)
->setMessage($message);
} else if (!$index_sane) {
$summary = pht(
'Elasticsearch index exists but needs correction.');
$message = pht(
- 'Either the Phabricator schema for Elasticsearch has changed '.
+ 'Either the schema for Elasticsearch has changed '.
'or Elasticsearch created the index automatically. '.
'Use the following command to rebuild the index.');
$this
->newIssue('elastic.broken-index')
->setName(pht('Elasticsearch Index Schema Mismatch'))
->addCommand('./bin/search init')
->setSummary($summary)
->setMessage($message);
}
}
}
}
}
diff --git a/src/applications/config/check/PhabricatorFileinfoSetupCheck.php b/src/applications/config/check/PhabricatorFileinfoSetupCheck.php
index 543fc4fb7c..9896daac2c 100644
--- a/src/applications/config/check/PhabricatorFileinfoSetupCheck.php
+++ b/src/applications/config/check/PhabricatorFileinfoSetupCheck.php
@@ -1,23 +1,23 @@
<?php
final class PhabricatorFileinfoSetupCheck extends PhabricatorSetupCheck {
public function getDefaultGroup() {
return self::GROUP_OTHER;
}
protected function executeChecks() {
if (!extension_loaded('fileinfo')) {
$message = pht(
"The '%s' extension is not installed. Without '%s', ".
- "support, Phabricator may not be able to determine the MIME types ".
+ "support, this software may not be able to determine the MIME types ".
"of uploaded files.",
'fileinfo',
'fileinfo');
$this->newIssue('extension.fileinfo')
->setName(pht("Missing '%s' Extension", 'fileinfo'))
->setMessage($message);
}
}
}
diff --git a/src/applications/config/check/PhabricatorGDSetupCheck.php b/src/applications/config/check/PhabricatorGDSetupCheck.php
index 7ada204801..0aa23035c7 100644
--- a/src/applications/config/check/PhabricatorGDSetupCheck.php
+++ b/src/applications/config/check/PhabricatorGDSetupCheck.php
@@ -1,58 +1,58 @@
<?php
final class PhabricatorGDSetupCheck extends PhabricatorSetupCheck {
public function getDefaultGroup() {
return self::GROUP_OTHER;
}
protected function executeChecks() {
if (!extension_loaded('gd')) {
$message = pht(
"The '%s' extension is not installed. Without '%s', support, ".
- "Phabricator will not be able to process or resize images ".
+ "this server will not be able to process or resize images ".
"(for example, to generate thumbnails). Install or enable '%s'.",
'gd',
'gd',
'gd');
$this->newIssue('extension.gd')
->setName(pht("Missing '%s' Extension", 'gd'))
->setMessage($message)
->addPHPExtension('gd');
} else {
$image_type_map = array(
'imagecreatefrompng' => 'PNG',
'imagecreatefromgif' => 'GIF',
'imagecreatefromjpeg' => 'JPEG',
);
$have = array();
foreach ($image_type_map as $function => $image_type) {
if (function_exists($function)) {
$have[] = $image_type;
}
}
$missing = array_diff($image_type_map, $have);
if ($missing) {
$missing = implode(', ', $missing);
$have = implode(', ', $have);
$message = pht(
"The '%s' extension has support for only some image types. ".
- "Phabricator will be unable to process images of the missing ".
+ "This server will be unable to process images of the missing ".
"types until you build '%s' with support for them. ".
"Supported types: %s. Missing types: %s.",
'gd',
'gd',
$have,
$missing);
$this->newIssue('extension.gd.support')
->setName(pht("Partial '%s' Support", 'gd'))
->setMessage($message);
}
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Nov 27, 12:21 AM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1259
Default Alt Text
(68 KB)

Event Timeline