diff --git a/resources/sql/patches/059.engines.php b/resources/sql/patches/059.engines.php index 5fdb3ea2e..8c1c7bc8e 100644 --- a/resources/sql/patches/059.engines.php +++ b/resources/sql/patches/059.engines.php @@ -1,31 +1,31 @@ AND s.TABLE_NAME != 'search_documentfield' AND s.ENGINE != 'InnoDB'", '{$NAMESPACE}_'); if (!$tables) { return; } echo "There are ".count($tables)." tables using the MyISAM engine. These will ". "now be converted to InnoDB. This process may take a few minutes, please ". "be patient.\n"; foreach ($tables as $table) { $name = $table['db'].'.'.$table['tbl']; echo "Converting {$name}...\n"; queryfx( $conn, - "ALTER TABLE %T.%T ENGINE=InnoDB", + 'ALTER TABLE %T.%T ENGINE=InnoDB', $table['db'], $table['tbl']); } echo "Done!\n"; diff --git a/resources/sql/patches/079.nametokenindex.php b/resources/sql/patches/079.nametokenindex.php index f8b74ee39..931a850bf 100644 --- a/resources/sql/patches/079.nametokenindex.php +++ b/resources/sql/patches/079.nametokenindex.php @@ -1,18 +1,18 @@ openTransaction(); $table->beginReadLocking(); $users = $table->loadAll(); -echo count($users)." users to index"; +echo count($users).' users to index'; foreach ($users as $user) { $user->updateNameTokens(); - echo "."; + echo '.'; } $table->endReadLocking(); $table->saveTransaction(); echo "\nDone.\n"; diff --git a/resources/sql/patches/090.forceuniqueprojectnames.php b/resources/sql/patches/090.forceuniqueprojectnames.php index 1bd525588..f8a7508d9 100644 --- a/resources/sql/patches/090.forceuniqueprojectnames.php +++ b/resources/sql/patches/090.forceuniqueprojectnames.php @@ -1,107 +1,107 @@ openTransaction(); $table->beginReadLocking(); $projects = $table->loadAll(); $slug_map = array(); foreach ($projects as $project) { $project->setPhrictionSlug($project->getName()); $slug = $project->getPhrictionSlug(); if ($slug == '/') { $project_id = $project->getID(); echo "Project #{$project_id} doesn't have a meaningful name...\n"; $project->setName(trim('Unnamed Project '.$project->getName())); } $slug_map[$slug][] = $project->getID(); } foreach ($slug_map as $slug => $similar) { if (count($similar) <= 1) { continue; } echo "Too many projects are similar to '{$slug}'...\n"; foreach (array_slice($similar, 1, null, true) as $key => $project_id) { $project = $projects[$project_id]; $old_name = $project->getName(); $new_name = rename_project($project, $projects); echo "Renaming project #{$project_id} ". "from '{$old_name}' to '{$new_name}'.\n"; $project->setName($new_name); } } $update = $projects; while ($update) { $size = count($update); foreach ($update as $key => $project) { $id = $project->getID(); $name = $project->getName(); $project->setPhrictionSlug($name); $slug = $project->getPhrictionSlug(); echo "Updating project #{$id} '{$name}' ({$slug})..."; try { queryfx( $project->establishConnection('w'), 'UPDATE %T SET name = %s, phrictionSlug = %s WHERE id = %d', $project->getTableName(), $name, $slug, $project->getID()); unset($update[$key]); echo "okay.\n"; } catch (AphrontQueryDuplicateKeyException $ex) { echo "failed, will retry.\n"; } } if (count($update) == $size) { throw new Exception( - "Failed to make any progress while updating projects. Schema upgrade ". - "has failed. Go manually fix your project names to be unique (they are ". - "probably ridiculous?) and then try again."); + 'Failed to make any progress while updating projects. Schema upgrade '. + 'has failed. Go manually fix your project names to be unique (they are '. + 'probably ridiculous?) and then try again.'); } } $table->endReadLocking(); $table->saveTransaction(); echo "Done.\n"; /** * Rename the project so that it has a unique slug, by appending (2), (3), etc. * to its name. */ function rename_project($project, $projects) { $suffix = 2; while (true) { $new_name = $project->getName().' ('.$suffix.')'; $project->setPhrictionSlug($new_name); $new_slug = $project->getPhrictionSlug(); $okay = true; foreach ($projects as $other) { if ($other->getID() == $project->getID()) { continue; } if ($other->getPhrictionSlug() == $new_slug) { $okay = false; break; } } if ($okay) { break; } else { $suffix++; } } return $new_name; } diff --git a/resources/sql/patches/102.heraldcleanup.php b/resources/sql/patches/102.heraldcleanup.php index 22f198df8..24bf382c0 100644 --- a/resources/sql/patches/102.heraldcleanup.php +++ b/resources/sql/patches/102.heraldcleanup.php @@ -1,39 +1,39 @@ openTransaction(); $table->beginReadLocking(); $rules = $table->loadAll(); foreach ($rules as $key => $rule) { $first_policy = HeraldRepetitionPolicyConfig::toInt( HeraldRepetitionPolicyConfig::FIRST); if ($rule->getRepetitionPolicy() != $first_policy) { unset($rules[$key]); } } $conn_w = $table->establishConnection('w'); $clause = ''; if ($rules) { $clause = qsprintf( $conn_w, 'WHERE ruleID NOT IN (%Ld)', mpull($rules, 'getID')); } -echo "This may take a moment"; +echo 'This may take a moment'; do { queryfx( $conn_w, 'DELETE FROM %T %Q LIMIT 1000', HeraldRule::TABLE_RULE_APPLIED, $clause); - echo "."; + echo '.'; } while ($conn_w->getAffectedRows()); $table->endReadLocking(); $table->saveTransaction(); echo "\nDone.\n"; diff --git a/resources/sql/patches/117.repositorydescription.php b/resources/sql/patches/117.repositorydescription.php index 18ee4a463..692a78335 100644 --- a/resources/sql/patches/117.repositorydescription.php +++ b/resources/sql/patches/117.repositorydescription.php @@ -1,6 +1,6 @@ establishConnection('w'); if (queryfx_one($conn, "SHOW COLUMNS FROM `repository` LIKE 'description'")) { - queryfx($conn, "ALTER TABLE `repository` DROP `description`"); + queryfx($conn, 'ALTER TABLE `repository` DROP `description`'); } diff --git a/resources/sql/patches/131.migraterevisionquery.php b/resources/sql/patches/131.migraterevisionquery.php index 027edbaeb..cf505b629 100644 --- a/resources/sql/patches/131.migraterevisionquery.php +++ b/resources/sql/patches/131.migraterevisionquery.php @@ -1,35 +1,35 @@ openTransaction(); $table->beginReadLocking(); $conn_w = $table->establishConnection('w'); -echo "Migrating revisions"; +echo 'Migrating revisions'; do { $revisions = $table->loadAllWhere('branchName IS NULL LIMIT 1000'); foreach ($revisions as $revision) { - echo "."; + echo '.'; $diff = $revision->loadActiveDiff(); if (!$diff) { continue; } $branch_name = $diff->getBranch(); $arc_project_phid = $diff->getArcanistProjectPHID(); queryfx( $conn_w, 'UPDATE %T SET branchName = %s, arcanistProjectPHID = %s WHERE id = %d', $table->getTableName(), $branch_name, $arc_project_phid, $revision->getID()); } } while (count($revisions) == 1000); $table->endReadLocking(); $table->saveTransaction(); echo "\nDone.\n"; diff --git a/resources/sql/patches/20121209.xmacromigrate.php b/resources/sql/patches/20121209.xmacromigrate.php index 0372faafa..df52cb317 100644 --- a/resources/sql/patches/20121209.xmacromigrate.php +++ b/resources/sql/patches/20121209.xmacromigrate.php @@ -1,23 +1,23 @@ openTransaction(); foreach (new LiskMigrationIterator($table) as $macro) { if ($macro->getPHID()) { continue; } - echo "."; + echo '.'; queryfx( $macro->establishConnection('w'), 'UPDATE %T SET phid = %s WHERE id = %d', $macro->getTableName(), $macro->generatePHID(), $macro->getID()); } $table->saveTransaction(); echo "\nDone.\n"; diff --git a/resources/sql/patches/20130201.revisionunsubscribed.php b/resources/sql/patches/20130201.revisionunsubscribed.php index b15379893..38f1e5207 100644 --- a/resources/sql/patches/20130201.revisionunsubscribed.php +++ b/resources/sql/patches/20130201.revisionunsubscribed.php @@ -1,33 +1,33 @@ openTransaction(); // We couldn't use new LiskMigrationIterator($table) because the $unsubscribed // property gets deleted. $revs = queryfx_all( $table->establishConnection('w'), 'SELECT id, phid, unsubscribed FROM differential_revision'); foreach ($revs as $rev) { - echo "."; + echo '.'; $unsubscribed = json_decode($rev['unsubscribed']); if (!$unsubscribed) { continue; } $editor = new PhabricatorEdgeEditor(); $editor->setSuppressEvents(true); foreach ($unsubscribed as $user_phid => $_) { $editor->addEdge( $rev['phid'], PhabricatorEdgeConfig::TYPE_OBJECT_HAS_UNSUBSCRIBER, $user_phid); } $editor->save(); } $table->saveTransaction(); echo "Done.\n"; diff --git a/resources/sql/patches/20130409.commitdrev.php b/resources/sql/patches/20130409.commitdrev.php index 300b28238..86d00ac21 100644 --- a/resources/sql/patches/20130409.commitdrev.php +++ b/resources/sql/patches/20130409.commitdrev.php @@ -1,33 +1,33 @@ setSuppressEvents(true); $commit_table->establishConnection('w'); $edges = 0; foreach (new LiskMigrationIterator($commit_table) as $commit) { $data = $commit->loadOneRelative($data_table, 'commitID'); if (!$data) { continue; } $revision_phid = $data->getCommitDetail('differential.revisionPHID'); if (!$revision_phid) { continue; } $commit_drev = PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV; $editor->addEdge($commit->getPHID(), $commit_drev, $revision_phid); $edges++; if ($edges % 256 == 0) { - echo "."; + echo '.'; $editor->save(); $editor = id(new PhabricatorEdgeEditor())->setSuppressEvents(true); } } -echo "."; +echo '.'; $editor->save(); echo "\nDone.\n"; diff --git a/resources/sql/patches/20130502.countdownrevamp2.php b/resources/sql/patches/20130502.countdownrevamp2.php index f4fc96413..6f7e41e1d 100644 --- a/resources/sql/patches/20130502.countdownrevamp2.php +++ b/resources/sql/patches/20130502.countdownrevamp2.php @@ -1,23 +1,23 @@ openTransaction(); foreach (new LiskMigrationIterator($table) as $countdown) { if ($countdown->getPHID()) { continue; } - echo "."; + echo '.'; queryfx( $countdown->establishConnection('w'), 'UPDATE %T SET phid = %s WHERE id = %d', $countdown->getTableName(), $countdown->generatePHID(), $countdown->getID()); } $table->saveTransaction(); echo "\nDone.\n"; diff --git a/resources/sql/patches/20130703.legalpaddocdenorm.php b/resources/sql/patches/20130703.legalpaddocdenorm.php index 9492e26bc..da31e906f 100644 --- a/resources/sql/patches/20130703.legalpaddocdenorm.php +++ b/resources/sql/patches/20130703.legalpaddocdenorm.php @@ -1,46 +1,46 @@ openTransaction(); foreach (new LiskMigrationIterator($table) as $document) { $updated = false; $id = $document->getID(); echo "Document {$id}: "; if (!$document->getTitle()) { $document_body = id(new LegalpadDocumentBody()) ->loadOneWhere('phid = %s', $document->getDocumentBodyPHID()); $title = $document_body->getTitle(); $document->setTitle($title); $updated = true; echo "Added title: $title\n"; } else { echo "-\n"; } if (!$document->getContributorCount() || !$document->getRecentContributorPHIDs()) { $updated = true; $type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_CONTRIBUTOR; $contributors = PhabricatorEdgeQuery::loadDestinationPHIDs( $document->getPHID(), $type); $document->setRecentContributorPHIDs(array_slice($contributors, 0, 3)); echo "Added recent contributor phids.\n"; $document->setContributorCount(count($contributors)); echo "Added contributor count.\n"; } if (!$updated) { echo "-\n"; continue; } $document->save(); } $table->saveTransaction(); echo "Done.\n"; diff --git a/resources/sql/patches/20130711.pholioimageobsolete.php b/resources/sql/patches/20130711.pholioimageobsolete.php index 738f82085..eae9a479d 100644 --- a/resources/sql/patches/20130711.pholioimageobsolete.php +++ b/resources/sql/patches/20130711.pholioimageobsolete.php @@ -1,23 +1,23 @@ openTransaction(); foreach (new LiskMigrationIterator($table) as $image) { if ($image->getPHID()) { continue; } - echo "."; + echo '.'; queryfx( $image->establishConnection('w'), 'UPDATE %T SET phid = %s WHERE id = %d', $image->getTableName(), $image->generatePHID(), $image->getID()); } $table->saveTransaction(); echo "\nDone.\n"; diff --git a/resources/sql/patches/20131205.buildstepordermig.php b/resources/sql/patches/20131205.buildstepordermig.php index 9721e1527..b17724252 100644 --- a/resources/sql/patches/20131205.buildstepordermig.php +++ b/resources/sql/patches/20131205.buildstepordermig.php @@ -1,41 +1,41 @@ establishConnection('w'); $viewer = PhabricatorUser::getOmnipotentUser(); // Since HarbormasterBuildStepQuery has been updated to handle the // correct order, we can't use the built in database access. foreach (new LiskMigrationIterator($table) as $plan) { $planname = $plan->getName(); echo "Migrating steps in {$planname}...\n"; $rows = queryfx_all( $conn_w, - "SELECT id, sequence FROM harbormaster_buildstep ". - "WHERE buildPlanPHID = %s ". - "ORDER BY id ASC", + 'SELECT id, sequence FROM harbormaster_buildstep '. + 'WHERE buildPlanPHID = %s '. + 'ORDER BY id ASC', $plan->getPHID()); $sequence = 1; foreach ($rows as $row) { $id = $row['id']; $existing = $row['sequence']; if ($existing != 0) { echo " - {$id} (already migrated)...\n"; continue; } echo " - {$id} to position {$sequence}...\n"; queryfx( $conn_w, - "UPDATE harbormaster_buildstep ". - "SET sequence = %d ". - "WHERE id = %d", + 'UPDATE harbormaster_buildstep '. + 'SET sequence = %d '. + 'WHERE id = %d', $sequence, $id); $sequence++; } } echo "Done.\n"; diff --git a/scripts/calendar/import_us_holidays.php b/scripts/calendar/import_us_holidays.php index 2ae82a4d5..78d1d50c3 100755 --- a/scripts/calendar/import_us_holidays.php +++ b/scripts/calendar/import_us_holidays.php @@ -1,62 +1,62 @@ #!/usr/bin/env php "New Year's Day", - '2014-01-20' => "Birthday of Martin Luther King, Jr.", + '2014-01-20' => 'Birthday of Martin Luther King, Jr.', '2014-02-17' => "Washington's Birthday", - '2014-05-26' => "Memorial Day", - '2014-07-04' => "Independence Day", - '2014-09-01' => "Labor Day", - '2014-10-13' => "Columbus Day", - '2014-11-11' => "Veterans Day", - '2014-11-27' => "Thanksgiving Day", - '2014-12-25' => "Christmas Day", + '2014-05-26' => 'Memorial Day', + '2014-07-04' => 'Independence Day', + '2014-09-01' => 'Labor Day', + '2014-10-13' => 'Columbus Day', + '2014-11-11' => 'Veterans Day', + '2014-11-27' => 'Thanksgiving Day', + '2014-12-25' => 'Christmas Day', '2015-01-01' => "New Year's Day", - '2015-01-19' => "Birthday of Martin Luther King, Jr.", + '2015-01-19' => 'Birthday of Martin Luther King, Jr.', '2015-02-16' => "Washington's Birthday", - '2015-05-25' => "Memorial Day", - '2015-07-03' => "Independence Day", - '2015-09-07' => "Labor Day", - '2015-10-12' => "Columbus Day", - '2015-11-11' => "Veterans Day", - '2015-11-26' => "Thanksgiving Day", - '2015-12-25' => "Christmas Day", + '2015-05-25' => 'Memorial Day', + '2015-07-03' => 'Independence Day', + '2015-09-07' => 'Labor Day', + '2015-10-12' => 'Columbus Day', + '2015-11-11' => 'Veterans Day', + '2015-11-26' => 'Thanksgiving Day', + '2015-12-25' => 'Christmas Day', '2016-01-01' => "New Year's Day", - '2016-01-18' => "Birthday of Martin Luther King, Jr.", + '2016-01-18' => 'Birthday of Martin Luther King, Jr.', '2016-02-15' => "Washington's Birthday", - '2016-05-30' => "Memorial Day", - '2016-07-04' => "Independence Day", - '2016-09-05' => "Labor Day", - '2016-10-10' => "Columbus Day", - '2016-11-11' => "Veterans Day", - '2016-11-24' => "Thanksgiving Day", - '2016-12-26' => "Christmas Day", + '2016-05-30' => 'Memorial Day', + '2016-07-04' => 'Independence Day', + '2016-09-05' => 'Labor Day', + '2016-10-10' => 'Columbus Day', + '2016-11-11' => 'Veterans Day', + '2016-11-24' => 'Thanksgiving Day', + '2016-12-26' => 'Christmas Day', '2017-01-02' => "New Year's Day", - '2017-01-16' => "Birthday of Martin Luther King, Jr.", + '2017-01-16' => 'Birthday of Martin Luther King, Jr.', '2017-02-10' => "Washington's Birthday", - '2017-05-29' => "Memorial Day", - '2017-07-04' => "Independence Day", - '2017-09-04' => "Labor Day", - '2017-10-09' => "Columbus Day", - '2017-11-10' => "Veterans Day", - '2017-11-23' => "Thanksgiving Day", - '2017-12-25' => "Christmas Day", + '2017-05-29' => 'Memorial Day', + '2017-07-04' => 'Independence Day', + '2017-09-04' => 'Labor Day', + '2017-10-09' => 'Columbus Day', + '2017-11-10' => 'Veterans Day', + '2017-11-23' => 'Thanksgiving Day', + '2017-12-25' => 'Christmas Day', ); $table = new PhabricatorCalendarHoliday(); $conn_w = $table->establishConnection('w'); $table_name = $table->getTableName(); foreach ($holidays as $day => $name) { queryfx( $conn_w, 'INSERT IGNORE INTO %T (day, name) VALUES (%s, %s)', $table_name, $day, $name); } diff --git a/scripts/mail/mail_handler.php b/scripts/mail/mail_handler.php index 63d6688a5..214b51ee1 100755 --- a/scripts/mail/mail_handler.php +++ b/scripts/mail/mail_handler.php @@ -1,94 +1,94 @@ #!/usr/bin/env php 1) { foreach (array_slice($argv, 1) as $arg) { if (!preg_match('/^-/', $arg)) { $_SERVER['PHABRICATOR_ENV'] = $arg; break; } } } $root = dirname(dirname(dirname(__FILE__))); require_once $root.'/scripts/__init_script__.php'; require_once $root.'/externals/mimemailparser/MimeMailParser.class.php'; $args = new PhutilArgumentParser($argv); $args->parseStandardArguments(); $args->parse( array( array( 'name' => 'process-duplicates', 'help' => pht( "Process this message, even if it's a duplicate of another message. ". "This is mostly useful when debugging issues with mail routing."), ), array( 'name' => 'env', 'wildcard' => true, ), )); $parser = new MimeMailParser(); $parser->setText(file_get_contents('php://stdin')); $text_body = $parser->getMessageBody('text'); $text_body_headers = $parser->getMessageBodyHeaders('text'); $content_type = idx($text_body_headers, 'content-type'); if ( !phutil_is_utf8($text_body) && (preg_match('/charset="(.*?)"/', $content_type, $matches) || preg_match('/charset=(\S+)/', $content_type, $matches)) ) { - $text_body = phutil_utf8_convert($text_body, "UTF-8", $matches[1]); + $text_body = phutil_utf8_convert($text_body, 'UTF-8', $matches[1]); } $headers = $parser->getHeaders(); -$headers['subject'] = iconv_mime_decode($headers['subject'], 0, "UTF-8"); -$headers['from'] = iconv_mime_decode($headers['from'], 0, "UTF-8"); +$headers['subject'] = iconv_mime_decode($headers['subject'], 0, 'UTF-8'); +$headers['from'] = iconv_mime_decode($headers['from'], 0, 'UTF-8'); if ($args->getArg('process-duplicates')) { $headers['message-id'] = Filesystem::readRandomCharacters(64); } $received = new PhabricatorMetaMTAReceivedMail(); $received->setHeaders($headers); $received->setBodies(array( 'text' => $text_body, 'html' => $parser->getMessageBody('html'), )); $attachments = array(); foreach ($parser->getAttachments() as $attachment) { if (preg_match('@text/(plain|html)@', $attachment->getContentType()) && $attachment->getContentDisposition() == 'inline') { // If this is an "inline" attachment with some sort of text content-type, // do not treat it as a file for attachment. MimeMailParser already picked // it up in the getMessageBody() call above. We still want to treat 'inline' // attachments with other content types (e.g., images) as attachments. continue; } $file = PhabricatorFile::newFromFileData( $attachment->getContent(), array( 'name' => $attachment->getFilename(), )); $attachments[] = $file->getPHID(); } try { $received->setAttachments($attachments); $received->save(); $received->processReceivedMail(); } catch (Exception $e) { $received ->setMessage('EXCEPTION: '.$e->getMessage()) ->save(); throw $e; } diff --git a/scripts/repository/rebuild_summaries.php b/scripts/repository/rebuild_summaries.php index d0bb215c3..a98a6f01b 100755 --- a/scripts/repository/rebuild_summaries.php +++ b/scripts/repository/rebuild_summaries.php @@ -1,53 +1,53 @@ #!/usr/bin/env php establishConnection('w'); $sizes = queryfx_all( $conn_w, 'SELECT repositoryID, count(*) N FROM %T GROUP BY repositoryID', $commit->getTableName()); $sizes = ipull($sizes, 'N', 'repositoryID'); $maxes = queryfx_all( $conn_w, 'SELECT repositoryID, max(epoch) maxEpoch FROM %T GROUP BY repositoryID', $commit->getTableName()); $maxes = ipull($maxes, 'maxEpoch', 'repositoryID'); $repository_ids = array_keys($sizes + $maxes); -echo "Updating ".count($repository_ids)." repositories"; +echo 'Updating '.count($repository_ids).' repositories'; foreach ($repository_ids as $repository_id) { $last_commit = queryfx_one( $conn_w, 'SELECT id FROM %T WHERE repositoryID = %d AND epoch = %d LIMIT 1', $commit->getTableName(), $repository_id, idx($maxes, $repository_id, 0)); if ($last_commit) { $last_commit = $last_commit['id']; } else { $last_commit = 0; } queryfx( $conn_w, 'INSERT INTO %T (repositoryID, lastCommitID, size, epoch) VALUES (%d, %d, %d, %d) ON DUPLICATE KEY UPDATE lastCommitID = VALUES(lastCommitID), size = VALUES(size), epoch = VALUES(epoch)', PhabricatorRepository::TABLE_SUMMARY, $repository_id, $last_commit, idx($sizes, $repository_id, 0), idx($maxes, $repository_id, 0)); - echo "."; + echo '.'; } echo "\ndone.\n"; diff --git a/scripts/repository/reparse.php b/scripts/repository/reparse.php index 37730a7b0..9e4e86e9e 100755 --- a/scripts/repository/reparse.php +++ b/scripts/repository/reparse.php @@ -1,291 +1,291 @@ #!/usr/bin/env php setSynopsis(<<parseStandardArguments(); $args->parse( array( // what array( 'name' => 'revision', 'wildcard' => true, ), array( 'name' => 'all', 'param' => 'callsign or phid', 'help' => 'Reparse all commits in the specified repository. This '. 'mode queues parsers into the task queue; you must run '. 'taskmasters to actually do the parses. Use with '. '__--force-local__ to run the tasks locally instead of '. 'with taskmasters.', ), array( 'name' => 'min-date', 'param' => 'date', 'help' => 'Must be used with __--all__, this will exclude commits '. 'which are earlier than __date__.'. "\n".$min_date_usage_examples, ), // which parts array( 'name' => 'message', 'help' => 'Reparse commit messages.', ), array( 'name' => 'change', 'help' => 'Reparse changes.', ), array( 'name' => 'herald', 'help' => 'Reevaluate Herald rules (may send huge amounts of email!)', ), array( 'name' => 'owners', 'help' => 'Reevaluate related commits for owners packages (may '. 'delete existing relationship entries between your '. 'package and some old commits!)', ), array( 'name' => 'harbormaster', 'help' => 'EXPERIMENTAL. Execute Harbormaster.', ), // misc options array( 'name' => 'force', 'short' => 'f', 'help' => 'Act noninteractively, without prompting.', ), array( 'name' => 'force-local', 'help' => 'Only used with __--all__, use this to run the tasks '. 'locally instead of deferring them to taskmaster daemons.', ), )); $all_from_repo = $args->getArg('all'); $reparse_message = $args->getArg('message'); $reparse_change = $args->getArg('change'); $reparse_herald = $args->getArg('herald'); $reparse_owners = $args->getArg('owners'); $reparse_harbormaster = $args->getArg('harbormaster'); $reparse_what = $args->getArg('revision'); $force = $args->getArg('force'); $force_local = $args->getArg('force-local'); $min_date = $args->getArg('min-date'); if (!$all_from_repo && !$reparse_what) { - usage("Specify a commit or repository to reparse."); + usage('Specify a commit or repository to reparse.'); } if ($all_from_repo && $reparse_what) { $commits = implode(', ', $reparse_what); usage( "Specify a commit or repository to reparse, not both:\n". "All from repo: ".$all_from_repo."\n". "Commit(s) to reparse: ".$commits); } if (!$reparse_message && !$reparse_change && !$reparse_herald && !$reparse_owners && !$reparse_harbormaster) { - usage("Specify what information to reparse with --message, --change, ". - "--herald, --harbormaster, and/or --owners"); + usage('Specify what information to reparse with --message, --change, '. + '--herald, --harbormaster, and/or --owners'); } $min_timestamp = false; if ($min_date) { $min_timestamp = strtotime($min_date); if (!$all_from_repo) { usage( "You must use --all if you specify --min-date\n". "e.g.\n". " ./reparse.php --all TEST --owners --min-date yesterday"); } // previous to PHP 5.1.0 you would compare with -1, instead of false if (false === $min_timestamp) { usage( "Supplied --min-date is not valid\n". "Supplied value: '".$min_date."'\n". $min_date_usage_examples); } } if ($reparse_owners && !$force) { echo phutil_console_wrap( - "You are about to recreate the relationship entries between the commits ". - "and the packages they touch. This might delete some existing ". - "relationship entries for some old commits."); + 'You are about to recreate the relationship entries between the commits '. + 'and the packages they touch. This might delete some existing '. + 'relationship entries for some old commits.'); if (!phutil_console_confirm('Are you ready to continue?')) { echo "Cancelled.\n"; exit(1); } } $commits = array(); if ($all_from_repo) { $repository = id(new PhabricatorRepository())->loadOneWhere( 'callsign = %s OR phid = %s', $all_from_repo, $all_from_repo); if (!$repository) { throw new Exception("Unknown repository {$all_from_repo}!"); } $constraint = ''; if ($min_timestamp) { echo "Excluding entries before UNIX timestamp: ".$min_timestamp."\n"; $table = new PhabricatorRepositoryCommit(); $conn_r = $table->establishConnection('r'); $constraint = qsprintf( $conn_r, 'AND epoch >= %d', $min_timestamp); } $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere( 'repositoryID = %d %Q', $repository->getID(), $constraint); $callsign = $repository->getCallsign(); if (!$commits) { echo "No commits have been discovered in {$callsign} repository!\n"; exit; } } else { $commits = array(); foreach ($reparse_what as $identifier) { $matches = null; if (!preg_match('/r([A-Z]+)([a-z0-9]+)/', $identifier, $matches)) { throw new Exception("Can't parse commit identifier!"); } $callsign = $matches[1]; $commit_identifier = $matches[2]; $repository = id(new PhabricatorRepository())->loadOneWhere( 'callsign = %s', $callsign); if (!$repository) { throw new Exception("No repository with callsign '{$callsign}'!"); } $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere( 'repositoryID = %d AND commitIdentifier = %s', $repository->getID(), $commit_identifier); if (!$commit) { throw new Exception( "No matching commit '{$commit_identifier}' in repository ". "'{$callsign}'. (For git and mercurial repositories, you must specify ". "the entire commit hash.)"); } $commits[] = $commit; } } if ($all_from_repo && !$force_local) { echo phutil_console_format( '**NOTE**: This script will queue tasks to reparse the data. Once the '. 'tasks have been queued, you need to run Taskmaster daemons to execute '. 'them.'); echo "\n\n"; echo "QUEUEING TASKS (".number_format(count($commits))." Commits):\n"; } $tasks = array(); foreach ($commits as $commit) { $classes = array(); switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: if ($reparse_message) { $classes[] = 'PhabricatorRepositoryGitCommitMessageParserWorker'; } if ($reparse_change) { $classes[] = 'PhabricatorRepositoryGitCommitChangeParserWorker'; } break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: if ($reparse_message) { $classes[] = 'PhabricatorRepositoryMercurialCommitMessageParserWorker'; } if ($reparse_change) { $classes[] = 'PhabricatorRepositoryMercurialCommitChangeParserWorker'; } break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: if ($reparse_message) { $classes[] = 'PhabricatorRepositorySvnCommitMessageParserWorker'; } if ($reparse_change) { $classes[] = 'PhabricatorRepositorySvnCommitChangeParserWorker'; } break; } if ($reparse_herald) { $classes[] = 'PhabricatorRepositoryCommitHeraldWorker'; } if ($reparse_owners) { $classes[] = 'PhabricatorRepositoryCommitOwnersWorker'; } if ($reparse_harbormaster) { $classes[] = 'HarbormasterRunnerWorker'; } $spec = array( 'commitID' => $commit->getID(), 'only' => true, ); if ($all_from_repo && !$force_local) { foreach ($classes as $class) { PhabricatorWorker::scheduleTask($class, $spec); $commit_name = 'r'.$callsign.$commit->getCommitIdentifier(); echo " Queued '{$class}' for commit '{$commit_name}'.\n"; } } else { foreach ($classes as $class) { $worker = newv($class, array($spec)); echo "Running '{$class}'...\n"; $worker->executeTask(); } } } echo "\nDone.\n"; function usage($message) { echo phutil_console_format( '**Usage Exception:** '.$message."\n". "Use __--help__ to display full help\n"); exit(1); } diff --git a/scripts/repository/save_lint.php b/scripts/repository/save_lint.php index 0a23adb49..6cd066413 100755 --- a/scripts/repository/save_lint.php +++ b/scripts/repository/save_lint.php @@ -1,58 +1,58 @@ #!/usr/bin/env php setTagline('save lint errors to database') ->setSynopsis($synopsis) ->parseStandardArguments() ->parse(array( array( 'name' => 'all', 'help' => - "Discover problems in the whole repository instead of just changes ". - "since the last run.", + 'Discover problems in the whole repository instead of just changes '. + 'since the last run.', ), array( 'name' => 'arc', 'param' => 'path', 'default' => 'arc', - 'help' => "Path to Arcanist executable.", + 'help' => 'Path to Arcanist executable.', ), array( 'name' => 'severity', 'param' => 'string', 'default' => ArcanistLintSeverity::SEVERITY_ADVICE, - 'help' => "Minimum severity, one of ArcanistLintSeverity constants.", + 'help' => 'Minimum severity, one of ArcanistLintSeverity constants.', ), array( 'name' => 'chunk-size', 'param' => 'number', 'default' => 256, - 'help' => "Number of paths passed to `arc` at once.", + 'help' => 'Number of paths passed to `arc` at once.', ), array( 'name' => 'blame', - 'help' => "Assign lint errors to authors who last modified the line.", + 'help' => 'Assign lint errors to authors who last modified the line.', ), )); echo "Saving lint errors to database...\n"; $count = id(new DiffusionLintSaveRunner()) ->setAll($args->getArg('all', false)) ->setArc($args->getArg('arc')) ->setSeverity($args->getArg('severity')) ->setChunkSize($args->getArg('chunk-size')) ->setNeedsBlame($args->getArg('blame')) ->run('.'); echo "\nProcessed {$count} files.\n"; diff --git a/scripts/ssh/ssh-auth.php b/scripts/ssh/ssh-auth.php index aba2b20f0..eeebfecd1 100755 --- a/scripts/ssh/ssh-auth.php +++ b/scripts/ssh/ssh-auth.php @@ -1,53 +1,53 @@ #!/usr/bin/env php establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT userName, keyBody, keyType FROM %T u JOIN %T ssh ON u.phid = ssh.userPHID', $user_dao->getTableName(), $ssh_dao->getTableName()); if (!$rows) { - echo pht("No keys found.")."\n"; + echo pht('No keys found.')."\n"; exit(1); } $bin = $root.'/bin/ssh-exec'; foreach ($rows as $row) { $user = $row['userName']; $cmd = csprintf('%s --phabricator-ssh-user %s', $bin, $user); // This is additional escaping for the SSH 'command="..."' string. $cmd = addcslashes($cmd, '"\\'); // Strip out newlines and other nonsense from the key type and key body. $type = $row['keyType']; $type = preg_replace('@[\x00-\x20]+@', '', $type); $key = $row['keyBody']; $key = preg_replace('@[\x00-\x20]+@', '', $key); $options = array( 'command="'.$cmd.'"', 'no-port-forwarding', 'no-X11-forwarding', 'no-agent-forwarding', 'no-pty', ); $options = implode(',', $options); $lines[] = $options.' '.$type.' '.$key."\n"; } echo implode('', $lines); exit(0); diff --git a/scripts/ssh/ssh-exec.php b/scripts/ssh/ssh-exec.php index 6dba54b1e..395ae4bd9 100755 --- a/scripts/ssh/ssh-exec.php +++ b/scripts/ssh/ssh-exec.php @@ -1,159 +1,159 @@ #!/usr/bin/env php setTagline('receive SSH requests'); $args->setSynopsis(<<parse( array( array( 'name' => 'phabricator-ssh-user', 'param' => 'username', ), array( 'name' => 'ssh-command', 'param' => 'command', ), )); try { $user_name = $args->getArg('phabricator-ssh-user'); if (!strlen($user_name)) { - throw new Exception("No username."); + throw new Exception('No username.'); } $user = id(new PhabricatorUser())->loadOneWhere( 'userName = %s', $user_name); if (!$user) { - throw new Exception("Invalid username."); + throw new Exception('Invalid username.'); } $ssh_log->setData( array( 'u' => $user->getUsername(), 'P' => $user->getPHID(), )); if (!$user->isUserActivated()) { - throw new Exception(pht("Your account is not activated.")); + throw new Exception(pht('Your account is not activated.')); } if ($args->getArg('ssh-command')) { $original_command = $args->getArg('ssh-command'); } else { $original_command = getenv('SSH_ORIGINAL_COMMAND'); } $workflows = id(new PhutilSymbolLoader()) ->setAncestorClass('PhabricatorSSHWorkflow') ->loadObjects(); $workflow_names = mpull($workflows, 'getName', 'getName'); // Now, rebuild the original command. $original_argv = id(new PhutilShellLexer()) ->splitArguments($original_command); if (!$original_argv) { throw new Exception( pht( "Welcome to Phabricator.\n\n". "You are logged in as %s.\n\n". "You haven't specified a command to run. This means you're requesting ". "an interactive shell, but Phabricator does not provide an ". "interactive shell over SSH.\n\n". "Usually, you should run a command like `git clone` or `hg push` ". "rather than connecting directly with SSH.\n\n". "Supported commands are: %s.", $user->getUsername(), implode(', ', $workflow_names))); } $ssh_log->setData( array( 'C' => $original_argv[0], 'U' => phutil_utf8_shorten( implode(' ', array_slice($original_argv, 1)), 128), )); $command = head($original_argv); array_unshift($original_argv, 'phabricator-ssh-exec'); $original_args = new PhutilArgumentParser($original_argv); if (empty($workflow_names[$command])) { - throw new Exception("Invalid command."); + throw new Exception('Invalid command.'); } $workflow = $original_args->parseWorkflows($workflows); $workflow->setUser($user); $sock_stdin = fopen('php://stdin', 'r'); if (!$sock_stdin) { - throw new Exception("Unable to open stdin."); + throw new Exception('Unable to open stdin.'); } $sock_stdout = fopen('php://stdout', 'w'); if (!$sock_stdout) { - throw new Exception("Unable to open stdout."); + throw new Exception('Unable to open stdout.'); } $sock_stderr = fopen('php://stderr', 'w'); if (!$sock_stderr) { - throw new Exception("Unable to open stderr."); + throw new Exception('Unable to open stderr.'); } $socket_channel = new PhutilSocketChannel( $sock_stdin, $sock_stdout); $error_channel = new PhutilSocketChannel(null, $sock_stderr); $metrics_channel = new PhutilMetricsChannel($socket_channel); $workflow->setIOChannel($metrics_channel); $workflow->setErrorChannel($error_channel); $rethrow = null; try { $err = $workflow->execute($original_args); $metrics_channel->flush(); $error_channel->flush(); } catch (Exception $ex) { $rethrow = $ex; } // Always write this if we got as far as building a metrics channel. $ssh_log->setData( array( 'i' => $metrics_channel->getBytesRead(), 'o' => $metrics_channel->getBytesWritten(), )); if ($rethrow) { throw $rethrow; } } catch (Exception $ex) { fwrite(STDERR, "phabricator-ssh-exec: ".$ex->getMessage()."\n"); $err = 1; } $ssh_log->setData( array( 'c' => $err, 'T' => (int)(1000000 * (microtime(true) - $ssh_start_time)), )); exit($err); diff --git a/scripts/symbols/generate_ctags_symbols.php b/scripts/symbols/generate_ctags_symbols.php index 9a8ee3060..42d3dbf25 100755 --- a/scripts/symbols/generate_ctags_symbols.php +++ b/scripts/symbols/generate_ctags_symbols.php @@ -1,124 +1,124 @@ #!/usr/bin/env php limit(8) as $file => $future) { $tags = $future->resolve(); $tags = explode("\n", $tags[1]); foreach ($tags as $tag) { - $parts = explode(";", $tag); + $parts = explode(';', $tag); // skip lines that we can not parse if (count($parts) < 2) { continue; } // split ctags information $tag_info = explode("\t", $parts[0]); // split exuberant ctags "extension fields" (additional information) $parts[1] = trim($parts[1], "\t \""); $extension_fields = explode("\t", $parts[1]); // skip lines that we can not parse if (count($tag_info) < 3 || count($extension_fields) < 2) { continue; } // default $context to empty $extension_fields[] = ''; list($token, $file_path, $line_num) = $tag_info; list($type, $language, $context) = $extension_fields; // skip lines with tokens containing a space if (strpos($token, ' ') !== false) { continue; } // strip "language:" $language = substr($language, 9); // To keep consistent with "Separate with commas, for example: php, py" // in Arcanist Project edit form. - $language = str_ireplace("python", "py", $language); + $language = str_ireplace('python', 'py', $language); // also, "normalize" c++ and c# - $language = str_ireplace("c++", "cpp", $language); - $language = str_ireplace("c#", "cs", $language); + $language = str_ireplace('c++', 'cpp', $language); + $language = str_ireplace('c#', 'cs', $language); // Ruby has "singleton method", for example $type = substr(str_replace(' ', '_', $type), 0, 12); // class:foo, struct:foo, union:foo, enum:foo, ... $context = last(explode(':', $context, 2)); $ignore = array( 'variable' => true, ); if (empty($ignore[$type])) { print_symbol($file_path, $line_num, $type, $token, $context, $language); } } } function ctags_get_parser_future($file_path) { $future = new ExecFuture('ctags -n --fields=Kls -o - %s', $file_path); return $future; } function ctags_check_executable() { $future = new ExecFuture('ctags --version'); $result = $future->resolve(); if (empty($result[1])) { return false; } return true; } function print_symbol($file, $line_num, $type, $token, $context, $language) { // get rid of relative path $file = explode('/', $file); - if ($file[0] == '.' || $file[0] == "..") { + if ($file[0] == '.' || $file[0] == '..') { array_shift($file); } $file = '/' . implode('/', $file); $parts = array( $context, $token, $type, strtolower($language), $line_num, $file, ); echo implode(' ', $parts)."\n"; } diff --git a/scripts/user/account_admin.php b/scripts/user/account_admin.php index e980b5cef..3d18c16ac 100755 --- a/scripts/user/account_admin.php +++ b/scripts/user/account_admin.php @@ -1,226 +1,226 @@ #!/usr/bin/env php establishConnection('r'), 'SELECT * FROM %T LIMIT 1', $table->getTableName()); $is_first_user = (!$any_user); if ($is_first_user) { echo pht( "WARNING\n\n". "You're about to create the first account on this install. Normally, you ". "should use the web interface to create the first account, not this ". "script.\n\n". "If you use the web interface, it will drop you into a nice UI workflow ". "which gives you more help setting up your install. If you create an ". "account with this script instead, you will skip the setup help and you ". "will not be able to access it later."); - if (!phutil_console_confirm(pht("Skip easy setup and create account?"))) { - echo pht("Cancelled.")."\n"; + if (!phutil_console_confirm(pht('Skip easy setup and create account?'))) { + echo pht('Cancelled.')."\n"; exit(1); } } -echo "Enter a username to create a new account or edit an existing account."; +echo 'Enter a username to create a new account or edit an existing account.'; -$username = phutil_console_prompt("Enter a username:"); +$username = phutil_console_prompt('Enter a username:'); if (!strlen($username)) { echo "Cancelled.\n"; exit(1); } if (!PhabricatorUser::validateUsername($username)) { $valid = PhabricatorUser::describeValidUsername(); echo "The username '{$username}' is invalid. {$valid}\n"; exit(1); } $user = id(new PhabricatorUser())->loadOneWhere( 'username = %s', $username); if (!$user) { $original = new PhabricatorUser(); echo "There is no existing user account '{$username}'.\n"; $ok = phutil_console_confirm( "Do you want to create a new '{$username}' account?", $default_no = false); if (!$ok) { echo "Cancelled.\n"; exit(1); } $user = new PhabricatorUser(); $user->setUsername($username); $is_new = true; } else { $original = clone $user; echo "There is an existing user account '{$username}'.\n"; $ok = phutil_console_confirm( "Do you want to edit the existing '{$username}' account?", $default_no = false); if (!$ok) { echo "Cancelled.\n"; exit(1); } $is_new = false; } $user_realname = $user->getRealName(); if (strlen($user_realname)) { $realname_prompt = ' ['.$user_realname.']'; } else { $realname_prompt = ''; } $realname = nonempty( phutil_console_prompt("Enter user real name{$realname_prompt}:"), $user_realname); $user->setRealName($realname); // When creating a new user we prompt for an email address; when editing an // existing user we just skip this because it would be quite involved to provide // a reasonable CLI interface for editing multiple addresses and managing email // verification and primary addresses. $create_email = null; if ($is_new) { do { - $email = phutil_console_prompt("Enter user email address:"); + $email = phutil_console_prompt('Enter user email address:'); $duplicate = id(new PhabricatorUserEmail())->loadOneWhere( 'address = %s', $email); if ($duplicate) { echo "ERROR: There is already a user with that email address. ". "Each user must have a unique email address.\n"; } else { break; } } while (true); $create_email = $email; } $changed_pass = false; // This disables local echo, so the user's password is not shown as they type // it. phutil_passthru('stty -echo'); $password = phutil_console_prompt( - "Enter a password for this user [blank to leave unchanged]:"); + 'Enter a password for this user [blank to leave unchanged]:'); phutil_passthru('stty echo'); if (strlen($password)) { $changed_pass = $password; } $is_system_agent = $user->getIsSystemAgent(); $set_system_agent = phutil_console_confirm( 'Is this user a bot/script?', $default_no = !$is_system_agent); $verify_email = null; $set_verified = false; // Allow administrators to verify primary email addresses at this time in edit // scenarios. (Create will work just fine from here as we auto-verify email // on create.) if (!$is_new) { $verify_email = $user->loadPrimaryEmail(); if (!$verify_email->getIsVerified()) { $set_verified = phutil_console_confirm( 'Should the primary email address be verified?', $default_no = true); } else { // already verified so let's not make a fuss $verify_email = null; } } $is_admin = $user->getIsAdmin(); $set_admin = phutil_console_confirm( 'Should this user be an administrator?', $default_no = !$is_admin); echo "\n\nACCOUNT SUMMARY\n\n"; $tpl = "%12s %-30s %-30s\n"; printf($tpl, null, 'OLD VALUE', 'NEW VALUE'); printf($tpl, 'Username', $original->getUsername(), $user->getUsername()); printf($tpl, 'Real Name', $original->getRealName(), $user->getRealName()); if ($is_new) { printf($tpl, 'Email', '', $create_email); } printf($tpl, 'Password', null, ($changed_pass !== false) ? 'Updated' : 'Unchanged'); printf( $tpl, 'Bot/Script', $original->getIsSystemAgent() ? 'Y' : 'N', $set_system_agent ? 'Y' : 'N'); if ($verify_email) { printf( $tpl, 'Verify Email', $verify_email->getIsVerified() ? 'Y' : 'N', $set_verified ? 'Y' : 'N'); } printf( $tpl, 'Admin', $original->getIsAdmin() ? 'Y' : 'N', $set_admin ? 'Y' : 'N'); echo "\n"; -if (!phutil_console_confirm("Save these changes?", $default_no = false)) { +if (!phutil_console_confirm('Save these changes?', $default_no = false)) { echo "Cancelled.\n"; exit(1); } $user->openTransaction(); $editor = new PhabricatorUserEditor(); // TODO: This is wrong, but we have a chicken-and-egg problem when you use // this script to create the first user. $editor->setActor($user); if ($is_new) { $email = id(new PhabricatorUserEmail()) ->setAddress($create_email) ->setIsVerified(1); // Unconditionally approve new accounts created from the CLI. $user->setIsApproved(1); $editor->createNewUser($user, $email); } else { if ($verify_email) { $user->setIsEmailVerified(1); $verify_email->setIsVerified($set_verified ? 1 : 0); } $editor->updateUser($user, $verify_email); } $editor->makeAdminUser($user, $set_admin); $editor->makeSystemAgentUser($user, $set_system_agent); if ($changed_pass !== false) { $envelope = new PhutilOpaqueEnvelope($changed_pass); $editor->changePassword($user, $envelope); } $user->saveTransaction(); echo "Saved changes.\n"; diff --git a/scripts/user/add_user.php b/scripts/user/add_user.php index 958ff4b89..f2cdac396 100755 --- a/scripts/user/add_user.php +++ b/scripts/user/add_user.php @@ -1,57 +1,57 @@ #!/usr/bin/env php \n"; exit(1); } $username = $argv[1]; $email = $argv[2]; $realname = $argv[3]; $admin = $argv[4]; $admin = id(new PhabricatorUser())->loadOneWhere( 'username = %s', $argv[4]); if (!$admin) { throw new Exception( - "Admin user must be the username of a valid Phabricator account, used ". - "to send the new user a welcome email."); + 'Admin user must be the username of a valid Phabricator account, used '. + 'to send the new user a welcome email.'); } $existing_user = id(new PhabricatorUser())->loadOneWhere( 'username = %s', $username); if ($existing_user) { throw new Exception( "There is already a user with the username '{$username}'!"); } $existing_email = id(new PhabricatorUserEmail())->loadOneWhere( 'address = %s', $email); if ($existing_email) { throw new Exception( "There is already a user with the email '{$email}'!"); } $user = new PhabricatorUser(); $user->setUsername($username); $user->setRealname($realname); $user->setIsApproved(1); $email_object = id(new PhabricatorUserEmail()) ->setAddress($email) ->setIsVerified(1); id(new PhabricatorUserEditor()) ->setActor($admin) ->createNewUser($user, $email_object); $user->sendWelcomeEmail($admin); echo "Created user '{$username}' (realname='{$realname}', email='{$email}').\n"; diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php index c83746c5e..cf609011b 100644 --- a/src/aphront/AphrontRequest.php +++ b/src/aphront/AphrontRequest.php @@ -1,599 +1,599 @@ host = $host; $this->path = $path; } final public function setApplicationConfiguration( $application_configuration) { $this->applicationConfiguration = $application_configuration; return $this; } final public function getApplicationConfiguration() { return $this->applicationConfiguration; } final public function setPath($path) { $this->path = $path; return $this; } final public function getPath() { return $this->path; } final public function getHost() { // The "Host" header may include a port number, or may be a malicious // header in the form "realdomain.com:ignored@evil.com". Invoke the full // parser to extract the real domain correctly. See here for coverage of // a similar issue in Django: // // https://www.djangoproject.com/weblog/2012/oct/17/security/ $uri = new PhutilURI('http://'.$this->host); return $uri->getDomain(); } /* -( Accessing Request Data )--------------------------------------------- */ /** * @task data */ final public function setRequestData(array $request_data) { $this->requestData = $request_data; return $this; } /** * @task data */ final public function getRequestData() { return $this->requestData; } /** * @task data */ final public function getInt($name, $default = null) { if (isset($this->requestData[$name])) { return (int)$this->requestData[$name]; } else { return $default; } } /** * @task data */ final public function getBool($name, $default = null) { if (isset($this->requestData[$name])) { if ($this->requestData[$name] === 'true') { return true; } else if ($this->requestData[$name] === 'false') { return false; } else { return (bool)$this->requestData[$name]; } } else { return $default; } } /** * @task data */ final public function getStr($name, $default = null) { if (isset($this->requestData[$name])) { $str = (string)$this->requestData[$name]; // Normalize newline craziness. $str = str_replace( array("\r\n", "\r"), array("\n", "\n"), $str); return $str; } else { return $default; } } /** * @task data */ final public function getArr($name, $default = array()) { if (isset($this->requestData[$name]) && is_array($this->requestData[$name])) { return $this->requestData[$name]; } else { return $default; } } /** * @task data */ final public function getStrList($name, $default = array()) { if (!isset($this->requestData[$name])) { return $default; } $list = $this->getStr($name); $list = preg_split('/[\s,]+/', $list, $limit = -1, PREG_SPLIT_NO_EMPTY); return $list; } /** * @task data */ final public function getExists($name) { return array_key_exists($name, $this->requestData); } final public function getFileExists($name) { return isset($_FILES[$name]) && (idx($_FILES[$name], 'error') !== UPLOAD_ERR_NO_FILE); } final public function isHTTPGet() { return ($_SERVER['REQUEST_METHOD'] == 'GET'); } final public function isHTTPPost() { return ($_SERVER['REQUEST_METHOD'] == 'POST'); } final public function isAjax() { return $this->getExists(self::TYPE_AJAX); } final public function isJavelinWorkflow() { return $this->getExists(self::TYPE_WORKFLOW); } final public function isConduit() { return $this->getExists(self::TYPE_CONDUIT); } public static function getCSRFTokenName() { return '__csrf__'; } public static function getCSRFHeaderName() { return 'X-Phabricator-Csrf'; } final public function validateCSRF() { $token_name = self::getCSRFTokenName(); $token = $this->getStr($token_name); // No token in the request, check the HTTP header which is added for Ajax // requests. if (empty($token)) { $token = self::getHTTPHeader(self::getCSRFHeaderName()); } $valid = $this->getUser()->validateCSRFToken($token); if (!$valid) { // Add some diagnostic details so we can figure out if some CSRF issues // are JS problems or people accessing Ajax URIs directly with their // browsers. $more_info = array(); if ($this->isAjax()) { $more_info[] = pht('This was an Ajax request.'); } else { $more_info[] = pht('This was a Web request.'); } if ($token) { $more_info[] = pht('This request had an invalid CSRF token.'); } else { $more_info[] = pht('This request had no CSRF token.'); } // Give a more detailed explanation of how to avoid the exception // in developer mode. if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) { // TODO: Clean this up, see T1921. $more_info[] = "To avoid this error, use phabricator_form() to construct forms. " . "If you are already using phabricator_form(), make sure the form " . "'action' uses a relative URI (i.e., begins with a '/'). Forms " . "using absolute URIs do not include CSRF tokens, to prevent " . "leaking tokens to external sites.\n\n" . "If this page performs writes which do not require CSRF " . "protection (usually, filling caches or logging), you can use " . "AphrontWriteGuard::beginScopedUnguardedWrites() to temporarily " . "bypass CSRF protection while writing. You should use this only " . "for writes which can not be protected with normal CSRF " . "mechanisms.\n\n" . "Some UI elements (like PhabricatorActionListView) also have " . "methods which will allow you to render links as forms (like " . "setRenderAsForm(true))."; } // This should only be able to happen if you load a form, pull your // internet for 6 hours, and then reconnect and immediately submit, // but give the user some indication of what happened since the workflow // is incredibly confusing otherwise. throw new AphrontCSRFException( pht( "You are trying to save some data to Phabricator, but the request ". "your browser made included an incorrect token. Reload the page ". "and try again. You may need to clear your cookies.\n\n%s", implode("\n", $more_info))); } return true; } final public function isFormPost() { $post = $this->getExists(self::TYPE_FORM) && !$this->getExists(self::TYPE_HISEC) && $this->isHTTPPost(); if (!$post) { return false; } return $this->validateCSRF(); } final public function setCookiePrefix($prefix) { $this->cookiePrefix = $prefix; return $this; } final private function getPrefixedCookieName($name) { if (strlen($this->cookiePrefix)) { return $this->cookiePrefix.'_'.$name; } else { return $name; } } final public function getCookie($name, $default = null) { $name = $this->getPrefixedCookieName($name); $value = idx($_COOKIE, $name, $default); // Internally, PHP deletes cookies by setting them to the value 'deleted' // with an expiration date in the past. // At least in Safari, the browser may send this cookie anyway in some // circumstances. After logging out, the 302'd GET to /login/ consistently // includes deleted cookies on my local install. If a cookie value is // literally 'deleted', pretend it does not exist. if ($value === 'deleted') { return null; } return $value; } final public function clearCookie($name) { $name = $this->getPrefixedCookieName($name); $this->setCookieWithExpiration($name, '', time() - (60 * 60 * 24 * 30)); unset($_COOKIE[$name]); } /** * Get the domain which cookies should be set on for this request, or null * if the request does not correspond to a valid cookie domain. * * @return PhutilURI|null Domain URI, or null if no valid domain exists. * * @task cookie */ private function getCookieDomainURI() { if (PhabricatorEnv::getEnvConfig('security.require-https') && !$this->isHTTPS()) { return null; } $host = $this->getHost(); // If there's no base domain configured, just use whatever the request // domain is. This makes setup easier, and we'll tell administrators to // configure a base domain during the setup process. $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); if (!strlen($base_uri)) { return new PhutilURI('http://'.$host.'/'); } $alternates = PhabricatorEnv::getEnvConfig('phabricator.allowed-uris'); $allowed_uris = array_merge( array($base_uri), $alternates); foreach ($allowed_uris as $allowed_uri) { $uri = new PhutilURI($allowed_uri); if ($uri->getDomain() == $host) { return $uri; } } return null; } /** * Determine if security policy rules will allow cookies to be set when * responding to the request. * * @return bool True if setCookie() will succeed. If this method returns * false, setCookie() will throw. * * @task cookie */ final public function canSetCookies() { return (bool)$this->getCookieDomainURI(); } /** * Set a cookie which does not expire for a long time. * * To set a temporary cookie, see @{method:setTemporaryCookie}. * * @param string Cookie name. * @param string Cookie value. * @return this * @task cookie */ final public function setCookie($name, $value) { $far_future = time() + (60 * 60 * 24 * 365 * 5); return $this->setCookieWithExpiration($name, $value, $far_future); } /** * Set a cookie which expires soon. * * To set a durable cookie, see @{method:setCookie}. * * @param string Cookie name. * @param string Cookie value. * @return this * @task cookie */ final public function setTemporaryCookie($name, $value) { return $this->setCookieWithExpiration($name, $value, 0); } /** * Set a cookie with a given expiration policy. * * @param string Cookie name. * @param string Cookie value. * @param int Epoch timestamp for cookie expiration. * @return this * @task cookie */ final private function setCookieWithExpiration( $name, $value, $expire) { $is_secure = false; $base_domain_uri = $this->getCookieDomainURI(); if (!$base_domain_uri) { $configured_as = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); $accessed_as = $this->getHost(); throw new Exception( pht( 'This Phabricator install is configured as "%s", but you are '. 'using the domain name "%s" to access a page which is trying to '. 'set a cookie. Acccess Phabricator on the configured primary '. 'domain or a configured alternate domain. Phabricator will not '. 'set cookies on other domains for security reasons.', $configured_as, $accessed_as)); } $base_domain = $base_domain_uri->getDomain(); $is_secure = ($base_domain_uri->getProtocol() == 'https'); $name = $this->getPrefixedCookieName($name); if (php_sapi_name() == 'cli') { // Do nothing, to avoid triggering "Cannot modify header information" // warnings. // TODO: This is effectively a test for whether we're running in a unit // test or not. Move this actual call to HTTPSink? } else { setcookie( $name, $value, $expire, $path = '/', $base_domain, $is_secure, $http_only = true); } $_COOKIE[$name] = $value; return $this; } final public function setUser($user) { $this->user = $user; return $this; } final public function getUser() { return $this->user; } final public function getRequestURI() { $get = $_GET; unset($get['__path__']); $path = phutil_escape_uri($this->getPath()); return id(new PhutilURI($path))->setQueryParams($get); } final public function isDialogFormPost() { return $this->isFormPost() && $this->getStr('__dialog__'); } final public function getRemoteAddr() { return $_SERVER['REMOTE_ADDR']; } public function isHTTPS() { if (empty($_SERVER['HTTPS'])) { return false; } - if (!strcasecmp($_SERVER["HTTPS"], "off")) { + if (!strcasecmp($_SERVER['HTTPS'], 'off')) { return false; } return true; } public function isContinueRequest() { return $this->isFormPost() && $this->getStr('__continue__'); } public function isPreviewRequest() { return $this->isFormPost() && $this->getStr('__preview__'); } /** * Get application request parameters in a flattened form suitable for * inclusion in an HTTP request, excluding parameters with special meanings. * This is primarily useful if you want to ask the user for more input and * then resubmit their request. * * @return dict Original request parameters. */ public function getPassthroughRequestParameters() { return self::flattenData($this->getPassthroughRequestData()); } /** * Get request data other than "magic" parameters. * * @return dict Request data, with magic filtered out. */ public function getPassthroughRequestData() { $data = $this->getRequestData(); // Remove magic parameters like __dialog__ and __ajax__. foreach ($data as $key => $value) { if (!strncmp($key, '__', 2)) { unset($data[$key]); } } return $data; } /** * Flatten an array of key-value pairs (possibly including arrays as values) * into a list of key-value pairs suitable for submitting via HTTP request * (with arrays flattened). * * @param dict Data to flatten. * @return dict Flat data suitable for inclusion in an HTTP * request. */ public static function flattenData(array $data) { $result = array(); foreach ($data as $key => $value) { if (is_array($value)) { foreach (self::flattenData($value) as $fkey => $fvalue) { $fkey = '['.preg_replace('/(?=\[)|$/', ']', $fkey, $limit = 1); $result[$key.$fkey] = $fvalue; } } else { $result[$key] = (string)$value; } } ksort($result); return $result; } /** * Read the value of an HTTP header from `$_SERVER`, or a similar datasource. * * This function accepts a canonical header name, like `"Accept-Encoding"`, * and looks up the appropriate value in `$_SERVER` (in this case, * `"HTTP_ACCEPT_ENCODING"`). * * @param string Canonical header name, like `"Accept-Encoding"`. * @param wild Default value to return if header is not present. * @param array? Read this instead of `$_SERVER`. * @return string|wild Header value if present, or `$default` if not. */ public static function getHTTPHeader($name, $default = null, $data = null) { // PHP mangles HTTP headers by uppercasing them and replacing hyphens with // underscores, then prepending 'HTTP_'. $php_index = strtoupper($name); $php_index = str_replace('-', '_', $php_index); $try_names = array(); $try_names[] = 'HTTP_'.$php_index; if ($php_index == 'CONTENT_TYPE' || $php_index == 'CONTENT_LENGTH') { // These headers may be available under alternate names. See // http://www.php.net/manual/en/reserved.variables.server.php#110763 $try_names[] = $php_index; } if ($data === null) { $data = $_SERVER; } foreach ($try_names as $try_name) { if (array_key_exists($try_name, $data)) { return $data[$try_name]; } } return $default; } } diff --git a/src/aphront/configuration/AphrontApplicationConfiguration.php b/src/aphront/configuration/AphrontApplicationConfiguration.php index b3abbb103..b15908c2f 100644 --- a/src/aphront/configuration/AphrontApplicationConfiguration.php +++ b/src/aphront/configuration/AphrontApplicationConfiguration.php @@ -1,231 +1,231 @@ request = $request; return $this; } final public function getRequest() { return $this->request; } final public function getConsole() { return $this->console; } final public function setConsole($console) { $this->console = $console; return $this; } final public function setHost($host) { $this->host = $host; return $this; } final public function getHost() { return $this->host; } final public function setPath($path) { $this->path = $path; return $this; } final public function getPath() { return $this->path; } public function willBuildRequest() { } /* -( URI Routing )-------------------------------------------------------- */ /** * Using builtin and application routes, build the appropriate * @{class:AphrontController} class for the request. To route a request, we * first test if the HTTP_HOST is configured as a valid Phabricator URI. If * it isn't, we do a special check to see if it's a custom domain for a blog * in the Phame application and if that fails we error. Otherwise, we test * the URI against all builtin routes from @{method:getURIMap}, then against * all application routes from installed @{class:PhabricatorApplication}s. * * If we match a route, we construct the controller it points at, build it, * and return it. * * If we fail to match a route, but the current path is missing a trailing * "/", we try routing the same path with a trailing "/" and do a redirect * if that has a valid route. The idea is to canoncalize URIs for consistency, * but avoid breaking noncanonical URIs that we can easily salvage. * * NOTE: We only redirect on GET. On POST, we'd drop parameters and most * likely mutate the request implicitly, and a bad POST usually indicates a * programming error rather than a sloppy typist. * * If the failing path already has a trailing "/", or we can't route the * version with a "/", we call @{method:build404Controller}, which build a * fallback @{class:AphrontController}. * * @return pair Controller and dictionary of request * parameters. * @task routing */ final public function buildController() { $request = $this->getRequest(); if (PhabricatorEnv::getEnvConfig('security.require-https')) { if (!$request->isHTTPS()) { $https_uri = $request->getRequestURI(); $https_uri->setDomain($request->getHost()); $https_uri->setProtocol('https'); return $this->buildRedirectController($https_uri); } } $path = $request->getPath(); $host = $request->getHost(); $base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); $prod_uri = PhabricatorEnv::getEnvConfig('phabricator.production-uri'); $file_uri = PhabricatorEnv::getEnvConfig( 'security.alternate-file-domain'); $conduit_uris = PhabricatorEnv::getEnvConfig('conduit.servers'); $allowed_uris = PhabricatorEnv::getEnvConfig('phabricator.allowed-uris'); $uris = array_merge( array( $base_uri, $prod_uri, $file_uri, ), $conduit_uris, $allowed_uris); $host_match = false; foreach ($uris as $uri) { if ($host === id(new PhutilURI($uri))->getDomain()) { $host_match = true; break; } } // NOTE: If the base URI isn't defined yet, don't activate alternate // domains. if ($base_uri && !$host_match) { try { $blog = id(new PhameBlogQuery()) ->setViewer(new PhabricatorUser()) ->withDomain($host) ->executeOne(); } catch (PhabricatorPolicyException $ex) { throw new Exception( - "This blog is not visible to logged out users, so it can not be ". - "visited from a custom domain."); + 'This blog is not visible to logged out users, so it can not be '. + 'visited from a custom domain.'); } if (!$blog) { if ($prod_uri && $prod_uri != $base_uri) { $prod_str = ' or '.$prod_uri; } else { $prod_str = ''; } throw new Exception( 'Specified domain '.$host.' is not configured for Phabricator '. 'requests. Please use '.$base_uri.$prod_str.' to visit this instance.' ); } // TODO: Make this more flexible and modular so any application can // do crazy stuff here if it wants. $path = '/phame/live/'.$blog->getID().'/'.$path; } list($controller, $uri_data) = $this->buildControllerForPath($path); if (!$controller) { if (!preg_match('@/$@', $path)) { // If we failed to match anything but don't have a trailing slash, try // to add a trailing slash and issue a redirect if that resolves. list($controller, $uri_data) = $this->buildControllerForPath($path.'/'); // NOTE: For POST, just 404 instead of redirecting, since the redirect // will be a GET without parameters. if ($controller && !$request->isHTTPPost()) { $slash_uri = $request->getRequestURI()->setPath($path.'/'); return $this->buildRedirectController($slash_uri); } } return $this->build404Controller(); } return array($controller, $uri_data); } /** * Map a specific path to the corresponding controller. For a description * of routing, see @{method:buildController}. * * @return pair Controller and dictionary of request * parameters. * @task routing */ final public function buildControllerForPath($path) { $maps = array(); $maps[] = array(null, $this->getURIMap()); $applications = PhabricatorApplication::getAllInstalledApplications(); foreach ($applications as $application) { $maps[] = array($application, $application->getRoutes()); } $current_application = null; $controller_class = null; foreach ($maps as $map_info) { list($application, $map) = $map_info; $mapper = new AphrontURIMapper($map); list($controller_class, $uri_data) = $mapper->mapPath($path); if ($controller_class) { if ($application) { $current_application = $application; } break; } } if (!$controller_class) { return array(null, null); } $request = $this->getRequest(); $controller = newv($controller_class, array($request)); if ($current_application) { $controller->setCurrentApplication($current_application); } return array($controller, $uri_data); } } diff --git a/src/aphront/response/AphrontProxyResponse.php b/src/aphront/response/AphrontProxyResponse.php index 8e031a977..36f2830e2 100644 --- a/src/aphront/response/AphrontProxyResponse.php +++ b/src/aphront/response/AphrontProxyResponse.php @@ -1,74 +1,74 @@ proxy) { $this->proxy = $this->buildProxy(); } return $this->proxy; } public function setRequest($request) { $this->getProxy()->setRequest($request); return $this; } public function getRequest() { return $this->getProxy()->getRequest(); } public function getHeaders() { return $this->getProxy()->getHeaders(); } public function setCacheDurationInSeconds($duration) { $this->getProxy()->setCacheDurationInSeconds($duration); return $this; } public function setLastModified($epoch_timestamp) { $this->getProxy()->setLastModified($epoch_timestamp); return $this; } public function setHTTPResponseCode($code) { $this->getProxy()->setHTTPResponseCode($code); return $this; } public function getHTTPResponseCode() { return $this->getProxy()->getHTTPResponseCode(); } public function setFrameable($frameable) { $this->getProxy()->setFrameable($frameable); return $this; } public function getCacheHeaders() { return $this->getProxy()->getCacheHeaders(); } abstract protected function buildProxy(); abstract public function reduceProxyResponse(); final public function buildResponseString() { throw new Exception( - "AphrontProxyResponse must implement reduceProxyResponse()."); + 'AphrontProxyResponse must implement reduceProxyResponse().'); } } diff --git a/src/aphront/sink/AphrontHTTPSink.php b/src/aphront/sink/AphrontHTTPSink.php index 15822051e..cddca7b65 100644 --- a/src/aphront/sink/AphrontHTTPSink.php +++ b/src/aphront/sink/AphrontHTTPSink.php @@ -1,120 +1,120 @@ emitHTTPStatus($code, $message); } /** * Write HTTP headers to the output. * * @param list List of pairs. * @return void */ final public function writeHeaders(array $headers) { foreach ($headers as $header) { if (!is_array($header) || count($header) !== 2) { throw new Exception('Malformed header.'); } list($name, $value) = $header; if (strpos($name, ':') !== false) { throw new Exception( - "Declining to emit response with malformed HTTP header name: ". + 'Declining to emit response with malformed HTTP header name: '. $name); } // Attackers may perform an "HTTP response splitting" attack by making // the application emit certain types of headers containing newlines: // // http://en.wikipedia.org/wiki/HTTP_response_splitting // // PHP has built-in protections against HTTP response-splitting, but they // are of dubious trustworthiness: // // http://news.php.net/php.internals/57655 if (preg_match('/[\r\n\0]/', $name.$value)) { throw new Exception( "Declining to emit response with unsafe HTTP header: ". "<'".$name."', '".$value."'>."); } } foreach ($headers as $header) { list($name, $value) = $header; $this->emitHeader($name, $value); } } /** * Write HTTP body data to the output. * * @param string Body data. * @return void */ final public function writeData($data) { $this->emitData($data); } /** * Write an entire @{class:AphrontResponse} to the output. * * @param AphrontResponse The response object to write. * @return void */ final public function writeResponse(AphrontResponse $response) { // Do this first, in case it throws. $response_string = $response->buildResponseString(); $all_headers = array_merge( $response->getHeaders(), $response->getCacheHeaders()); $this->writeHTTPStatus( $response->getHTTPResponseCode(), $response->getHTTPResponseMessage()); $this->writeHeaders($all_headers); $this->writeData($response_string); } /* -( Emitting the Response )---------------------------------------------- */ abstract protected function emitHTTPStatus($code, $message = ''); abstract protected function emitHeader($name, $value); abstract protected function emitData($data); } diff --git a/src/aphront/sink/__tests__/AphrontHTTPSinkTestCase.php b/src/aphront/sink/__tests__/AphrontHTTPSinkTestCase.php index 4892de96c..05235a77b 100644 --- a/src/aphront/sink/__tests__/AphrontHTTPSinkTestCase.php +++ b/src/aphront/sink/__tests__/AphrontHTTPSinkTestCase.php @@ -1,82 +1,82 @@ writeHTTPStatus(200); $sink->writeHeaders(array(array('X-Test', 'test'))); $sink->writeData('test'); $this->assertEqual(200, $sink->getEmittedHTTPStatus()); $this->assertEqual( array(array('X-Test', 'test')), $sink->getEmittedHeaders()); $this->assertEqual('test', $sink->getEmittedData()); } public function testHTTPSinkStatusCode() { $input = $this->tryTestCaseMap( array( 200 => true, '201' => true, 1 => false, 1000 => false, 'apple' => false, '' => false, ), array($this, 'tryHTTPSinkStatusCode')); } protected function tryHTTPSinkStatusCode($input) { $sink = new AphrontIsolatedHTTPSink(); $sink->writeHTTPStatus($input); } public function testHTTPSinkResponseSplitting() { $input = $this->tryTestCaseMap( array( - "test" => true, + 'test' => true, "test\nx" => false, "test\rx" => false, "test\0x" => false, ), array($this, 'tryHTTPSinkResponseSplitting')); } protected function tryHTTPSinkResponseSplitting($input) { $sink = new AphrontIsolatedHTTPSink(); $sink->writeHeaders(array(array('X-Test', $input))); } public function testHTTPHeaderNames() { $input = $this->tryTestCaseMap( array( 'test' => true, 'test:' => false, ), array($this, 'tryHTTPHeaderNames')); } protected function tryHTTPHeaderNames($input) { $sink = new AphrontIsolatedHTTPSink(); $sink->writeHeaders(array(array($input, 'value'))); } public function testJSONContentSniff() { $response = id(new AphrontJSONResponse()) ->setContent( array( 'x' => '