multiple pref sets

svn path=/trunk/boinc/; revision=1020
This commit is contained in:
David Anderson 2003-03-06 00:42:18 +00:00
parent 03b6a7be4c
commit c805370756
22 changed files with 319 additions and 81 deletions

View File

@ -3658,3 +3658,36 @@ David Mar 4 2003
prefs.inc
prefs_edit_action.php
project_specific_prefs.inc
David Mar 5 2003
- use venue-specific prefs on client side:
- schedule server passes <host_venue> element in scheduler reply
(but only when it passes back new global prefs)
- when parsing global prefs, if find a matching
<venue> element, parse its contents and ignore all else
- when passing project-specific prefs to app,
look for matching <venue> element
- changed XML syntax of venue-specific prefs: instead of
<home>...</home>, e.g., use
<venue name="home">...</venue>
- added safe_strncpy(): always adds zero byte at end.
Use this instead of strncpy().
client/
app.C
client_state.C,h
cs_scheduler.C
hostinfo.C
prefs.C,h
scheduler_op.C,h
html_users/
add_venue_action.php
prefs.inc
show_host_detail.php
user.inc
lib/
parse.C,h
util.C,h
sched/
Makefile.in
server_types.C

View File

@ -104,7 +104,8 @@ int ACTIVE_TASK::init(RESULT* rp) {
return 0;
}
// Start a task in a slot directory. This includes setting up soft links,
// Start a task in a slot directory.
// This includes setting up soft links,
// passing preferences, and starting the process
//
// Current dir is top-level BOINC dir
@ -134,13 +135,13 @@ int ACTIVE_TASK::start(bool first_time) {
memset(&aid, 0, sizeof(aid));
strncpy(aid.user_name, wup->project->user_name, sizeof(aid.user_name));
strncpy(aid.team_name, wup->project->team_name, sizeof(aid.team_name));
safe_strncpy(aid.user_name, wup->project->user_name, sizeof(aid.user_name));
safe_strncpy(aid.team_name, wup->project->team_name, sizeof(aid.team_name));
if (wup->project->project_specific_prefs) {
strncpy(
aid.app_preferences,
extract_venue(
wup->project->project_specific_prefs,
sizeof(aid.app_preferences)
gstate.host_venue,
aid.app_preferences
);
}
aid.user_total_credit = wup->project->user_total_credit;

View File

@ -83,6 +83,7 @@ CLIENT_STATE::CLIENT_STATE() {
proxy_server_port = 80;
strcpy(socks_user_name,"");
strcpy(socks_user_passwd,"");
strcpy(host_venue,"");
suspend_requested = false;
start_saver = false;
#ifdef _WIN32
@ -104,14 +105,6 @@ int CLIENT_STATE::init() {
srand(time(NULL));
// Read the global preferences file, if it exists.
//
retval = global_prefs.parse_file();
if (retval) {
printf("No global preferences file; will use defaults.\n");
}
install_global_prefs();
// parse account files.
// If there are none, prompt user for project URL and create file
//
@ -142,13 +135,23 @@ int CLIENT_STATE::init() {
print_summary();
}
// Run the time tests and host information check if needed
// Read the global preferences file, if it exists.
// Do this after reading the state file so we know our venue
//
retval = global_prefs.parse_file(host_venue);
if (retval) {
printf("No global preferences file; will use defaults.\n");
}
install_global_prefs();
// Getting host info is very fast, so we can do it anytime
//
get_host_info(host_info);
// running CPU benchmarks is slow, so do it infrequently
//
if (gstate.should_run_time_tests()) {
time_tests_start = time(NULL);
time_tests_start = time(0);
show_message(NULL, "Running time tests", "low");
#ifdef _WIN32
time_tests_handle = CreateThread(
@ -477,9 +480,6 @@ int CLIENT_STATE::parse_state_file() {
int retval=0;
int failnum;
global_prefs.confirm_before_connecting = false;
global_prefs.hangup_if_dialed = false;
if (!f) {
if (log_flags.state_debug) {
printf("No state file; will create one\n");
@ -579,16 +579,13 @@ int CLIENT_STATE::parse_state_file() {
// after core client update
} else if (match_tag(buf, "<core_client_major_version>")) {
} else if (match_tag(buf, "<core_client_minor_version>")) {
} else if (match_tag(buf, "<confirm_before_connect/>")) {
global_prefs.confirm_before_connecting = true;
} else if (match_tag(buf, "<hangup_if_dialed/>")) {
global_prefs.hangup_if_dialed = true;
} else if (match_tag(buf, "<use_http_proxy/>")) {
use_http_proxy = true;
} else if (parse_str(buf, "<proxy_server_name>", proxy_server_name, sizeof(proxy_server_name))) {
} else if (parse_int(buf, "<proxy_server_port>", proxy_server_port)) {
} else if (parse_str(buf, "<socks_user_name>", socks_user_name, sizeof(socks_user_name))) {
} else if (parse_str(buf, "<socks_user_passwd>", socks_user_passwd, sizeof(socks_user_passwd))) {
} else if (parse_str(buf, "<host_venue>", host_venue, sizeof(host_venue))) {
} else {
fprintf(stderr, "CLIENT_STATE::parse_state_file: unrecognized: %s\n", buf);
}
@ -647,11 +644,9 @@ int CLIENT_STATE::write_state_file() {
core_client_minor_version
);
// save proxy and preferences info
// save proxy info
//
fprintf(f,
"%s"
"%s"
"%s"
"%s"
"<proxy_server_name>%s</proxy_server_name>\n"
@ -660,13 +655,14 @@ int CLIENT_STATE::write_state_file() {
"<socks_user_passwd>%s</socks_user_passwd>\n",
use_http_proxy?"<use_http_proxy/>\n":"",
use_socks_proxy?"<use_socks_proxy/>\n":"",
global_prefs.confirm_before_connecting?"<confirm_before_connect/>\n":"",
global_prefs.hangup_if_dialed?"<hangup_if_dialed/>\n":"",
proxy_server_name,
proxy_server_port,
socks_user_name,
socks_user_passwd
);
if (strlen(host_venue)) {
fprintf(f, "<host_venue>%s</host_venue>\n", host_venue);
}
fprintf(f, "</client_state>\n");
fclose(f);
retval = boinc_rename(STATE_FILE_TEMP, STATE_FILE_NAME);
@ -1300,7 +1296,6 @@ int CLIENT_STATE::report_project_error(
strcat(res.stderr_out, total_err );
}
}
if (res.state == RESULT_NEW) {
for (i=0;i<res.wup->input_files.size();i++) {

View File

@ -100,7 +100,8 @@ public:
char proxy_server_name[256];
char socks_user_name[256];
char socks_user_passwd[256];
char host_venue[256]; // venue, as reported by project that sent us
// most recent global prefs
private:
bool client_state_dirty;

View File

@ -382,7 +382,8 @@ int CLIENT_STATE::handle_scheduler_reply(
sr.global_prefs_xml
);
fclose(f);
global_prefs.parse_file();
safe_strncpy(host_venue, sr.host_venue, sizeof(host_venue));
global_prefs.parse_file(host_venue);
install_global_prefs();
}

View File

@ -40,6 +40,7 @@
#include <winsock.h>
#endif
#include "util.h"
#include "parse.h"
#include "hostinfo.h"
#include "error_numbers.h"
@ -198,7 +199,7 @@ int get_local_domain_name(char* p, int len) {
gethostname(buf, 256);
struct hostent* he = gethostbyname(buf);
if (!he) return -1;
strncpy(p, he->h_name, len);
safe_strncpy(p, he->h_name, len);
return 0;
}

View File

@ -33,7 +33,7 @@
// the following values determine how the client behaves
// if there are no global prefs yet
//
GLOBAL_PREFS::GLOBAL_PREFS() {
void GLOBAL_PREFS::init() {
run_on_batteries = true;
run_if_user_active = true;
run_minimized = false;
@ -50,12 +50,47 @@ GLOBAL_PREFS::GLOBAL_PREFS() {
max_bytes_sec_down = 1e9;
};
// Parse XML global prefs
GLOBAL_PREFS::GLOBAL_PREFS() {
init();
}
// Parse XML global prefs.
// If host_venue is nonempty and we find an element of the form
// <venue name="X">
// ...
// </venue>
// then parse that and ignore the rest.
// Otherwise ignore <venue> elements.
//
int GLOBAL_PREFS::parse(FILE* in) {
char buf[256];
int GLOBAL_PREFS::parse(FILE* in, char* host_venue) {
char buf[256], buf2[256];
bool in_venue = false, in_correct_venue=false;
while (fgets(buf, 256, in)) {
if (in_venue) {
if (match_tag(buf, "</venue>")) {
if (in_correct_venue) {
return 0;
} else {
in_venue = false;
continue;
}
} else {
if(!in_correct_venue) continue;
}
} else {
if (match_tag(buf, "<venue")) {
in_venue = true;
parse_attr(buf, "name", buf2, sizeof(buf2));
if (!strcmp(buf2, host_venue)) {
init();
in_correct_venue = true;
} else {
in_correct_venue = false;
}
continue;
}
}
if (match_tag(buf, "</global_preferences>")) {
return 0;
} else if (match_tag(buf, "<run_on_batteries/>")) {
@ -86,10 +121,10 @@ int GLOBAL_PREFS::parse(FILE* in) {
} else if (parse_double(buf, "<idle_time_to_run>", idle_time_to_run)) {
continue;
} else if (parse_double(buf, "<max_bytes_sec_up>", max_bytes_sec_up)) {
if (max_bytes_sec_up <= 0) max_bytes_sec_up = 1e9;
if (max_bytes_sec_up <= 0) max_bytes_sec_up = 1e12;
continue;
} else if (parse_double(buf, "<max_bytes_sec_down>", max_bytes_sec_down)) {
if (max_bytes_sec_down <= 0) max_bytes_sec_down = 1e9;
if (max_bytes_sec_down <= 0) max_bytes_sec_down = 1e12;
continue;
}
}
@ -98,13 +133,13 @@ int GLOBAL_PREFS::parse(FILE* in) {
// Parse global prefs file
//
int GLOBAL_PREFS::parse_file() {
int GLOBAL_PREFS::parse_file(char* host_venue) {
FILE* f;
int retval;
f = fopen(GLOBAL_PREFS_FILE_NAME, "r");
if (!f) return ERR_FOPEN;
retval = parse(f);
retval = parse(f, host_venue);
fclose(f);
return retval;
}

View File

@ -50,8 +50,9 @@ struct GLOBAL_PREFS {
double max_bytes_sec_down;
GLOBAL_PREFS();
int parse(FILE*);
int parse_file();
void init();
int parse(FILE*, char* venue);
int parse_file(char* venue);
};
#endif

View File

@ -507,6 +507,7 @@ int SCHEDULER_REPLY::parse(FILE* in) {
strcpy(user_name, "");
user_total_credit = 0;
user_expavg_credit = 0;
strcpy(host_venue, "");
user_create_time = 0;
code_sign_key = 0;
code_sign_key_signature = 0;
@ -535,6 +536,7 @@ int SCHEDULER_REPLY::parse(FILE* in) {
else if (parse_int(buf, "<hostid>", hostid)) continue;
else if (parse_double(buf, "<host_total_credit>", host_total_credit)) continue;
else if (parse_double(buf, "<host_expavg_credit>", host_expavg_credit)) continue;
else if (parse_str(buf, "<host_venue>", host_venue, sizeof(host_venue))) continue;
else if (parse_int(buf, "<host_create_time>", (int &)host_create_time)) continue;
else if (parse_int(buf, "<request_delay>", request_delay)) continue;
else if (match_tag(buf, "<global_preferences>")) {

View File

@ -106,6 +106,7 @@ struct SCHEDULER_REPLY {
char user_name[256],team_name[256];
double user_total_credit;
double user_expavg_credit;
char host_venue[256];
unsigned int user_create_time;
vector<APP> apps;
vector<FILE_INFO> file_infos;

View File

@ -24,7 +24,7 @@
$prefs->$venue = $new_prefs;
$retval = project_prefs_update($user, $prefs);
}
if (retval) {
if ($retval) {
Header("Location: prefs.php?subset=$subset");
} else {
db_error_page();

View File

@ -0,0 +1,33 @@
<?php
require_once("db.inc");
require_once("util.inc");
$authenticator = init_session();
db_init();
$user = get_user_from_auth($authenticator);
require_login($user);
$venue = $_GET["venue"];
$hostid = $_GET["hostid"];
$result = mysql_query("select * from host where id = $hostid");
$host = mysql_fetch_object($result);
mysql_free_result($result);
if (!$host) {
echo "Couldn't find host.";
exit();
}
if ($host->userid != $user->id) {
echo "Not your host\n";
exit();
}
$retval = mysql_query("update host set venue='$venue' where id = $hostid");
if ($retval) {
Header("Location: show_host_detail.php?hostid=$hostid");
} else {
db_error_page();
}
?>

View File

@ -44,6 +44,7 @@ global $text;
global $parse_result;
global $top_parse_result;
global $in_project_specific;
global $venue_name;
// functions to parse preferences XML into a struct
//
@ -52,11 +53,11 @@ function element_start_project($parser, $name, $attrs) {
global $parse_result;
global $text;
global $in_project_specific;
global $venue_name;
switch($name) {
case "home":
case "school":
case "work":
case "venue":
$venue_name = $attrs["name"];
$top_parse_result = $parse_result;
$parse_result = null;
break;
@ -77,11 +78,11 @@ function element_start_global($parser, $name, $attrs) {
global $top_parse_result;
global $parse_result;
global $text;
global $venue_name;
switch($name) {
case "home":
case "school":
case "work":
case "venue":
$venue_name = $attrs["name"];
$top_parse_result = $parse_result;
$parse_result = null;
break;
@ -94,12 +95,11 @@ function element_end_project($parser, $name) {
global $parse_result;
global $in_project_specific;
global $top_parse_result;
global $venue_name;
switch($name) {
case "home":
case "school":
case "work":
$top_parse_result->$name = $parse_result;
case "venue":
$top_parse_result->$venue_name = $parse_result;
$parse_result = $top_parse_result;
break;
case "project_specific":
@ -130,12 +130,11 @@ function element_end_global($parser, $name) {
global $text;
global $parse_result;
global $top_parse_result;
global $venue_name;
switch($name) {
case "home":
case "school":
case "work":
$top_parse_result->$name = $parse_result;
case "venue":
$top_parse_result->$venue_name = $parse_result;
$parse_result = $top_parse_result;
break;
case "run_on_batteries":
@ -565,13 +564,13 @@ function global_prefs_make_xml($prefs, $primary=true) {
."<max_bytes_sec_down>$prefs->max_bytes_sec_down</max_bytes_sec_down>\n"
."<max_bytes_sec_up>$prefs->max_bytes_sec_up</max_bytes_sec_up>\n";
if ($prefs->home) {
$xml = $xml."<home>\n".global_prefs_make_xml($prefs->home, false)."</home>\n";
$xml = $xml."<venue name=\"home\">\n".global_prefs_make_xml($prefs->home, false)."</venue>\n";
}
if ($prefs->work) {
$xml = $xml."<work>\n".global_prefs_make_xml($prefs->work, false)."</work>\n";
$xml = $xml."<venue name=\"work\">\n".global_prefs_make_xml($prefs->work, false)."</venue>\n";
}
if ($prefs->school) {
$xml = $xml."<school>\n".global_prefs_make_xml($prefs->school, false)."</school>\n";
$xml = $xml."<venue name=\"school\">\n".global_prefs_make_xml($prefs->school, false)."</venue>\n";
}
if ($primary) {
$xml = $xml."</global_preferences>\n";
@ -600,13 +599,13 @@ function project_prefs_make_xml($prefs, $primary=true) {
."<project_specific>\n$x\n</project_specific>\n";
}
if ($prefs->home) {
$xml = $xml."<home>\n".project_prefs_make_xml($prefs->home, false)."</home>\n";
$xml = $xml."<venue name=\"home\">\n".project_prefs_make_xml($prefs->home, false)."</venue>\n";
}
if ($prefs->work) {
$xml = $xml."<work>\n".project_prefs_make_xml($prefs->work, false)."</work>\n";
$xml = $xml."<venue name=\"work\">\n".project_prefs_make_xml($prefs->work, false)."</venue>\n";
}
if ($prefs->school) {
$xml = $xml."<school>\n".project_prefs_make_xml($prefs->school, false)."</school>\n";
$xml = $xml."<venue name=\"school\">\n".project_prefs_make_xml($prefs->school, false)."</venue>\n";
}
if ($primary) {
$xml = $xml."</project_preferences>\n";

View File

@ -0,0 +1,71 @@
<?php
include_once("db.inc");
include_once("util.inc");
include_once("prefs.inc");
$authenticator = init_session();
db_init();
$user = get_user_from_auth($authenticator);
if ($user == NULL) {
print_login_form();
exit();
}
$subset = $_GET["subset"];
$venue = $_GET["venue"];
$title = "Edit ".subset_name($subset)." preferences";
if ($venue) $title = "$title for $venue";
page_head($title, $user);
echo "<h3>$title</h3>\n";
if ($subset == "global") {
$prefs = prefs_parse_global($user->global_prefs);
if ($venue) {
$prefs = $prefs->$venue;
}
echo "
These preferences apply to all the BOINC projects
in which you participate.
<br>If you participate in multiple BOINC projects,
edit your preferences only one project's web site;
<br>otherwise edits may be overwritten.
";
} else {
$prefs = prefs_parse_project($user->project_prefs);
if ($venue) {
$prefs = $prefs->$venue;
}
}
$x = $venue?"&\$venue=$venue":"";
echo "<form action=prefs_edit_action.php>
<input type=hidden name=subset value=$subset>
";
if ($venue) {
echo "<input type=hidden name=venue value=$venue>\n";
}
start_table();
if ($subset == "global") {
prefs_form_global($user, $prefs);
} else {
prefs_form_resource($prefs);
prefs_form_project($prefs->project_specific);
if (!$venue) {
prefs_form_email($prefs);
venue_form($user);
}
}
row2("<br>", "<input type=submit value=\"OK\">");
end_table();
echo "</form>\n";
echo "<a href=prefs.php?subset=$subset$x>Back to preferences</a>\n";
page_tail();
?>

View File

@ -4,10 +4,26 @@
require_once("db.inc");
require_once("user.inc");
function show_host($host) {
function location_form($host) {
if ($host->venue == "home") $h = "selected";
if ($host->venue == "work") $w = "selected";
if ($host->venue == "school") $s = "selected";
$x = "<form action=host_venue_action.php>
<input type=hidden name=hostid value=$host->id>
<select name=venue>
<option value=home $h>Home
<option value=work $w>Work
<option value=school $s>School
</select>
<input type=submit value=Update>
</form>
";
return $x;
}
echo TABLE2."\n";
echo "<tr>".TD2.LG_FONT."<b>Host Information:</b></font></td></tr>\n";
function show_host($host) {
start_table();
row1("Host Information");
row2("IP address", "$host->last_ip_addr<br>(same the last $host->nsame_ip_addr times)");
row2("Domain name", $host->domain_name);
$x = $host->timezone/3600;
@ -45,22 +61,23 @@ function show_host($host) {
$x = $host->n_bwup/(1024);
$y = round($x, 2);
if ($y > 0) {
row2("Average upload speed", "$y KB/sec");
row2("Average upload rate", "$y KB/sec");
} else {
row2("Average upload speed", "Unknown");
row2("Average upload rate", "Unknown");
}
$x = $host->n_bwdown/(1024);
$y = round($x, 2);
if ($y > 0) {
row2("Average download speed", "$y KB/sec");
row2("Average download rate", "$y KB/sec");
} else {
row2("Average download speed", "Unknown");
row2("Average download rate", "Unknown");
}
row2("Number of times client has contacted server", $host->rpc_seqno);
row2("Last time contacted server", time_str($host->rpc_time));
row2("% of time client on", 100*$host->on_frac." %");
row2("% of time host connected", 100*$host->connected_frac." %");
row2("% of time user active", 100*$host->active_frac." %");
row2("% of time client is on", 100*$host->on_frac." %");
row2("% of time host is connected", 100*$host->connected_frac." %");
row2("% of time user is active", 100*$host->active_frac." %");
row2("Location", location_form($host));
echo "</table>\n";
}

View File

@ -41,7 +41,7 @@ function show_user_profile($user) {
function show_hosts($user) {
$result = mysql_query("select * from host where userid=$user->id order by rpc_time desc");
echo VISTABLE."\n";
start_table();
echo "<tr><td>Host name</td><td>Total Credit</td><td>Recent credit</td></tr>";
while ($host = mysql_fetch_object($result)) {
echo "<p>\n";
@ -78,9 +78,11 @@ function show_user_page_private($user) {
}
function user_table_start() {
echo VISTABLE."
start_table();
echo "
<tr><th>Name</th><th>Average credit</th><th>Total credit</th>
<th>Participant since</th></tr>";
<th>Participant since</th></tr>
";
}
function show_user_row($user) {

View File

@ -31,8 +31,9 @@
#include <string.h>
#include <stdlib.h>
#include "parse.h"
#include "error_numbers.h"
#include "util.h"
#include "parse.h"
// return true if the tag appears in the line
//
@ -70,7 +71,7 @@ bool parse_str(char* buf, char* tag, char* dest, int len) {
char* q = strchr(p+1, '<');
if (!q) return false;
*q = 0;
strncpy(dest, p+1, len);
safe_strncpy(dest, p+1, len);
dest[len-1] = 0;
*q = '<';
return true;
@ -89,7 +90,7 @@ void parse_attr(char* buf, char* name, char* dest, int len) {
q = strchr(p+1, '"');
if (!q) return;
*q = 0;
strncpy(dest, p+1, len);
safe_strncpy(dest, p+1, len);
dest[len-1] = 0;
}
@ -164,6 +165,7 @@ int read_file_malloc(char* pathname, char*& str) {
}
#if 0
// replace XML element contents. not currently used
//
void replace_element(char* buf, char* start, char* end, char* replacement) {
@ -176,4 +178,33 @@ void replace_element(char* buf, char* start, char* end, char* replacement) {
strcpy(p, replacement);
strcat(p, temp);
}
#endif
// if the given XML has an element of the form
// <venue name="venue_name">
// ...
// </venue>
// then return the contents of that element.
// Otherwise strip out all <venue> elements
//
void extract_venue(char* in, char* venue_name, char* out) {
char* p, *q;
char buf[256];
sprintf(buf, "<venue name=\"%s\">", venue_name);
p = strstr(in, buf);
if (p) {
p += strlen(buf);
strcpy(out, p);
q = strstr(out, "</venue");
if (q) *q = 0;
} else {
strcpy(out, in);
while (1) {
p = strstr(out, "<venue");
if (!p) break;
q = strstr(p, "</venue>\n");
if (!q) break;
strcpy(p, q+strlen("</venue>\n"));
}
}
}

View File

@ -30,4 +30,7 @@ extern void strcatdup(char*& p, char* buf);
extern int dup_element_contents(FILE* in, char* end_tag, char** pp);
extern int copy_element_contents(FILE* in, char* end_tag, char* p, int len);
extern int read_file_malloc(char* pathname, char*& str);
#if 0
extern void replace_element(char* buf, char* start, char* end, char* replacement);
#endif
extern void extract_venue(char* in, char* venue_name, char* out);

View File

@ -279,3 +279,7 @@ void escape_url(char *in, char*out) {
out[y] = 0;
}
void safe_strncpy(char* dst, char* src, int len) {
strncpy(dst, src, len);
dst[len-1]=0;
}

View File

@ -27,6 +27,7 @@ extern int lock_file(char*);
extern double drand();
extern void unescape_url(char *url);
extern void escape_url(char *in, char*out);
extern void safe_strncpy(char*, char*, int);
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))

View File

@ -31,6 +31,7 @@ CGI_OBJS = \
../db/db_mysql.o \
../db/mysql_util.o \
../lib/shmem.o \
../lib/util.o \
../lib/parse.o
FEEDER_OBJS = \
@ -49,6 +50,7 @@ SHOW_SHMEM_OBJS = \
config.o \
../db/db_mysql.o \
../db/mysql_util.o \
../lib/util.o \
../lib/parse.o \
../lib/shmem.o
@ -56,6 +58,7 @@ FILE_UPLOAD_OBJS = \
file_upload_handler.o \
config.o \
../lib/crypt.o \
../lib/util.o \
../lib/parse.o \
../lib/md5.o \
../lib/md5_file.o \
@ -124,6 +127,7 @@ DB_DUMP_OBJS = \
db_dump.o
START_SERVERS_OBJS = \
../lib/util.o \
../lib/parse.o \
config.o \
start_servers.o

View File

@ -145,13 +145,15 @@ int SCHEDULER_REPLY::write(FILE* fout) {
"<user_expavg_credit>%f</user_expavg_credit>\n"
"<user_create_time>%d</user_create_time>\n"
"<host_total_credit>%f</host_total_credit>\n"
"<host_expavg_credit>%f</host_expavg_credit>\n",
"<host_expavg_credit>%f</host_expavg_credit>\n"
"<host_venue>%s</host_venue>\n",
user.name,
user.total_credit,
user.expavg_credit,
user.create_time,
host.total_credit,
host.expavg_credit
host.expavg_credit,
host.venue
);
// might want to send team credit too.