1<?php 2// $Id$ 3 4$ts = $_SERVER['REQUEST_TIME']; 5 6if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) { 7 die("Magic quotes should not be enabled."); 8} 9 10if (ini_get('filter.default') != 'unsafe_raw') { 11 die("filter.default should not be set."); 12} 13 14// ----------------------------------------------------------------------------------- 15 16// used in scripts which should only be called from particular machines 17function require_token() 18{ 19 if (!isset($_GET['token']) || md5($_GET['token']) != "19a3ec370affe2d899755f005e5cd90e") { 20 die("Token not correct."); 21 } 22} 23 24// ----------------------------------------------------------------------------------- 25 26function head($title="", $config = []) { 27 $dconfig = ["columns" => 1]; 28 29 $config = array_merge($dconfig, $config); 30 $SUBDOMAIN = "master"; 31 $TITLE = $title ?: "master"; 32 $LINKS = [ 33 ["href" => "/manage/event.php", "text" => "Events"], 34 ["href" => "/manage/users.php", "text" => "Users"], 35 ["href" => "/manage/user-notes.php", "text" => "Notes"], 36 ["href" => "/manage/github.php", "text" => "Github"], 37 ]; 38 $CSS = ["/styles/master.css"]; 39 $SEARCH = []; 40 41 if (strstr($_SERVER["SCRIPT_FILENAME"], "users.php")) { 42 $SEARCH = ["method" => "get", "action" => "/manage/users.php", "placeholder" => "Search profiles", "name" => "search"]; 43 $CSS[] = "/styles/user-autocomplete.css"; 44 } 45 if (strstr($_SERVER["SCRIPT_FILENAME"], "event.php")) { 46 $SEARCH = ["method" => "get", "action" => "/manage/event.php", "placeholder" => "Search Events", "name" => "search"]; 47 } 48 if (strstr($_SERVER["SCRIPT_FILENAME"], "user-notes.php")) { 49 $SEARCH = ["method" => "get", "action" => "/manage/user-notes.php", "placeholder" => "Search notes (keyword, ID or sect:)", "name" => "keyword"]; 50 } 51 if (isset($_SESSION['credentials'])) { 52 array_unshift($LINKS, ["href" => "/manage/users.php?username=" . $_SESSION["credentials"][0], "text" => "My Profile"]); 53 $LINKS[] = ["href" => "/login.php?action=logout", "text" => "Logout"]; 54 } 55 include __DIR__ . "/../shared/templates/header.inc"; 56 if ($config["columns"] > 1) { 57 echo '<section class="mainscreen">'; 58 } else { 59 echo '<section class="fullscreen">'; 60 } 61} 62 63function foot($secondscreen = null) { 64 $SECONDSCREEN = $secondscreen; 65 66 $JS = [ 67 "/js/master.js", 68 ]; 69 if (strstr($_SERVER["SCRIPT_FILENAME"], "users.php")) { 70 array_push( 71 $JS, 72 "//people.php.net/js/jquery.autocomplete.min.js", 73 "//people.php.net/js/userlisting.php", 74 "//people.php.net/js/search.js" 75 ); 76 } 77 78?> 79 </section> 80<?php 81 include __DIR__ . "/../shared/templates/footer.inc"; 82} 83 84// ----------------------------------------------------------------------------------- 85 86function hsc($str) { return htmlspecialchars((string)$str,ENT_QUOTES,'UTF-8'); } 87function format_warn($message) { return "<p class=\"warning\">$message</p>"; } 88function warn($message) { echo format_warn($message); } 89 90// ----------------------------------------------------------------------------------- 91 92function db_connect($dieonerror = TRUE) 93{ 94 if (!mysql_connect("localhost", "nobody", "")) { 95 if ($dieonerror) { 96 die(format_warn("Unable to connect to database!")); 97 } 98 return FALSE; 99 } 100 elseif (!mysql_select_db("phpmasterdb")) { 101 if ($dieonerror) { 102 die(format_warn("Unable to select database!")); 103 } 104 return FALSE; 105 } 106 return TRUE; 107} 108 109class Query { 110 private $query = ''; 111 112 public function __construct($str = '', $params = []) { 113 $this->add($str, $params); 114 } 115 116 public function add($str, $params = []) { 117 if (substr_count($str, '?') !== count($params)) { 118 die("Incorrect number of parameters to query."); 119 } 120 121 $i = 0; 122 $this->query .= preg_replace_callback('/\?(int)?/', function($matches) use ($params, &$i) { 123 if (isset($matches[1]) && $matches[1] === 'int') { 124 return (int) $params[$i++]; 125 } else { 126 return "'" . mysql_real_escape_string($params[$i++]) . "'"; 127 } 128 }, $str); 129 } 130 131 public function addQuery(Query $q) { 132 $this->query .= $q->get(); 133 } 134 135 public function get() { 136 return $this->query; 137 } 138} 139 140function db_query(Query $query) 141{ 142 $query = $query->get(); 143 //var_dump($query); 144 $res = mysql_query($query); 145 if (!$res) { 146 $bt = debug_backtrace(); 147 die(format_warn("Query failed: " . hsc(mysql_error()) . "<tt>\n" . hsc($query) . "</tt><br />({$bt[0]['file']}:{$bt[0]['line']})")); 148 } 149 return $res; 150} 151 152function db_query_safe($query, array $params = []) 153{ 154 return db_query(new Query($query, $params)); 155} 156 157function db_get_one($query) 158{ 159 $res = mysql_query($query); 160 if ($res && mysql_num_rows($res)) { 161 return mysql_result($res, 0); 162 } 163 return FALSE; 164} 165 166// ----------------------------------------------------------------------------------- 167 168function array_to_url($array,$overlay=[]) { 169 $params = []; 170 foreach($array as $k => $v) { 171 $params[$k] = rawurlencode($k) . "=" . rawurlencode($v); 172 } 173 foreach($overlay as $k => $v) { 174 if ($array[$k] == $v) { 175 $params["forward"] = $array["forward"] ? "forward=0" : "forward=1"; 176 continue; 177 } 178 $params["forward"] = "forward=0"; 179 $params[$k] = rawurlencode($k) . "=" . rawurlencode($v); 180 } 181 182 return implode("&", $params); 183} 184 185/** 186 * @param int $begin 187 * @param int $rows 188 * @param int $skip 189 * @param int $total 190 */ 191function show_prev_next($begin, $rows, $skip, $total, $extra = [], $table = true) 192{?> 193<?php if ($table): ?> 194<table border="0" cellspacing="1" width="100%"> 195<?php endif ?> 196 <tr> 197 <td> 198 <?php 199 if ($begin > 0) { 200 printf("<a href=\"%s?%s\">« Previous %d", 201 $_SERVER['PHP_SELF'], 202 array_to_url($extra, ["begin" => max(0,$begin-$skip)]), 203 min($skip,$begin)); 204 } 205 ?> 206 207 </td> 208<?php if($table): ?> 209 <td> 210<?php else: ?> 211 <td colspan="4"> 212<?php endif; ?> 213 <?php echo "Displaying ",$begin+1,"-",$begin+$rows," of $total";?> 214 </td> 215 <td> 216 217 <?php 218 if ($begin+$rows < $total) { 219 printf("<a href=\"%s?%s\">Next %d »", 220 $_SERVER['PHP_SELF'], 221 array_to_url($extra, ["begin" => $begin+$skip]), 222 min($skip,$total-($begin+$skip))); 223 } 224 ?> 225 </td> 226 </tr> 227<?php if ($table): ?> 228</table> 229<?php endif ?> 230<?php 231} 232 233function show_country_options($cc = "") 234{ 235 $res = db_query_safe("SELECT id, name FROM country ORDER BY name"); 236 while ($row = mysql_fetch_assoc($res)) { 237 echo "<option value=\"{$row['id']}\"", $cc == $row['id'] ? " selected" : "", ">{$row['name']}</option>"; 238 } 239} 240 241function is_sqlite_type_available($avails, $check) { 242 243 // All possible sqlite types associated with our assigned bitwise values 244 $all = ['sqlite' => 1, 'sqlite3' => 2, 'pdo_sqlite' => 4, 'pdo_sqlite2' => 8]; 245 246 if (!$avails || empty($all[$check])) { 247 return false; 248 } 249 250 $avail = (int) $all[$check]; 251 $avails = (int) $avails; 252 253 if (($avails & $avail) === $avail) { 254 return true; 255 } 256 return false; 257} 258 259function verify_ssh_keys($string) { 260 return count(get_ssh_keys($string)) > 0; 261} 262 263function get_ssh_keys($string) { 264 $results = []; 265 if (preg_match_all('@(ssh-(?:rsa|dss) ([^\s]+) ([^\s]*))@', $string, $matches, PREG_SET_ORDER)) { 266 foreach ($matches as $match) { 267 $results[] = ['key' => $match[1], 268 'name' => $match[3]]; 269 } 270 } 271 272 return $results; 273} 274 275// We use markdown for people profiles 276include_once dirname(__FILE__) . '/../vendor/michelf/php-markdown-extra/markdown.php'; 277 278// ----------------------------------------------------------------------------------- 279// 280 281 282function find_group_address_from_notes_for($id) { 283 $res = db_query_safe("SELECT note FROM users_note WHERE userid = ? LIMIT 1", [$id]); 284 $row = mysql_fetch_assoc($res); 285 $cc = ""; 286 if (preg_match("/\[group: (\w+)\]/", $row["note"], $matches)) { 287 switch($matches[1]) { 288 case "php": 289 $cc = "internals@lists.php.net"; 290 break; 291 case "pear": 292 $cc = "pear-group@lists.php.net"; 293 break; 294 case "pecl": 295 $cc = "pecl-dev@lists.php.net"; 296 break; 297 case "doc": 298 $cc = "phpdoc@lists.php.net"; 299 break; 300 } 301 } 302 return $cc; 303} 304 305define("MT_USER_APPROVE_MAIL", "group@php.net"); 306define("MT_USER_REMOVE_MAIL", "group@php.net"); 307function user_approve($id) { 308 $res = db_query_safe("UPDATE users SET cvsaccess=1, enable=1 WHERE userid=?", [$id]); 309 if ($res && mysql_affected_rows()) { 310 $cc = find_group_address_from_notes_for($id); 311 $mailtext = $cc ? $cc : EMAIL_DEFAULT_CC; 312 $userinfo = fetch_user($id); 313 314 $message = mt_approve_user($userinfo, $mailtext); 315 /* Notify the user */ 316 mail($userinfo["email"], "VCS Account Request: $userinfo[username]", $message, "From: PHP Group <group@php.net>", "-fnoreply@php.net"); 317 318 /* Notify the public records */ 319 $to = MT_USER_APPROVE_MAIL . ($cc ? ",$cc" : ""); 320 $subject = "Re: VCS Account Request: $userinfo[username]"; 321 $message = "VCS Account Approved: $userinfo[username] approved by {$_SESSION["username"]} \o/"; 322 $headers = "From: PHP Group <group@php.net>\nIn-Reply-To: <cvs-account-$id@php.net>"; 323 mail($to, $subject, $message, $headers, "-fnoreply@php.net"); 324 warn("record $id ($userinfo[username]) approved"); 325 return true; 326 } 327 else { 328 warn("wasn't able to grant access to id $id."); 329 return false; 330 } 331} 332 333function user_remove($id) { 334 $userinfo = fetch_user($id); 335 $res = db_query_safe("DELETE FROM users WHERE userid=?", [$id]); 336 if ($res && mysql_affected_rows()) { 337 $cc = find_group_address_from_notes_for($id); 338 339 $message = $userinfo['cvsaccess'] ? mt_remove_user($userinfo) : mt_deny_user($userinfo); 340 341 /* Notify the user */ 342 mail($userinfo['email'],"VCS Account Request: $userinfo[username]",$message,"From: PHP Group <group@php.net>", "-fnoreply@php.net"); 343 344 $to = MT_USER_REMOVE_MAIL . ($cc ? ",$cc" : ""); 345 $subject = "Re: VCS Account Request: $userinfo[username]"; 346 $message = $userinfo['cvsaccess'] 347 ? "VCS Account Deleted: $userinfo[username] deleted by {$_SESSION["username"]} /o\\" 348 : "VCS Account Rejected: $userinfo[username] rejected by {$_SESSION["username"]} /o\\"; 349 350 /* Notify public records */ 351 mail($to, $subject, $message,"From: PHP Group <group@php.net>\nIn-Reply-To: <cvs-account-$id@php.net>", "-fnoreply@php.net"); 352 db_query_safe("DELETE FROM users_note WHERE userid=?", [$id]); 353 db_query_safe("DELETE FROM users_profile WHERE userid=?", [$id]); 354 warn("record $id ($userinfo[username]) removed"); 355 return true; 356 } 357 else { 358 warn("wasn't able to delete id $id."); 359 return false; 360 } 361} 362 363function is_admin($user) { 364 $admins = [ 365 "jimw", 366 "rasmus", 367 "andrei", 368 "zeev", 369 "andi", 370 "sas", 371 "thies", 372 "rubys", 373 "ssb", 374 "wez", 375 "shane", 376 "sterling", 377 "goba", 378 "imajes", 379 "jon", 380 "alan_k", 381 "stas", 382 "iliaa", 383 "jmcastagnetto", 384 "mj", 385 "gwynne", 386 "lsmith", 387 "dsp", 388 "philip", 389 "davidc", 390 "helly", 391 "derick", 392 "bjori", 393 "pajoye", 394 "danbrown", 395 "felipe", 396 "johannes", 397 "tyrael", 398 "salathe", 399 "cmb", 400 "kalle", 401 "krakjoe", 402 "nikic" 403 ]; 404 return in_array($user, $admins); 405} 406 407function is_mirror_site_admin($user) { 408 $admins = [ 409 "jimw", 410 "rasmus", 411 "andrei", 412 "zeev", 413 "andi", 414 "sas", 415 "thies", 416 "rubys", 417 "ssb", 418 "imajes", 419 "goba", 420 "derick", 421 "cortesi", 422 "wez", 423 "bjori", 424 "philip", 425 "danbrown", 426 "tyrael", 427 "dm", 428 "kalle", 429 "googleguy", 430 "nikic" 431 ]; 432 return in_array($user, $admins); 433} 434 435# returns false if $user is not allowed to modify $userid 436function can_modify($user,$userid) { 437 if (is_admin($user)) return true; 438 439 $query = "SELECT userid FROM users WHERE userid = ? AND (email = ? OR username = ?)"; 440 $res = db_query_safe($query, [$userid, $user, $user]); 441 return $res ? mysql_num_rows($res) : false; 442} 443 444function fetch_user($user) { 445 if ((int)$user) { 446 $res = db_query_safe( 447 "SELECT * FROM users LEFT JOIN users_note USING (userid) WHERE users.userid = ?", 448 [$user]); 449 } else { 450 $res = db_query_safe( 451 "SELECT * FROM users LEFT JOIN users_note USING (userid) WHERE username = ? OR email = ?", 452 [$user, $user]); 453 } 454 455 return mysql_fetch_array($res); 456} 457function invalid_input($in) { 458 if (!empty($in['email']) && strlen($in['email']) && !is_emailable_address($in['email'])) { 459 return "'". hsc($in['email']) ."' does not look like a valid email address"; 460 } 461 if (!empty($in['username']) && !preg_match("/^[-\w]+\$/",$in['username'])) { 462 return "'". hsc($in['username']) ."' is not a valid username"; 463 } 464 if (!empty($in['rawpasswd']) && $in['rawpasswd'] != $in['rawpasswd2']) { 465 return "the passwords you specified did not match!"; 466 } 467 if (!empty($in['sshkey']) && !verify_ssh_keys($in['sshkey'])) { 468 return "the ssh key doesn't seem to have the necessary format"; 469 } 470 471 return false; 472} 473 474function validateAction($k) { 475 switch($k) { 476 case "approve": 477 case "remove": 478 return $k; 479 default: 480 warn("that action ('" . hsc($k) . "') is not understood."); 481 } 482 483 return false; 484} 485 486function fetch_event($id) { 487 $res = db_query_safe("SELECT * FROM phpcal WHERE id = ?", [$id]); 488 return mysql_fetch_array($res,MYSQL_ASSOC); 489} 490 491function display_options($options,$current) { 492 foreach ($options as $k => $v) { 493 echo '<option value="', $k, '"', 494 ($k == $current ? ' selected="selected"' : ''), 495 '>', html_entity_decode($v,ENT_QUOTES), "</option>\n"; 496 } 497} 498