- client: support notice feeds from account managers.

Implementation: create a base class PROJ_AM,
    from which both PROJECT and ACCT_MGR_INFO are derived,
    with basic stuff like name, URL, and RSS feed list

svn path=/trunk/boinc/; revision=22324
This commit is contained in:
David Anderson 2010-09-08 18:06:56 +00:00
parent 027d8e5485
commit f31e311dd2
9 changed files with 116 additions and 81 deletions

View File

@ -6401,3 +6401,16 @@ Rom 16 Aug 2010
/
configure.ac
version.h
David 8 Sept 2010
- client: support notice feeds from account managers.
Implementation: create a base class PROJ_AM,
from which both PROJECT and ACCT_MGR_INFO are derived,
with basic stuff like name, URL, and RSS feed list
client/
acct_mgr.cpp,h
app_start.cpp
client_types.cpp,h
cs_notice.cpp,h
gui_rpc_server_ops.cpp

View File

@ -67,7 +67,7 @@ int ACCT_MGR_OP::do_rpc(
// if null URL, detach from current AMS
//
if (!strlen(url) && strlen(gstate.acct_mgr_info.acct_mgr_url)) {
if (!strlen(url) && strlen(gstate.acct_mgr_info.master_url)) {
msg_printf(NULL, MSG_INFO, "Removing account manager info");
gstate.acct_mgr_info.clear();
boinc_delete_file(ACCT_MGR_URL_FILENAME);
@ -87,8 +87,8 @@ int ACCT_MGR_OP::do_rpc(
return 0;
}
strlcpy(ami.acct_mgr_url, url, sizeof(ami.acct_mgr_url));
strlcpy(ami.acct_mgr_name, "", sizeof(ami.acct_mgr_name));
strlcpy(ami.master_url, url, sizeof(ami.master_url));
strlcpy(ami.project_name, "", sizeof(ami.project_name));
strlcpy(ami.login_name, name.c_str(), sizeof(ami.login_name));
strlcpy(ami.password_hash, password_hash.c_str(), sizeof(ami.password_hash));
@ -282,7 +282,7 @@ int ACCT_MGR_OP::parse(FILE* f) {
continue;
}
if (!strcmp(tag, "/acct_mgr_reply")) return 0;
if (xp.parse_str(tag, "name", ami.acct_mgr_name, 256)) continue;
if (xp.parse_str(tag, "name", ami.project_name, 256)) continue;
if (xp.parse_int(tag, "error_num", error_num)) continue;
if (xp.parse_string(tag, "error", error_str)) continue;
if (xp.parse_double(tag, "repeat_sec", repeat_sec)) continue;
@ -369,7 +369,7 @@ void ACCT_MGR_OP::handle_reply(int http_op_retval) {
if (error_str.size()) {
msg_printf(NULL, MSG_USER_ALERT,
"%s %s: %s",
gstate.acct_mgr_info.acct_mgr_name,
gstate.acct_mgr_info.project_name,
_("error"),
error_str.c_str()
);
@ -379,7 +379,7 @@ void ACCT_MGR_OP::handle_reply(int http_op_retval) {
} else if (error_num) {
msg_printf(NULL, MSG_USER_ALERT,
"%s %s: %s",
gstate.acct_mgr_info.acct_mgr_name,
gstate.acct_mgr_info.project_name,
_("error"),
boincerror(error_num)
);
@ -411,8 +411,8 @@ void ACCT_MGR_OP::handle_reply(int http_op_retval) {
}
if (sig_ok) {
strcpy(gstate.acct_mgr_info.acct_mgr_url, ami.acct_mgr_url);
strcpy(gstate.acct_mgr_info.acct_mgr_name, ami.acct_mgr_name);
strcpy(gstate.acct_mgr_info.master_url, ami.master_url);
strcpy(gstate.acct_mgr_info.project_name, ami.project_name);
strcpy(gstate.acct_mgr_info.signing_key, ami.signing_key);
strcpy(gstate.acct_mgr_info.login_name, ami.login_name);
strcpy(gstate.acct_mgr_info.password_hash, ami.password_hash);
@ -517,7 +517,7 @@ void ACCT_MGR_OP::handle_reply(int http_op_retval) {
//
if (global_prefs_xml) {
retval = gstate.save_global_prefs(
global_prefs_xml, ami.acct_mgr_url, ami.acct_mgr_url
global_prefs_xml, ami.master_url, ami.master_url
);
if (retval) {
msg_printf(NULL, MSG_INTERNAL_ERROR, "Can't save global prefs");
@ -544,15 +544,15 @@ void ACCT_MGR_OP::handle_reply(int http_op_retval) {
int ACCT_MGR_INFO::write_info() {
FILE* p;
if (strlen(acct_mgr_url)) {
if (strlen(master_url)) {
p = fopen(ACCT_MGR_URL_FILENAME, "w");
if (p) {
fprintf(p,
"<acct_mgr>\n"
" <name>%s</name>\n"
" <url>%s</url>\n",
acct_mgr_name,
acct_mgr_url
project_name,
master_url
);
if (send_gui_rpc_info) fprintf(p," <send_gui_rpc_info/>\n");
if (strlen(signing_key)) {
@ -594,8 +594,8 @@ int ACCT_MGR_INFO::write_info() {
}
void ACCT_MGR_INFO::clear() {
strcpy(acct_mgr_name, "");
strcpy(acct_mgr_url, "");
strcpy(project_name, "");
strcpy(master_url, "");
strcpy(login_name, "");
strcpy(password_hash, "");
strcpy(signing_key, "");
@ -668,8 +668,8 @@ int ACCT_MGR_INFO::init() {
continue;
}
if (!strcmp(tag, "/acct_mgr")) break;
else if (xp.parse_str(tag, "name", acct_mgr_name, 256)) continue;
else if (xp.parse_str(tag, "url", acct_mgr_url, 256)) continue;
else if (xp.parse_str(tag, "name", project_name, 256)) continue;
else if (xp.parse_str(tag, "url", master_url, 256)) continue;
else if (xp.parse_bool(tag, "send_gui_rpc_info", send_gui_rpc_info)) continue;
else if (!strcmp(tag, "signing_key")) {
retval = xp.element_contents("</signing_key>", signing_key, sizeof(signing_key));
@ -695,14 +695,13 @@ int ACCT_MGR_INFO::init() {
}
bool ACCT_MGR_INFO::poll() {
if (!using_am()) return false;
if (gstate.gui_http.is_busy()) return false;
if (!strlen(login_name) && !strlen(password_hash)) return false;
if (gstate.now > next_rpc_time) {
next_rpc_time = gstate.now + 86400;
gstate.acct_mgr_op.do_rpc(
acct_mgr_url, login_name, password_hash, false
master_url, login_name, password_hash, false
);
return true;
}

View File

@ -28,12 +28,10 @@
// represents info stored in acct_mgr_url.xml and acct_mgr_login.xml
struct ACCT_MGR_INFO {
struct ACCT_MGR_INFO : PROJ_AM {
// the following used to be std::string but there
// were mysterious bugs where setting it to "" didn't work
//
char acct_mgr_name[256];
char acct_mgr_url[256];
char login_name[256];
char password_hash[256];
// md5 of password.lowercase(login_name)
@ -58,6 +56,13 @@ struct ACCT_MGR_INFO {
// successfully attached to an account manager
bool password_error;
inline bool using_am() {
if (!strlen(master_url)) return false;
if (!strlen(login_name)) return false;
if (!strlen(password_hash)) return false;
return true;
}
ACCT_MGR_INFO();
int parse_login_file(FILE*);
int write_info();
@ -66,6 +71,8 @@ struct ACCT_MGR_INFO {
bool poll();
};
// stuff after here related to RPCs to account managers
struct OPTIONAL_BOOL {
bool present;
bool value;
@ -80,8 +87,6 @@ struct OPTIONAL_DOUBLE {
inline void set(double v) {value=v; present=true;}
};
// stuff after here related to RPCs to account managers
struct AM_ACCOUNT {
std::string url;
std::string authenticator;

View File

@ -206,7 +206,7 @@ int ACTIVE_TASK::write_app_init_file() {
aid.app_version = app_version->version_num;
safe_strcpy(aid.app_name, wup->app->name);
safe_strcpy(aid.symstore, wup->project->symstore);
safe_strcpy(aid.acct_mgr_url, gstate.acct_mgr_info.acct_mgr_url);
safe_strcpy(aid.acct_mgr_url, gstate.acct_mgr_info.master_url);
if (wup->project->project_specific_prefs.length()) {
aid.project_preferences = strdup(wup->project->project_specific_prefs.c_str());
}

View File

@ -460,14 +460,6 @@ int PROJECT::write_statistics(MIOFILE& out, bool /*gui_rpc*/) {
return 0;
}
char* PROJECT::get_project_name() {
if (strlen(project_name)) {
return project_name;
} else {
return master_url;
}
}
const char* PROJECT::get_scheduler_url(int index, double r) {
int n = (int) scheduler_urls.size();
int ir = (int)(r*n);

View File

@ -181,12 +181,26 @@ struct DAILY_STATS {
};
bool operator < (const DAILY_STATS&, const DAILY_STATS&);
struct PROJECT {
// base class for PROJECT and ACCT_MGR_INFO
//
struct PROJ_AM {
char master_url[256];
char project_name[256];
// descriptive. not unique
std::vector<RSS_FEED> proj_feeds;
inline char *get_project_name() {
if (strlen(project_name)) {
return project_name;
} else {
return master_url;
}
}
};
struct PROJECT : PROJ_AM {
// the following items come from the account file
// They are a function only of the user and the project
//
char master_url[256];
// url of site that contains scheduler tags for this project
char authenticator[256];
// user's authenticator on this project
std::string project_prefs;
@ -231,8 +245,6 @@ struct PROJECT {
//
std::vector<std::string> scheduler_urls;
// where to find scheduling servers
char project_name[256];
// descriptive. not unique
char symstore[256];
// URL of symbol server (Windows)
char user_name[256];
@ -405,7 +417,6 @@ struct PROJECT {
~PROJECT(){}
void init();
void copy_state_fields(PROJECT&);
char *get_project_name();
int write_account_file();
int parse_account(FILE*);
int parse_account_file_venue();
@ -419,9 +430,6 @@ struct PROJECT {
int parse_statistics(FILE*);
int write_statistics(MIOFILE&, bool gui_rpc=false);
int write_statistics_file();
// feed-related
std::vector<RSS_FEED> proj_feeds;
};
struct APP {

View File

@ -52,7 +52,7 @@ static bool cmp(NOTICE n1, NOTICE n2) {
return (strcmp(n1.guid, n2.guid) > 0);
}
static void project_feed_list_file_name(PROJECT* p, char* buf) {
static void project_feed_list_file_name(PROJ_AM* p, char* buf) {
char url[256];
escape_project_url(p->master_url, url);
sprintf(buf, "notices/feeds_%s.xml", url);
@ -97,7 +97,7 @@ static void write_rss_feed_descs(MIOFILE& fout, vector<RSS_FEED>& feeds) {
fout.printf("</rss_feeds>\n");
}
static void write_project_feed_list(PROJECT* p) {
static void write_project_feed_list(PROJ_AM* p) {
char buf[256];
project_feed_list_file_name(p, buf);
FILE* f = fopen(buf, "w");
@ -112,7 +112,7 @@ static void write_project_feed_list(PROJECT* p) {
// Add new ones to the project's set,
// and remove ones from the project's set that aren't in the list.
//
void handle_sr_feeds(vector<RSS_FEED>& feeds, PROJECT* p) {
void handle_sr_feeds(vector<RSS_FEED>& feeds, PROJ_AM* p) {
unsigned int i, j;
bool feed_set_changed = false;
@ -658,25 +658,33 @@ void RSS_FEED_OP::handle_reply(int http_op_retval) {
///////////// RSS_FEEDS ////////////////
static void init_proj_am(PROJ_AM* p) {
FILE* f;
MIOFILE fin;
char path[256];
project_feed_list_file_name(p, path);
f = fopen(path, "r");
if (f) {
fin.init_file(f);
parse_rss_feed_descs(fin, p->proj_feeds);
fclose(f);
}
}
// called on startup. Get list of feeds. Read archives.
//
void RSS_FEEDS::init() {
unsigned int i;
MIOFILE fin;
FILE* f;
boinc_mkdir(NOTICES_DIR);
for (i=0; i<gstate.projects.size(); i++) {
PROJECT* p = gstate.projects[i];
char path[256];
project_feed_list_file_name(p, path);
f = fopen(path, "r");
if (f) {
fin.init_file(f);
parse_rss_feed_descs(fin, p->proj_feeds);
fclose(f);
}
init_proj_am(p);
}
if (gstate.acct_mgr_info.using_am()) {
init_proj_am(&gstate.acct_mgr_info);
}
update_feed_list();
@ -705,7 +713,7 @@ RSS_FEED* RSS_FEEDS::lookup_url(char* url) {
// arrange to fetch the project's feeds
//
void RSS_FEEDS::trigger_fetch(PROJECT* p) {
void RSS_FEEDS::trigger_fetch(PROJ_AM* p) {
for (unsigned int i=0; i<p->proj_feeds.size(); i++) {
RSS_FEED& rf = p->proj_feeds[i];
RSS_FEED* rfp = lookup_url(rf.url);
@ -715,34 +723,42 @@ void RSS_FEEDS::trigger_fetch(PROJECT* p) {
}
}
void RSS_FEEDS::update_proj_am(PROJ_AM* p) {
unsigned int j;
for (j=0; j<p->proj_feeds.size(); j++) {
RSS_FEED& rf = p->proj_feeds[j];
RSS_FEED* rfp = lookup_url(rf.url);
if (rfp) {
rfp->found = true;
} else {
rf.found = true;
strcpy(rf.project_name, p->get_project_name());
feeds.push_back(rf);
if (log_flags.notice_debug) {
msg_printf(0, MSG_INFO,
"[notice] adding feed: %s, %.0f sec",
rf.url, rf.poll_interval
);
}
}
}
}
// the set of project feeds has changed.
// update the master list.
//
void RSS_FEEDS::update_feed_list() {
unsigned int i, j;
unsigned int i;
for (i=0; i<feeds.size(); i++) {
RSS_FEED& rf = feeds[i];
rf.found = false;
}
for (i=0; i<gstate.projects.size(); i++) {
PROJECT* p = gstate.projects[i];
for (j=0; j<p->proj_feeds.size(); j++) {
RSS_FEED& rf = p->proj_feeds[j];
RSS_FEED* rfp = lookup_url(rf.url);
if (rfp) {
rfp->found = true;
} else {
rf.found = true;
strcpy(rf.project_name, p->get_project_name());
feeds.push_back(rf);
if (log_flags.notice_debug) {
msg_printf(0, MSG_INFO,
"[notice] adding feed: %s, %.0f sec",
rf.url, rf.poll_interval
);
}
}
}
update_proj_am(p);
}
if (gstate.acct_mgr_info.using_am()) {
update_proj_am(&gstate.acct_mgr_info);
}
vector<RSS_FEED>::iterator iter = feeds.begin();
while (iter != feeds.end()) {

View File

@ -19,7 +19,7 @@
#define _CS_NOTICE_
// Code related to "notices", which come from
// 1) RSS feeds specified by projects
// 1) RSS feeds specified by projects and account managers
// 2) Scheduler replies (high-priority messages)
// 3) the client (MSG_USER_ALERT messages)
//
@ -63,6 +63,7 @@
#include "miofile.h"
#include "gui_http.h"
#include "client_types.h"
#include "notice.h"
@ -120,16 +121,17 @@ extern RSS_FEED_OP rss_feed_op;
struct RSS_FEEDS {
std::vector<RSS_FEED> feeds;
void init();
void trigger_fetch(PROJECT*);
void trigger_fetch(struct PROJ_AM*);
void update_feed_list();
RSS_FEED* lookup_url(char*);
void update_proj_am(PROJ_AM*);
void write_feed_list();
};
extern RSS_FEEDS rss_feeds;
int parse_rss_feed_descs(MIOFILE& fin, std::vector<RSS_FEED>&);
void handle_sr_feeds(std::vector<RSS_FEED>&, struct PROJECT*);
void handle_sr_feeds(std::vector<RSS_FEED>&, struct PROJ_AM*);
// process the feeds in a scheduler reply
#endif

View File

@ -526,8 +526,8 @@ static void handle_acct_mgr_info(char*, MIOFILE& fout) {
"<acct_mgr_info>\n"
" <acct_mgr_url>%s</acct_mgr_url>\n"
" <acct_mgr_name>%s</acct_mgr_name>\n",
gstate.acct_mgr_info.acct_mgr_url,
gstate.acct_mgr_info.acct_mgr_name
gstate.acct_mgr_info.master_url,
gstate.acct_mgr_info.project_name
);
if (strlen(gstate.acct_mgr_info.login_name)) {
@ -816,13 +816,13 @@ static void handle_acct_mgr_rpc(char* buf, MIOFILE& fout) {
}
}
} else {
if (!strlen(gstate.acct_mgr_info.acct_mgr_url)) {
if (!strlen(gstate.acct_mgr_info.master_url)) {
bad_arg = true;
msg_printf(NULL, MSG_INTERNAL_ERROR,
"Account manager info missing from config file"
);
} else {
url = gstate.acct_mgr_info.acct_mgr_url;
url = gstate.acct_mgr_info.master_url;
name = gstate.acct_mgr_info.login_name;
password_hash = gstate.acct_mgr_info.password_hash;
}