\\2\n\\3",
+ "\\2 ",
+ "\\1 ",
+ "\\1 ",
+ "#\\1 ",
+ "\\1 ",
+ );
+ }
+
+ // Do the actual replacing - iterations for nested items
+ $lasttext = "";
+ $i = 0;
+ // $i<1000 to prevent DoS
+ while ($text != $lasttext && $i<1000) {
+ $lasttext = $text;
+ $text = replace_pre_code($text, $export);
+ $text = preg_replace($bbtags, $htmltags, $text);
+ $i = $i + 1;
+ }
+ return $text;
+}
+
+// Removes any tags added by nl2br which are not wanted,
+// for example inside containers
+// The original \n was retained after the br when it was added
+//
+function remove_br($text){
+ return str_replace(" ", "", $text);
+}
+
+// Make links open in new windows.
+//
+function externalize_links($text) {
+ // TODO: Convert this to PCRE
+ $i=0;
+ $linkpos=true;
+ $out = "";
+ while (true){
+ //Find a link
+ //
+ $linkpos=strpos($text, "]+?)>@si';
+ $replacement = ' [Image link] '; // Turns that URL into a hyperlink
+ $text = preg_replace($pattern, $replacement, $text);
+ return $text;
+}
+
+// Highlight terms in text (most likely used with searches)
+
+function highlight_terms($text, $terms) {
+ $search = $terms;
+ $replace = array();
+
+ foreach ($search as $key => $value) {
+ $replace[$key] = "".$value." ";
+ }
+ if (substr(phpversion(), 0, 1) > 4) { // PHP 4.x doesn't support str_ireplace
+ return str_ireplace($search, $replace, $text);
+ } else {
+ return str_replace($search, $replace, $text);
+ }
+}
+
+$cvs_version_tracker[]="\$Id$"; //Generated automatically - do not edit
+?>
diff --git a/inc/time.inc b/inc/time.inc
new file mode 100755
index 0000000..fa69f77
--- /dev/null
+++ b/inc/time.inc
@@ -0,0 +1,70 @@
+.
+
+// express a time difference in readable form, e.g. "7 days ago".
+// If it's more than 30 days, just show the date
+//
+function time_diff_str($t1, $t2) {
+ if (!$t1 || !$t2) return "---";
+ $diff = $t2 - $t1;
+ if ($diff<0){
+ $pre="In ";
+ $post="";
+ $diff=-$diff;
+ } else {
+ if ($diff > 86400*30) {
+ return date_str($t1);
+ }
+ $pre="";
+ $post=" ago";
+ }
+ $x = "";
+
+ if ($diff >= 3600*24) {
+ $n = (int) ($diff/(3600*24));
+ if ($n == 1) {
+ $x .= "1 day ";
+ } else {
+ $x .= "$n days ";
+ }
+ return $pre.$x.$post;
+ //$diff -= $n*3600*24;
+ } elseif ($diff >= 3600) {
+ $n = (int) ($diff/3600);
+ if ($n == 1) {
+ $x .= "1 hour ";
+ } else {
+ $x .= $n." hours ";
+ }
+ return $pre.$x.$post;
+ //$diff -= $n*3600;
+ } elseif ($diff >= 60) {
+ $n = (int) ($diff/60);
+ if ($n == 1) {
+ $x .= "1 minute ";
+ } else {
+ $x .= $n." minutes ";
+ }
+ return $pre.$x.$post;
+ } elseif($diff > 1 || $diff==0) {
+ return $pre."$diff seconds".$post;
+ } elseif($diff == 1){
+ return $pre."$diff seconds".$post;
+ }
+}
+?>
diff --git a/inc/translation.inc b/inc/translation.inc
new file mode 100755
index 0000000..39d33dc
--- /dev/null
+++ b/inc/translation.inc
@@ -0,0 +1,307 @@
+.
+
+$lang_language_dir = "../languages/";
+$lang_translations_dir = "translations/";
+$lang_prj_translations_dir = "project_specific_translations/";
+$lang_compiled_dir = "compiled/";
+$lang_log_level = 1;
+
+// Get a list of compiled languages by scanning the compiled/ dir
+// @returns A list of languages that have been compiled
+//
+function get_supported_languages() {
+ global $lang_language_dir, $lang_compiled_dir;
+ $list = array();
+ if (!is_dir($lang_language_dir.$lang_compiled_dir)) {
+ echo "\"".$lang_language_dir.$lang_compiled_dir."\" is not a directory. Please consult the documentation for correctly setting up the translation system.";
+ exit;
+ }
+ $dh = opendir($lang_language_dir.$lang_compiled_dir);
+ if (!$dh) die("can't open language dir");
+
+ while ($file = readdir($dh)) {
+ if (substr($file, -7) != ".po.inc") continue;
+ if (is_numeric(substr($file, 0, 5))) continue;
+ $list[] = substr($file, 0, -7);
+ }
+ return $list;
+}
+
+// generate PHP files defining translation arrays.
+// For example, the file "ca.po.inc" would contain entries of the form
+// $language_lookup_array["ca"]["Default"] = "Defecte";
+//
+// Append to these files if they already exist
+// (this may get done for both generic and project-specific translations)
+//
+// @param langdir The language base directory
+// @param transdir The location of the .po files to compile relative to langdir
+// @param compdir The output location relative to langdir
+//
+function build_translation_array_files($langdir, $transdir, $compdir) {
+
+ // Run through each language and compile their lookup arrays.
+ //
+ if (!is_dir($langdir.$transdir)) {
+ //debug("$info_dir not found or is not a directory");
+ }
+ $dh = opendir($langdir.$transdir);
+ if (!$dh) die("can't open translation dir");
+ while (($file = readdir($dh)) !== false) {
+ if ($file==".." || $file==".") {
+ continue;
+ }
+ // only do files ending in .po
+ if (substr($file,-3) != ".po"){
+ //debug("File $file with unknown extension found in $info_dir");
+ continue;
+ }
+ language_log(
+ "-------------Compiling $transdir$file------------", 0
+ );
+ $language = parse_po_file($langdir.$transdir.$file);
+ if (!$language){
+ language_log(
+ "WARNING: Could not parse language ".$file
+ );
+ continue;
+ }
+ $path = $langdir.$compdir.$file.".inc";
+ if (file_exists($path)) {
+ $fh = fopen($path, "a");
+ } else {
+ $fh = fopen($path, "w");
+ fwrite($fh, " $value){
+ if ($value !== "") {
+ // Skip if the msgstr is empty
+ fwrite($fh, "\$language_lookup_array[\"".str_replace("\"", "\\\"", substr($file,0,-3))."\"][\"".$key."\"] = \"".$value."\";\n");
+ }
+ }
+ // don't write \?\> - may append
+
+ fclose($fh);
+ }
+ closedir($dh);
+}
+
+// Parses a gettext .po-file into an associative PHP array.
+// @param file The file to parse
+// checking for inconsistencies if needed.
+//
+function parse_po_file($file) {
+ $translation_file = file($file);
+ $first_entry = true;
+ $current_token_text="";
+ $current_token ="";
+ $parsing_token = false;
+ $parsing_text = false;
+ $output = array();
+ for ($i=0; $i= $lang_log_level){
+ echo gmdate("Y-m-d H:i:s", time())." ".$msg." ".$message."\n";
+ }
+}
+
+// Make a list of languages which the user prefers
+// (by looking at cookies and browser settings)
+// cookies have highest priority.
+
+if (isset($_COOKIE['lang'])){
+ $language_string = $_COOKIE['lang'].",";
+} else {
+ $language_string = '';
+}
+if (isset($_SERVER["HTTP_ACCEPT_LANGUAGE"])) {
+ $language_string .= strtolower($_SERVER["HTTP_ACCEPT_LANGUAGE"]);
+}
+
+// Find out which language to use by iterating through list
+// The list is comma-separated, so split it into an array of the following type:
+// Array (
+// [0] => da
+// [1] => en-us;q=0.7
+// [2] => en;q=0.3
+// )
+
+$client_languages = explode(",", $language_string);
+
+// A language is either defined as primary-secondary or primary.
+// It can also have a quality attribute set,
+// which orders the languages in a user preferred ordering.
+// Since this is usally the same order as the array indices
+// we just ignore this attribute (TODO: don't ignore this attribute)
+// A missing quality attribute means q=1
+
+$languages_in_use = array();
+
+// Loop over languages that the client requests
+//
+for ($i=0; $i2)
+ && (substr($client_languages[$i], 2, 1) == "_" || substr($client_languages[$i], 2, 1) == "-")
+ ){
+ // If this is defined as primary-secondary, represent it as xx_YY
+ //
+ $language = substr(
+ $client_languages[$i], 0, 2)."_".strtoupper(substr($client_languages[$i], 3, 2)
+ );
+
+ // And also check for the primary language
+ //
+ $language2 = substr($client_languages[$i], 0, 2);
+ } else {
+ // else just use xx
+ //
+ $language = substr($client_languages[$i], 0, 2);
+ $language2 = null;
+ }
+
+ // if main language is english, look no further
+ //
+ if ((count($languages_in_use)==0) && ($language == 'en' || $language2 == 'en')) {
+ break;
+ }
+
+ // If we have a translation for the language, include it
+ //
+ $file_name = $lang_language_dir.$lang_compiled_dir.$language.".po.inc";
+ if (file_exists($file_name)) {
+ if (!in_array($language, $languages_in_use)){
+ require_once($file_name);
+ $languages_in_use[] = $language;
+ }
+ }
+ if ($language2) {
+ $file_name = $lang_language_dir.$lang_compiled_dir.$language2.".po.inc";
+ if (file_exists($file_name)) {
+ if (!in_array($language2, $languages_in_use)){
+ require_once($file_name);
+ $languages_in_use[] = $language2;
+ }
+ }
+ }
+}
+
+$GLOBALS['languages_in_use'] = $languages_in_use; // for Drupal
+
+$cvs_version_tracker[]="\$Id$"; //Generated automatically - do not edit
+?>
diff --git a/inc/untitled text 6.html b/inc/untitled text 6.html
new file mode 100644
index 0000000..c145b86
--- /dev/null
+++ b/inc/untitled text 6.html
@@ -0,0 +1,8 @@
+
+ATTENTION
+
+Hello, and welcome to the landing page for the BOINC website at TACC at UT Austin. BOINC is a volunteer computing project dedicated to moving the necessities of high-powered computing to volunteers willing to share their computing resources on their personal devices. In doing so, this provides another set of options for researchers to acquire the necessary resources to analyze their data to progress the STEM fields.
+
+link to homepage "
+
+
\ No newline at end of file
diff --git a/inc/uotd.inc b/inc/uotd.inc
new file mode 100755
index 0000000..761dbfc
--- /dev/null
+++ b/inc/uotd.inc
@@ -0,0 +1,224 @@
+.
+
+require_once('../inc/boinc_db.inc');
+require_once('../inc/email.inc');
+require_once('../inc/profile.inc');
+
+if (!defined('UOTD_THRESHOLD')) {
+ define('UOTD_THRESHOLD', 7);
+ // email sysadmin if # of UOTD candidates falls below this
+}
+
+function uotd_thumbnail($profile, $user) {
+ if ($profile->has_picture) {
+ return "id\"> id)."\" alt=\"".tra("User profile")."\"> ";
+ } else {
+ return "";
+ }
+}
+
+// show UOTD in a small box
+//
+function show_uotd($profile) {
+ $user = BoincUser::lookup_id($profile->userid);
+ echo uotd_thumbnail($profile, $user);
+ echo user_links($user, BADGE_HEIGHT_MEDIUM)." ";
+ $x = output_transform($profile->response1);
+ $x = sanitize_tags($x);
+ echo sub_sentence($x, ' ', 150, true);
+}
+
+// return the last UOTD profile, or null
+//
+function get_current_uotd() {
+ $profiles = BoincProfile::enum("uotd_time is not NULL and uotd_time>0", "ORDER BY uotd_time DESC LIMIT 1");
+ if (sizeof($profiles)) {
+ return $profiles[0];
+ }
+ return null;
+}
+
+// Select a (possibly new) UOTD
+//
+function select_uotd($force_new = false) {
+ echo gmdate("F d Y", time())." UTC: Starting\n";
+ $current_uotd = get_current_uotd();
+ if ($current_uotd && !$force_new) {
+ $assigned = getdate($current_uotd->uotd_time);
+ $now = getdate(time());
+ if ($assigned['mday'] == $now['mday']) {
+ $user = BoincUser::lookup_id($current_uotd->userid);
+ echo "Already have UOTD for today\n";
+ generate_uotd_gadget($current_uotd, $user);
+ exit();
+ }
+ }
+ if ($force_new) {
+ echo "Forcing new UOTD\n";
+ }
+
+ // get a list of profiles that have been 'approved' for UOTD,
+ // using a project-specific query if supplied in project.inc
+ //
+ if (function_exists('uotd_candidates_query')) {
+ $query = uotd_candidates_query();
+ echo "using project supplied candidates_query\n";
+ } else {
+ $query = default_uotd_candidates_query();
+ echo "using default candidates_query\n";
+ }
+ $db = BoincDb::get();
+ $result = $db->do_query($query);
+
+ // If the number of approved profiles dips below a threshold,
+ // email the sys admin every time we pick a new one.
+ //
+ if (defined('UOTD_ADMIN_EMAIL')
+ && $result
+ && $result->num_rows < UOTD_THRESHOLD
+ ) {
+ echo "approved candidates for UOTD under UOTD_THRESHOLD\n";
+ $u = new BoincUser;
+ $u->email_addr = UOTD_ADMIN_EMAIL;
+ $u->name = "UOTD admin";
+ send_email($u,
+ PROJECT . ": User of the Day pool is running low!",
+ "The pool of approved candidates for User of the Day has".
+ " reached your assigned threshold: there are now only " . $result->num_rows . " approved users.\n\n".
+ "To approve more candidates for User of the Day,".
+ " go to the " . PROJECT . " administration page and click \"Screen user profiles\""
+ );
+ }
+
+ if ($result && $result->num_rows == 0) {
+ echo "no new verified profile found, selecting old UOTD that was shown least recently\n";
+ $result->free();
+ // If all verified profiles have been selected as UOTD,
+ // reshow a random one of the 100 least recently shown profiles.
+ //
+ $inner = "SELECT profile.userid FROM profile,user WHERE profile.userid=user.id AND verification=1 AND uotd_time>0 ORDER BY uotd_time ASC LIMIT 100";
+ $result = $db->do_query("SELECT * from ($inner) as t ORDER BY RAND() LIMIT 1");
+ }
+
+ if (!$result || $result->num_rows == 0) {
+ // No valid users of the day - do something.
+ echo "No screened users found\n";
+ exit();
+ }
+ $candidate = $result->fetch_object();
+ $result->free();
+
+ // depending on the candidates query the profile must not exist
+ //
+ $profile = BoincProfile::lookup_userid($candidate->userid);
+ if (!$profile) {
+ echo "Could not find profile returned from candidates query.\n";
+ exit();
+ }
+
+ // "orphaned" profiles can only be detected if the candidate query doesn't join profile and user table
+ // if this happens, delete the profile and try again
+ //
+ $user = BoincUser::lookup_id($candidate->userid);
+ if (!$user) {
+ echo "Profile for user $candidate->userid is orphaned and will be deleted\n";
+ $profile->delete();
+ select_uotd($force_new);
+ exit();
+ }
+
+ $profile->uotd_time = time();
+ $profile->update("uotd_time = ".time());
+
+ send_email($user,
+ "You're the " . PROJECT . " user of the day!",
+ "Congratulations!\n\nYou've been chosen as the "
+ . PROJECT . " user of the day!
+ Your profile will be featured on the " . PROJECT . " website for the next 24 hours."
+ );
+ echo "Chose user $user->id as UOTD\n";
+
+ generate_uotd_gadget($profile, $user);
+}
+
+// This query defines the set of users eligible to be UOTD.
+// To override this with your own policy, create a similar function in
+// your own project.inc called uotd_candidates_query()
+//
+function default_uotd_candidates_query(){
+ $query = "SELECT * FROM profile,user WHERE profile.userid=user.id ";
+ $query .= " AND verification=1 ";
+ $query .= " AND expavg_credit>1 ";
+ $query .= " AND (uotd_time IS NULL or uotd_time=0) ";
+ $query .= "ORDER BY RAND()";
+ return $query;
+}
+
+// get a list of profiles that have been 'approved' for UOTD,
+// using a project-specific query if supplied in project.inc
+//
+function count_uotd_candidates(){
+ $n = -1; // negative value returned on error
+ if (function_exists('uotd_candidates_query')) {
+ $query = uotd_candidates_query();
+ } else {
+ $query = default_uotd_candidates_query();
+ }
+
+ $db = BoincDb::get();
+ $result = $db->do_query($query);
+ if($result) {
+ $n = $result->num_rows;
+ }
+ $result->free();
+
+ return $n;
+}
+
+// iGoogle gadget - generate the gadget content page
+//
+function generate_uotd_gadget($profile, $user) {
+ $x = "\n";
+ $gadget = PROFILE_PATH."uotd_gadget.html";
+ if( $h = fopen($gadget, "w") ){
+ $age = time()-$profile->uotd_time;
+ echo "age: $age";
+ if($age <= 86400+3600) { // allow for slop
+ $x .= uotd_thumbnail($profile, $user);
+ $x .= user_links($user, BADGE_HEIGHT_MEDIUM);
+ $resp = sanitize_tags(output_transform($profile->response1));
+ $x .= " ". sub_sentence($resp, ' ', 250, true);
+ }
+ else {
+ $x .= "
+ There is no User of the Day today.
+ Only volunteers who have created a Profile
+ (with a picture), and have recent credit,
+ are eligible to be chosen as User of the Day.
+ We have run out of these, so there isn't a
+ User of the Day.
+ ";
+ }
+ $x .= "\n \n";
+ fwrite($h, $x);
+ fclose($h);
+ }
+}
+
+?>
diff --git a/inc/user.inc b/inc/user.inc
new file mode 100755
index 0000000..ba5f55a
--- /dev/null
+++ b/inc/user.inc
@@ -0,0 +1,551 @@
+.
+
+require_once("../inc/credit.inc");
+require_once("../inc/email.inc");
+require_once("../inc/util.inc");
+require_once("../inc/team.inc");
+require_once("../inc/friend.inc");
+require_once("../inc/forum_db.inc");
+require_once("../inc/notify.inc");
+require_once("../inc/ldap.inc");
+
+if (!defined('REMOTE_PROJECTS_TTL')) {
+ define('REMOTE_PROJECTS_TTL', 86400);
+}
+
+// add an element "projects" to user consisting of array of projects
+// they've participated in
+//
+function get_other_projects($user) {
+ $cpid = md5($user->cross_project_id . $user->email_addr);
+ $url = "http://boinc.netsoft-online.com/get_user.php?cpid=".$cpid;
+
+ // Check the cache for that URL
+ //
+ $cacheddata = get_cached_data(REMOTE_PROJECTS_TTL, $url);
+ if ($cacheddata){
+ $remote = unserialize($cacheddata);
+ } else {
+ // Fetch the XML, use curl if fopen() is disallowed
+ //
+ if (ini_get('allow_url_fopen')) {
+ $timeout = 3;
+ $old_timeout = ini_set('default_socket_timeout', $timeout);
+ $xml_object = null;
+ $f = @file_get_contents($url);
+ if ($f) {
+ $xml_object = @simplexml_load_string($f);
+ }
+ ini_set('default_socket_timeout', $old_timeout);
+ if (!$xml_object) {
+ return $user;
+ }
+ } else {
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_HEADER, false);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 3);
+ $rawxml = @curl_exec($ch);
+ $xml_object = null;
+ if ($rawxml) {
+ $xml_object = @simplexml_load_string($rawxml);
+ }
+ curl_close($ch);
+ if (!xml_object) {
+ return $user;
+ }
+ }
+
+ // auto-cast the project list to an array of stdClass projects
+ //
+ $remote = @json_decode(json_encode((array)$xml_object))->project;
+ if (count($remote) == 1) {
+ $remote = array($remote);
+ }
+
+ if (!$remote) {
+ return $user;
+ } else {
+ // Cache the results
+ set_cached_data(REMOTE_PROJECTS_TTL, serialize($remote), $url);
+ }
+ }
+
+ $user->projects = $remote;
+ return $user;
+}
+
+function show_project($project) {
+ if ($project->url == "http://www.worldcommunitygrid.org/") {
+ $x = $project->name;
+ } else {
+ $x = "url"."show_user.php?userid=$project->id\">$project->name ";
+ }
+ echo "
+ $x
+ ".number_format($project->total_credit, 0)."
+ ".number_format($project->expavg_credit, 0)."
+ ".date_str($project->create_time)."
+
+ ";
+}
+
+function cmp($a, $b) {
+ if ($a->expavg_credit == $b->expavg_credit) return 0;
+ return ($a->expavg_credit < $b->expavg_credit)? 1 : -1;
+}
+
+function show_other_projects($user, $personal) {
+ if (!isset($user->projects)) return;
+ if (count($user->projects) < 2) return;
+
+ usort($user->projects, "cmp");
+ if ($personal) {
+ echo "".tra("Projects in which you are participating")." ";
+ } else {
+ echo "".tra("Projects in which %1 is participating", $user->name)." ";
+ }
+ start_table('table-striped');
+ row_heading_array(
+ array(
+ tra("Project")."".tra("Click for user page")." ",
+ tra("Total credit"),
+ tra("Average credit"),
+ tra("Since")
+ ),
+ array("", ALIGN_RIGHT, ALIGN_RIGHT, ALIGN_RIGHT)
+ );
+ foreach($user->projects as $project) {
+ show_project($project);
+ }
+ end_table();
+}
+
+function total_posts($user) {
+ return BoincPost::count("user=$user->id");
+}
+
+function show_credit($user) {
+ row2(tra("Total credit"), format_credit_large($user->total_credit));
+ row2(tra("Recent average credit"), format_credit($user->expavg_credit));
+ if (function_exists("project_user_credit")) {
+ project_user_credit($user);
+ }
+}
+
+require_once("../inc/stats_sites.inc");
+// show dynamic user info (private)
+//
+function show_user_stats_private($user) {
+ global $cpid_stats_sites;
+
+ if (NO_COMPUTING && NO_STATS && NO_HOSTS) {
+ return;
+ }
+ row1(tra("Computing"));
+
+ if (!NO_STATS) {
+ show_credit($user);
+ }
+
+ if (!NO_HOSTS) {
+ row2(tra("Computers on this account"), "".tra("View")." ");
+ }
+ if (!NO_COMPUTING) {
+ row2(tra("Tasks"), "id\">".tra("View")." ");
+ }
+
+ if (!NO_STATS) {
+ $cpid = md5($user->cross_project_id . $user->email_addr);
+ $x = "";
+ shuffle($cpid_stats_sites);
+ foreach ($cpid_stats_sites as $site) {
+ $name = $site[0];
+ $y = sprintf($site[1], $cpid);
+ $x .= "$name \n";
+ }
+ $x .= "".tra("Cross-project ID").": $cpid \n";
+ row2(tra("Cross-project statistics"), $x);
+ $x = ''.tra("Account").' ';
+ if ($user->teamid) {
+ $x .= ' · '.tra("Team").' ';
+ }
+ $x .= ' · '.tra("Cross-project").' ';
+ row2(tra("Certificate"), $x);
+ row2(tra("Stats on your cell phone"), url_base()."userw.php?id=$user->id");
+ }
+}
+
+function notify_description($notify) {
+ switch ($notify->type) {
+ case NOTIFY_FRIEND_REQ:
+ return friend_notify_req_web_line($notify);
+ case NOTIFY_FRIEND_ACCEPT:
+ return friend_notify_accept_web_line($notify);
+ case NOTIFY_PM:
+ return pm_web_line($notify);
+ case NOTIFY_SUBSCRIBED_POST:
+ return subscribed_post_web_line($notify);
+ }
+ return null;
+}
+
+function weak_auth($user) {
+ $x = md5($user->authenticator.$user->passwd_hash);
+ return "{$user->id}_$x";
+}
+
+// originally user URLs were assumed to be http://,
+// and this prefix wasn't stored.
+// Now the prefix can be http:// or https://.
+// This function takes a user URL in any form and converts
+// it to a canonical form, with the protocol prefix.
+//
+function normalize_user_url($url) {
+ $x = strtolower($url);
+ if (substr($x, 0, 7) == 'http://') {
+ return 'http://'.substr($url, 7);
+ }
+ if (substr($x, 0, 8) == 'https://') {
+ return 'https://'.substr($url, 8);
+ }
+ return 'http://'.$url;
+}
+
+// show static user info (private)
+//
+function show_user_info_private($user) {
+ row2(tra("Name"), $user->name);
+ if (LDAP_HOST && is_ldap_email($user->email_addr)) {
+ row2("LDAP ID", ldap_email_to_uid($user->email_addr));
+ } else {
+ $email_text = $user->email_addr;
+ if (defined("SHOW_NONVALIDATED_EMAIL_ADDR") && !$user->email_validated) {
+ $email_text .= " (must be validated )";
+ }
+ row2(tra("Email address"), $email_text);
+ }
+ if (strlen($user->url)) {
+ $u = normalize_user_url($user->url);
+ row2(tra("URL"), sprintf('%s ', $u, $u));
+ }
+ row2(tra("Country"), $user->country);
+ if (POSTAL_CODE) {
+ row2(tra("Postal code"), $user->postal_code);
+ }
+ row2(tra("%1 member since", PROJECT), date_str($user->create_time));
+ $url_tokens = url_tokens($user->authenticator);
+ if (LDAP_HOST && is_ldap_email($user->email_addr)) {
+ // LDAP accounts can't change email or password
+ //
+ row2(tra("Change"),
+ "Account info "
+ );
+ } else {
+ row2(tra("Change"),
+ "".tra("email address")."
+ · ".tra("password")."
+ · ".tra("other account info")." "
+ );
+ }
+ row2(tra("User ID")."".tra("Used in community functions")."
", $user->id);
+ if (!NO_COMPUTING) {
+ row2(
+ tra("Account keys"),
+ "".tra("View")." "
+ );
+ }
+}
+
+function show_preference_links() {
+ row1(" ".tra("Preferences"));
+ if (!NO_GLOBAL_PREFS) {
+ row2(
+ tra("When and how BOINC uses your computer"),
+ "".tra("Computing preferences")." "
+ );
+ }
+ row2(tra("Message boards and private messages"),
+ "".tra("Community preferences")." "
+ );
+ if (!NO_COMPUTING) {
+ row2(tra("Preferences for this project"),
+ "".tra("%1 preferences", PROJECT)." "
+ );
+ }
+}
+
+function friend_links($user) {
+ if (is_banished($user)) {
+ return "";
+ }
+ $x = "";
+ if ($user->has_profile) {
+ $profile = BoincProfile::lookup_fields("has_picture", "userid=$user->id");
+ if ($profile && $profile->has_picture) {
+ $img_url = profile_thumb_url($user->id);
+ } else {
+ $img_url = url_base()."img/head_20.png";
+ }
+ $title = tra("View the profile of %1", $user->name);
+ $alt = tra("Profile");
+ $x .= ' ';
+ }
+ $x .= " id."\">".$user->name." ";
+ if (function_exists("project_user_links")) {
+ $x .= project_user_links($user);
+ }
+ $x .= "
\n";
+ return $x;
+}
+
+// show user name, with links to profile if present.
+// if $badge_height is > 0, show badges
+//
+function user_links($user, $badge_height=0) {
+ BoincForumPrefs::lookup($user);
+ if (is_banished($user)) {
+ return "(banished: ID $user->id)";
+ }
+ $x = "";
+ if ($user->has_profile) {
+ $img_url = url_base()."img/head_20.png";
+ $x .= ' ';
+ }
+ $x .= " id."\">".$user->name." ";
+ if (function_exists("project_user_links")){
+ $x .= project_user_links($user);
+ }
+ if ($badge_height) {
+ $x .= badges_string(true, $user, $badge_height);
+ }
+ return $x;
+}
+
+function show_community_private($user) {
+ show_badges_row(true, $user);
+ if (!DISABLE_PROFILES) {
+ if ($user->has_profile) {
+ $x = "id\">".tra("View")." · ".tra("Delete")." ";
+ } else {
+ $x = "".tra("Create")." ";
+ }
+ row2(tra("Profile"), $x);
+ }
+ if (!DISABLE_FORUMS) {
+ $tot = total_posts($user);
+ if ($tot) {
+ row2(tra("Message boards"), "id\">".tra("%1 posts", $tot)." ");
+ }
+ }
+
+ row2(tra("Private messages"), pm_notification($user).pm_email_remind($user));
+
+ $notifies = BoincNotify::enum("userid=$user->id");
+ if (count($notifies)) {
+ $x = "";
+ foreach ($notifies as $notify) {
+ $y = notify_description($notify);
+ if ($y) {
+ $x .= "• $y ";
+ } else {
+ $notify->delete();
+ }
+ }
+ $x .= " ";
+ row2(tra("Notifications"), $x);
+ }
+
+ if (!DISABLE_TEAMS) {
+ if ($user->teamid && ($team = BoincTeam::lookup_id($user->teamid))) {
+ $x = "id\">$team->name
+ · ".tra("Quit team")." ";
+ if (is_team_admin($user, $team)) {
+ $x .= " · teamid\">".tra("Administer")." ";
+ }
+
+ // if there's a foundership request, notify the founder
+ //
+ if ($user->id==$team->userid && $team->ping_user >0) {
+ $x .= "".tra("(foundership change request pending)")."
";
+ }
+ row2(tra("Member of team"), $x);
+ } else {
+ row2(tra("Team"), tra("None")." · ".tra("find a team")." ");
+ }
+
+ $teams_founded = BoincTeam::enum("userid=$user->id");
+ foreach ($teams_founded as $team) {
+ if ($team->id != $user->teamid) {
+ $x = "id\">$team->name ";
+ $x .= " | id."\">".tra("Administer")." ";
+ if ($team->ping_user > 0) {
+ $x .= "".tra("(foundership change request pending)")."";
+ }
+ row2(tra("Founder but not member of"), $x);
+ }
+ }
+ }
+
+ $friends = BoincFriend::enum("user_src=$user->id and reciprocated=1");
+ $x = "".tra("Find friends")." \n";
+ $n = count($friends);
+ if ($n) {
+ foreach($friends as $friend) {
+ $fuser = BoincUser::lookup_id($friend->user_dest);
+ if (!$fuser) continue;
+ $x .= friend_links($fuser);
+ }
+ row2(tra("Friends")." ($n)", $x);
+ } else {
+ row2(tra("Friends"), $x);
+ }
+}
+
+// show summary of dynamic and static info (public)
+//
+function show_user_summary_public($user) {
+ global $g_logged_in_user;
+ row2(tra("User ID"), $user->id);
+ row2(tra("%1 member since", PROJECT), date_str($user->create_time));
+ row2(tra("Country"), $user->country);
+ // don't show URL if user has no recent credit (spam suppression)
+ //
+ if (strlen($user->url)) {
+ if (!NO_COMPUTING || $user->expavg_credit > 1) {
+ $u = normalize_user_url($user->url);
+ row2(tra("URL"), sprintf('%s ', $u, $u));
+ }
+ }
+ if (!NO_COMPUTING) {
+ show_credit($user);
+
+ if ($user->show_hosts) {
+ row2(tra("Computers"), "id\">".tra("View")." ");
+ } else {
+ row2(tra("Computers"), tra("hidden"));
+ }
+ }
+ if (function_exists("project_user_summary_public")) {
+ project_user_summary_public($user);
+ }
+}
+
+// Returns a cacheable community links data object
+// @param user The user to produce a community links object for
+
+function get_community_links_object($user){
+ $cache_object = new StdClass;
+ $cache_object->post_count = total_posts($user);
+ $cache_object->user = $user;
+ $cache_object->team = BoincTeam::lookup_id($user->teamid);
+ $cache_object->friends = array();
+
+ $friends = BoincFriend::enum("user_src=$user->id and reciprocated=1");
+ foreach($friends as $friend) {
+ $fuser = BoincUser::lookup_id($friend->user_dest);
+ if (!$fuser) continue;
+ $cache_object->friends[] = $fuser;
+ }
+ return $cache_object;
+}
+
+function community_links($clo, $logged_in_user){
+ $user = $clo->user;
+ $team = $clo->team;
+ $friends = $clo->friends;
+ $tot = $clo->post_count;
+
+ if (!DISABLE_TEAMS) {
+ if ($user->teamid && $team) {
+ row2(tra("Team"), "id\">$team->name ");
+ } else {
+ row2(tra("Team"), tra("None"));
+ }
+ }
+ if (!DISABLE_FORUMS) {
+ if ($tot) {
+ row2(tra("Message boards"), "id\">".tra("%1 posts", $tot)." ");
+ }
+ }
+ if ($logged_in_user && $logged_in_user->id != $user->id) {
+ row2(tra("Contact"), "id."\">".tra("Send private message")." ");
+ $friend = BoincFriend::lookup($logged_in_user->id, $user->id);
+ if ($friend && $friend->reciprocated) {
+ row2(tra("This person is a friend"),
+ "id\">".tra("Cancel friendship")." "
+ );
+ } else if ($friend) {
+ row2(tra("Friends"), "id\">".tra("Request pending")." ");
+ } else {
+ row2(tra("Friends"), "id\">".tra("Add as friend")." ");
+ }
+ }
+
+ if ($friends) {
+ $x = "";
+ foreach($friends as $friend) {
+ $x .= friend_links($friend);
+ }
+ row2(tra("Friends")." (".sizeof($friends).")", $x);
+ }
+}
+
+function show_profile_link($user) {
+ if ($user->has_profile) {
+ row2(tra("Profile"), "id\">".tra("View")." ");
+ }
+}
+
+function show_account_private($user) {
+ grid(
+ false,
+ function() use ($user) {
+ start_table();
+ row1(tra("Account information"), 2, 'heading');
+ show_user_info_private($user);
+ show_preference_links();
+ show_user_stats_private($user);
+
+ if (function_exists('show_user_donations_private')) {
+ show_user_donations_private($user);
+ }
+ end_table();
+ if (!NO_COMPUTING) {
+ show_other_projects($user, true);
+ }
+ if (function_exists("project_user_page_private")) {
+ project_user_page_private($user);
+ }
+ //echo "Delete account \n";
+ },
+ function() use ($user) {
+ start_table();
+ row1(tra("Community"));
+ show_community_private($user);
+ end_table();
+ }
+ );
+}
+
+$cvs_version_tracker[]="\$Id$"; //Generated automatically - do not edit
+
+?>
diff --git a/inc/user_gallery_1_edit.html b/inc/user_gallery_1_edit.html
new file mode 100755
index 0000000..a487d0e
--- /dev/null
+++ b/inc/user_gallery_1_edit.html
@@ -0,0 +1,16 @@
+
Profile Gallery: Page 1 of 1
+
+Last updated 20 Jun 2018, 19:10:02 UTC
+Browse the user profiles by picture.
+ Only user profiles with pictures are listed here.
+Page 1 of 1
Jump to Page:
+1
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/inc/user_util.inc b/inc/user_util.inc
new file mode 100755
index 0000000..4aece96
--- /dev/null
+++ b/inc/user_util.inc
@@ -0,0 +1,246 @@
+.
+
+// functions for creating and deleting users
+
+
+include_once("../inc/boinc_db.inc");
+include_once("../inc/util.inc");
+include_once("../inc/email.inc");
+include_once("../inc/recaptchalib.php");
+
+function is_banned_email_addr($email_addr) {
+ global $banned_email_domains;
+ if (isset($banned_email_domains)) {
+ foreach($banned_email_domains as $d) {
+ $x = strstr($email_addr, $d);
+ if ($x == $d) return true;
+ }
+ }
+ return false;
+}
+
+function is_valid_user_name($name, &$reason) {
+ if (trim($name) !== $name) {
+ $reason = tra("user name cannot have leading or trailing white space");
+ return false;
+ }
+ if (strlen($name) == 0) {
+ $reason = tra("user name must be nonempty");
+ return false;
+ }
+ if (sanitize_tags($name) !== $name) {
+ $reason = tra("user name may not contain HTML tags");
+ return false;
+ }
+ return true;
+}
+
+// the following DB-escapes its args
+//
+function make_user(
+ $email_addr, $name, $passwd_hash,
+ $country=null, $postal_code=null, $project_prefs=null, $teamid=0
+) {
+ if (!is_valid_email_addr($email_addr)) return null;
+ if (is_banned_email_addr($email_addr)) return null;
+
+ $authenticator = random_string();
+ $cross_project_id = random_string();
+ $now = time();
+ if (!is_valid_country($country)) return null;
+
+ $email_addr = BoincDb::escape_string($email_addr);
+ $name = sanitize_tags($name);
+ $name = BoincDb::escape_string($name);
+ $passwd_hash = BoincDb::escape_string($passwd_hash);
+
+ $country = BoincDb::escape_string($country);
+ $postal_code = sanitize_tags(BoincDb::escape_string($postal_code));
+
+ $uid = BoincUser::insert("(create_time, email_addr, name, authenticator, country, postal_code, total_credit, expavg_credit, expavg_time, project_prefs, teamid, venue, send_email, show_hosts, posts, seti_id, seti_nresults, seti_last_result_time, seti_total_cpu, has_profile, cross_project_id, passwd_hash, email_validated, donated) values($now, '$email_addr', '$name', '$authenticator', '$country', '$postal_code', 0, 0, unix_timestamp(), '$project_prefs', $teamid, '', 1, 1, 0, 0, 0, 0, 0, 0, '$cross_project_id', '$passwd_hash', 0, 0)");
+
+ if (!$uid) {
+ return null;
+ }
+ $user = BoincUser::lookup_id($uid);
+ if (defined('RECORD_USER_IP')) {
+ $ip = $_SERVER['REMOTE_ADDR'];
+ $ip = BoincDb::escape_string($ip);
+ $user->update("venue='$ip'");
+ }
+ return $user;
+}
+
+function make_user_ldap($email_addr, $name) {
+ $email_addr = BoincDb::escape_string($email_addr);
+ $name = sanitize_tags($name);
+ $name = BoincDb::escape_string($name);
+ $authenticator = random_string();
+ $cross_project_id = random_string();
+ $passwd_hash = random_string();
+ $now = time();
+ $uid = BoincUser::insert("(create_time, email_addr, name, authenticator, country, postal_code, total_credit, expavg_credit, expavg_time, project_prefs, teamid, send_email, show_hosts, cross_project_id, passwd_hash) values($now, '$email_addr', '$name', '$authenticator', '', '', 0, 0, unix_timestamp(), '', 0, 1, 1, '$cross_project_id', '$passwd_hash')");
+
+ if ($uid) {
+ return BoincUser::lookup_id($uid);
+ } else {
+ return null;
+ }
+}
+
+function show_error($str) {
+ page_head(tra("Can't create account"));
+ echo "$str \n";
+ echo BoincDb::error();
+ echo "".tra("Click your browser's Back button to try again.")."\n
\n";
+ page_tail();
+ exit();
+}
+
+// validate POST args and make user.
+// If error show error page and exit.
+// Else return user object
+//
+function validate_post_make_user() {
+ global $recaptcha_private_key;
+ $config = get_config();
+ if (parse_bool($config, "disable_account_creation")
+ || parse_bool($config, "no_web_account_creation")
+ ) {
+ error_page("Account creation is disabled");
+ }
+
+ if ($recaptcha_private_key) {
+ if (!boinc_recaptcha_isValidated($recaptcha_private_key)) {
+ show_error(
+ tra("Your reCAPTCHA response was not correct. Please try again.")
+ );
+ }
+ }
+
+ // see whether the new account should be pre-enrolled in a team,
+ // and initialized with its founder's project prefs
+ //
+ $teamid = post_int("teamid", true);
+ if ($teamid) {
+ $team = BoincTeam::lookup_id($teamid);
+ $clone_user = BoincUser::lookup_id($team->userid);
+ if (!$clone_user) {
+ error_page("User $userid not found");
+ }
+ $project_prefs = $clone_user->project_prefs;
+ } else {
+ $teamid = 0;
+ $project_prefs = "";
+ }
+
+ if (defined('INVITE_CODES')) {
+ $invite_code = post_str("invite_code");
+ if (strlen($invite_code) == 0) {
+ show_error(tra("You must supply an invitation code to create an account."));
+ }
+ if (!preg_match(INVITE_CODES, $invite_code)) {
+ show_error(tra("The invitation code you gave is not valid."));
+ }
+ }
+
+ $new_name = post_str("new_name");
+ if (!is_valid_user_name($new_name, $reason)) {
+ show_error($reason);
+ }
+
+ $new_email_addr = strtolower(post_str("new_email_addr"));
+ if (!is_valid_email_addr($new_email_addr)) {
+ show_error(tra("Invalid email address: please enter a valid address of the form name@xxx.yyy"));
+ }
+ $user = BoincUser::lookup_email_addr($new_email_addr);
+ if ($user) {
+ show_error(tra("There's already an account with that email address."));
+ }
+
+ $passwd = post_str("passwd");
+
+ $min_passwd_length = parse_config($config, "");
+ if (!$min_passwd_length) $min_passwd_length = 6;
+
+ if (!is_ascii($passwd)) {
+ show_error(tra("Passwords may only include ASCII characters."));
+ }
+
+ if (strlen($passwd)<$min_passwd_length) {
+ show_error(
+ tra("New password is too short: minimum password length is %1 characters.", $min_passwd_length)
+ );
+ }
+
+ $passwd_hash = md5($passwd.$new_email_addr);
+
+ $country = post_str("country", true);
+ if (!$country) {
+ $country = "None";
+ }
+ if (!is_valid_country($country)) {
+ error_page("bad country");
+ }
+
+ if (POSTAL_CODE) {
+ $postal_code = sanitize_tags(post_str("postal_code", true));
+ } else {
+ $postal_code = '';
+ }
+
+ $user = make_user(
+ $new_email_addr, $new_name, $passwd_hash,
+ $country, $postal_code, $project_prefs, $teamid
+ );
+ if (!$user) {
+ show_error(
+ tra("Couldn't create account").": ".BoincDb::error()
+ );
+ }
+
+ if (defined('INVITE_CODES')) {
+ error_log("Account '$new_email_addr' created using invitation code '$invite_code'");
+ }
+ return $user;
+}
+
+// delete a user and all associated records except
+// result
+// host
+// batch
+// team
+// user_submit
+// user_submit_app
+// credited_job
+// donation_paypal
+// sent_email
+//
+function delete_user($user) {
+ delete_profile($user);
+ forum_delete_user($user);
+ // deletes post, thread, subscription, forum_preferences, forum_logging
+ BoincPrivateMessage::delete_aux("userid=$user->id or senderid=$user->id");
+ BoincNotify::delete_aux("userid=$user->id");
+ BoincCreditUser::delete_user($user);
+ BoincBadgeUser::delete("user_id=$user->id");
+ BoincFriend::delete_aux("user_src=$user->id or user_dest=$user->id");
+ $user->delete();
+}
+?>
diff --git a/inc/util.inc b/inc/util.inc
new file mode 100755
index 0000000..929cc15
--- /dev/null
+++ b/inc/util.inc
@@ -0,0 +1,1115 @@
+.
+
+// Utility functions for BOINC web pages
+
+error_reporting(E_ALL);
+ini_set('display_errors', true);
+ini_set('display_startup_errors', true);
+
+require_once("../inc/util_basic.inc");
+require_once("../project/project.inc");
+require_once("../inc/countries.inc");
+require_once("../inc/db.inc");
+require_once("../inc/boinc_db.inc");
+require_once("../inc/translation.inc");
+require_once("../inc/profile.inc");
+require_once("../inc/bootstrap.inc");
+
+// parse some stuff from config (do it here for efficiency)
+//
+$config = get_config();
+$master_url = parse_config($config , "");
+$recaptcha_public_key = parse_config($config, "");
+$recaptcha_private_key = parse_config($config, "");
+
+// don't allow /... at the end of URL
+
+if (array_key_exists("PATH_INFO", $_SERVER)) {
+ die("bad URL");
+}
+
+// define TIMEZONE in project.inc
+//
+if (defined('TIMEZONE')) {
+ date_default_timezone_set(TIMEZONE);
+} else {
+ date_default_timezone_set('UTC');
+}
+
+if (!defined('DISABLE_PROFILES')) {
+ define('DISABLE_PROFILES', false);
+}
+if (!defined('DISABLE_FORUMS')) {
+ define('DISABLE_FORUMS', false);
+}
+if (!defined('DISABLE_TEAMS')) {
+ define('DISABLE_TEAMS', false);
+}
+if (!defined('DISABLE_BADGES')) {
+ define('DISABLE_BADGES', false);
+}
+if (!defined('BADGE_HEIGHT_SMALL')) {
+ define('BADGE_HEIGHT_SMALL', 20);
+}
+if (!defined('BADGE_HEIGHT_MEDIUM')) {
+ define('BADGE_HEIGHT_MEDIUM', 24);
+}
+if (!defined('BADGE_HEIGHT_LARGE')) {
+ define('BADGE_HEIGHT_LARGE', 56);
+}
+if (!defined('LDAP_HOST')) {
+ define('LDAP_HOST', null);
+}
+if (!defined('POSTAL_CODE')) {
+ define('POSTAL_CODE', false);
+}
+if (!defined('NO_COMPUTING')) {
+ define('NO_COMPUTING', false);
+}
+if (!defined('NO_HOSTS')) {
+ define('NO_HOSTS', false);
+}
+if (!defined('NO_STATS')) {
+ define('NO_STATS', false);
+}
+if (!defined('NO_GLOBAL_PREFS')) {
+ define('NO_GLOBAL_PREFS', false);
+}
+if (!defined('USER_HOME')) {
+ define('USER_HOME', 'home.php');
+}
+if (!defined('POST_MAX_LINKS')) {
+ define('POST_MAX_LINKS', 0);
+}
+
+// sleep this long on any login failure
+// (slow the rate of hacker attacks)
+//
+define('LOGIN_FAIL_SLEEP_SEC', 5);
+
+$caching = false;
+ // if set, we're writing to a file rather than to client
+$did_page_head = false;
+
+define('KILO', 1024);
+define('MEGA', 1024*KILO);
+define('GIGA', 1024*MEGA);
+
+// return true if this page is HTTPS
+//
+function is_https() {
+ return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'];
+}
+
+function secure_url_base() {
+ if (defined('SECURE_URL_BASE')) return SECURE_URL_BASE;
+ return URL_BASE;
+}
+
+function url_base() {
+ return is_https()?secure_url_base():URL_BASE;
+}
+
+function send_cookie($name, $value, $permanent, $ops=false) {
+ global $master_url;
+
+ // the following allows independent login for projects on the same server
+ //
+ $url = parse_url($master_url);
+ $path = $url['path'];
+ if ($ops) {
+ $path = substr($path, 0, -1);
+ $path .= "_ops/";
+ }
+ $expire = $permanent?time()+3600*24*365:0;
+ setcookie($name, $value, $expire, $path);
+}
+
+function clear_cookie($name, $ops=false) {
+ global $master_url;
+ $url = parse_url($master_url);
+ $path = $url['path'];
+ if ($ops) {
+ $path = substr($path, 0, -1);
+ $path .= "_ops/";
+ }
+ setcookie($name, '', time()-3600, $path);
+}
+
+$g_logged_in_user = null;
+$got_logged_in_user = false;
+
+function get_logged_in_user($must_be_logged_in=true) {
+ global $g_logged_in_user, $got_logged_in_user;
+ if ($got_logged_in_user) return $g_logged_in_user;
+
+ if (web_stopped()) return null;
+
+ $authenticator = null;
+ if (isset($_COOKIE['auth'])) $authenticator = $_COOKIE['auth'];
+
+ $authenticator = BoincDb::escape_string($authenticator);
+ if ($authenticator) {
+ $g_logged_in_user = BoincUser::lookup("authenticator='$authenticator'");
+ }
+ if ($must_be_logged_in && !$g_logged_in_user) {
+ $next_url = '';
+ if (array_key_exists('REQUEST_URI', $_SERVER)) {
+ $next_url = $_SERVER['REQUEST_URI'];
+ $n = strrpos($next_url, "/");
+ if ($n) {
+ $next_url = substr($next_url, $n+1);
+ }
+ }
+ $next_url = urlencode($next_url);
+ Header("Location: ".url_base()."login_form.php?next_url=$next_url");
+ exit;
+ }
+ $got_logged_in_user = true;
+ return $g_logged_in_user;
+}
+
+function show_login_info($prefix="") {
+ $user = get_logged_in_user(false);
+ if ($user) {
+ $url_tokens = url_tokens($user->authenticator);
+ echo "$user->name · ".tra("log out")." ";
+ } else {
+ echo "".tra("log in")." ";
+ }
+}
+
+$cache_control_extra="";
+$is_login_page = false;
+
+// Call this to start pages.
+// Outputs some HTML boilerplate,
+// then calls project_banner() (in html/project/project.inc)
+// to output whatever you want at the top of your web pages.
+//
+// Page_head() is overridable so that projects that want to integrate BOINC
+// with an existing web framework can more easily do so.
+// To do so, define page_head() in the project include file.
+//
+if (!function_exists("page_head")){
+function page_head(
+ $title,
+ // page title. Put in , used as title for browser tab.
+ $body_attrs=null,
+ //
+ // e.g. Javascript to put focus in an input field
+ // (onload="document.form.foo.focus()")
+ // or to jump to a particular post (onload="jumpToUnread();")
+ $is_main = false,
+ // if set, include schedulers.txt.
+ // also pass to project_banner() in case you want a different
+ // header for your main page.
+ $url_prefix="",
+ // prepend this to links.
+ // Use for web pages not in the top directory
+ $head_extra=null
+ // extra stuff to put in . E.g.:
+ // reCAPTCHA code (create_profile.php)
+ // bbcode javascript (forums)
+) {
+ global $caching, $cache_control_extra, $did_page_head;
+ global $is_login_page, $fixed_navbar;
+
+ $did_page_head = true;
+ $url_base = url_base();
+
+ $rssname = "RSS 2.0";
+ $rsslink = $url_base."rss_main.php";
+
+ if (!$caching) {
+ header("Content-type: text/html; charset=utf-8");
+ header ("Expires: Mon, 26 Jul 1997 05:00:00 UTC"); // Date in the past
+ header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " UTC"); // always modified
+ header ("Cache-Control: $cache_control_extra no-cache, must-revalidate, post-check=0, pre-check=0"); // HTTP/1.1
+ header ("Pragma: no-cache"); // HTTP/1.0
+ }
+
+ echo '
+
+
+
+ ';
+ if ($head_extra) {
+ echo "\n$head_extra\n";
+ }
+ if ($is_main && (!defined('NO_COMPUTING')||!NO_COMPUTING)) {
+ readfile("schedulers.txt");
+ }
+
+ $t = $title?$title:PROJECT;
+ echo "$t \n";
+ echo '
+
+
+ ';
+ if (defined('STYLESHEET')) {
+ $stylesheet = $url_base.STYLESHEET;
+ echo "
+
+ ";
+ }
+ if (defined('STYLESHEET2')) {
+ $stylesheet2 = $url_base.STYLESHEET2;
+ echo "
+
+ ";
+ }
+
+ if (defined("SHORTCUT_ICON")) {
+ echo '
+';
+ }
+
+ echo "
+
+
+ ";
+ if ($fixed_navbar) {
+ $body_attrs .= ' style="padding-top:70px"';
+ }
+ echo "";
+ display_cvs_versions();
+ echo '
+ ';
+
+ switch($title) { //kludge
+ case tra("Log in"):
+ case tra("Create an account"):
+ case tra("Server status page"):
+ $is_login_page = true;
+ break;
+ default:
+ break;
+ }
+ project_banner($title, $url_prefix, $is_main);
+}
+}
+
+// See the comments for page_head()
+//
+if (!function_exists("page_tail")){
+function page_tail(
+ $show_date=false,
+ // true for pages that are generated periodically rather than on the fly
+ $url_prefix="",
+ // use for pages not at top level
+ $is_main=false
+ // passed to project_footer;
+) {
+ echo " \n";
+ project_footer($is_main, $show_date, $url_prefix);
+ echo '
+
+
+
+
+
+ ';
+}
+}
+
+function display_cvs_versions(){
+ global $cvs_version_tracker;
+ echo "\n\n";
+ for ($i=0;$i\n";
+ }
+}
+
+function db_error_page() {
+ page_head("Database error");
+ echo tra("A database error occurred while handling your request; please try again later.");
+ page_tail();
+}
+
+function error_page($msg) {
+ global $generating_xml;
+ if ($generating_xml) {
+ xml_error(-1, $msg);
+ }
+ page_head(tra("Unable to handle request"));
+ echo $msg;
+ page_tail();
+ exit();
+}
+
+// takes argument in second and returns a human formatted time string
+// in the form D days + h Hours + m Min + s sec.
+
+function time_diff($x, $res=3) {
+ $days = (int)($x/86400);
+ $hours = (int)(($x-$days*86400)/3600);
+ $minutes = (int)(($x-$days*86400-$hours*3600)/60);
+ $seconds = (int)($x % 60);
+
+ $datestring = "";
+ if ($days) {
+ $datestring .= "$days ".tra("days")." ";
+ }
+ if ($res>0 && ($hours || strlen($datestring))) {
+ $datestring .= "$hours ".tra("hours")." ";
+ }
+ if ($res>1 && ($minutes || strlen($datestring))) {
+ $datestring .= "$minutes ".tra("min")." ";
+ }
+ if ($res>2 && ($seconds)) {
+ $datestring .= "$seconds ".tra("sec")." ";
+ }
+
+ return $datestring;
+}
+
+
+function date_str($x) {
+ if ($x == 0) return "---";
+ return gmdate('j M Y', $x);
+}
+
+function time_str($x) {
+ if ($x == 0) return "---";
+ return gmdate('j M Y, G:i:s', $x) . " UTC";
+}
+
+function local_time_str($x) {
+ if ($x == 0) return "---";
+ return date('j M Y, H:i T', $x);
+}
+
+function pretty_time_str($x) {
+ return time_str($x);
+}
+
+function start_table_str($class="", $style="") {
+ $s = $style?'style="'.$style.'"':'';
+ return '
+
+ ';
+}
+
+function start_table($class="", $style="") {
+ echo start_table_str($class, $style);
+}
+
+function end_table_str() {
+ return '
+
+ ';
+}
+
+function end_table() {
+ echo end_table_str();
+}
+
+// Table header row with unlimited number of columns
+
+function table_header() {
+ echo "\n";
+ $c = 'class="bg-primary"';
+ for ($i = 0; $i < func_num_args(); $i++) {
+ if (is_array(func_get_arg($i))) {
+ $col = func_get_arg($i);
+ echo "".$col[0]." \n";
+ } else {
+ echo "".func_get_arg($i)." \n";
+ }
+ }
+ echo " \n";
+}
+
+// Table row with unlimited number of columns
+
+function table_row() {
+ echo "\n";
+ for ($i = 0; $i < func_num_args(); $i++) {
+ if (is_array(func_get_arg($i))) {
+ $col = func_get_arg($i);
+ echo "".$col[0]." \n";
+ } else {
+ echo "".func_get_arg($i)." \n";
+ }
+ }
+ echo " \n";
+}
+
+function row1($x, $ncols=2, $class="heading") {
+ if ($class == "heading") {
+ echo "$x \n";
+ } else {
+ echo "$x \n";
+ }
+}
+
+define('NAME_ATTRS', 'class="text-right " style="padding-right:12px"');
+define('VALUE_ATTRS', 'style="padding-left:12px"');
+define('VALUE_ATTRS_ERR', 'class="danger" style="padding-left:12px"');
+
+function row2($x, $y, $show_error=false, $lwidth='40%') {
+ if ($x==="") $x=" ";
+ if ($y==="") $y=" ";
+ $attrs = $show_error?VALUE_ATTRS_ERR:VALUE_ATTRS;
+ echo "
+ $x
+ $y
+
+ ";
+}
+
+function row2_init($x, $y, $lwidth='40%') {
+ echo '
+ '.$x.'
+ '.$y.'
+ ';
+}
+
+function row2_plain($x, $y) {
+ echo " $x $y \n";
+}
+
+function rowify($string) {
+ echo "$string ";
+}
+
+function row_array($x) {
+ echo "\n";
+ foreach ($x as $h) {
+ echo "$h \n";
+ }
+ echo " \n";
+}
+
+define ('ALIGN_RIGHT', 'style="text-align:right;"');
+
+function row_heading_array($x, $attrs=null, $class='bg-primary') {
+ echo "";
+ $i = 0;
+ foreach ($x as $h) {
+ $a = $attrs?$attrs[$i]:"";
+ echo "$h ";
+ $i++;
+ }
+ echo " \n";
+}
+
+function row_heading($x, $class='bg-primary') {
+ echo sprintf('%s
+ ', $class, $x
+ );
+}
+
+// return hard-to-guess string of 32 random hex chars
+//
+function random_string() {
+ return md5(uniqid(rand(), true));
+}
+
+function url_tokens($auth) {
+ $now = time();
+ $ttok = md5((string)$now.$auth);
+ return "&tnow=$now&ttok=$ttok";
+}
+
+function form_tokens($auth) {
+ $now = time();
+ $ttok = md5((string)$now.$auth);
+ return "
+
+ ";
+}
+
+function valid_tokens($auth) {
+ $tnow = get_str('tnow', true);
+ $ttok = get_str('ttok', true);
+ if (!$tnow) {
+ $tnow = $_POST['tnow'];
+ }
+ if (!$ttok) {
+ $ttok = $_POST['ttok'];
+ }
+ if (!$tnow) return false;
+ if (!$ttok) return false;
+ $t = md5((string)$tnow.$auth);
+ if ($t != $ttok) return false;
+ if (time() > $tnow + 86400) return false;
+ return true;
+}
+
+function check_tokens($auth) {
+ if (valid_tokens($auth)) return;
+ error_page(
+ tra("Link has timed out. Please click Back, refresh the page, and try again.")
+ );
+}
+
+// Generates a legal filename from a parameter string.
+
+function get_legal_filename($name) {
+ return strtr($name, array(','=>'', ' '=>'_'));
+}
+
+// Returns a string containing as many words
+// (being collections of characters separated by the character $delimiter)
+// as possible such that the total string length is <= $chars characters long.
+// If $ellipsis is true, then an ellipsis is added to any sentence which
+// is cut short.
+
+function sub_sentence($sentence, $delimiter, $max_chars, $ellipsis=false) {
+ $words = explode($delimiter, $sentence);
+ $total_chars = 0;
+ $trunc = false;
+ $result = "";
+
+ foreach ($words as $word) {
+ if (strlen($result) + strlen($word) > $max_chars) {
+ $trunc = true;
+ break;
+ }
+ if ($result) {
+ $result .= " $word";
+ } else {
+ $result = $word;
+ }
+ }
+
+ if ($ellipsis && $trunc) {
+ $result .= "...";
+ }
+
+ return $result;
+}
+
+// use this for user RAC and result credit
+//
+function format_credit($x) {
+ return number_format($x, 2);
+}
+
+// use this when credit is likely to be large, e.g. team RAC
+//
+function format_credit_large($x) {
+ return number_format($x, 0);
+}
+
+function host_link($hostid) {
+ if ($hostid) {
+ return "$hostid ";
+ } else {
+ return "---";
+ }
+}
+
+function open_output_buffer() {
+ ob_start();
+ ob_implicit_flush(0);
+}
+
+function close_output_buffer($filename) {
+ $fh = fopen($filename, "w");
+ $page = ob_get_contents();
+ ob_end_clean();
+ fwrite($fh, $page);
+ fclose($fh);
+}
+
+function bbcode_info() {
+ return "".tra("Use BBCode tags to format your text")." \n";
+}
+
+// strip slashes if magic quotes in effect
+function undo_magic_quotes($x) {
+ if (get_magic_quotes_gpc()) {
+ return stripslashes($x);
+ }
+ return $x;
+}
+
+// check for bogus GET args
+//
+function check_get_args($args) {
+ foreach ($_GET as $key => $val) {
+ if (!in_array($key, $args)) {
+ Header("Location: extra_arg_$key.html");
+ die;
+ }
+ }
+}
+
+// returns null if the arg is optional and missing
+//
+function get_int($name, $optional=false) {
+ $x=null;
+ if (isset($_GET[$name])) $x = $_GET[$name];
+ if (!is_numeric($x)) {
+ if ($optional) {
+ if ($x) {
+ Header("Location: non_num_arg.html");
+ die;
+ }
+ return null;
+ } else {
+ Header("Location: missing_arg_$name.html");
+ die;
+ }
+ }
+ return (int)$x;
+}
+
+// returns null if the arg is optional and missing
+//
+function post_num($name, $optional=false) {
+ $x = null;
+ if (isset($_POST[$name])) $x = $_POST[$name];
+ if (!is_numeric($x)) {
+ if ($optional) {
+ return null;
+ } else {
+ error_page("missing or bad parameter: $name; supplied: ".htmlspecialchars($x));
+ }
+ }
+ return (double)$x;
+}
+
+// returns null if the arg is optional and missing
+//
+function post_int($name, $optional=false) {
+ $x = post_num($name, $optional);
+ if (is_null($x)) return null;
+ $y = (int)$x;
+ if ($x != $y) {
+ error_page("param $name must be an integer");
+ }
+ return $y;
+}
+
+function get_array($name) {
+ if (isset($_GET[$name])) {
+ return $_GET[$name];
+ } else {
+ return array();
+ }
+}
+
+function get_str($name, $optional=false) {
+ if (isset($_GET[$name])) {
+ $x = $_GET[$name];
+ } else {
+ if (!$optional) {
+ error_page("missing or bad parameter: $name");
+ }
+ $x = null;
+ }
+ return undo_magic_quotes($x);
+}
+
+function post_str($name, $optional=false) {
+ if (isset($_POST[$name])) {
+ $x = $_POST[$name];
+ } else {
+ if (!$optional) {
+ error_page("missing or bad parameter: $name");
+ }
+ $x = null;
+ }
+ return undo_magic_quotes($x);
+}
+
+function post_arr($name, $optional=false) {
+ if (isset($_POST[$name]) && is_array($_POST[$name])) {
+ $x = $_POST[$name];
+ } else {
+ if (!$optional) {
+ error_page("missing or bad parameter: $name");
+ }
+ $x = null;
+ }
+ return $x;
+}
+
+function is_ascii($str) {
+ // the mb_* functions are not included by default
+ // return (mb_detect_encoding($passwd) -= 'ASCII');
+
+ for ($i=0; $i 127) return false;
+ }
+ return true;
+}
+
+// This function replaces some often made mistakes while entering numbers
+// and gives back an error if there are false characters
+// It will also be checked if the value is within certain borders
+// @param string &$value reference to the value that should be verified
+// @param double $low the lowest number of value if verified
+// @param double $high the highest number of value if verified
+// @return bool true if $value is numeric and within the defined borders,
+// false if $value is not numeric, no changes were made in this case
+//
+function verify_numeric(&$value, $low, $high = false) {
+ $number = trim($value);
+ $number = str_replace('o', '0', $number);
+ $number = str_replace('O', '0', $number);
+ $number = str_replace('x', '', $number); //if someone enters '0x100'
+ $number = str_replace(',', '.', $number); // replace the german decimal separator
+ // if no value was entered and this is ok
+ //
+ if ($number=='' && !$low) return true;
+
+ // the supplied value contains alphabetic characters
+ //
+ if (!is_numeric($number)) return false;
+
+ if ($number < $low) return false;
+
+ if ($high) {
+ if ($number > $high) return false;
+ }
+ $value = (double)$number;
+ return true;
+}
+
+// Generate a "select" element from an array of values
+//
+function select_from_array($name, $array, $selection=null, $width=240) {
+ $out = '"';
+
+ foreach ($array as $key => $value) {
+ if ($value) {
+ $out .= "".$value." \n";
+ }
+ }
+ $out .= " \n";
+ return $out;
+}
+
+// Convert to entities, while preserving already-encoded entities.
+// Do NOT use if $str contains valid HTML tags.
+//
+function boinc_htmlentities($str) {
+ $str = html_entity_decode($str, ENT_COMPAT, "UTF-8");
+ $str = htmlentities($str, ENT_COMPAT, "UTF-8");
+ return $str;
+}
+
+function strip_bbcode($string){
+ return preg_replace("/((\[.+\])+?)(.+?)((\[\/.+\])+?)/","",$string);
+}
+
+function current_url() {
+ $url = "http";
+ if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") {
+ $url .= "s";
+ }
+ $url .= "://";
+ $url .= $_SERVER['SERVER_NAME'];
+ $url .= ":".$_SERVER['SERVER_PORT'];
+ if (isset($_SERVER['REQUEST_URI'])) {
+ $url .= $_SERVER['REQUEST_URI'];
+ } else {
+ if ($_SERVER['QUERY_STRING']) {
+ $url .= "?".$_SERVER['QUERY_STRING'];
+ }
+ }
+ return $url;
+}
+
+// Show a single link formatted to look like a button.
+// @param url The destination URL of the button
+// @param text The text to display on the button
+// @param desc The title of the destination - typically used as a popup
+// @param class The optional CSS class of the button. Defaults to a standard button
+//
+
+function button_text($url, $text, $desc=null, $class="btn-success btn-sm") {
+ if (!$desc) {
+ $desc = $text;
+ }
+ return sprintf(' %s ',
+ $url, $desc, $class, $text
+ );
+}
+
+function show_button($url, $text, $desc=null, $class="btn-success btn-sm") {
+ echo button_text($url, $text, $desc=null, $class);
+}
+
+// for places with a bunch of buttons, like forum posts
+//
+function show_button_small($url, $text, $desc=null) {
+ echo button_text($url, $text, $desc, "btn-primary btn-xs");
+}
+
+// used for showing icons
+//
+function show_image($src, $title, $alt, $height=null) {
+ $h = "";
+ if ($height) {
+ $h = "height=\"$height\"";
+ }
+ echo " ";
+}
+
+function show_project_down() {
+ global $did_page_head;
+ if (!$did_page_head) {
+ page_head(tra("Project down for maintenance"));
+ }
+ echo tra(
+ "%1 is temporarily shut down for maintenance. Please try again later.",
+ PROJECT
+ );
+ page_tail();
+ exit();
+}
+
+function check_web_stopped() {
+ global $generating_xml;
+ if (web_stopped()) {
+ if ($generating_xml) {
+ xml_error(-183);
+ } else {
+ show_project_down();
+ }
+ }
+}
+
+// Connects to database server and selects database as noted in config.xml
+// If only read-only access is necessary,
+// tries instead to connect to if tag exists.
+// DEPRECATED - use boinc_db.inc
+//
+function db_init($try_replica=false) {
+ check_web_stopped();
+ $retval = db_init_aux($try_replica);
+ if ($retval == 1) {
+ echo tra("Unable to connect to database - please try again later");
+ exit();
+ }
+ if ($retval == 2) {
+ echo tra("Unable to select database - please try again later");
+ exit();
+ }
+ return 0;
+}
+
+// return a structure indicating whether project has non-deprecated
+// apps versions for various resource types,
+// and with a count of app versions
+//
+function get_app_types() {
+ $t = new StdClass;
+ $t->cpu = false;
+ $t->cuda = false;
+ $t->ati = false;
+ $t->intel_gpu = false;
+ $t->count = 0;
+ $avs = BoincAppVersion::enum("deprecated=0");
+ foreach ($avs as $av) {
+ if (strstr($av->plan_class, "ati")) {
+ $t->ati = true;
+ $t->count++;
+ } else if (strstr($av->plan_class, "cuda")) {
+ $t->cuda = true;
+ $t->count++;
+ } else if (strstr($av->plan_class, "nvidia")) {
+ $t->cuda = true;
+ $t->count++;
+ } else if (strstr($av->plan_class, "intel_gpu")) {
+ $t->intel_gpu = true;
+ $t->count++;
+ } else {
+ $t->cpu = true;
+ $t->count++;
+ }
+ }
+ return $t;
+}
+
+// Functions to sanitize GET and POST args
+
+// "next_url" arguments (must be local, not full URLs)
+//
+function sanitize_local_url($x) {
+ $x = trim($x, "/");
+ if (strstr($x, "/")) return "";
+ if (strstr($x, "<")) return "";
+ if (strstr($x, "\"")) return "";
+ return $x;
+}
+
+// strip HTML tags
+//
+function sanitize_tags($x) {
+ return strip_tags($x);
+}
+
+function sanitize_numeric($x) {
+ if (is_numeric($x)) {
+ return $x;
+ } else if (trim($x) == '' ) {
+ return '';
+ } else {
+ return "not numeric";
+ }
+}
+
+function sanitize_email($x) {
+ if (function_exists('filter_var')) {
+ return filter_var($x, FILTER_SANITIZE_EMAIL);
+ } else {
+ return strip_tags($x);
+ }
+}
+
+function flops_to_credit($f) {
+ return $f*(200/86400e9);
+}
+
+function credit_to_gflop_hours($c) {
+ return $c/(200/24);
+}
+
+function do_download($path,$name="") {
+ if (strcmp($name,"") == 0) {
+ $name=basename($path);
+ }
+ header('Content-Description: File Transfer');
+ header('Content-Type: application/octet-stream');
+ header('Content-Disposition: attachment; filename='.$name);
+ header('Content-Transfer-Encoding: binary');
+ header('Expires: 0');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Pragma: public');
+ header('Content-Length: ' . filesize($path));
+ flush();
+ readfile($path);
+}
+
+function redirect_to_secure_url($url) {
+ if (defined('SECURE_URL_BASE')
+ && strstr(SECURE_URL_BASE, "https://")
+ && (!isset($_SERVER['HTTPS']) || !$_SERVER['HTTPS'])
+ ) {
+ Header("Location: ".SECURE_URL_BASE."/$url");
+ exit;
+ }
+}
+
+function badges_string($is_user, $item, $height) {
+ if (DISABLE_BADGES) return null;
+ if ($is_user) {
+ $bus = BoincBadgeUser::enum("user_id=$item->id");
+ } else {
+ $bus = BoincBadgeTeam::enum("team_id=$item->id");
+ }
+ if (!$bus) return null;
+ $x = "";
+ foreach ($bus as $bu) {
+ $badge = BoincBadge::lookup_id($bu->badge_id);
+ $x .= " title\" valign=top height=$height src=$badge->image_url> ";
+ }
+ return $x;
+}
+
+function show_badges_row($is_user, $item) {
+ if (BADGE_HEIGHT_LARGE == 0) return;
+ $x = badges_string($is_user, $item, BADGE_HEIGHT_LARGE);
+ if ($x) {
+ row2("Badges", $x);
+ }
+}
+
+function dtime() {
+ return microtime(true);
+}
+
+// If this request is from a BOINC client, return its version as MMmmRR.
+// Otherwise return 0.
+// Format of user agent string is "BOINC client (windows_x86_64 7.3.17)"
+//
+function boinc_client_version(){
+ if (!array_key_exists('HTTP_USER_AGENT', $_SERVER)) return 0;
+ $x = $_SERVER['HTTP_USER_AGENT'];
+ $e = "/BOINC client [^ ]* (\d+).(\d+).(\d+)\)/";
+ if (preg_match($e, $x, $matches)) {
+ return $matches[1]*10000 + $matches[2]*100 + $matches[3];
+ }
+ return 0;
+}
+
+// output a script for counting chars left in text field
+//
+function text_counter_script() {
+ echo "
+ ";
+}
+
+// return HTML for a textarea with chars-remaining counter.
+// Call text_counter_script() before using this.
+//
+function textarea_with_counter($name, $maxlen, $text) {
+ $rem_name = $name."_remaining";
+ return "
+ ".tra("characters remaining")
+ ;
+}
+
+// convert number MMmmrr to string MM.mm.rr
+//
+function version_string_maj_min_rel($v) {
+ $maj = (int)($v/10000);
+ $v -= $maj*10000;
+ $min = (int)($v/100);
+ $v -= $min*100;
+ return sprintf("%d.%d.%d", $maj, $min, $v);
+}
+
+function google_search_form($url) {
+ echo "
+
+
+
+ ";
+}
+
+$cvs_version_tracker[]="\$Id$"; //Generated automatically - do not edit
+
+?>
diff --git a/inc/util_basic.inc b/inc/util_basic.inc
new file mode 100755
index 0000000..db090de
--- /dev/null
+++ b/inc/util_basic.inc
@@ -0,0 +1,204 @@
+.
+
+// minimal set of utility functions, usable outside a BOINC project.
+// Doesn't pull in translation.inc etc.
+
+$generating_xml = false;
+
+function project_dir() {
+ $d = dirname(__FILE__);
+ return "$d/";
+}
+
+function web_stopped() {
+ $d = project_dir();
+ return file_exists("$d/stop_web");
+}
+
+function sched_stopped() {
+ $d = project_dir();
+ return file_exists("$d/stop_sched");
+}
+
+function show_page($x, $y) {
+ echo "
+ $x
+ $x
+ $y
+ ";
+}
+
+function xml_error($num, $msg=null, $file=null, $line=null) {
+ global $xml_outer_tag;
+ if (!$msg) {
+ switch($num) {
+ case -112: $msg = "Invalid XML"; break;
+ case -136: $msg = "Not found"; break;
+ case -137: $msg = "Name or email address is not unique"; break;
+ case -138: $msg = "Can't access database"; break;
+ case -183: $msg = "Project is temporarily offline"; break;
+ case -205: $msg = "Email address has invalid syntax"; break;
+ case -206: $msg = "Invalid password"; break;
+ case -207: $msg = "Email address is not unique"; break;
+ case -208: $msg = "Account creation is disabled"; break;
+ case -209: $msg = "Invalid invitation code"; break;
+ case -210: $msg = "Invalid request method"; break;
+ default: $msg = "Unknown error"; break;
+ }
+ }
+ echo "
+ $num
+ $msg
+";
+ if ($file) {
+ echo " $file \n";
+ }
+ if ($line) {
+ echo " $line \n";
+ }
+ echo " \n";
+ if (isset($xml_outer_tag) && $xml_outer_tag != "") {
+ echo "$xml_outer_tag>\n";
+ }
+ exit();
+}
+
+$g_config = null;
+function get_config() {
+ global $g_config;
+ if ($g_config == null) {
+ $d = project_dir();
+ $g_config = file_get_contents("$d/config.xml");
+ }
+ return $g_config;
+}
+
+// Look for an element in a line of XML text
+// If it's a single-tag element, and it's present, just return the tag
+//
+function parse_element($xml, $tag) {
+ $element = null;
+ $closetag = "" . substr($tag,1);
+ $x = strstr($xml, $tag);
+ if ($x) {
+ if (strstr($tag, "/>")) return $tag;
+ $y = substr($x, strlen($tag));
+ $n = strpos($y, $closetag);
+ if ($n) {
+ $element = substr($y, 0, $n);
+ }
+ }
+ return trim($element);
+}
+
+function parse_next_element($xml, $tag, &$cursor) {
+ $element = null;
+ $closetag = "" . substr($tag,1);
+ $pos = substr($xml,$cursor);
+ $x = strstr($pos, $tag);
+ if ($x) {
+ if (strstr($tag, "/>")) return $tag;
+ $y = substr($x, strlen($tag));
+ $n = strpos($y, $closetag);
+ if ($n) {
+ $element = substr($y, 0, $n);
+ }
+ $cursor = (strlen($xml) - strlen($x)) + strlen($tag) + strlen($closetag) + strlen($element);
+ }
+ return trim($element);
+}
+
+// return true if XML contains either or 1
+//
+function parse_bool($xml, $tag) {
+ $x = "<$tag/>";
+ if (strstr($xml, $x)) return true;
+ $x = "<$tag>";
+ $y = (int)parse_element($xml, $x);
+ if ($y != 0) return true;
+ return false;
+}
+
+// look for a particular element in the config file
+//
+function parse_config($config, $tag) {
+ $element = parse_element($config, $tag);
+ return $element;
+}
+
+function drand() {
+ return ((double)rand())/getrandmax();
+}
+
+// kludge
+//
+function is_gpu($plan_class) {
+ if (strstr($plan_class, "ati")) return true;
+ if (strstr($plan_class, "cuda")) return true;
+ if (strstr($plan_class, "nvidia")) return true;
+ if (strstr($plan_class, "intel_gpu")) return true;
+ return false;
+}
+
+// the same as file_get_contents() but uses curl
+//
+function url_get_contents($url) {
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_HEADER, false);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
+ $content = curl_exec($ch);
+ curl_close($ch);
+ return $content;
+}
+
+// Stuff related to password hashing.
+// Originally we hashed with MD5.
+// But MD5 is so fast that brute-force cracking would be easy
+// for hackers who break into a project's server.
+// So now we additionally hash with bcrypt,
+// if available (PHP >= 5.5) via PHP's password_hash() function.
+//
+// So there are two levels of hash:
+//
+// hash_0: md5(password.email_addr)
+// hash_1: password_hash(hash_0, PASSWORD_DEFAULT)
+//
+// hash_0 is what gets sent over the network,
+// from the client and account manager RPCs.
+//
+// hash_1 is what gets stored in the DB
+// except that hash_0 is stored in the DB if
+// - the project has pre-5.5 PHP, or
+// - the project hasn't run the update script (see below)
+//
+// It would be slightly more secure if hash_1 was used on the network.
+// But that would require a client and account manager change,
+// and it would require them to know whether the server has password_hash().
+//
+// We supply a script update_password_hash.php.
+// This changes the user table from hash_0 to hash_1.
+// Projects can run it whenever they want;
+// those using old PHP can wait until they upgrade PHP.
+//
+// To simplify the transition:
+// - When we're authorizing a user,
+
+?>
diff --git a/inc/util_ops.inc b/inc/util_ops.inc
new file mode 100755
index 0000000..0787f98
--- /dev/null
+++ b/inc/util_ops.inc
@@ -0,0 +1,405 @@
+.
+
+error_reporting(E_ALL);
+ini_set('display_errors', true);
+ini_set('display_startup_errors', true);
+
+require_once("../inc/db_ops.inc");
+require_once("../inc/util.inc");
+require_once("../project/project.inc");
+
+function admin_page_head($title) {
+ echo "";
+ echo sprintf('%s
+
+
+
+
+
+
+
%s: %s
+ ',
+ $title,
+ secure_url_base(),
+ secure_url_base(),
+ PROJECT,
+ $title
+ );
+ show_login_info();
+ echo "
";
+}
+
+function admin_page_tail() {
+ echo sprintf('
+
Main page
+
+
+
+
+
+ ',
+ secure_url_base(),
+ secure_url_base()
+ );
+}
+
+// TODO: get rid of all the following
+
+function print_checkbox($text,$name,$checked) {
+ echo " "
+ . "$text\n"
+ . "\n";
+}
+
+function print_radio_button($text,$name,$value,$checked) {
+ echo " "
+ . "$text\n"
+ . " \n";
+}
+
+function print_text_field($text,$name,$value) {
+ echo "$text \n"
+ . "
\n";
+}
+
+function row($x, $y) {
+ echo "
$x \n$y \n \n";
+}
+
+function c_row2($color, $x, $y) {
+ echo "$x $y \n";
+}
+
+function show_profile_link_ops($user) {
+ if ($user->has_profile) {
+ row2("Profile",
+ "id\">View "
+ );
+ }
+}
+
+// initialize database connection with username & password from
+// command line instead of config.xml
+//
+function db_init_cli() {
+ $config = get_config();
+ $db_name = parse_config($config, "");
+ $host = parse_config($config, "");
+ if ($host == null) {
+ $host = "localhost";
+ }
+ $in = fopen("php://stdin","r");
+ print "Database username (default: owner of mysqld process): ";
+ $user = rtrim(fgets($in, 80));
+ print "Database password (if any): ";
+ $pass = rtrim(fgets($in, 80));
+
+ $retval = _mysql_connect($host, $user, $pass, $db_name);
+ if (!$retval) {
+ die("Can't connect to DB\n");
+ }
+}
+
+function print_login_form_ops($next_url='') {
+ if ($next_url == '') $next_url = $_SERVER['REQUEST_URI'];
+ start_table();
+ echo "
+