1<?php
2// init.php -- HotCRP initialization (test or site)
3// Copyright (c) 2006-2018 Eddie Kohler; see LICENSE.
4
5define("HOTCRP_VERSION", "2.102");
6
7// All review types must be 1 digit
8define("REVIEW_META", 5);
9define("REVIEW_PRIMARY", 4);
10define("REVIEW_SECONDARY", 3);
11define("REVIEW_PC", 2);
12define("REVIEW_EXTERNAL", 1);
13
14define("CONFLICT_NONE", 0);
15define("CONFLICT_PCMARK", 1); /* unused */
16define("CONFLICT_AUTHORMARK", 2);
17define("CONFLICT_MAXAUTHORMARK", 7);
18define("CONFLICT_CHAIRMARK", 8);
19define("CONFLICT_AUTHOR", 9);
20define("CONFLICT_CONTACTAUTHOR", 10);
21
22define("REV_RATINGS_PC", 0);
23define("REV_RATINGS_PC_EXTERNAL", 1);
24define("REV_RATINGS_NONE", 2);
25
26define("DTYPE_SUBMISSION", 0);
27define("DTYPE_FINAL", -1);
28define("DTYPE_COMMENT", -2);
29
30define("VIEWSCORE_FALSE", -3);
31define("VIEWSCORE_ADMINONLY", -2);
32define("VIEWSCORE_REVIEWERONLY", -1);
33define("VIEWSCORE_PC", 0);
34define("VIEWSCORE_AUTHORDEC", 1);
35define("VIEWSCORE_AUTHOR", 2);
36define("VIEWSCORE_MAX", 3);
37
38define("COMMENTTYPE_DRAFT", 1);
39define("COMMENTTYPE_BLIND", 2);
40define("COMMENTTYPE_RESPONSE", 4);
41define("COMMENTTYPE_BYAUTHOR", 8);
42define("COMMENTTYPE_BYSHEPHERD", 16);
43define("COMMENTTYPE_ADMINONLY", 0x00000);
44define("COMMENTTYPE_PCONLY", 0x10000);
45define("COMMENTTYPE_REVIEWER", 0x20000);
46define("COMMENTTYPE_AUTHOR", 0x30000);
47define("COMMENTTYPE_VISIBILITY", 0xFFF0000);
48
49define("TAG_REGEX_NOTWIDDLE", '[a-zA-Z@*_:.][-a-zA-Z0-9!@*_:.\/]*');
50define("TAG_REGEX", '~?~?' . TAG_REGEX_NOTWIDDLE);
51define("TAG_MAXLEN", 80);
52define("TAG_INDEXBOUND", 2147483646);
53
54define("CAPTYPE_RESETPASSWORD", 1);
55define("CAPTYPE_CHANGEEMAIL", 2);
56
57global $Now, $ConfSitePATH;
58$Now = time();
59$ConfSitePATH = null;
60
61
62// set $ConfSitePATH (path to conference site)
63function set_path_variables() {
64    global $ConfSitePATH;
65    if (!isset($ConfSitePATH)) {
66        $ConfSitePATH = substr(__FILE__, 0, strrpos(__FILE__, "/"));
67        while ($ConfSitePATH !== "" && !file_exists("$ConfSitePATH/src/init.php"))
68            $ConfSitePATH = substr($ConfSitePATH, 0, strrpos($ConfSitePATH, "/"));
69        if ($ConfSitePATH === "")
70            $ConfSitePATH = "/var/www/html";
71    }
72    require_once("$ConfSitePATH/lib/navigation.php");
73}
74set_path_variables();
75
76
77// Load code
78class SiteLoader {
79    static $map = [
80        "AbbreviationClass" => "lib/abbreviationmatcher.php",
81        "AssignmentCountSet" => "src/assignmentset.php",
82        "AutoassignerCosts" => "src/autoassigner.php",
83        "BanalSettings" => "src/settings/s_subform.php",
84        "CapabilityManager" => "src/capability.php",
85        "ContactCountMatcher" => "src/papersearch.php",
86        "CsvGenerator" => "lib/csv.php",
87        "CsvParser" => "lib/csv.php",
88        "FormatChecker" => "src/formatspec.php",
89        "JsonSerializable" => "lib/json.php",
90        "LoginHelper" => "lib/login.php",
91        "MailPreparation" => "lib/mailer.php",
92        "MimeText" => "lib/mailer.php",
93        "NameInfo" => "lib/text.php",
94        "NumericOrderPaperColumn" => "src/papercolumn.php",
95        "Option_SearchTerm" => "src/search/st_option.php",
96        "PaperInfoSet" => "src/paperinfo.php",
97        "PaperOptionList" => "src/paperoption.php",
98        "Review_Assigner" => "src/assignmentset.php",
99        "ReviewField" => "src/review.php",
100        "ReviewFieldInfo" => "src/review.php",
101        "ReviewForm" => "src/review.php",
102        "ReviewSearchMatcher" => "src/search/st_review.php",
103        "ReviewValues" => "src/review.php",
104        "SearchSplitter" => "src/papersearch.php",
105        "SearchTerm" => "src/papersearch.php",
106        "TagAnno" => "lib/tagger.php",
107        "TagInfo" => "lib/tagger.php",
108        "TagMap" => "lib/tagger.php",
109        "TextPaperOption" => "src/paperoption.php",
110        "XlsxGenerator" => "lib/xlsx.php"
111    ];
112
113    static $suffix_map = [
114        "_api.php" => ["api_", "api"],
115        "_assignmentparser.php" => ["a_", "assigners"],
116        "_helptopic.php" => ["h_", "help"],
117        "_listaction.php" => ["la_", "listactions"],
118        "_papercolumn.php" => ["pc_", "papercolumns"],
119        "_papercolumnfactory.php" => ["pc_", "papercolumns"],
120        "_searchterm.php" => ["st_", "search"],
121        "_settingrenderer.php" => ["s_", "settings"],
122        "_settingparser.php" => ["s_", "settings"],
123        "_userinfo.php" => ["u_", "userinfo"]
124    ];
125
126    static function read_main_options() {
127        global $ConfSitePATH, $Opt;
128        if (defined("HOTCRP_OPTIONS"))
129            $files = [HOTCRP_OPTIONS];
130        else
131            $files = ["$ConfSitePATH/conf/options.php", "$ConfSitePATH/conf/options.inc", "$ConfSitePATH/Code/options.inc"];
132        foreach ($files as $f)
133            if ((@include $f) !== false) {
134                $Opt["loaded"][] = $f;
135                break;
136            }
137    }
138}
139
140spl_autoload_register(function ($class_name) {
141    global $ConfSitePATH;
142    $f = null;
143    if (isset(SiteLoader::$map[$class_name]))
144        $f = SiteLoader::$map[$class_name];
145    if (!$f)
146        $f = strtolower($class_name) . ".php";
147    foreach (expand_includes($f, ["autoload" => true]) as $fx)
148        require_once($fx);
149});
150
151require_once("$ConfSitePATH/lib/base.php");
152require_once("$ConfSitePATH/lib/redirect.php");
153require_once("$ConfSitePATH/lib/dbl.php");
154require_once("$ConfSitePATH/src/helpers.php");
155require_once("$ConfSitePATH/src/conference.php");
156require_once("$ConfSitePATH/src/contact.php");
157
158
159// Set locale to C (so that, e.g., strtolower() on UTF-8 data doesn't explode)
160setlocale(LC_COLLATE, "C");
161setlocale(LC_CTYPE, "C");
162
163// Don't want external entities parsed by default
164if (function_exists("libxml_disable_entity_loader"))
165    libxml_disable_entity_loader(true);
166
167
168// Set up conference options (also used in mailer.php)
169function expand_includes_once($file, $includepath, $globby) {
170    foreach ($file[0] === "/" ? [""] : $includepath as $idir) {
171        $try = $idir . $file;
172        if (!$globby && is_readable($try))
173            return [$try];
174        else if ($globby && ($m = glob($try, GLOB_BRACE)))
175            return $m;
176    }
177    return [];
178}
179
180function expand_includes($files, $expansions = array()) {
181    global $Opt, $ConfSitePATH;
182    if (!is_array($files))
183        $files = array($files);
184    $confname = get($Opt, "confid") ? : get($Opt, "dbName");
185    $expansions["confid"] = $expansions["confname"] = $confname;
186    $expansions["siteclass"] = get($Opt, "siteclass");
187
188    if (isset($expansions["autoload"]) && strpos($files[0], "/") === false)
189        $includepath = [$ConfSitePATH . "/src/", $ConfSitePATH . "/lib/"];
190    else
191        $includepath = [$ConfSitePATH . "/"];
192    if (isset($Opt["includepath"]) && is_array($Opt["includepath"])) {
193        foreach ($Opt["includepath"] as $i)
194            if ($i)
195                $includepath[] = str_ends_with($i, "/") ? $i : $i . "/";
196    }
197
198    $results = array();
199    foreach ($files as $f) {
200        if (strpos((string) $f, '$') !== false) {
201            foreach ($expansions as $k => $v)
202                if ($v !== false && $v !== null)
203                    $f = preg_replace(',\$\{' . $k . '\}|\$' . $k . '\b,', $v, $f);
204                else if (preg_match(',\$\{' . $k . '\}|\$' . $k . '\b,', $f)) {
205                    $f = "";
206                    break;
207                }
208        }
209        if ((string) $f === "")
210            continue;
211        $matches = [];
212        $ignore_not_found = $globby = false;
213        if (str_starts_with($f, "?")) {
214            $ignore_not_found = true;
215            $f = substr($f, 1);
216        }
217        if (preg_match(',[\[\]\*\?\{\}],', $f))
218            $ignore_not_found = $globby = true;
219        $matches = expand_includes_once($f, $includepath, $globby);
220        if (empty($matches)
221            && isset($expansions["autoload"])
222            && ($underscore = strpos($f, "_"))
223            && ($f2 = get(SiteLoader::$suffix_map, substr($f, $underscore)))) {
224            $xincludepath = array_merge($f2[1] ? ["{$ConfSitePATH}/src/{$f2[1]}/"] : [], $includepath);
225            $matches = expand_includes_once($f2[0] . substr($f, 0, $underscore) . ".php", $xincludepath, $globby);
226        }
227        $results = array_merge($results, $matches);
228        if (empty($matches) && !$ignore_not_found)
229            $results[] = $f[0] === "/" ? $f : $includepath[0] . $f;
230    }
231    return $results;
232}
233
234function read_included_options(&$files) {
235    global $Opt;
236    if (is_string($files))
237        $files = [$files];
238    for ($i = 0; $i != count($files); ++$i)
239        foreach (expand_includes($files[$i]) as $f) {
240            $key = "missing";
241            if ((@include $f) !== false)
242                $key = "loaded";
243            $Opt[$key][] = $f;
244        }
245}
246
247function expand_json_includes_callback($includelist, $callback) {
248    $includes = [];
249    foreach (is_array($includelist) ? $includelist : [$includelist] as $k => $str) {
250        $expandable = null;
251        if (is_string($str)) {
252            if (str_starts_with($str, "@"))
253                $expandable = substr($str, 1);
254            else if (!preg_match('/\A[\s\[\{]/', $str)
255                     || ($str[0] === "[" && !preg_match('/\]\s*\z/', $str)))
256                $expandable = $str;
257        }
258        if ($expandable) {
259            foreach (expand_includes($expandable) as $f)
260                if (($x = file_get_contents($f)))
261                    $includes[] = [$x, $f];
262        } else
263            $includes[] = [$str, "entry $k"];
264    }
265    foreach ($includes as $xentry) {
266        list($entry, $landmark) = $xentry;
267        if (is_string($entry)) {
268            $x = json_decode($entry);
269            if ($x === null && json_last_error()) {
270                $x = Json::decode($entry);
271                if ($x === null)
272                    error_log("$landmark: Invalid JSON: " . Json::last_error_msg());
273            }
274            if ($x === null)
275                continue;
276            $entry = $x;
277        }
278        foreach (is_array($entry) ? $entry : [$entry] as $k => $v) {
279            if (is_object($v))
280                $v->__subposition = ++Conf::$next_xt_subposition;
281            if (!call_user_func($callback, $v, $k, $landmark))
282                error_log("$landmark: Invalid expansion " . json_encode($v) . ".");
283        }
284    }
285}
286
287global $Opt;
288if (!$Opt)
289    $Opt = array();
290if (!get($Opt, "loaded")) {
291    SiteLoader::read_main_options();
292    if (get($Opt, "multiconference"))
293        Multiconference::init();
294    if (get($Opt, "include"))
295        read_included_options($Opt["include"]);
296}
297if (!get($Opt, "loaded") || get($Opt, "missing"))
298    Multiconference::fail_bad_options();
299if (get($Opt, "dbLogQueries"))
300    Dbl::log_queries($Opt["dbLogQueries"], get($Opt, "dbLogQueryFile"));
301
302
303// Allow lots of memory
304if (!get($Opt, "memoryLimit") && ini_get_bytes("memory_limit") < (128 << 20))
305    $Opt["memoryLimit"] = "128M";
306if (get($Opt, "memoryLimit"))
307    ini_set("memory_limit", $Opt["memoryLimit"]);
308
309
310// Create the conference
311global $Conf;
312if (!$Conf)
313    $Conf = Conf::$g = new Conf($Opt, true);
314if (!$Conf->dblink)
315    Multiconference::fail_bad_database();
316