Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
* upstream/master: (78 commits)
  Don't hashtarget new comments.
  Linked-to review is highlighted.
  Linked-to comment is highlighted.
  Fix links to comments from action log.
  Hotcrp-daemonize forks.
  An active matching job token can't be invalid.
  Oops
  Add `hotcrp-daemonize` command to close unwanted file descriptors.
  Add Job_Capability::canonical_token.
  api/job not found is not mistaken for OK.
  Introduce BatchProcess class.
  Update country listing
  Invalidate review-accept capabilities after 45 days, not 30.
  Nits.
  Fix prior commit for truncated s=1 graphs and flip.
  Limit size of image generated by scorechart.php.
  Canonicalize response names in document requests.
  Internal: PaperStatus::_execute_tags to save tag changes.
  Improve spacing on assign page.
  Remove references to PaperInfo::$paperTags.
  ...
  • Loading branch information
xcompass committed Oct 27, 2024
2 parents 882f4db + 9d12899 commit da90cdf
Show file tree
Hide file tree
Showing 82 changed files with 1,957 additions and 1,318 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
},
"globals": {
"Set": "readonly",
"WeakMap": "readonly"
"WeakMap": "readonly",
"Promise": "readonly"
},
"rules": {
"no-empty": 0,
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/devel/d3/d3-hotcrp.min.js
/devel/d3/node_modules
/devel/d3/package-lock.json
/devel/hotcrp-daemonize
/docs
/filestore
/logs
Expand Down
4 changes: 2 additions & 2 deletions batch/apispec.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ private function expand($fn) {
private function resolve_common_schema($name) {
if (!isset($this->schemas[$name])) {
if ($name === "pid") {
$this->schemas[$name] = [
$this->schemas[$name] = (object) [
"type" => "integer",
"minimum" => 1
];
Expand All @@ -94,7 +94,7 @@ private function resolve_common_schema($name) {
private function resolve_common_param($name) {
if (!isset($this->parameters[$name])) {
if ($name === "p") {
$this->parameters[$name] = [
$this->parameters[$name] = (object) [
"name" => "p",
"in" => "path",
"required" => true,
Expand Down
48 changes: 31 additions & 17 deletions batch/autoassign.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Autoassign_Batch {
public $q = "";
/** @var string */
public $t;
/** @var list<array{int,int}> */
/** @var list<list<int>> */
public $no_coassign = [];
/** @var list<int> */
public $pcc;
Expand Down Expand Up @@ -143,10 +143,11 @@ private function parse_arg($arg) {
$this->param["count"] = $arg["count"];
}
foreach ($arg["_"] ?? [] as $x) {
if (($eq = strpos($x, "=")) === false) {
if (($eq = strpos($x, "=")) > 0) {
$this->param[substr($x, 0, $eq)] = substr($x, $eq + 1);
} else {
$this->report([MessageItem::error("<0>`NAME=VALUE` format expected for parameter arguments")], 3);
}
$this->param[substr($x, 0, $eq)] = substr($x, $eq + 1);
}
$this->q = $arg["q"] ?? $this->q;
if (isset($arg["all"])) {
Expand All @@ -165,31 +166,39 @@ private function parse_arg($arg) {
if (($neg = str_starts_with($utxt, "-"))) {
$utxt = substr($utxt, 1);
}
$cs = new ContactSearch(ContactSearch::F_USER | ContactSearch::F_TAG | ContactSearch::F_USERID | ContactSearch::F_PC, $utxt, $this->user);
$uids = $this->find_pc($utxt);
if ($neg) {
$pcc = array_diff($pcc, $cs->user_ids());
$pcc = array_diff($pcc, $uids);
} else {
$pcc = array_unique(array_merge($pcc, $cs->user_ids()));
$pcc = array_unique(array_merge($pcc, $uids));
}
$pcc = array_values($pcc);
}
$this->pcc = $pcc;
foreach ($arg["disjoint"] ?? [] as $dtxt) {
if (($comma = strpos($dtxt, ",")) !== false
&& ($uid1 = $this->find_pc(substr($dtxt, 0, $comma))) !== null
&& ($uid2 = $this->find_pc(substr($dtxt, $comma + 2))) !== null) {
$this->no_coassign[] = [$uid1, $uid2];
$l = [];
foreach (explode(",", $dtxt) as $w) {
$uids = $w === "" ? [] : $this->find_pc($w);
if (count($uids) > 0) {
$l = array_merge($l, $uids);
} else {
$l = [];
break;
}
}
if (count($l) >= 2) {
$this->no_coassign[] = array_values(array_unique($l));
} else {
$this->reportx([MessageItem::error("<0>`USER1,USER2` expected for `--disjoint`")], 3);
$this->reportx([MessageItem::error("<0>`USER1,USER2[,...]` expected for `--disjoint`")], 3);
}
}
}

/** @return int */
/** @param string $s
* @return list<int> */
private function find_pc($s) {
$cs = new ContactSearch(ContactSearch::F_USER | ContactSearch::F_PC | ContactSearch::F_USERID, $s, $this->user);
$uids = $cs->user_ids();
return count($uids) === 1 ? $uids[0] : null;
$cs = new ContactSearch(ContactSearch::F_USER | ContactSearch::F_TAG | ContactSearch::F_PC | ContactSearch::F_USERID, $s, $this->user);
return $cs->user_ids();
}

private function complete_arg() {
Expand Down Expand Up @@ -253,8 +262,13 @@ function execute() {
$aa = call_user_func($this->gj->function, $this->user, $this->pcc, $pids, $this->param, $this->gj);
}
'@phan-var-force Autoassigner $aa';
foreach ($this->no_coassign as $pair) {
$aa->avoid_coassignment($pair[0], $pair[1]);
foreach ($this->no_coassign as $l) {
$n = count($l);
for ($i = 0; $i < $n - 1; ++$i) {
for ($j = $i + 1; $j < $n; ++$j) {
$aa->avoid_coassignment($l[$i], $l[$j]);
}
}
}
$this->report($aa->message_list(), $aa->has_error() ? 1 : null);

Expand Down
25 changes: 23 additions & 2 deletions batch/checkinvariants.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
// checkinvariants.php -- HotCRP batch invariant checking script
// Copyright (c) 2006-2023 Eddie Kohler; see LICENSE.
// Copyright (c) 2006-2024 Eddie Kohler; see LICENSE.

if (realpath($_SERVER["PHP_SELF"]) === __FILE__) {
require_once(dirname(__DIR__) . "/src/init.php");
Expand Down Expand Up @@ -141,6 +141,10 @@ function run() {
$this->report_fix("document match");
$this->fix_document_match();
}
if (isset($ic->problems["user_whitespace"]) && $this->want_fix("whitespace")) {
$this->report_fix("whitespace");
$this->fix_whitespace();
}
return 0;
}

Expand Down Expand Up @@ -171,6 +175,23 @@ private function fix_document_match() {
$this->conf->qe("update Paper p join PaperStorage s on (s.paperId=p.paperId and s.paperStorageId=p.finalPaperStorageId) set p.size=s.size where p.size<0 and p.finalPaperStorageId>1");
}

private function fix_whitespace() {
$result = $this->conf->qe("select * from ContactInfo");
$mq = Dbl::make_multi_qe_stager($this->conf->dblink);
while (($u = Contact::fetch($result, $this->conf))) {
$u->firstName = simplify_whitespace(($fn = $u->firstName));
$u->lastName = simplify_whitespace(($ln = $u->lastName));
$u->affiliation = simplify_whitespace(($af = $u->affiliation));
$un = $u->unaccentedName;
$u->unaccentedName = $u->db_searchable_name();
if ($fn !== $u->firstName || $ln !== $u->lastName
|| $af !== $u->affiliation || $un !== $u->unaccentedName) {
$mq("update ContactInfo set firstName=?, lastName=?, affiliation=?, unaccentedName=? where contactId=?", $u->firstName, $u->lastName, $u->affiliation, $u->unaccentedName, $u->contactId);
}
}
$mq(null);
}

/** @return CheckInvariants_Batch */
static function make_args($argv) {
$arg = (new Getopt)->long(
Expand All @@ -180,7 +201,7 @@ static function make_args($argv) {
"verbose,V Be verbose",
"fix-autosearch ! Repair any incorrect autosearch tags",
"fix-inactive ! Repair any inappropriately inactive documents",
"fix[] =PROBLEM Repair PROBLEM [all, autosearch, inactive, setting, document-match]",
"fix[] =PROBLEM Repair PROBLEM [all, autosearch, inactive, setting, document-match, whitespace]",
"color",
"no-color !",
"pad-prefix !"
Expand Down
1 change: 1 addition & 0 deletions batch/makedist.sh
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ lib/abbreviationmatcher.php
lib/archiveinfo.php
lib/backupdb.sh
lib/base.php
lib/batchprocess.php
lib/cleanhtml.php
lib/collatorshim.php
lib/column.php
Expand Down
28 changes: 28 additions & 0 deletions devel/hotcrp-daemonize.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>

int main(int argc, char** argv) {
DIR* dir = opendir("/dev/fd");
struct dirent* de;
while (dir && (de = readdir(dir))) {
if (!isdigit((unsigned char) de->d_name[0])) {
continue;
}
char* ends;
unsigned long u = strtoul(de->d_name, &ends, 10);
if (*ends == 0 && (int) u != dirfd(dir) && (int) u > 2) {
close((int) u);
}
}
closedir(dir);
if (fork() > 0) {
exit(0);
}
setsid();
execvp(argv[1], argv + 1);
exit(127);
}
26 changes: 26 additions & 0 deletions devel/manual/oauth.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,32 @@ users. If `$Opt["loginType"]` is `"oauth"` or `"none"`, then HotCRP will not
use its own password storage or allow attempts to sign in other than through
OAuth.

## Importing group permissions

Group permissions can be imported from an openID provider if the token contains
a "group" claim. This can be configured via a "group mappings" setting in an
oAuthProvider setting:

```
$Opt["oAuthProviders"][] = '{
"name": "Google",
"issuer": "https://accounts.google.com",
"auth_uri": "https://accounts.google.com/o/oauth2/v2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"client_id": "123456789-nnnnnnnnnnnnnnnnnnnnnnnnn.apps.googleusercontent.com",
"client_secret": "GOCSPX-nnnnnnnnnnnnnnnnnnnnnnnn",
"button_html": "Sign in with Google",
"remove_groups": true,
"group_mappings": {
"operators": "sysadmin",
"reviewers": "pc",
"chairs": "chair"
}
}';
```

Setting `remove_groups` to `true` enables removing group permissions if these
are absent in the OpenID claim.

[OAuth]: https://en.wikipedia.org/wiki/OAuth
[OpenID Connect]: https://en.wikipedia.org/wiki/OpenID
Expand Down
6 changes: 5 additions & 1 deletion etc/affiliationmatchers.json
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,9 @@
"korea": {
"alternate": [{"word": "kaist", "if": "advanced institute science technology"}]
},
"law": {
"weak": true
},
"london": {
"weak": true,
"alternate": [{"word": "ucl", "if": "university college"}]
Expand Down Expand Up @@ -961,7 +964,8 @@
"weak": true,
"alternate": [
{"word": "nyu", "if": "new university"}
]
],
"sync": ["new"]
},
"zurich": {
"weak": true
Expand Down
2 changes: 1 addition & 1 deletion etc/reviewfieldlibrary.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"type": "checkboxes",
"start": "A",
"visibility": "au",
"scheme": "catx",
"scheme": "observablex",
"sample_view": {
"description": "Select zero or more options.",
"values": [
Expand Down
2 changes: 1 addition & 1 deletion etc/settinginfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@
{
"name_pattern": "rf/$/scheme",
"title": "/Color scheme",
"type": "radio", "values": ["sv", "svr", "bupu", "pubu", "rdpk", "pkrd", "viridisr", "viridis", "orbu", "buor", "turbo", "turbor", "catx", "none"]
"type": "radio", "values": ["sv", "svr", "bupu", "pubu", "rdpk", "pkrd", "viridisr", "viridis", "orbu", "buor", "turbo", "turbor", "observablex", "catx", "none"]
},


Expand Down
16 changes: 6 additions & 10 deletions lib/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,13 @@ function preg_matchpos($pattern, $subject) {
/** @param string $text
* @return string */
function cleannl($text) {
if (substr($text, 0, 3) === "\xEF\xBB\xBF") {
if (str_starts_with($text, "\xEF\xBB\xBF")) {
$text = substr($text, 3);
}
if (strpos($text, "\r") !== false) {
$text = str_replace("\r\n", "\n", $text);
$text = str_replace("\r", "\n", $text);
}
if ($text !== "" && $text[strlen($text) - 1] !== "\n") {
$text .= "\n";
}
return $text;
}

Expand Down Expand Up @@ -193,13 +190,12 @@ function convert_to_utf8($str) {
}
if (is_valid_utf8($str)) {
return $str;
}
$pfx = substr($str, 0, 5000);
if (substr_count($pfx, "\r") > 1.5 * substr_count($pfx, "\n")) {
return mac_os_roman_to_utf8($str);
} else {
$pfx = substr($str, 0, 5000);
if (substr_count($pfx, "\r") > 1.5 * substr_count($pfx, "\n")) {
return mac_os_roman_to_utf8($str);
} else {
return windows_1252_to_utf8($str);
}
return windows_1252_to_utf8($str);
}
}

Expand Down
72 changes: 72 additions & 0 deletions lib/batchprocess.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php
// batchprocess.php -- HotCRP code for running batch processes
// Copyright (c) 2006-2024 Eddie Kohler; see LICENSE.

class BatchProcess {
/** @param Throwable $ex
* @suppress PhanUndeclaredProperty */
static function exception_handler($ex) {
global $argv;
$s = $ex->getMessage();
if (defined("HOTCRP_TESTHARNESS") || $ex instanceof Error) {
$s = $ex->getFile() . ":" . $ex->getLine() . ": " . $s;
}
if ($s !== "" && strpos($s, ":") === false) {
$script = $argv[0] ?? "";
if (($slash = strrpos($script, "/")) !== false) {
if (($slash === 5 && str_starts_with($script, "batch"))
|| ($slash > 5 && substr_compare($script, "/batch", $slash - 6, 6) === 0)) {
$slash -= 6;
}
$script = substr($script, $slash + 1);
}
if ($script !== "") {
$s = "{$script}: {$s}";
}
}
if ($s !== "" && substr($s, -1) !== "\n") {
$s = "{$s}\n";
}
$exitStatus = 3;
if (property_exists($ex, "exitStatus") && is_int($ex->exitStatus)) {
$exitStatus = $ex->exitStatus;
}
if (property_exists($ex, "getopt")
&& $ex->getopt instanceof Getopt
&& $exitStatus !== 0) {
$s .= $ex->getopt->short_usage();
}
if (property_exists($ex, "context") && is_array($ex->context)) {
foreach ($ex->context as $c) {
$i = 0;
while ($i !== strlen($c) && $c[$i] === " ") {
++$i;
}
$s .= prefix_word_wrap(str_repeat(" ", $i + 2), trim($c), 2);
}
}
if (defined("HOTCRP_TESTHARNESS") || $ex instanceof Error) {
$s .= debug_string_backtrace($ex) . "\n";
}
fwrite(STDERR, $s);
exit($exitStatus);
}

/** @return bool */
static function daemonize() {
if (!function_exists("pcntl_fork")) {
return false;
}
if (($f = pcntl_fork()) < 0) {
return false;
} else if ($f > 0) {
exit(0);
}
if (function_exists("posix_setsid")) {
if (posix_setsid() < 0) {
error_log("posix_setsid error: " . posix_strerror(posix_get_last_error()));
}
}
return true;
}
}
Loading

0 comments on commit da90cdf

Please sign in to comment.