xref: /web-master/include/functions.inc (revision 9cfe978d)
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("&amp;", $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\">&laquo; Previous %d",
201              $_SERVER['PHP_SELF'],
202              array_to_url($extra, ["begin" => max(0,$begin-$skip)]),
203              min($skip,$begin));
204     }
205   ?>
206   &nbsp;
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   &nbsp;
217   <?php
218     if ($begin+$rows < $total) {
219       printf("<a href=\"%s?%s\">Next %d &raquo;",
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