void NOTICES::clear_keep() { deque<NOTICE>::iterator i = notices.begin(); while (i != notices.end()) { NOTICE& n = *i; n.keep = false; i++; } }
// called at the end of client initialization // void NOTICES::init_rss() { rss_feeds.init(); if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "read %d total notices", (int)notices.size()); } // sort by decreasing arrival time, then assign seqnos // sort(notices.begin(), notices.end(), cmp); size_t n = notices.size(); for (unsigned int i=0; i<n; i++) { notices[i].seqno = (int)(n - i); } }
// write archive file for the given RSS feed // (or, if NULL, non-RSS notices) // void NOTICES::write_archive(RSS_FEED* rfp) { char path[MAXPATHLEN]; if (rfp) { rfp->archive_file_name(path); } else { safe_strcpy(path, NOTICES_DIR"/archive.xml"); } FILE* f = fopen(path, "w"); if (!f) return; MIOFILE fout; fout.init_file(f); fout.printf("<notices>\n"); if (!f) return; for (unsigned int i=0; i<notices.size(); i++) { NOTICE& n = notices[i]; if (rfp) { if (strcmp(rfp->url, n.feed_url)) continue; } else { if (strlen(n.feed_url)) continue; } n.write(fout, false); } fout.printf("</notices>\n"); fclose(f); }
// we're considering adding a notice n. // If there's already an identical message n2 // return false (don't add n) // If there's a message n2 with same title and text, // and n is significantly newer than n2, // delete n2 // // Also remove notices older than 30 days // bool NOTICES::remove_dups(NOTICE& n) { deque<NOTICE>::iterator i = notices.begin(); bool removed_something = false; bool retval = true; double min_time = gstate.now - 30*86400; while (i != notices.end()) { NOTICE& n2 = *i; if (n2.arrival_time < min_time || (n2.create_time && n2.create_time < min_time) ) { i = notices.erase(i); removed_something = true; #if 0 // this check prevents news item edits from showing; skip it } else if (same_guid(n, n2)) { n2.keep = true; return false; #endif } else if (same_text(n, n2)) { int min_diff = 0; // show a given scheduler notice at most once a week // if (!strcmp(n.category, "scheduler")) { min_diff = 7*86400; } if (n.create_time > n2.create_time + min_diff) { i = notices.erase(i); removed_something = true; } else { n2.keep = true; retval = false; ++i; } } else { ++i; } } #ifndef SIM if (removed_something) { gstate.gui_rpcs.set_notice_refresh(); } #endif return retval; }
// Remove "need network access" notices // void NOTICES::remove_network_msg() { deque<NOTICE>::iterator i = notices.begin(); while (i != notices.end()) { NOTICE& n = *i; if (!strcmp(n.description.c_str(), NEED_NETWORK_MSG)) { i = notices.erase(i); #ifndef SIM gstate.gui_rpcs.set_notice_refresh(); #endif if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "REMOVING NETWORK MESSAGE"); } } else { ++i; } } }
void NOTICES::unkeep(const char* url) { deque<NOTICE>::iterator i = notices.begin(); bool removed_something = false; while (i != notices.end()) { NOTICE& n = *i; if (!strcmp(url, n.feed_url) && !n.keep) { i = notices.erase(i); removed_something = true; } else { i++; } } #ifndef SIM if (removed_something) { gstate.gui_rpcs.set_notice_refresh(); } #endif }
// called at the start of client initialization // void NOTICES::init() { #if 0 read_archive_file(NOTICES_DIR"/archive.xml", NULL); if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "read %d BOINC notices", (int)notices.size()); } write_archive(NULL); #endif }
// Remove scheduler notices from the given project. // This is called if we did an RPC to the project requesting work, // and no notices were returned. // void NOTICES::remove_scheduler_notices(PROJECT* p) { deque<NOTICE>::iterator i = notices.begin(); while (i != notices.end()) { NOTICE& n = *i; if (!strcmp(n.project_name, p->get_project_name()) && !strcmp(n.category, "scheduler") ) { i = notices.erase(i); #ifndef SIM gstate.gui_rpcs.set_notice_refresh(); #endif if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "REMOVING PROJECT MESSAGE"); } } else { ++i; } } }
// Remove outdated notices // void NOTICES::remove_notices(PROJECT* p, int which) { deque<NOTICE>::iterator i = notices.begin(); while (i != notices.end()) { NOTICE& n = *i; if (p && strcmp(n.project_name, p->get_project_name())) { ++i; continue; } bool remove = false; switch (which) { case REMOVE_NETWORK_MSG: remove = !strcmp(n.description.c_str(), NEED_NETWORK_MSG); break; case REMOVE_SCHEDULER_MSG: remove = !strcmp(n.category, "scheduler"); break; case REMOVE_NO_WORK_MSG: remove = !strcmp(n.description.c_str(), NO_WORK_MSG); break; case REMOVE_CONFIG_MSG: remove = (strstr(n.description.c_str(), "cc_config.xml") != NULL); break; case REMOVE_APP_INFO_MSG: remove = (strstr(n.description.c_str(), "app_info.xml") != NULL); break; case REMOVE_APP_CONFIG_MSG: remove = (strstr(n.description.c_str(), "app_config.xml") != NULL); break; } if (remove) { i = notices.erase(i); #ifndef SIM gstate.gui_rpcs.set_notice_refresh(); #endif if (log_flags.notice_debug) { msg_printf(p, MSG_INFO, "Removing notices of type %d", which); } } else { ++i; } } }
// write notices newer than seqno as XML (for GUI RPC). // Write them in order of increasing seqno // void NOTICES::write(int seqno, GUI_RPC_CONN& grc, bool public_only) { size_t i; MIOFILE mf; if (!net_status.need_physical_connection) { remove_notices(NULL, REMOVE_NETWORK_MSG); } if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "NOTICES::write: seqno %d, refresh %s, %d notices", seqno, grc.get_notice_refresh()?"true":"false", (int)notices.size() ); } grc.mfout.printf("<notices>\n"); if (grc.get_notice_refresh()) { grc.clear_notice_refresh(); NOTICE n; n.seqno = -1; seqno = -1; i = notices.size(); n.write(grc.mfout, true); if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "NOTICES::write: sending -1 seqno notice"); } } else { for (i=0; i<notices.size(); i++) { NOTICE& n = notices[i]; if (n.seqno <= seqno) break; } } for (; i>0; i--) { NOTICE& n = notices[i-1]; if (public_only && n.is_private) continue; if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "NOTICES::write: sending notice %d", n.seqno); } n.write(grc.mfout, true); } grc.mfout.printf("</notices>\n"); }
// add a notice. // bool NOTICES::append(NOTICE& n) { if (!remove_dups(n)) { return false; } if (notices.empty()) { n.seqno = 1; } else { n.seqno = notices.front().seqno + 1; } if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "[notice] appending notice %d: %s", n.seqno, strlen(n.title)?n.title:n.description.c_str() ); } notices.push_front(n); #if 0 if (!strlen(n.feed_url)) { write_archive(NULL); } #endif return true; }
// handle a completed RSS feed fetch // void RSS_FEED_OP::handle_reply(int http_op_retval) { char filename[256]; int nitems; if (!rfp) return; // op was canceled if (http_op_retval) { if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "[notice] fetch of %s failed: %d", rfp->url, http_op_retval ); } return; } if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "[notice] handling reply from %s", rfp->url ); } rfp->feed_file_name(filename); FILE* f = fopen(filename, "r"); if (!f) { msg_printf(0, MSG_INTERNAL_ERROR, "RSS feed file '%s' not found", filename ); return; } MIOFILE fin; fin.init_file(f); XML_PARSER xp(&fin); int retval = rfp->parse_items(xp, nitems); if (retval) { if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "[notice] RSS parse error: %d", retval ); } } fclose(f); notices.write_archive(rfp); }
// parse the actual RSS feed. // int RSS_FEED::parse_items(XML_PARSER& xp, int& nitems) { nitems = 0; int ntotal = 0, nerror = 0; int retval, func_ret = ERR_XML_PARSE; vector<NOTICE> new_notices; notices.clear_keep(); while (!xp.get_tag()) { if (!xp.is_tag) continue; if (xp.match_tag("/rss")) { if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "[notice] parsed RSS feed: total %d error %d added %d", ntotal, nerror, nitems ); } func_ret = 0; break; } if (xp.match_tag("item")) { NOTICE n; ntotal++; retval = n.parse_rss(xp); if (retval) { nerror++; } else if (n.create_time < gstate.now - 30*86400) { if (log_flags.notice_debug) { msg_printf(0, MSG_INFO, "[notice] item is older than 30 days: %s", n.title ); } } else { n.arrival_time = gstate.now; n.keep = true; safe_strcpy(n.feed_url, url); safe_strcpy(n.project_name, project_name); new_notices.push_back(n); } continue; } if (xp.parse_int("error_num", retval)) { if (log_flags.notice_debug) { msg_printf(0,MSG_INFO, "[notice] RSS fetch returned error %d (%s)", retval, boincerror(retval) ); } return retval; } } // sort new notices by increasing create time, and append them // std::sort(new_notices.begin(), new_notices.end(), create_time_asc); for (unsigned int i=0; i<new_notices.size(); i++) { NOTICE& n = new_notices[i]; if (notices.append(n)) { nitems++; } } notices.unkeep(url); return func_ret; }
// read and parse the contents of the archive file; // insert items in NOTICES // int RSS_FEED::read_archive_file() { char path[MAXPATHLEN]; archive_file_name(path); return notices.read_archive_file(path, this); }