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

View File

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

View File

@ -206,7 +206,7 @@ int ACTIVE_TASK::write_app_init_file() {
aid.app_version = app_version->version_num; aid.app_version = app_version->version_num;
safe_strcpy(aid.app_name, wup->app->name); safe_strcpy(aid.app_name, wup->app->name);
safe_strcpy(aid.symstore, wup->project->symstore); 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()) { if (wup->project->project_specific_prefs.length()) {
aid.project_preferences = strdup(wup->project->project_specific_prefs.c_str()); 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; 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) { const char* PROJECT::get_scheduler_url(int index, double r) {
int n = (int) scheduler_urls.size(); int n = (int) scheduler_urls.size();
int ir = (int)(r*n); int ir = (int)(r*n);

View File

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

View File

@ -52,7 +52,7 @@ static bool cmp(NOTICE n1, NOTICE n2) {
return (strcmp(n1.guid, n2.guid) > 0); 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]; char url[256];
escape_project_url(p->master_url, url); escape_project_url(p->master_url, url);
sprintf(buf, "notices/feeds_%s.xml", 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"); 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]; char buf[256];
project_feed_list_file_name(p, buf); project_feed_list_file_name(p, buf);
FILE* f = fopen(buf, "w"); 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, // Add new ones to the project's set,
// and remove ones from the project's set that aren't in the list. // 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; unsigned int i, j;
bool feed_set_changed = false; bool feed_set_changed = false;
@ -658,25 +658,33 @@ void RSS_FEED_OP::handle_reply(int http_op_retval) {
///////////// RSS_FEEDS //////////////// ///////////// 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. // called on startup. Get list of feeds. Read archives.
// //
void RSS_FEEDS::init() { void RSS_FEEDS::init() {
unsigned int i; unsigned int i;
MIOFILE fin;
FILE* f;
boinc_mkdir(NOTICES_DIR); boinc_mkdir(NOTICES_DIR);
for (i=0; i<gstate.projects.size(); i++) { for (i=0; i<gstate.projects.size(); i++) {
PROJECT* p = gstate.projects[i]; PROJECT* p = gstate.projects[i];
char path[256]; init_proj_am(p);
project_feed_list_file_name(p, path); }
f = fopen(path, "r"); if (gstate.acct_mgr_info.using_am()) {
if (f) { init_proj_am(&gstate.acct_mgr_info);
fin.init_file(f);
parse_rss_feed_descs(fin, p->proj_feeds);
fclose(f);
}
} }
update_feed_list(); update_feed_list();
@ -705,7 +713,7 @@ RSS_FEED* RSS_FEEDS::lookup_url(char* url) {
// arrange to fetch the project's feeds // 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++) { for (unsigned int i=0; i<p->proj_feeds.size(); i++) {
RSS_FEED& rf = p->proj_feeds[i]; RSS_FEED& rf = p->proj_feeds[i];
RSS_FEED* rfp = lookup_url(rf.url); 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. // the set of project feeds has changed.
// update the master list. // update the master list.
// //
void RSS_FEEDS::update_feed_list() { void RSS_FEEDS::update_feed_list() {
unsigned int i, j; unsigned int i;
for (i=0; i<feeds.size(); i++) { for (i=0; i<feeds.size(); i++) {
RSS_FEED& rf = feeds[i]; RSS_FEED& rf = feeds[i];
rf.found = false; rf.found = false;
} }
for (i=0; i<gstate.projects.size(); i++) { for (i=0; i<gstate.projects.size(); i++) {
PROJECT* p = gstate.projects[i]; PROJECT* p = gstate.projects[i];
for (j=0; j<p->proj_feeds.size(); j++) { update_proj_am(p);
RSS_FEED& rf = p->proj_feeds[j]; }
RSS_FEED* rfp = lookup_url(rf.url); if (gstate.acct_mgr_info.using_am()) {
if (rfp) { update_proj_am(&gstate.acct_mgr_info);
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
);
}
}
}
} }
vector<RSS_FEED>::iterator iter = feeds.begin(); vector<RSS_FEED>::iterator iter = feeds.begin();
while (iter != feeds.end()) { while (iter != feeds.end()) {

View File

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

View File

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