[boinc] 01/05: Imported Upstream version 7.4.27+dfsg

Gianfranco Costamagna locutusofborg-guest at moszumanska.debian.org
Fri Nov 7 10:03:55 UTC 2014


This is an automated email from the git hooks/post-receive script.

locutusofborg-guest pushed a commit to branch experimental
in repository boinc.

commit d366bbc1bd1fc9de835a570a01485a096b6c84b6
Author: Gianfranco Costamagna <costamagnagianfranco at yahoo.it>
Date:   Fri Nov 7 10:59:26 2014 +0100

    Imported Upstream version 7.4.27+dfsg
---
 android/BOINC/AndroidManifest.xml |   4 +-
 api/boinc_api.cpp                 |  16 +-
 api/boinc_api.h                   |   3 +
 client/acct_setup.cpp             |  32 ++-
 client/app_control.cpp            |  30 ++-
 client/boinc.xml                  |   2 +
 client/client_state.cpp           |  22 ++-
 client/client_types.cpp           |  30 +++
 client/client_types.h             |   6 +
 client/coproc_sched.cpp           | 111 +----------
 client/cpu_sched.cpp              |  27 +--
 client/cs_notice.cpp              |  40 +++-
 client/cs_scheduler.cpp           |   3 +
 client/gpu_detect.cpp             |  28 +++
 client/gpu_nvidia.cpp             |  15 +-
 client/gpu_opencl.cpp             |  25 ++-
 client/hostinfo_unix.cpp          |   4 +-
 client/hostinfo_win.cpp           |  13 +-
 client/log_flags.cpp              |  60 ++++--
 client/log_flags.h                |   4 +-
 client/project.cpp                |   1 -
 client/project.h                  |   6 -
 client/result.h                   |   2 +-
 client/sandbox.cpp                |   2 +-
 client/scheduler_op.cpp           |   4 +-
 client/sim.cpp                    |   1 +
 client/work_fetch.cpp             | 402 ++++++++++++++------------------------
 client/work_fetch.h               |  29 +--
 clientgui/AccountInfoPage.cpp     |  20 +-
 clientgui/AdvancedFrame.cpp       |  32 ++-
 clientgui/AdvancedFrame.h         |   1 +
 clientgui/CompletionPage.cpp      |   2 +-
 clientgui/Events.h                |   1 +
 clientgui/MainDocument.cpp        |  13 +-
 clientgui/Makefile.am             |   1 +
 clientgui/ProjectInfoPage.cpp     |  71 ++-----
 clientgui/ProjectWelcomePage.cpp  | 291 +++++++++++++++++++++++++++
 clientgui/ProjectWelcomePage.h    |  95 +++++++++
 clientgui/WizardAttach.cpp        |  53 ++++-
 clientgui/WizardAttach.h          |  23 ++-
 clientgui/browser.cpp             | 100 ++++++++--
 clientgui/browser.h               |  29 ++-
 configure.ac                      |   2 +-
 html/user/get_project_config.php  |   4 +
 html/user/lookup_account.php      |  80 +++++---
 lib/app_ipc.cpp                   |   2 +-
 lib/cc_config.cpp                 |  18 +-
 lib/cc_config.h                   |   4 +-
 lib/coproc.h                      |  36 +---
 lib/error_numbers.h               |   1 +
 lib/gui_rpc_client.h              |   4 +-
 lib/gui_rpc_client_ops.cpp        |  11 +-
 lib/notice.cpp                    |   5 +-
 lib/opencl_boinc.cpp              |  13 +-
 lib/opencl_boinc.h                |   1 +
 lib/procinfo_mac.cpp              |   5 +
 lib/str_util.cpp                  |   1 +
 lib/url.cpp                       |   3 +
 lib/url.h                         |   1 +
 py/Boinc/boinc_db.py              |   6 +
 sched/script_validator.cpp        | 128 ++++++++++++
 version.log                       |   2 +-
 62 files changed, 1321 insertions(+), 660 deletions(-)

diff --git a/android/BOINC/AndroidManifest.xml b/android/BOINC/AndroidManifest.xml
index 88ee33a..0c148ba 100644
--- a/android/BOINC/AndroidManifest.xml
+++ b/android/BOINC/AndroidManifest.xml
@@ -20,8 +20,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="edu.berkeley.boinc"
     android:installLocation="internalOnly"
-    android:versionCode="98"
-    android:versionName="7.4.23" > <!-- installation on SD card would break boot receiver -->
+    android:versionCode="101"
+    android:versionName="7.4.27" > <!-- installation on SD card would break boot receiver -->
 
 
     <!-- Add Google Play store metadata informing the store we can run on tablets and other large screen devices -->
diff --git a/api/boinc_api.cpp b/api/boinc_api.cpp
index f24f8c2..dea49f9 100644
--- a/api/boinc_api.cpp
+++ b/api/boinc_api.cpp
@@ -682,7 +682,7 @@ static void send_trickle_up_msg() {
 // an "unrecoverable error", which will be reported back to server. 
 // A zero exit-status tells the client we've successfully finished the result.
 //
-int boinc_finish(int status) {
+int boinc_finish_message(int status, const char* msg, bool is_notice) {
     char buf[256];
     fraction_done = 1;
     fprintf(stderr,
@@ -693,9 +693,15 @@ int boinc_finish(int status) {
     boinc_sleep(2.0);   // let the timer thread send final messages
     boinc_disable_timer_thread = true;     // then disable it
 
-    if (options.main_program && status==0) {
+    if (options.main_program) {
         FILE* f = fopen(BOINC_FINISH_CALLED_FILE, "w");
-        if (f) fclose(f);
+        if (f) {
+            fprintf(f, "%d\n", status);
+            if (msg) {
+                fprintf(f, "%s\n%s\n", msg, is_notice?"notice":"");
+            }
+            fclose(f);
+        }
     }
 
     boinc_exit(status);
@@ -703,6 +709,10 @@ int boinc_finish(int status) {
     return 0;   // never reached
 }
 
+int boinc_finish(int status) {
+    return boinc_finish_message(status, NULL, false);
+}
+
 int boinc_temporary_exit(int delay, const char* reason, bool is_notice) {
     FILE* f = fopen(TEMPORARY_EXIT_FILE, "w");
     if (!f) {
diff --git a/api/boinc_api.h b/api/boinc_api.h
index 9c56df3..f34d8d1 100644
--- a/api/boinc_api.h
+++ b/api/boinc_api.h
@@ -145,6 +145,9 @@ extern int boinc_report_app_status_aux(
 extern int boinc_temporary_exit(
     int delay, const char* reason=NULL, bool is_notice=false
 );
+extern int boinc_finish_message(
+    int status, const char* message, bool is_notice
+);
 
 /////////// API ENDS HERE
 
diff --git a/client/acct_setup.cpp b/client/acct_setup.cpp
index 5faaff4..1161ca1 100644
--- a/client/acct_setup.cpp
+++ b/client/acct_setup.cpp
@@ -132,16 +132,32 @@ int LOOKUP_ACCOUNT_OP::do_rpc(ACCOUNT_IN& ai) {
 
     url = ai.url;
     canonicalize_master_url(url);
+    url += "lookup_account.php";
 
-    url += "lookup_account.php?email_addr=";
-    parameter = ai.email_addr;
-    escape_url(parameter);
-    url += parameter;
+    if (strchr(ai.email_addr.c_str(), '@')) {
+        url += "?email_addr=";
+        parameter = ai.email_addr;
+        escape_url(parameter);
+        url += parameter;
 
-    url += "&passwd_hash=";
-    parameter = ai.passwd_hash;
-    escape_url(parameter);
-    url += parameter;
+        url += "&passwd_hash=";
+        parameter = ai.passwd_hash;
+        escape_url(parameter);
+        url += parameter;
+    } else {
+        // LDAP case
+        //
+        if (!is_https(ai.url.c_str())) return ERR_NEED_HTTPS;
+        url += "?ldap_auth=1&ldap_uid=";
+        parameter = ai.email_addr;
+        escape_url(parameter);
+        url += parameter;
+
+        url += "&passwd=";
+        parameter = ai.passwd_hash;
+        escape_url(parameter);
+        url += parameter;
+    }
 
     retval = gui_http->do_rpc(
         this, url.c_str(), LOOKUP_ACCOUNT_FILENAME, false
diff --git a/client/app_control.cpp b/client/app_control.cpp
index 9b683ba..fb1df52 100644
--- a/client/app_control.cpp
+++ b/client/app_control.cpp
@@ -371,12 +371,12 @@ void ACTIVE_TASK::handle_temporary_exit(
     } else {
         if (is_notice) {
             msg_printf(result->project, MSG_USER_ALERT,
-                "Can't run task: %s", reason
+                "Task postponed: %s", reason
             );
         } else {
-            if (log_flags.task_debug) {
+            if (log_flags.task) {
                 msg_printf(result->project, MSG_INFO,
-                    "[task] task called temporary_exit(%f, %s)", backoff, reason
+                    "task postponed %f sec: %s", backoff, reason
                 );
             }
         }
@@ -587,10 +587,30 @@ void ACTIVE_TASK::handle_exited_app(int stat) {
     gstate.request_work_fetch("application exited");
 }
 
+// structure of a finish file (see boinc_api.cpp)):
+// exit status (int)
+// message
+// "notice" or blank line
+// ... or empty
+//
 bool ACTIVE_TASK::finish_file_present() {
-    char path[MAXPATHLEN];
+    char path[MAXPATHLEN], buf[1024], buf2[256];
+    strcpy(buf, "");
+    strcpy(buf2, "");
     sprintf(path, "%s/%s", slot_dir, BOINC_FINISH_CALLED_FILE);
-    return (boinc_file_exists(path) != 0);
+    FILE* f = fopen(path, "r");
+    if (!f) return false;
+    fgets(buf, sizeof(buf), f);
+    fgets(buf, sizeof(buf), f);
+    fgets(buf2, sizeof(buf2), f);
+    if (strlen(buf)) {
+        msg_printf(result->project,
+            strstr(buf2, "notice")?MSG_USER_ALERT:MSG_INFO,
+            "Message from task: %s", buf
+        );
+    }
+    fclose(f);
+    return true;
 }
 
 bool ACTIVE_TASK::temporary_exit_file_present(
diff --git a/client/boinc.xml b/client/boinc.xml
index 0e7903a..1612f66 100644
--- a/client/boinc.xml
+++ b/client/boinc.xml
@@ -11,6 +11,8 @@
       <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
       <!-- Windows 8.1 -->
       <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+      <!-- Windows 10 -->
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
     </application>
   </compatibility>
 </assembly>
diff --git a/client/client_state.cpp b/client/client_state.cpp
index 73be34d..af5d20d 100644
--- a/client/client_state.cpp
+++ b/client/client_state.cpp
@@ -246,11 +246,8 @@ void CLIENT_STATE::show_host_info() {
     } else {
 #if defined (_WIN32) && !defined(_WIN64)
         if (!strcmp(get_primary_platform(), "windows_x86_64")) {
-            msg_printf(NULL, MSG_INFO,
-                "VirtualBox: can't detect because this is a 32-bit client"
-            );
-            msg_printf(NULL, MSG_INFO,
-                "  (to use VirtualBox, install a 64-bit BOINC client)."
+            msg_printf(NULL, MSG_USER_ALERT,
+                "Can't detect VirtualBox because this is a 32-bit version of BOINC; to fix, please install a 64-bit version."
             );
         }
 #endif
@@ -876,7 +873,7 @@ bool CLIENT_STATE::poll_slow_events() {
 #endif
 
     if (user_active != old_user_active) {
-        request_schedule_cpus("Idle state change");
+        request_schedule_cpus(user_active?"Not idle":"Idle");
     }
 
 #if 0
@@ -1602,6 +1599,10 @@ bool CLIENT_STATE::garbage_collect_always() {
 
     for (fi_iter = file_infos.begin(); fi_iter!=file_infos.end(); fi_iter++) {
         fip = *fi_iter;
+        if (fip->sticky_expire_time && now > fip->sticky_expire_time) {
+            fip->sticky = false;
+            fip->sticky_expire_time = 0;
+        }
         if (!fip->sticky) continue;
         if (fip->status < 0) continue;
         fip->ref_cnt++;
@@ -1957,6 +1958,15 @@ int CLIENT_STATE::reset_project(PROJECT* project, bool detaching) {
         }
         garbage_collect_always();
     }
+#ifdef ANDROID
+    // space is likely to be an issue on Android, so clean out project dir
+    // If we did this on other platforms we'd need to avoid deleting
+    // app_config.xml, but this isn't likely to exist on Android.
+    //
+    if (!project->anonymous_platform) {
+        client_clean_out_dir(project.project_dir(), "reset project");
+    }
+#endif
 
     // force refresh of scheduler URLs
     //
diff --git a/client/client_types.cpp b/client/client_types.cpp
index 28036ab..400d121 100644
--- a/client/client_types.cpp
+++ b/client/client_types.cpp
@@ -188,6 +188,8 @@ FILE_INFO::FILE_INFO() {
     executable = false;
     uploaded = false;
     sticky = false;
+    sticky_lifetime = 0;
+    sticky_expire_time = 0;
     gzip_when_done = false;
     download_gzipped = false;
     signature_required = false;
@@ -279,6 +281,8 @@ int FILE_INFO::set_permissions(const char* path) {
 }
 #endif
 
+// parse a <file_info>, from state file or scheduler RPC reply
+//
 int FILE_INFO::parse(XML_PARSER& xp) {
     char buf2[1024];
     std::string url;
@@ -366,6 +370,10 @@ int FILE_INFO::parse(XML_PARSER& xp) {
         if (xp.parse_bool("executable", executable)) continue;
         if (xp.parse_bool("uploaded", uploaded)) continue;
         if (xp.parse_bool("sticky", sticky)) continue;
+        if (xp.parse_double("sticky_expire_time", sticky_expire_time)) continue;
+            // state file has this
+        if (xp.parse_double("sticky_lifetime", sticky_lifetime)) continue;
+            // scheduler RPC reply has this
         if (xp.parse_bool("gzip_when_done", gzip_when_done)) continue;
         if (xp.parse_bool("download_gzipped", download_gzipped)) continue;
         if (xp.parse_bool("signature_required", signature_required)) continue;
@@ -450,6 +458,11 @@ int FILE_INFO::write(MIOFILE& out, bool to_server) {
         if (is_user_file) out.printf("    <is_user_file/>\n");
         if (strlen(file_signature)) out.printf("    <file_signature>\n%s\n</file_signature>\n", file_signature);
     }
+    if (sticky_expire_time) {
+        out.printf("    <sticky_expire_time>%f</sticky_expire_time>\n",
+            sticky_expire_time
+        );
+    }
     for (i=0; i<download_urls.urls.size(); i++) {
         xml_escape(download_urls.urls[i].c_str(), buf, sizeof(buf));
         out.printf("    <download_url>%s</download_url>\n", buf);
@@ -618,6 +631,23 @@ int FILE_INFO::merge_info(FILE_INFO& new_info) {
         return retval;
     }
 
+    // sticky attributes
+    //
+    if (new_info.sticky) {
+        sticky = true;
+        if (new_info.sticky_lifetime) {
+            double x = gstate.now + new_info.sticky_lifetime;
+            if (x > sticky_expire_time) {
+                sticky_expire_time = x;
+            }
+        } else {
+            sticky_expire_time = 0;
+        }
+    } else {
+        sticky = false;
+        sticky_expire_time = 0;
+    }
+
     return 0;
 }
 
diff --git a/client/client_types.h b/client/client_types.h
index 908b21e..c8cd98f 100644
--- a/client/client_types.h
+++ b/client/client_types.h
@@ -105,6 +105,12 @@ struct FILE_INFO {
     bool executable;        // change file protections to make executable
     bool uploaded;          // file has been uploaded
     bool sticky;            // don't delete unless instructed to do so
+    double sticky_lifetime;
+        // how long file should stay sticky.
+        // passed from the server;
+        // used by client to calculate sticky_expire_time.
+    double sticky_expire_time;
+        // if nonzero, when sticky status expires
     bool signature_required;    // true iff associated with app version
     bool is_user_file;
     bool is_project_file;
diff --git a/client/coproc_sched.cpp b/client/coproc_sched.cpp
index 14babce..34d6956 100644
--- a/client/coproc_sched.cpp
+++ b/client/coproc_sched.cpp
@@ -120,9 +120,8 @@ static inline void increment_pending_usage(
 // if the app is still running, it has enough RAM
 //
 static inline bool current_assignment_ok(
-    RESULT* rp, double usage, COPROC* cp, bool& defer_sched
+    RESULT* rp, double usage, COPROC* cp
 ) {
-    defer_sched = false;
     double x = (usage<1)?usage:1;
     for (int i=0; i<usage; i++) {
         int j = rp->coproc_indices[i];
@@ -153,17 +152,11 @@ static inline void confirm_current_assignment(
                 cp->type, j, x, rp->name
             );
         }
-#if DEFER_ON_GPU_AVAIL_RAM
-        cp->available_ram_temp[j] -= rp->avp->gpu_ram;
-#endif
     }
 }
 
-static inline bool get_fractional_assignment(
-    RESULT* rp, double usage, COPROC* cp, bool& defer_sched
-) {
+static inline bool get_fractional_assignment(RESULT* rp, double usage, COPROC* cp) {
     int i;
-    defer_sched = false;
 
     // try to assign an instance that's already fractionally assigned
     //
@@ -174,13 +167,6 @@ static inline bool get_fractional_assignment(
         if ((cp->usage[i] || cp->pending_usage[i])
             && (cp->usage[i] + cp->pending_usage[i] + usage <= 1)
         ) {
-#if DEFER_ON_GPU_AVAIL_RAM
-            if (rp->avp->gpu_ram > cp->available_ram_temp[i]) {
-                defer_sched = true;
-                continue;
-            }
-            cp->available_ram_temp[i] -= rp->avp->gpu_ram;
-#endif
             rp->coproc_indices[0] = i;
             cp->usage[i] += usage;
             if (log_flags.coproc_debug) {
@@ -200,13 +186,6 @@ static inline bool get_fractional_assignment(
             continue;
         }
         if (!cp->usage[i]) {
-#if DEFER_ON_GPU_AVAIL_RAM
-            if (rp->avp->gpu_ram > cp->available_ram_temp[i]) {
-                defer_sched = true;
-                continue;
-            }
-            cp->available_ram_temp[i] -= rp->avp->gpu_ram;
-#endif
             rp->coproc_indices[0] = i;
             cp->usage[i] += usage;
             if (log_flags.coproc_debug) {
@@ -229,10 +208,9 @@ static inline bool get_fractional_assignment(
 }
 
 static inline bool get_integer_assignment(
-    RESULT* rp, double usage, COPROC* cp, bool& defer_sched
+    RESULT* rp, double usage, COPROC* cp
 ) {
     int i;
-    defer_sched = false;
 
     // make sure we have enough free instances
     //
@@ -242,18 +220,6 @@ static inline bool get_integer_assignment(
             continue;
         }
         if (!cp->usage[i]) {
-#if DEFER_ON_GPU_AVAIL_RAM
-            if (rp->avp->gpu_ram > cp->available_ram_temp[i]) {
-                defer_sched = true;
-                if (log_flags.coproc_debug) {
-                    msg_printf(rp->project, MSG_INFO,
-                        "[coproc]  task %s needs %.0fMB RAM, %s GPU %d has %.0fMB available",
-                        rp->name, rp->avp->gpu_ram/MEGA, cp->type, i, cp->available_ram_temp[i]/MEGA
-                    );
-                }
-                continue;
-            };
-#endif
             nfree++;
         }
     }
@@ -263,11 +229,6 @@ static inline bool get_integer_assignment(
                 "[coproc] Insufficient %s for %s; need %d, available %d",
                 cp->type, rp->name, (int)usage, nfree
             );
-            if (defer_sched) {
-                msg_printf(rp->project, MSG_INFO,
-                    "[coproc] some instances lack available memory"
-                );
-            }
         }
         return false;
     }
@@ -280,16 +241,8 @@ static inline bool get_integer_assignment(
         if (!can_use_gpu(rp, cp, i)) {
             continue;
         }
-        if (!cp->usage[i]
-            && !cp->pending_usage[i]
-#if DEFER_ON_GPU_AVAIL_RAM
-            && (rp->avp->gpu_ram <= cp->available_ram_temp[i])
-#endif
-        ) {
+        if (!cp->usage[i] && !cp->pending_usage[i]) {
             cp->usage[i] = 1;
-#if DEFER_ON_GPU_AVAIL_RAM
-            cp->available_ram_temp[i] -= rp->avp->gpu_ram;
-#endif
             rp->coproc_indices[n++] = i;
             if (log_flags.coproc_debug) {
                 msg_printf(rp->project, MSG_INFO,
@@ -307,15 +260,8 @@ static inline bool get_integer_assignment(
         if (!can_use_gpu(rp, cp, i)) {
             continue;
         }
-        if (!cp->usage[i]
-#if DEFER_ON_GPU_AVAIL_RAM
-            && (rp->avp->gpu_ram <= cp->available_ram_temp[i])
-#endif
-        ) {
+        if (!cp->usage[i]) {
             cp->usage[i] = 1;
-#if DEFER_ON_GPU_AVAIL_RAM
-            cp->available_ram_temp[i] -= rp->avp->gpu_ram;
-#endif
             rp->coproc_indices[n++] = i;
             if (log_flags.coproc_debug) {
                 msg_printf(rp->project, MSG_INFO,
@@ -335,43 +281,12 @@ static inline bool get_integer_assignment(
     return false;
 }
 
-static inline void mark_as_defer_sched(RESULT* rp) {
-    int i = rp->avp->gpu_usage.rsc_type;
-    if (i) {
-        rp->project->rsc_defer_sched[i] = true;
-    }
-    rp->schedule_backoff = gstate.now + 300; // try again in 5 minutes
-    gstate.request_schedule_cpus("insufficient GPU RAM");
-}
-
-#if DEFER_ON_GPU_AVAIL_RAM
-static void copy_available_ram(COPROC& cp, const char* name) {
-    int rt = rsc_index(name);
-    if (rt > 0) {
-        for (int i=0; i<MAX_COPROC_INSTANCES; i++) {
-            coprocs.coprocs[rt].available_ram_temp[i] = cp.available_ram;
-        }
-    }
-}
-#endif
-
 void assign_coprocs(vector<RESULT*>& jobs) {
     unsigned int i;
     COPROC* cp;
     double usage;
 
     coprocs.clear_usage();
-#if DEFER_ON_GPU_AVAIL_RAM
-    if (coprocs.have_nvidia()) {
-        copy_available_ram(coprocs.nvidia, GPU_TYPE_NVIDIA);
-    }
-    if (coprocs.have_ati()) {
-        copy_available_ram(coprocs.ati, GPU_TYPE_ATI);
-    }
-    if (coprocs.have_intel()) {
-        copy_available_ram(coprocs.intel_gpu, GPU_TYPE_INTEL);
-    }
-#endif
 
     // fill in pending usage
     //
@@ -407,34 +322,24 @@ void assign_coprocs(vector<RESULT*>& jobs) {
         }
 
         ACTIVE_TASK* atp = gstate.lookup_active_task_by_result(rp);
-        bool defer_sched;
         if (atp && atp->is_gpu_task_running()) {
-            if (current_assignment_ok(rp, usage, cp, defer_sched)) {
+            if (current_assignment_ok(rp, usage, cp)) {
                 confirm_current_assignment(rp, usage, cp);
                 job_iter++;
             } else {
-                if (defer_sched) {
-                    mark_as_defer_sched(rp);
-                }
                 job_iter = jobs.erase(job_iter);
             }
         } else {
             if (usage < 1) {
-                if (get_fractional_assignment(rp, usage, cp, defer_sched)) {
+                if (get_fractional_assignment(rp, usage, cp)) {
                     job_iter++;
                 } else {
-                    if (defer_sched) {
-                        mark_as_defer_sched(rp);
-                    }
                     job_iter = jobs.erase(job_iter);
                 }
             } else {
-                if (get_integer_assignment(rp, usage, cp, defer_sched)) {
+                if (get_integer_assignment(rp, usage, cp)) {
                     job_iter++;
                 } else {
-                    if (defer_sched) {
-                        mark_as_defer_sched(rp);
-                    }
                     job_iter = jobs.erase(job_iter);
                 }
             }
diff --git a/client/cpu_sched.cpp b/client/cpu_sched.cpp
index b54a515..d4b6ef7 100644
--- a/client/cpu_sched.cpp
+++ b/client/cpu_sched.cpp
@@ -159,6 +159,7 @@ struct PROC_RESOURCES {
                 return false;
             }
         } else if (rp->avp->avg_ncpus > 1) {
+            if (ncpus_used_mt == 0) return true;
             return (ncpus_used_mt + rp->avp->avg_ncpus <= ncpus);
         } else {
             return (ncpus_used_st < ncpus);
@@ -525,13 +526,6 @@ static RESULT* earliest_deadline_result(int rsc_type) {
     }
     if (!best_result) return NULL;
 
-    if (log_flags.cpu_sched_debug) {
-        msg_printf(best_result->project, MSG_INFO,
-            "[cpu_sched_debug] earliest deadline: %.0f %s",
-            best_result->report_deadline, best_result->name
-        );
-    }
-
     return best_result;
 }
 
@@ -1147,12 +1141,6 @@ bool CLIENT_STATE::enforce_run_list(vector<RESULT*>& run_list) {
         );
     }
 
-    for (i=0; i<projects.size(); i++) {
-        for (int j=1; j<coprocs.n_rsc; j++) {
-            projects[i]->rsc_defer_sched[j] = false;
-        }
-    }
-
     // schedule non-CPU-intensive tasks,
     // and look for backed-off GPU jobs
     //
@@ -1174,17 +1162,6 @@ bool CLIENT_STATE::enforce_run_list(vector<RESULT*>& run_list) {
             //ram_left -= atp->procinfo.working_set_size_smoothed;
             swap_left -= atp->procinfo.swap_size;
         }
-        if (rp->schedule_backoff) {
-            if (rp->schedule_backoff > gstate.now) {
-                int r = rp->avp->gpu_usage.rsc_type;
-                if (r) {
-                    rp->project->rsc_defer_sched[r] = true;
-                }
-            } else {
-                rp->schedule_backoff = 0;
-                request_schedule_cpus("schedule backoff finished");
-            }
-        }
     }
 
     // assign coprocessors to coproc jobs,
@@ -1323,6 +1300,7 @@ bool CLIENT_STATE::enforce_run_list(vector<RESULT*>& run_list) {
     bool coproc_quit_pending = false;
     for (i=0; i<active_tasks.active_tasks.size(); i++) {
         atp = active_tasks.active_tasks[i];
+#if 0
         if (log_flags.cpu_sched_debug) {
             msg_printf(atp->result->project, MSG_INFO,
                 "[cpu_sched_debug] %s sched state %d next %d task state %d",
@@ -1330,6 +1308,7 @@ bool CLIENT_STATE::enforce_run_list(vector<RESULT*>& run_list) {
                 atp->next_scheduler_state, atp->task_state()
             );
         }
+#endif
         int preempt_type = REMOVE_MAYBE_SCHED;
         switch (atp->next_scheduler_state) {
         case CPU_SCHED_PREEMPTED:
diff --git a/client/cs_notice.cpp b/client/cs_notice.cpp
index a1a8121..f24f44f 100644
--- a/client/cs_notice.cpp
+++ b/client/cs_notice.cpp
@@ -286,8 +286,12 @@ static inline bool string_equal_nodigits(string& s1, string& s2) {
 }
 
 static inline bool same_text(NOTICE& n1, NOTICE& n2) {
-    if (strcmp(n1.title, n2.title)) return false;
-    if (!string_equal_nodigits(n1.description, n2.description)) return false;
+    if (strcmp(n1.title, n2.title)) {
+        return false;
+    }
+    if (!string_equal_nodigits(n1.description, n2.description)) {
+        return false;
+    }
     return true;
 }
 
@@ -340,11 +344,24 @@ bool NOTICES::remove_dups(NOTICE& n) {
     double min_time = gstate.now - 30*86400;
     while (i != notices.end()) {
         NOTICE& n2 = *i;
+
+        if (log_flags.notice_debug) {
+            msg_printf(0, MSG_INFO,
+                "[notice] scanning old notice %d: %s",
+                n2.seqno, strlen(n2.title)?n2.title:n2.description.c_str()
+            );
+        }
         if (n2.arrival_time < min_time
             || (n2.create_time && n2.create_time < min_time)
         ) {
             i = notices.erase(i);
             removed_something = true;
+            if (log_flags.notice_debug) {
+                msg_printf(0, MSG_INFO,
+                    "[notice] removing old notice %d: %s",
+                    n2.seqno, strlen(n2.title)?n2.title:n2.description.c_str()
+                );
+            }
 #if 0
         // this check prevents news item edits from showing; skip it
         } else if (same_guid(n, n2)) {
@@ -363,10 +380,20 @@ bool NOTICES::remove_dups(NOTICE& n) {
             if (n.create_time > n2.create_time + min_diff) {
                 i = notices.erase(i);
                 removed_something = true;
+                if (log_flags.notice_debug) {
+                    msg_printf(0, MSG_INFO,
+                        "[notice] replacing identical older notice %d", n2.seqno
+                    );
+                }
             } else {
                 n2.keep = true;
                 retval = false;
                 ++i;
+                if (log_flags.notice_debug) {
+                    msg_printf(0, MSG_INFO,
+                        "[notice] keeping identical older notice %d", n2.seqno
+                    );
+                }
             }
         } else {
             ++i;
@@ -383,6 +410,12 @@ bool NOTICES::remove_dups(NOTICE& n) {
 // add a notice.
 // 
 bool NOTICES::append(NOTICE& n) {
+    if (log_flags.notice_debug) {
+        msg_printf(0, MSG_INFO,
+            "[notice] processing notice: %s",
+            strlen(n.title)?n.title:n.description.c_str()
+        );
+    }
     if (!remove_dups(n)) {
         return false;
     }
@@ -393,8 +426,7 @@ bool NOTICES::append(NOTICE& n) {
     }
     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()
+            "[notice] adding notice %d", n.seqno
         );
     }
     notices.push_front(n);
diff --git a/client/cs_scheduler.cpp b/client/cs_scheduler.cpp
index 9a27525..2dbceac 100644
--- a/client/cs_scheduler.cpp
+++ b/client/cs_scheduler.cpp
@@ -811,6 +811,9 @@ int CLIENT_STATE::handle_scheduler_reply(
         } else {
             fip = new FILE_INFO;
             *fip = sr.file_infos[i];
+            if (fip->sticky_lifetime) {
+                fip->sticky_expire_time = now + fip->sticky_lifetime;
+            }
             retval = link_file_info(project, fip);
             if (retval) {
                 msg_printf(project, MSG_INTERNAL_ERROR,
diff --git a/client/gpu_detect.cpp b/client/gpu_detect.cpp
index f17380f..2d73b9c 100644
--- a/client/gpu_detect.cpp
+++ b/client/gpu_detect.cpp
@@ -200,6 +200,17 @@ void COPROCS::correlate_gpus(
                 "CUDA: NVIDIA GPU %d (not used): %s",
                 nvidia_gpus[i].device_num, buf
             );
+
+#ifdef __APPLE__
+            if ((nvidia_gpus[i].cuda_version >= 6050) &&
+                            nvidia_gpus[i].prop.major < 2) {
+                // This will be called only if CUDA recognized and reported the GPU
+                msg_printf(NULL, MSG_USER_ALERT, "NVIDIA GPU %d: %s %s",
+                    nvidia_gpus[i].device_num, nvidia_gpus[i].prop.name,
+                    _("cannot be used for CUDA or OpenCL computation with CUDA driver 6.5 or later")
+                );
+            }
+#endif
             break;
         }
         descs.push_back(string(buf2));
@@ -235,6 +246,13 @@ void COPROCS::correlate_gpus(
     // Create descriptions for OpenCL NVIDIA GPUs
     //
     for (i=0; i<nvidia_opencls.size(); i++) {
+        if (nvidia_opencls[i].warn_bad_cuda) {
+            // This will be called only if CUDA did _not_ recognize and report the GPU
+            msg_printf(NULL, MSG_USER_ALERT, "NVIDIA GPU %d: %s %s",
+                nvidia_opencls[i].device_num, nvidia_opencls[i].name,
+                _("cannot be used for CUDA or OpenCL computation with CUDA driver 6.5 or later")
+            );
+        }
         nvidia_opencls[i].description(buf, sizeof(buf), proc_type_name(PROC_TYPE_NVIDIA_GPU));
         descs.push_back(string(buf));
     }
@@ -349,6 +367,11 @@ int COPROCS::write_coproc_info_file(vector<string> &warnings) {
     
     mf.printf("    <coprocs>\n");
 
+    if (nvidia.have_cuda) {
+        mf.printf("    <have_cuda>1</have_cuda>\n");
+        mf.printf("    <cuda_version>%d</cuda_version>\n", nvidia.cuda_version);
+    }
+    
     for (i=0; i<ati_gpus.size(); ++i) {
        ati_gpus[i].write_xml(mf, false);
     }
@@ -425,6 +448,11 @@ int COPROCS::read_coproc_info_file(vector<string> &warnings) {
             return 0;
         }
 
+        if (xp.parse_bool("have_cuda", nvidia.have_cuda)) continue;
+        if (xp.parse_int("cuda_version", nvidia.cuda_version)) {
+             continue;
+        }
+
         if (xp.match_tag("coproc_ati")) {
             retval = my_ati_gpu.parse(xp);
             if (retval) {
diff --git a/client/gpu_nvidia.cpp b/client/gpu_nvidia.cpp
index 9f20296..cff103b 100644
--- a/client/gpu_nvidia.cpp
+++ b/client/gpu_nvidia.cpp
@@ -351,6 +351,8 @@ void COPROC_NVIDIA::get(
         return;
     }
 
+    have_cuda = true;
+
     retval = (*__cuDeviceGetCount)(&cuda_ndevs);
     if (retval) {
         sprintf(buf, "cuDeviceGetCount() returned %d", retval);
@@ -414,7 +416,6 @@ void COPROC_NVIDIA::get(
 #else
         cc.display_driver_version = nvidia_driver_version();
 #endif
-        have_cuda = true;
         cc.have_cuda = true;
         cc.cuda_version = cuda_version;
         cc.device_num = j;
@@ -441,6 +442,13 @@ void COPROC_NVIDIA::correlate(
     bool first = true;
     for (i=0; i<nvidia_gpus.size(); i++) {
         if (in_vector(nvidia_gpus[i].device_num, ignore_devs)) continue;
+#ifdef __APPLE__
+        if ((nvidia_gpus[i].cuda_version >= 6050) && nvidia_gpus[i].prop.major < 2) {
+            // Can't use GPUs with compute capability < 2 with CUDA drivers >= 6.5.x
+            nvidia_gpus[i].is_used = COPROC_UNUSED;
+            continue;
+        }
+#endif
         if (first) {
             *this = nvidia_gpus[i];
             first = false;
@@ -460,6 +468,11 @@ void COPROC_NVIDIA::correlate(
             nvidia_gpus[i].is_used = COPROC_UNUSED;
         } else if (this->have_cuda && !nvidia_gpus[i].have_cuda) {
             nvidia_gpus[i].is_used = COPROC_UNUSED;
+#ifdef __APPLE__
+        } else if (nvidia_gpus[i].is_used == COPROC_UNUSED) {
+            // Can't use GPUs with compute capability < 2 with CUDA drivers >= 6.5.x
+            continue;
+#endif
         } else if (use_all || !nvidia_compare(nvidia_gpus[i], *this, true)) {
             device_nums[count] = nvidia_gpus[i].device_num;
             pci_infos[count] = nvidia_gpus[i].pci_info;
diff --git a/client/gpu_opencl.cpp b/client/gpu_opencl.cpp
index 2ba3a14..9eddf7b 100644
--- a/client/gpu_opencl.cpp
+++ b/client/gpu_opencl.cpp
@@ -385,6 +385,7 @@ void COPROCS::get_opencl(
 
             //////////// NVIDIA //////////////
             if (is_NVIDIA(prop.vendor)) {
+                bool cuda_match_found = false;
                 if (nvidia.have_cuda) {
                     // Mac OpenCL does not recognize all NVIDIA GPUs returned by
                     // CUDA but we assume that OpenCL and CUDA return devices 
@@ -394,6 +395,8 @@ void COPROCS::get_opencl(
                     // On other systems, assume OpenCL and CUDA return devices 
                     // in the same order.
                     //
+                    int saved_CUDA_index = current_CUDA_index;
+                    
                     while (1) {
                         if (current_CUDA_index >= (int)(nvidia_gpus.size())) {
                             snprintf(buf, sizeof(buf),
@@ -401,11 +404,22 @@ void COPROCS::get_opencl(
                                 device_index
                             );
                             warnings.push_back(buf);
-                            return; // Should never happen
+                            // Newer versions of CUDA driver don't support older NVIDIA GPUs
+                            if (nvidia.cuda_version >= 6050) {
+                                prop.device_num = (int)(nvidia_opencls.size());
+                                current_CUDA_index = saved_CUDA_index;
+                                prop.warn_bad_cuda = true;
+                                break;
+                            } else {
+                                // Older CUDA drivers should report all NVIDIA GPUs reported by OpenCL
+                                return; // Should never happen
+                            }
                         }
                         if (!strcmp(prop.name,
                             nvidia_gpus[devnums_pci_slot_sort[current_CUDA_index]].prop.name)
                             ) {
+                            cuda_match_found = true;
+                            prop.device_num = devnums_pci_slot_sort[current_CUDA_index];
                             break;  // We have a match
                         }
                         // This CUDA GPU is not recognized by OpenCL,
@@ -413,13 +427,12 @@ void COPROCS::get_opencl(
                         //
                         ++current_CUDA_index;
                     }
-                    prop.device_num = devnums_pci_slot_sort[current_CUDA_index];
                 } else {
                     prop.device_num = (int)(nvidia_opencls.size());
                 }
                 prop.opencl_device_index = device_index;
 
-                if (nvidia.have_cuda) {
+                if (cuda_match_found) {
                     prop.peak_flops = nvidia_gpus[prop.device_num].peak_flops;
                 } else {
                     COPROC_NVIDIA c;
@@ -427,7 +440,7 @@ void COPROCS::get_opencl(
                     c.set_peak_flops();
                     prop.peak_flops = c.peak_flops;
                 }
-                if (nvidia_gpus.size()) {
+                if (cuda_match_found) {
                     // Assumes OpenCL device_num and CUDA device_num now match
                     //
                     prop.opencl_available_ram = nvidia_gpus[prop.device_num].available_ram;
@@ -441,7 +454,7 @@ void COPROCS::get_opencl(
                 }
                 nvidia_opencls.insert(it, prop);
                 
-                ++current_CUDA_index;
+                if (cuda_match_found) ++current_CUDA_index;
             }
             
             //////////// AMD / ATI //////////////
@@ -853,6 +866,8 @@ void COPROC::merge_opencl(
     unsigned int i, j;
 
     for (i=0; i<opencls.size(); i++) {
+        opencls[i].is_used = COPROC_UNUSED;
+        
         if (in_vector(opencls[i].device_num, ignore_dev)) {
             opencls[i].is_used = COPROC_IGNORED;
             continue;
diff --git a/client/hostinfo_unix.cpp b/client/hostinfo_unix.cpp
index c25a63c..8df69b6 100644
--- a/client/hostinfo_unix.cpp
+++ b/client/hostinfo_unix.cpp
@@ -1266,7 +1266,9 @@ int HOST_INFO::get_host_info() {
             "get_filesystem_info() failed: %s", boincerror(retval)
         );
     }
-    get_virtualbox_version();
+    if (!cc_config.dont_use_vbox) {
+        get_virtualbox_version();
+    }
 
 ///////////// p_vendor, p_model, p_features /////////////////
 #if LINUX_LIKE_SYSTEM
diff --git a/client/hostinfo_win.cpp b/client/hostinfo_win.cpp
index 195e963..b5cb29d 100644
--- a/client/hostinfo_win.cpp
+++ b/client/hostinfo_win.cpp
@@ -219,6 +219,15 @@ int get_os_information(
     switch (osvi.dwPlatformId) {
         case VER_PLATFORM_WIN32_NT:
 
+			if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 4 ) {
+                if( osvi.wProductType == VER_NT_WORKSTATION ) {
+                    strcat(os_name, "Windows 10");
+                } else {
+                    strcat(os_name, "Windows Server 2015");
+                }
+                pGPI( 6, 4, 0, 0, &dwType);
+            }
+
 			if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3 ) {
                 if( osvi.wProductType == VER_NT_WORKSTATION ) {
                     strcat(os_name, "Windows 8.1");
@@ -1135,7 +1144,9 @@ int HOST_INFO::get_host_info() {
     get_os_information(
         os_name, sizeof(os_name), os_version, sizeof(os_version)
     );
-    get_virtualbox_version();
+    if (!cc_config.dont_use_vbox) {
+        get_virtualbox_version();
+    }
     get_processor_info(
         p_vendor, sizeof(p_vendor),
         p_model, sizeof(p_model),
diff --git a/client/log_flags.cpp b/client/log_flags.cpp
index 433c07d..223176e 100644
--- a/client/log_flags.cpp
+++ b/client/log_flags.cpp
@@ -159,6 +159,9 @@ void CC_CONFIG::show() {
     if (no_gpus) {
         msg_printf(NULL, MSG_INFO, "Config: don't use coprocessors");
     }
+    if (dont_use_vbox) {
+        msg_printf(NULL, MSG_INFO, "Config: don't use VirtualBox");
+    }
     if (no_info_fetch) {
         msg_printf(NULL, MSG_INFO, "Config: don't fetch project list or client version info");
     }
@@ -245,7 +248,6 @@ void CC_CONFIG::show() {
 // (It's separate so that we can write messages in it)
 
 int CC_CONFIG::parse_options_client(XML_PARSER& xp) {
-    char path[MAXPATHLEN];
     string s;
     int n, retval;
 
@@ -308,16 +310,10 @@ int CC_CONFIG::parse_options_client(XML_PARSER& xp) {
             }
             continue;
         }
-        if (xp.parse_str("data_dir", path, sizeof(path))) {
-            if (chdir(path)) {
-                perror("chdir");
-                exit(1);
-            }
-            continue;
-        }
         if (xp.parse_bool("disallow_attach", disallow_attach)) continue;
         if (xp.parse_bool("dont_check_file_sizes", dont_check_file_sizes)) continue;
         if (xp.parse_bool("dont_contact_ref_site", dont_contact_ref_site)) continue;
+        if (xp.parse_bool("dont_use_vbox", dont_use_vbox)) continue;
         if (xp.match_tag("exclude_gpu")) {
             EXCLUDE_GPU eg;
             retval = eg.parse(xp);
@@ -521,19 +517,6 @@ int read_config_file(bool init, const char* fname) {
 
     if (init) {
         coprocs = cc_config.config_coprocs;
-        if (strlen(cc_config.data_dir)) {
-#ifdef _WIN32
-            _chdir(cc_config.data_dir);
-#else
-            if (chdir(cc_config.data_dir)) {
-                msg_printf(NULL, MSG_INFO,
-                    "Couldn't change to directory specified in cc_config.xml: %s",
-                    cc_config.data_dir
-                );
-                return ERR_OPENDIR;
-            }
-#endif
-        }
     } else {
         select_proxy_info();        // in case added or removed proxy info
     }
@@ -723,3 +706,38 @@ bool gpu_excluded(APP* app, COPROC& cp, int ind) {
     }
     return false;
 }
+
+// if the configuration file disallows the use of a GPU type
+// for a project, set a flag to that effect
+//
+void set_no_rsc_config() {
+    for (unsigned int i=0; i<gstate.projects.size(); i++) {
+        PROJECT& p = *gstate.projects[i];
+        for (int j=1; j<coprocs.n_rsc; j++) {
+            bool allowed[MAX_COPROC_INSTANCES];
+            memset(allowed, 0, sizeof(allowed));
+            COPROC& c = coprocs.coprocs[j];
+            for (int k=0; k<c.count; k++) {
+                allowed[c.device_nums[k]] = true;
+            }
+            for (unsigned int k=0; k<cc_config.exclude_gpus.size(); k++) {
+                EXCLUDE_GPU& e = cc_config.exclude_gpus[k];
+                if (strcmp(e.url.c_str(), p.master_url)) continue;
+                if (!e.type.empty() && strcmp(e.type.c_str(), c.type)) continue;
+                if (!e.appname.empty()) continue;
+                if (e.device_num < 0) {
+                    memset(allowed, 0, sizeof(allowed));
+                    break;
+                }
+                allowed[e.device_num] = false;
+            }
+            p.no_rsc_config[j] = true;
+            for (int k=0; k<c.count; k++) {
+                if (allowed[c.device_nums[k]]) {
+                    p.no_rsc_config[j] = false;
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/client/log_flags.h b/client/log_flags.h
index 9eeb47a..8b001a0 100644
--- a/client/log_flags.h
+++ b/client/log_flags.h
@@ -1,6 +1,6 @@
 // This file is part of BOINC.
 // http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2014 University of California
 //
 // BOINC is free software; you can redistribute it and/or modify it
 // under the terms of the GNU Lesser General Public License
@@ -39,6 +39,6 @@ extern CC_CONFIG cc_config;
 extern int read_config_file(bool init, const char* fname=CONFIG_FILE);
 extern void process_gpu_exclusions();
 extern bool gpu_excluded(APP* app, COPROC& cp, int ind);
-
+extern void set_no_rsc_config();
 
 #endif
diff --git a/client/project.cpp b/client/project.cpp
index a5d9b79..2710146 100644
--- a/client/project.cpp
+++ b/client/project.cpp
@@ -49,7 +49,6 @@ void PROJECT::init() {
         no_rsc_config[i] = false;
         no_rsc_apps[i] = false;
         no_rsc_ams[i] = false;
-        rsc_defer_sched[i] = false;
     }
     strcpy(host_venue, "");
     using_venue_specific_prefs = false;
diff --git a/client/project.h b/client/project.h
index b8d1d93..16de165 100644
--- a/client/project.h
+++ b/client/project.h
@@ -71,12 +71,6 @@ struct PROJECT : PROJ_AM {
     //
     bool no_rsc_ams[MAX_RSC];
 
-    // the following set dynamically
-    //
-    bool rsc_defer_sched[MAX_RSC];
-        // This project has a GPU job for which there's insuff. video RAM.
-        // Don't fetch more jobs of this type; they might have same problem
-
     char host_venue[256];
         // logically, this belongs in the client state file
         // rather than the account file.
diff --git a/client/result.h b/client/result.h
index aa39d88..685a36c 100644
--- a/client/result.h
+++ b/client/result.h
@@ -185,7 +185,7 @@ struct RESULT {
         // textual description of resources used
     double schedule_backoff;
         // don't try to schedule until this time
-        // (wait for free GPU RAM)
+        // (because the app called boinc_temporary_exit())
     char schedule_backoff_reason[256];
 };
 
diff --git a/client/sandbox.cpp b/client/sandbox.cpp
index df215e8..5a31917 100644
--- a/client/sandbox.cpp
+++ b/client/sandbox.cpp
@@ -226,7 +226,7 @@ int set_to_project_group(const char* path) {
 int get_project_gid() {
     return 0;
 }
-int set_to_project_group(const char* path) {
+int set_to_project_group(const char*) {
     return 0;
 }
 #endif // ! _WIN32
diff --git a/client/scheduler_op.cpp b/client/scheduler_op.cpp
index edc39fc..5ad82ec 100644
--- a/client/scheduler_op.cpp
+++ b/client/scheduler_op.cpp
@@ -247,9 +247,9 @@ int SCHEDULER_OP::start_rpc(PROJECT* p) {
         if (strlen(buf)) {
             msg_printf(p, MSG_INFO, "Requesting new tasks for %s", buf);
         } else {
-            if (p->pwf.cant_fetch_work_reason) {
+            if (p->pwf.project_reason) {
                 msg_printf(p, MSG_INFO,
-                    "Not requesting tasks: %s", cant_fetch_work_string(p, buf)
+                    "Not requesting tasks: %s", project_reason_string(p, buf)
                 );
             } else {
                 msg_printf(p, MSG_INFO, "Not requesting tasks");
diff --git a/client/sim.cpp b/client/sim.cpp
index 1056a64..aad116a 100644
--- a/client/sim.cpp
+++ b/client/sim.cpp
@@ -1454,6 +1454,7 @@ void do_client_simulation() {
             }
         }
     }
+    set_no_rsc_config();
     process_gpu_exclusions();
 
     get_app_params();
diff --git a/client/work_fetch.cpp b/client/work_fetch.cpp
index 1fc6078..5d7c33b 100644
--- a/client/work_fetch.cpp
+++ b/client/work_fetch.cpp
@@ -1,6 +1,6 @@
 // This file is part of BOINC.
 // http://boinc.berkeley.edu
-// Copyright (C) 2008 University of California
+// Copyright (C) 2014 University of California
 //
 // BOINC is free software; you can redistribute it and/or modify it
 // under the terms of the GNU Lesser General Public License
@@ -46,49 +46,6 @@ using std::vector;
 RSC_WORK_FETCH rsc_work_fetch[MAX_RSC];
 WORK_FETCH work_fetch;
 
-static inline int dont_fetch(PROJECT* p, int rsc_type) {
-    if (p->no_rsc_pref[rsc_type]) return DONT_FETCH_PREFS;
-    if (p->no_rsc_config[rsc_type]) return DONT_FETCH_CONFIG;
-    if (p->no_rsc_apps[rsc_type]) return DONT_FETCH_NO_APPS;
-    if (p->no_rsc_ams[rsc_type]) return DONT_FETCH_AMS;
-    return 0;
-}
-
-// if the configuration file disallows the use of a GPU type
-// for a project, set a flag to that effect
-//
-void set_no_rsc_config() {
-    for (unsigned int i=0; i<gstate.projects.size(); i++) {
-        PROJECT& p = *gstate.projects[i];
-        for (int j=1; j<coprocs.n_rsc; j++) {
-            bool allowed[MAX_COPROC_INSTANCES];
-            memset(allowed, 0, sizeof(allowed));
-            COPROC& c = coprocs.coprocs[j];
-            for (int k=0; k<c.count; k++) {
-                allowed[c.device_nums[k]] = true;
-            }
-            for (unsigned int k=0; k<cc_config.exclude_gpus.size(); k++) {
-                EXCLUDE_GPU& e = cc_config.exclude_gpus[k];
-                if (strcmp(e.url.c_str(), p.master_url)) continue;
-                if (!e.type.empty() && strcmp(e.type.c_str(), c.type)) continue;
-                if (!e.appname.empty()) continue;
-                if (e.device_num < 0) {
-                    memset(allowed, 0, sizeof(allowed));
-                    break;
-                }
-                allowed[e.device_num] = false;
-            }
-            p.no_rsc_config[j] = true;
-            for (int k=0; k<c.count; k++) {
-                if (allowed[c.device_nums[k]]) {
-                    p.no_rsc_config[j] = false;
-                    break;
-                }
-            }
-        }
-    }
-}
-
 // does the (NCI) project have a job that's running or uploading?
 // (don't request another job from NCI project if so)
 //
@@ -115,14 +72,8 @@ inline bool has_coproc_app(PROJECT* p, int rsc_type) {
 
 ///////////////  RSC_PROJECT_WORK_FETCH  ///////////////
 
-bool RSC_PROJECT_WORK_FETCH::compute_may_have_work(PROJECT* p, int rsc_type) {
-    if (dont_fetch(p, rsc_type)) return false;
-    if (p->rsc_defer_sched[rsc_type]) return false;
-    return (backoff_time < gstate.now);
-}
-
 void RSC_PROJECT_WORK_FETCH::rr_init(PROJECT* p, int rsc_type) {
-    may_have_work = compute_may_have_work(p, rsc_type);
+    rsc_project_reason = compute_rsc_project_reason(p, rsc_type);
     fetchable_share = 0;
     n_runnable_jobs = 0;
     sim_nused = 0;
@@ -146,6 +97,66 @@ void RSC_PROJECT_WORK_FETCH::resource_backoff(PROJECT* p, const char* name) {
     }
 }
 
+// checks for whether we should ask this project for work of this type.
+// check for backoff must go last, so that if that's the reason
+// we know that there are no other reasons (for piggyback)
+//
+int RSC_PROJECT_WORK_FETCH::compute_rsc_project_reason(
+    PROJECT *p, int rsc_type
+) {
+    RSC_WORK_FETCH& rwf = rsc_work_fetch[rsc_type];
+    // see whether work fetch for this resource is banned
+    // by prefs, config, project, or acct mgr
+    //
+    if (p->no_rsc_pref[rsc_type]) return DONT_FETCH_PREFS;
+    if (p->no_rsc_config[rsc_type]) return DONT_FETCH_CONFIG;
+    if (p->no_rsc_apps[rsc_type]) return DONT_FETCH_NO_APPS;
+    if (p->no_rsc_ams[rsc_type]) return DONT_FETCH_AMS;
+    if (p->rsc_pwf[rsc_type].has_deferred_job) return DONT_FETCH_DEFER_SCHED;
+
+    // if project has zero resource share,
+    // only fetch work if a device is idle
+    //
+    if (p->resource_share == 0 && rwf.nidle_now == 0 && rwf.sim_excluded_instances==0) {
+        return DONT_FETCH_ZERO_SHARE;
+    }
+
+    // if project has excluded GPUs of this type,
+    // we need to avoid fetching work just because there's an idle instance
+    // or a shortfall;
+    // fetching work might not alleviate either of these,
+    // and we'd end up fetching unbounded work.
+    // At the same time, we want to respect work buf params if possible.
+    //
+    // Current policy:
+    // don't fetch work if remaining time of this project's jobs
+    // exceeds work_buf_min * (#usable instances / #instances)
+    //
+    // TODO: THIS IS FAIRLY CRUDE. Making it smarter would require
+    // computing shortfall etc. on a per-project basis
+    //
+    int nexcl = ncoprocs_excluded;
+    if (rsc_type && nexcl) {
+        int n_not_excluded = rwf.ninstances - nexcl;
+        if (n_runnable_jobs >= n_not_excluded
+            && queue_est > (gstate.work_buf_min() * n_not_excluded)/rwf.ninstances
+        ) {
+            return DONT_FETCH_BUFFER_FULL;
+        }
+    }
+
+    if (anonymous_platform_no_apps) {
+        return DONT_FETCH_NO_APPS;
+    }
+
+    // this must go last
+    //
+    if (backoff_time > gstate.now) {
+        return DONT_FETCH_BACKED_OFF;
+    }
+    return 0;
+}
+
 ///////////////  RSC_WORK_FETCH  ///////////////
 
 void RSC_WORK_FETCH::copy_request(COPROC& c) {
@@ -278,34 +289,27 @@ void RSC_WORK_FETCH::print_state(const char* name) {
         shortfall, nidle_now, saturated_time,
         busy_time_estimator.get_busy_time()
     );
-    msg_printf(0, MSG_INFO, "[work_fetch] sim used inst %d sim excl inst %d",
-        sim_used_instances, sim_excluded_instances
-    );
+//    msg_printf(0, MSG_INFO, "[work_fetch] sim used inst %d sim excl inst %d",
+//        sim_used_instances, sim_excluded_instances
+//    );
     for (unsigned int i=0; i<gstate.projects.size(); i++) {
         char buf[256];
         PROJECT* p = gstate.projects[i];
         if (p->non_cpu_intensive) continue;
-        RSC_PROJECT_WORK_FETCH& pwf = project_state(p);
-        bool no_rsc_pref = p->no_rsc_pref[rsc_type];
-        bool no_rsc_config = p->no_rsc_config[rsc_type];
-        bool no_rsc_apps = p->no_rsc_apps[rsc_type];
-        bool no_rsc_ams = p->no_rsc_ams[rsc_type];
-        double bt = pwf.backoff_time>gstate.now?pwf.backoff_time-gstate.now:0;
+        RSC_PROJECT_WORK_FETCH& rpwf = project_state(p);
+        double bt = rpwf.backoff_time>gstate.now?rpwf.backoff_time-gstate.now:0;
         if (bt) {
             sprintf(buf, " (resource backoff: %.2f, inc %.2f)",
-                bt, pwf.backoff_interval
+                bt, rpwf.backoff_interval
             );
         } else {
             strcpy(buf, "");
         }
         msg_printf(p, MSG_INFO,
-            "[work_fetch] fetch share %.3f%s%s%s%s%s",
-            pwf.fetchable_share,
-            buf,
-            no_rsc_pref?" (blocked by prefs)":"",
-            no_rsc_apps?" (no apps)":"",
-            no_rsc_ams?" (blocked by account manager)":"",
-            no_rsc_config?" (blocked by configuration file)":""
+            "[work_fetch] share %.3f %s %s",
+            rpwf.fetchable_share,
+            rsc_project_reason_string(rpwf.rsc_project_reason),
+            buf
         );
     }
 }
@@ -317,7 +321,7 @@ void RSC_WORK_FETCH::clear_request() {
 
 ///////////////  PROJECT_WORK_FETCH  ///////////////
 
-int PROJECT_WORK_FETCH::compute_cant_fetch_work_reason(PROJECT* p) {
+int PROJECT_WORK_FETCH::compute_project_reason(PROJECT* p) {
     if (p->non_cpu_intensive) return CANT_FETCH_WORK_NON_CPU_INTENSIVE;
     if (p->suspended_via_gui) return CANT_FETCH_WORK_SUSPENDED_VIA_GUI;
     if (p->master_url_fetch_pending) return CANT_FETCH_WORK_MASTER_URL_FETCH_PENDING;
@@ -326,7 +330,8 @@ int PROJECT_WORK_FETCH::compute_cant_fetch_work_reason(PROJECT* p) {
     if (p->some_result_suspended()) return CANT_FETCH_WORK_RESULT_SUSPENDED;
     if (p->too_many_uploading_results) return CANT_FETCH_WORK_TOO_MANY_UPLOADS;
 
-    // this goes last
+    // this goes last, so that if this is the reason we know
+    // that there are no other reasons
     //
     if (p->min_rpc_time > gstate.now) return CANT_FETCH_WORK_MIN_RPC_TIME;
     return 0;
@@ -338,97 +343,64 @@ void PROJECT_WORK_FETCH::reset(PROJECT* p) {
     }
 }
 
-///////////////  WORK_FETCH  ///////////////
+void PROJECT_WORK_FETCH::rr_init(PROJECT* p) {
+    project_reason = compute_project_reason(p);
+    n_runnable_jobs = 0;
+}
 
-// mark the projects from which we can fetch work
-//
-void WORK_FETCH::compute_cant_fetch_work_reason() {
-    for (unsigned int i=0; i<gstate.projects.size(); i++) {
-        PROJECT* p = gstate.projects[i];
-        p->pwf.cant_fetch_work_reason = p->pwf.compute_cant_fetch_work_reason(p);
+void PROJECT_WORK_FETCH::print_state(PROJECT* p) {
+    char buf[1024], buf2[1024];
+    if (project_reason) {
+        sprintf(buf, "can't request work: %s", project_reason_string(p, buf2));
+    } else {
+        strcpy(buf, "can request work");
     }
+    if (p->min_rpc_time > gstate.now) {
+        sprintf(buf2, " (%.2f sec)", p->min_rpc_time - gstate.now);
+        strcat(buf, buf2);
+    }
+    msg_printf(p, MSG_INFO, "[work_fetch] REC %.3f prio %.3f %s",
+        rec,
+        p->sched_priority,
+        buf
+    );
 }
 
+///////////////  WORK_FETCH  ///////////////
+
 void WORK_FETCH::rr_init() {
-    for (int i=0; i<coprocs.n_rsc; i++) {
-        rsc_work_fetch[i].rr_init();
-    }
-    compute_cant_fetch_work_reason();
+    // compute PROJECT::RSC_PROJECT_WORK_FETCH::has_deferred_job
+    //
     for (unsigned int i=0; i<gstate.projects.size(); i++) {
         PROJECT* p = gstate.projects[i];
-        p->pwf.n_runnable_jobs = 0;
         for (int j=0; j<coprocs.n_rsc; j++) {
-            p->rsc_pwf[j].rr_init(p, j);
+            p->rsc_pwf[j].has_deferred_job = false;
         }
     }
-}
-
-#if 0
-// if the given project is highest-priority among the projects
-// eligible for the resource, set request fields
-//
-void RSC_WORK_FETCH::supplement(PROJECT* pp) {
-    double x = pp->sched_priority;
-    for (unsigned i=0; i<gstate.projects.size(); i++) {
-        PROJECT* p = gstate.projects[i];
-        if (p == pp) continue;
-        if (p->pwf.cant_fetch_work_reason) continue;
-        if (!project_state(p).may_have_work) continue;
-        RSC_PROJECT_WORK_FETCH& rpwf = project_state(p);
-        if (rpwf.anon_skip) continue;
-        if (p->sched_priority > x) {
-            if (log_flags.work_fetch_debug) {
-                msg_printf(pp, MSG_INFO,
-                    "[work_fetch]: not requesting work for %s: %s has higher priority",
-                    rsc_name_long(rsc_type), p->get_project_name()
-                );
+    for (unsigned int i=0; i<gstate.results.size(); i++) {
+        RESULT* rp = gstate.results[i];
+        if (rp->schedule_backoff) {
+            if (rp->schedule_backoff > gstate.now) {
+                int rt = rp->avp->gpu_usage.rsc_type;
+                rp->project->rsc_pwf[rt].has_deferred_job = true;
+            } else {
+                rp->schedule_backoff = 0;
+                gstate.request_schedule_cpus("schedule backoff finished");
             }
-            return;
         }
     }
-    // didn't find a better project; ask for work
-    //
-    set_request(pp);
-}
 
-// we're going to ask the given project for work of the given type.
-// (or -1 if none)
-// Set requests for this type and perhaps other types
-//
-void WORK_FETCH::set_all_requests_hyst(PROJECT* p, int rsc_type) {
     for (int i=0; i<coprocs.n_rsc; i++) {
-        if (i == rsc_type) {
-            rsc_work_fetch[i].set_request(p);
-        } else {
-            // don't fetch work for a resource if the buffer is above max
-            //
-            if (rsc_work_fetch[i].saturated_time > gstate.work_buf_total()) {
-                continue;
-            }
-            
-            // don't fetch work if backup project and no idle instances
-            //
-            if (p->resource_share==0 && rsc_work_fetch[i].nidle_now==0) {
-                continue;
-            }
-
-            if (i>0 && !gpus_usable) {
-                continue;
-            }
-            rsc_work_fetch[i].supplement(p);
-        }
+        rsc_work_fetch[i].rr_init();
     }
-}
-
-void WORK_FETCH::set_all_requests(PROJECT* p) {
-    for (int i=0; i<coprocs.n_rsc; i++) {
-        if (i==0 || gpus_usable) {
-            rsc_work_fetch[i].set_request(p);
+    for (unsigned int i=0; i<gstate.projects.size(); i++) {
+        PROJECT* p = gstate.projects[i];
+        p->pwf.rr_init(p);
+        for (int j=0; j<coprocs.n_rsc; j++) {
+            p->rsc_pwf[j].rr_init(p, j);
         }
     }
 }
-#endif
-
 // copy request fields from RSC_WORK_FETCH to COPROCS
 //
 void WORK_FETCH::copy_requests() {
@@ -458,21 +430,7 @@ void WORK_FETCH::print_state() {
     msg_printf(0, MSG_INFO, "[work_fetch] --- project states ---");
     for (unsigned int i=0; i<gstate.projects.size(); i++) {
         PROJECT* p = gstate.projects[i];
-        char buf[1024], buf2[1024];
-        if (p->pwf.cant_fetch_work_reason) {
-            sprintf(buf, "can't req work: %s", cant_fetch_work_string(p, buf2));
-        } else {
-            strcpy(buf, "can req work");
-        }
-        if (p->min_rpc_time > gstate.now) {
-            sprintf(buf2, " (backoff: %.2f sec)", p->min_rpc_time - gstate.now);
-            strcat(buf, buf2);
-        }
-        msg_printf(p, MSG_INFO, "[work_fetch] REC %.3f prio %.6f %s",
-            p->pwf.rec,
-            p->sched_priority,
-            buf
-        );
+        p->pwf.print_state(p);
     }
     for (int i=0; i<coprocs.n_rsc; i++) {
         rsc_work_fetch[i].print_state(rsc_name_long(i));
@@ -510,7 +468,7 @@ void WORK_FETCH::piggyback_work_request(PROJECT* p) {
 
     setup();
 
-    switch (p->pwf.cant_fetch_work_reason) {
+    switch (p->pwf.project_reason) {
     case 0:
     case CANT_FETCH_WORK_MIN_RPC_TIME:
         break;
@@ -539,8 +497,12 @@ void WORK_FETCH::piggyback_work_request(PROJECT* p) {
             rwf.dont_fetch_reason = DONT_FETCH_GPUS_NOT_USABLE;
             continue;
         }
-        rwf.dont_fetch_reason = rwf.cant_fetch(p);
-        if (rwf.dont_fetch_reason) {
+        RSC_PROJECT_WORK_FETCH& rpwf = rwf.project_state(p);
+        switch (rpwf.rsc_project_reason) {
+        case 0:
+        case DONT_FETCH_BACKED_OFF:
+            break;
+        default:
             WF_DEBUG(msg_printf(p, MSG_INFO, "piggyback: can't fetch %s", rsc_name_long(i));)
             continue;
         }
@@ -559,12 +521,13 @@ void WORK_FETCH::piggyback_work_request(PROJECT* p) {
             for (unsigned int j=0; j<gstate.projects.size(); j++) {
                 p2 = gstate.projects[j];
                 if (p2 == p) break;
-				if (p2->sched_priority == p->sched_priority) continue;
-                if (p2->pwf.cant_fetch_work_reason) {
+                if (p2->sched_priority == p->sched_priority) continue;
+                if (p2->pwf.project_reason) {
                     WF_DEBUG(msg_printf(p, MSG_INFO, "piggyback: %s can't fetch work", p2->project_name);)
                     continue;
                 }
-                if (!rwf.cant_fetch(p2) && !rwf.backed_off(p2)) {
+                RSC_PROJECT_WORK_FETCH& rpwf2 = rwf.project_state(p2);
+                if (!rpwf2.rsc_project_reason) {
                     WF_DEBUG(msg_printf(p, MSG_INFO, "piggyback: better proj %s", p2->project_name);)
                     break;
                 }
@@ -582,7 +545,7 @@ void WORK_FETCH::piggyback_work_request(PROJECT* p) {
         }
     }
     if (!requested_work()) {
-        p->pwf.cant_fetch_work_reason = CANT_FETCH_WORK_DONT_NEED;
+        p->pwf.project_reason = CANT_FETCH_WORK_DONT_NEED;
     }
 }
 
@@ -606,71 +569,6 @@ static bool higher_priority(PROJECT *p1, PROJECT *p2) {
     return (p1->sched_priority > p2->sched_priority);
 }
 
-// check resource-level backoff
-//
-bool RSC_WORK_FETCH::backed_off(PROJECT* p) {
-    if (project_state(p).backoff_time > gstate.now) {
-        WF_DEBUG(msg_printf(p, MSG_INFO, "skip: backoff");)
-        return true;
-    }
-    return false;
-}
-
-// a variety of checks for whether we should ask this project
-// for work of this type
-//
-int RSC_WORK_FETCH::cant_fetch(PROJECT *p) {
-    // see whether work fetch for this resource is banned
-    // by prefs, config, project, or acct mgr
-    //
-    int reason = dont_fetch(p, rsc_type);
-    if (reason) {
-        WF_DEBUG(msg_printf(p, MSG_INFO, "skip: dont_fetch");)
-        return reason;
-    }
-
-    RSC_PROJECT_WORK_FETCH& rpwf = project_state(p);
-
-    // if project has zero resource share,
-    // only fetch work if a device is idle
-    //
-    if (p->resource_share == 0 && nidle_now == 0 && sim_excluded_instances==0) {
-        WF_DEBUG(msg_printf(p, MSG_INFO, "skip: zero share");)
-        return DONT_FETCH_ZERO_SHARE;
-    }
-
-    // if project has excluded GPUs of this type,
-    // we need to avoid fetching work just because there's an idle instance
-    // or a shortfall;
-    // fetching work might not alleviate either of these,
-    // and we'd end up fetching unbounded work.
-    // At the same time, we want to respect work buf params if possible.
-    //
-    // Current policy:
-    // don't fetch work if remaining time of this project's jobs
-    // exceeds work_buf_min * (#usable instances / #instances)
-    //
-    // TODO: THIS IS FAIRLY CRUDE. Making it smarter would require
-    // computing shortfall etc. on a per-project basis
-    //
-    int nexcl = rpwf.ncoprocs_excluded;
-    if (rsc_type && nexcl) {
-        int n_not_excluded = ninstances - nexcl;
-        if (rpwf.n_runnable_jobs >= n_not_excluded
-            && rpwf.queue_est > (gstate.work_buf_min() * n_not_excluded)/ninstances
-        ) {
-            WF_DEBUG(msg_printf(p, MSG_INFO, "skip: too much work");)
-            return DONT_FETCH_BUFFER_FULL;
-        }
-    }
-
-    if (rpwf.anon_skip) {
-        WF_DEBUG(msg_printf(p, MSG_INFO, "skip: anon");)
-        return DONT_FETCH_NO_APPS;
-    }
-    return 0;
-}
-
 // return true if there is exclusion starvation
 // and this project can use the starved instances
 //
@@ -712,8 +610,8 @@ void WORK_FETCH::setup() {
     int job_limit = 1000;
     for (unsigned int i=0; i<gstate.projects.size(); i++) {
         PROJECT* p = gstate.projects[i];
-        if (p->pwf.n_runnable_jobs > job_limit && !p->pwf.cant_fetch_work_reason) {
-            p->pwf.cant_fetch_work_reason = CANT_FETCH_WORK_TOO_MANY_RUNNABLE;
+        if (p->pwf.n_runnable_jobs > job_limit && !p->pwf.project_reason) {
+            p->pwf.project_reason = CANT_FETCH_WORK_TOO_MANY_RUNNABLE;
         }
     }
 
@@ -735,10 +633,6 @@ void WORK_FETCH::setup() {
 PROJECT* WORK_FETCH::choose_project() {
     PROJECT* p;
 
-    if (log_flags.work_fetch_debug) {
-        msg_printf(0, MSG_INFO, "[work_fetch] entering choose_project()");
-    }
-
     p = non_cpu_intensive_project_needing_work();
     if (p) return p;
 
@@ -754,7 +648,7 @@ PROJECT* WORK_FETCH::choose_project() {
     for (unsigned int j=0; j<gstate.projects.size(); j++) {
         p = gstate.projects[j];
         WF_DEBUG(msg_printf(p, MSG_INFO, "scanning");)
-        if (p->pwf.cant_fetch_work_reason) {
+        if (p->pwf.project_reason) {
             WF_DEBUG(msg_printf(p, MSG_INFO, "skip: cfwr %d", p->pwf.cant_fetch_work_reason);)
             continue;
         }
@@ -770,7 +664,8 @@ PROJECT* WORK_FETCH::choose_project() {
         for (int i=0; i<coprocs.n_rsc; i++) {
             if (i && !gpus_usable) continue;
             RSC_WORK_FETCH& rwf = rsc_work_fetch[i];
-            if (!rwf.cant_fetch(p) && !rwf.backed_off(p)) {
+            RSC_PROJECT_WORK_FETCH& rpwf = rwf.project_state(p);
+            if (!rpwf.rsc_project_reason) {
                 if (!rwf.found_project) {
                     rwf.found_project = p;
                 }
@@ -821,7 +716,8 @@ PROJECT* WORK_FETCH::choose_project() {
                         WF_DEBUG(msg_printf(p, MSG_INFO, "%s don't need", rsc_name_long(i));)
                         continue;
                     }
-                    int reason = rwf.cant_fetch(p);
+                    RSC_PROJECT_WORK_FETCH& rpwf = rwf.project_state(p);
+                    int reason = rpwf.rsc_project_reason;
                     if (reason) {
                         WF_DEBUG(msg_printf(p, MSG_INFO, "%s can't fetch", rsc_name_long(i));)
                         continue;
@@ -879,9 +775,9 @@ void WORK_FETCH::compute_shares() {
     for (i=0; i<gstate.projects.size(); i++) {
         p = gstate.projects[i];
         if (p->non_cpu_intensive) continue;
-        if (p->pwf.cant_fetch_work_reason) continue;
+        if (p->pwf.project_reason) continue;
         for (int j=0; j<coprocs.n_rsc; j++) {
-            if (p->rsc_pwf[j].may_have_work) {
+            if (!p->rsc_pwf[j].rsc_project_reason) {
                 rsc_work_fetch[j].total_fetchable_share += p->resource_share;
             }
         }
@@ -889,9 +785,9 @@ void WORK_FETCH::compute_shares() {
     for (i=0; i<gstate.projects.size(); i++) {
         p = gstate.projects[i];
         if (p->non_cpu_intensive) continue;
-        if (p->pwf.cant_fetch_work_reason) continue;
+        if (p->pwf.project_reason) continue;
         for (int j=0; j<coprocs.n_rsc; j++) {
-            if (p->rsc_pwf[j].may_have_work) {
+            if (!p->rsc_pwf[j].rsc_project_reason) {
                 p->rsc_pwf[j].fetchable_share = rsc_work_fetch[j].total_fetchable_share?p->resource_share/rsc_work_fetch[j].total_fetchable_share:1;
             }
         }
@@ -1030,12 +926,12 @@ void WORK_FETCH::init() {
         PROJECT* p = gstate.projects[i];
         if (!p->anonymous_platform) continue;
         for (int k=0; k<coprocs.n_rsc; k++) {
-            p->rsc_pwf[k].anon_skip = true;
+            p->rsc_pwf[k].anonymous_platform_no_apps = true;
         }
         for (j=0; j<gstate.app_versions.size(); j++) {
             APP_VERSION* avp = gstate.app_versions[j];
             if (avp->project != p) continue;
-            p->rsc_pwf[avp->gpu_usage.rsc_type].anon_skip = false;
+            p->rsc_pwf[avp->gpu_usage.rsc_type].anonymous_platform_no_apps = false;
         }
     }
 }
@@ -1176,8 +1072,9 @@ void CLIENT_STATE::generate_new_host_cpid() {
     }
 }
 
-inline const char* dont_fetch_string(int reason) {
+const char* rsc_project_reason_string(int reason) {
     switch (reason) {
+    case 0: return "";
     case DONT_FETCH_GPUS_NOT_USABLE: return "GPUs not usable";
     case DONT_FETCH_PREFS: return "blocked by project preferences";
     case DONT_FETCH_CONFIG: return "client configuration";
@@ -1187,12 +1084,15 @@ inline const char* dont_fetch_string(int reason) {
     case DONT_FETCH_ZERO_SHARE: return "zero resource share";
     case DONT_FETCH_BUFFER_FULL: return "job cache full";
     case DONT_FETCH_NOT_HIGHEST_PRIO: return "not highest priority project";
+    case DONT_FETCH_BACKED_OFF: return "project is backed off";
+    case DONT_FETCH_DEFER_SCHED: return "a job is deferred";
     }
-    return "";
+    return "unknown project reason";
 }
 
-const char* cant_fetch_work_string(PROJECT* p, char* buf) {
-    switch (p->pwf.cant_fetch_work_reason) {
+const char* project_reason_string(PROJECT* p, char* buf) {
+    switch (p->pwf.project_reason) {
+    case 0: return "";
     case CANT_FETCH_WORK_NON_CPU_INTENSIVE:
         return "non CPU intensive";
     case CANT_FETCH_WORK_SUSPENDED_VIA_GUI:
@@ -1216,7 +1116,7 @@ const char* cant_fetch_work_string(PROJECT* p, char* buf) {
     case CANT_FETCH_WORK_DONT_NEED:
         if (coprocs.n_rsc == 1) {
             sprintf(buf, "don't need (%s)",
-                dont_fetch_string(rsc_work_fetch[0].dont_fetch_reason)
+                rsc_project_reason_string(rsc_work_fetch[0].dont_fetch_reason)
             );
         } else {
             string x;
@@ -1225,7 +1125,7 @@ const char* cant_fetch_work_string(PROJECT* p, char* buf) {
                 char buf2[256];
                 sprintf(buf2, "%s: %s",
                     rsc_name_long(i),
-                    dont_fetch_string(rsc_work_fetch[i].dont_fetch_reason)
+                    rsc_project_reason_string(rsc_work_fetch[i].dont_fetch_reason)
                 );
                 x += buf2;
                 if (i < coprocs.n_rsc-1) {
@@ -1237,5 +1137,5 @@ const char* cant_fetch_work_string(PROJECT* p, char* buf) {
         }
         return buf;
     }
-    return "";
+    return "unknown reason";
 }
diff --git a/client/work_fetch.h b/client/work_fetch.h
index a2d0e23..c99e9ba 100644
--- a/client/work_fetch.h
+++ b/client/work_fetch.h
@@ -27,7 +27,7 @@
 #define RSC_TYPE_ANY    -1
 #define RSC_TYPE_CPU    0
 
-// reasons for not fetching work
+// reasons for not fetching work from a project
 //
 #define CANT_FETCH_WORK_NON_CPU_INTENSIVE           1
 #define CANT_FETCH_WORK_SUSPENDED_VIA_GUI           2
@@ -52,6 +52,8 @@
 #define DONT_FETCH_ZERO_SHARE                       7
 #define DONT_FETCH_BUFFER_FULL                      8
 #define DONT_FETCH_NOT_HIGHEST_PRIO                 9
+#define DONT_FETCH_BACKED_OFF                       10
+#define DONT_FETCH_DEFER_SCHED                      11
 
 struct PROJECT;
 struct RESULT;
@@ -74,7 +76,7 @@ struct RSC_PROJECT_WORK_FETCH {
     }
     double queue_est;
         // an estimate of instance-secs of queued work;
-    bool anon_skip;
+    bool anonymous_platform_no_apps;
         // set if this project is anonymous platform
         // and it has no app version that uses this resource
     double fetchable_share;
@@ -95,13 +97,17 @@ struct RSC_PROJECT_WORK_FETCH {
         // copy of the above used during schedule_cpus()
     std::deque<RESULT*> pending;
     std::deque<RESULT*>::iterator pending_iter;
+    bool has_deferred_job;
+        // This project has a coproc job of the given type for which
+        // the job is deferred because of a temporary_exit() call.
+        // Don't fetch more jobs of this type; they might have same problem
 
     RSC_PROJECT_WORK_FETCH() {
         backoff_time = 0;
         backoff_interval = 0;
         secs_this_rec_interval = 0;
         queue_est = 0;
-        anon_skip = false;
+        anonymous_platform_no_apps = false;
         fetchable_share = 0;
         n_runnable_jobs = 0;
         sim_nused = 0;
@@ -110,6 +116,7 @@ struct RSC_PROJECT_WORK_FETCH {
         non_excluded_instances = 0;
         deadlines_missed = 0;
         deadlines_missed_copy = 0;
+        has_deferred_job = false;
     }
 
     inline void reset() {
@@ -117,8 +124,8 @@ struct RSC_PROJECT_WORK_FETCH {
         backoff_interval = 0;
     }
 
-    bool may_have_work;
-    bool compute_may_have_work(PROJECT*, int rsc_type);
+    int rsc_project_reason;
+    int compute_rsc_project_reason(PROJECT*, int rsc_type);
     void resource_backoff(PROJECT*, const char*);
     void rr_init(PROJECT*, int rsc_type);
     void clear_backoff() {
@@ -272,8 +279,8 @@ struct PROJECT_WORK_FETCH {
         // temporary copy used during schedule_cpus() and work fetch
     double rec_temp_save;
         // temporary used during RR simulation
-    int cant_fetch_work_reason;
-    int compute_cant_fetch_work_reason(PROJECT*);
+    int project_reason;
+    int compute_project_reason(PROJECT*);
     int n_runnable_jobs;
     bool request_if_idle_and_uploading;
         // Set when a job finishes.
@@ -283,6 +290,8 @@ struct PROJECT_WORK_FETCH {
         memset(this, 0, sizeof(*this));
     }
     void reset(PROJECT*);
+    void rr_init(PROJECT*);
+    void print_state(PROJECT*);
 };
 
 // global work fetch state
@@ -305,7 +314,6 @@ struct WORK_FETCH {
     void set_all_requests_hyst(PROJECT*, int rsc_type);
     void print_state();
     void init();
-    void compute_cant_fetch_work_reason();
     void rr_init();
     void clear_request();
     void compute_shares();
@@ -318,14 +326,13 @@ struct WORK_FETCH {
 extern RSC_WORK_FETCH rsc_work_fetch[MAX_RSC];
 extern WORK_FETCH work_fetch;
 
-extern void set_no_rsc_config();
-
 extern void project_priority_init(bool for_work_fetch);
 extern double project_priority(PROJECT*);
 extern void adjust_rec_sched(RESULT*);
 extern void adjust_rec_work_fetch(RESULT*);
 
 extern double total_peak_flops();
-extern const char* cant_fetch_work_string(PROJECT* p, char* buf);
+extern const char* project_reason_string(PROJECT* p, char* buf);
+extern const char* rsc_project_reason_string(int);
 
 #endif
diff --git a/clientgui/AccountInfoPage.cpp b/clientgui/AccountInfoPage.cpp
index 1a0ca51..39e2ece 100644
--- a/clientgui/AccountInfoPage.cpp
+++ b/clientgui/AccountInfoPage.cpp
@@ -492,9 +492,11 @@ void CAccountInfoPage::OnPageChanged( wxWizardExEvent& event ) {
             }
         }
 
-        m_pAccountEmailAddressCtrl->SetValidator(
-            CValidateEmailAddress(&m_strAccountEmailAddress)
-        );
+        if (!pc.ldap_auth) {
+            m_pAccountEmailAddressCtrl->SetValidator(
+                CValidateEmailAddress(&m_strAccountEmailAddress)
+            );
+        }
         m_pAccountUsernameCtrl->SetValidator(
             wxTextValidator(wxFILTER_NONE, &m_strAccountUsername)
         );
@@ -504,9 +506,15 @@ void CAccountInfoPage::OnPageChanged( wxWizardExEvent& event ) {
         m_pAccountUsernameStaticCtrl->Hide();
         m_pAccountUsernameCtrl->Hide();
 
-        m_pAccountEmailAddressStaticCtrl->SetLabel(
-            _("&Email address:")
-        );
+        if (pc.ldap_auth) {
+            m_pAccountEmailAddressStaticCtrl->SetLabel(
+                _("&Email address or LDAP ID:")
+            );
+        } else {
+            m_pAccountEmailAddressStaticCtrl->SetLabel(
+                _("&Email address:")
+            );
+        }
         m_pAccountEmailAddressCtrl->SetValue(m_strAccountEmailAddress);
     }
 
diff --git a/clientgui/AdvancedFrame.cpp b/clientgui/AdvancedFrame.cpp
index 9e8a976..f6bcded 100644
--- a/clientgui/AdvancedFrame.cpp
+++ b/clientgui/AdvancedFrame.cpp
@@ -616,7 +616,6 @@ bool CAdvancedFrame::CreateMenu() {
         _("Enable or disable various diagnostic messages")
     );
 
-
     // Help menu
     wxMenu *menuHelp = new wxMenu;
 
@@ -1542,6 +1541,37 @@ void CAdvancedFrame::OnLaunchNewInstance(wxCommandEvent& WXUNUSED(event)) {
 }
 
 
+void CAdvancedFrame::OnTest1ClickAttach(wxCommandEvent& WXUNUSED(event)) {
+    wxLogTrace(wxT("Function Start/End"), wxT("CAdvancedFrame::OnTest1ClickAttach - Function Begin"));
+
+    CMainDocument* pDoc = wxGetApp().GetDocument();
+
+    wxASSERT(pDoc);
+    wxASSERT(wxDynamicCast(pDoc, CMainDocument));
+
+    // Stop all timers so that the wizard is the only thing doing anything
+    StopTimers();
+
+    CWizardAttach* pWizard = new CWizardAttach(this);
+
+    pWizard->RunSimpleProjectAttach();
+
+    if (pWizard)
+        pWizard->Destroy();
+
+    DeleteMenu();
+    CreateMenu();
+    pDoc->ForceCacheUpdate();
+    FireRefreshView();
+    ResetReminderTimers();
+
+    // Restart timers to continue normal operations.
+    StartTimers();
+
+    wxLogTrace(wxT("Function Start/End"), wxT("CAdvancedFrame::OnTest1ClickAttach - Function End"));
+}
+
+
 void CAdvancedFrame::OnHelp(wxHelpEvent& event) {
     wxLogTrace(wxT("Function Start/End"), wxT("CAdvancedFrame::OnHelpBOINCManager - Function Begin"));
 
diff --git a/clientgui/AdvancedFrame.h b/clientgui/AdvancedFrame.h
index 393e0d7..67b48dd 100644
--- a/clientgui/AdvancedFrame.h
+++ b/clientgui/AdvancedFrame.h
@@ -80,6 +80,7 @@ public:
     void OnReadConfig( wxCommandEvent& event );
     void OnEventLog( wxCommandEvent& event );
     void OnLaunchNewInstance( wxCommandEvent& event );
+    void OnTest1ClickAttach( wxCommandEvent& event );
 
     void OnHelp( wxHelpEvent& event );
     void OnHelpBOINC( wxCommandEvent& event );
diff --git a/clientgui/CompletionPage.cpp b/clientgui/CompletionPage.cpp
index 354a6a5..48550b8 100644
--- a/clientgui/CompletionPage.cpp
+++ b/clientgui/CompletionPage.cpp
@@ -285,7 +285,7 @@ void CCompletionPage::OnPageChanged( wxWizardExEvent& event ) {
     
     // Is this supposed to be completely automated?
     // If so, then go ahead and close the wizard down now.
-    if (pWAP->close_when_completed) {
+    if (pWAP->m_bCloseWhenCompleted) {
         pWAP->SimulateNextButton();
     }
 }
diff --git a/clientgui/Events.h b/clientgui/Events.h
index 0e648f7..3927200 100644
--- a/clientgui/Events.h
+++ b/clientgui/Events.h
@@ -87,6 +87,7 @@
 #define ID_EVENTLOG                             6058
 #define ID_LAUNCHNEWINSTANCE                    6059
 #define ID_DIAGNOSTICLOGFLAGS                   6060
+#define ID_TEST1CLICKATTACH                     6061
 
 // Help Menu
 #define ID_HELPBOINC                            6035  // Locked: Used by manager_links.php
diff --git a/clientgui/MainDocument.cpp b/clientgui/MainDocument.cpp
index dc4bbe9..2c3845e 100644
--- a/clientgui/MainDocument.cpp
+++ b/clientgui/MainDocument.cpp
@@ -2518,11 +2518,7 @@ wxString result_description(RESULT* result, bool show_resources) {
             } else if (result->needs_shmem) {
                 strBuffer += _("Waiting for shared memory");
             } else if (result->scheduler_state == CPU_SCHED_SCHEDULED) {
-                if (result->edf_scheduled) {
-                    strBuffer += _("Running, high priority");
-                } else {
-                    strBuffer += _("Running");
-                }
+                strBuffer += _("Running");
                 if (project && project->non_cpu_intensive) {
                     strBuffer += _(" (non-CPU-intensive)");
                 }
@@ -2539,15 +2535,14 @@ wxString result_description(RESULT* result, bool show_resources) {
         }
         if (result->scheduler_wait) {
             if (strlen(result->scheduler_wait_reason)) {
-                strBuffer += _(" (Scheduler wait: ");
+                strBuffer = _("Postponed: ");
                 strBuffer += wxString(result->scheduler_wait_reason, wxConvUTF8);
-                strBuffer += _(")");
             } else {
-                strBuffer += _(" (Scheduler wait)");
+                strBuffer = _("Postponed");
             }
         }
         if (result->network_wait) {
-            strBuffer += _(" (Waiting for network access)");
+            strBuffer = _("Waiting for network access");
         }
         break;
     case RESULT_COMPUTE_ERROR:
diff --git a/clientgui/Makefile.am b/clientgui/Makefile.am
index 4c1f1f4..3fe999d 100644
--- a/clientgui/Makefile.am
+++ b/clientgui/Makefile.am
@@ -65,6 +65,7 @@ boincmgr_SOURCES = \
     ProjectInfoPage.cpp \
     ProjectProcessingPage.cpp \
     ProjectPropertiesPage.cpp \
+	ProjectWelcomePage.cpp \
     ProxyInfoPage.cpp \
     ProxyPage.cpp \
     sg_BoincSimpleFrame.cpp \
diff --git a/clientgui/ProjectInfoPage.cpp b/clientgui/ProjectInfoPage.cpp
index b8bab22..27bb9c5 100644
--- a/clientgui/ProjectInfoPage.cpp
+++ b/clientgui/ProjectInfoPage.cpp
@@ -218,12 +218,15 @@ void CProjectInfoPage::CreateControls()
     itemWizardPage23->SetSizer(itemBoxSizer24);
 
     m_pTitleStaticCtrl = new wxStaticText;
-    m_pTitleStaticCtrl->Create( itemWizardPage23, wxID_STATIC, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_pTitleStaticCtrl->Create( itemWizardPage23, wxID_STATIC, _("Choose a project"),
+                                wxDefaultPosition, wxDefaultSize, 0 );
     m_pTitleStaticCtrl->SetFont(wxFont(10, wxSWISS, wxNORMAL, wxBOLD, FALSE, _T("Verdana")));
     itemBoxSizer24->Add(m_pTitleStaticCtrl, 0, wxALIGN_LEFT|wxALL, 5);
 
     m_pDescriptionStaticCtrl = new wxStaticText;
-    m_pDescriptionStaticCtrl->Create( itemWizardPage23, wxID_STATIC, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_pDescriptionStaticCtrl->Create( itemWizardPage23, wxID_STATIC,
+                            _("To choose a project, click its name or type its URL below."),
+                            wxDefaultPosition, wxDefaultSize, 0 );
     itemBoxSizer24->Add(m_pDescriptionStaticCtrl, 0, wxALIGN_LEFT|wxALL, 5);
 
     itemBoxSizer24->Add(5, 5, 0, wxALIGN_LEFT|wxALL, 5);
@@ -239,7 +242,8 @@ void CProjectInfoPage::CreateControls()
     wxBoxSizer* itemBoxSizer7 = new wxBoxSizer(wxVERTICAL);
     itemFlexGridSizer6->Add(itemBoxSizer7, 0, wxALIGN_LEFT|wxALIGN_TOP, 0);
 
-    m_pProjectCategoriesStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_pProjectCategoriesStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC,
+                                        _("Categories:"), wxDefaultPosition, wxDefaultSize, 0 );
     itemBoxSizer7->Add(m_pProjectCategoriesStaticCtrl, 0, wxALIGN_LEFT|wxRIGHT|wxBOTTOM, 5);
 
     // We must populate the combo box before our sizers can calculate its width.
@@ -259,7 +263,8 @@ void CProjectInfoPage::CreateControls()
      );
     itemBoxSizer7->Add(m_pProjectCategoriesCtrl, 0, wxGROW|wxLEFT|wxRIGHT, 5);
 
-    m_pProjectsStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_pProjectsStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC,
+                                _("Projects:"), wxDefaultPosition, wxDefaultSize, 0 );
     itemBoxSizer7->Add(m_pProjectsStaticCtrl, 0, wxALIGN_LEFT|wxTOP|wxRIGHT|wxBOTTOM, 5);
 
     wxFlexGridSizer* itemFlexGridSizer11 = new wxFlexGridSizer(1, 0, 0);
@@ -271,7 +276,7 @@ void CProjectInfoPage::CreateControls()
     m_pProjectsCtrl = new wxListBox( itemWizardPage23, ID_PROJECTS, wxDefaultPosition, wxSize(-1, ADJUSTFORYDPI(175)), m_pProjectsCtrlStrings, wxLB_SINGLE|wxLB_SORT );
     itemFlexGridSizer11->Add(m_pProjectsCtrl, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 0);
 
-    m_pProjectDetailsStaticCtrl = new wxStaticBox(itemWizardPage23, wxID_ANY, wxT(""));
+    m_pProjectDetailsStaticCtrl = new wxStaticBox(itemWizardPage23, wxID_ANY, _("Project details"));
     wxStaticBoxSizer* itemStaticBoxSizer13 = new wxStaticBoxSizer(m_pProjectDetailsStaticCtrl, wxVERTICAL);
     itemFlexGridSizer6->Add(itemStaticBoxSizer13, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 
@@ -282,7 +287,8 @@ void CProjectInfoPage::CreateControls()
     itemFlexGridSizer16->AddGrowableCol(1);
     itemStaticBoxSizer13->Add(itemFlexGridSizer16, 0, wxGROW|wxALL, 0);
 
-    m_pProjectDetailsResearchAreaStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_pProjectDetailsResearchAreaStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC,
+                                                    _("Research area:"), wxDefaultPosition, wxDefaultSize, 0 );
     itemFlexGridSizer16->Add(m_pProjectDetailsResearchAreaStaticCtrl, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT|wxBOTTOM, 2);
 
     m_pProjectDetailsResearchAreaCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
@@ -292,7 +298,8 @@ void CProjectInfoPage::CreateControls()
     itemFlexGridSizer19->AddGrowableCol(1);
     itemStaticBoxSizer13->Add(itemFlexGridSizer19, 0, wxGROW|wxALL, 0);
 
-    m_pProjectDetailsOrganizationStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_pProjectDetailsOrganizationStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC,
+                                                    _("Organization:"), wxDefaultPosition, wxDefaultSize, 0 );
     itemFlexGridSizer19->Add(m_pProjectDetailsOrganizationStaticCtrl, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxRIGHT|wxBOTTOM, 2);
 
     m_pProjectDetailsOrganizationCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
@@ -302,7 +309,8 @@ void CProjectInfoPage::CreateControls()
     itemFlexGridSizer20->AddGrowableCol(1);
     itemStaticBoxSizer13->Add(itemFlexGridSizer20, 0, wxGROW|wxALL, 0);
 
-    m_pProjectDetailsURLStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_pProjectDetailsURLStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC,
+                                            _("Web site:"), wxDefaultPosition, wxDefaultSize, 0 );
     itemFlexGridSizer20->Add(m_pProjectDetailsURLStaticCtrl, 0, wxALIGN_LEFT|wxRIGHT|wxBOTTOM, 2);
 
     m_pProjectDetailsURLCtrl = new wxHyperlinkCtrl( itemWizardPage23, wxID_STATIC, wxT("BOINC"), wxT("http://boinc.berkeley.edu/"), wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxHL_CONTEXTMENU|wxHL_ALIGN_LEFT);
@@ -313,7 +321,8 @@ void CProjectInfoPage::CreateControls()
     itemFlexGridSizer24->AddGrowableCol(0);
     itemStaticBoxSizer13->Add(itemFlexGridSizer24, 0, wxGROW|wxALL, 0);
 
-    m_pProjectDetailsSupportedPlatformsStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_pProjectDetailsSupportedPlatformsStaticCtrl = new wxStaticText( itemWizardPage23, wxID_STATIC,
+                                                    _("Supported systems:"), wxDefaultPosition, wxDefaultSize, 0 );
     itemFlexGridSizer24->Add(m_pProjectDetailsSupportedPlatformsStaticCtrl, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxRIGHT|wxBOTTOM, 5);
 
     wxBoxSizer* itemBoxSizer26 = new wxBoxSizer(wxHORIZONTAL);
@@ -350,7 +359,8 @@ void CProjectInfoPage::CreateControls()
     itemFlexGridSizer33->AddGrowableCol(1);
     itemFlexGridSizer4->Add(itemFlexGridSizer33, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 0);
 
-    m_pProjectURLStaticCtrl = new wxStaticText( itemWizardPage23, ID_PROJECTURLSTATICCTRL, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
+    m_pProjectURLStaticCtrl = new wxStaticText( itemWizardPage23, ID_PROJECTURLSTATICCTRL,
+                                    _("Project URL:"), wxDefaultPosition, wxDefaultSize, 0 );
     itemFlexGridSizer33->Add(m_pProjectURLStaticCtrl, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
 
     m_pProjectURLCtrl = new wxTextCtrl( itemWizardPage23, ID_PROJECTURLCTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
@@ -622,47 +632,6 @@ void CProjectInfoPage::OnPageChanged( wxWizardExEvent& event ) {
     wxASSERT(m_pProjectURLStaticCtrl);
     wxASSERT(m_pProjectURLCtrl);
 
-    m_pTitleStaticCtrl->SetLabel(
-        _("Choose a project")
-    );
-
-    m_pDescriptionStaticCtrl->SetLabel(
-        _("To choose a project, click its name or type its URL below.")
-    );
-
-    m_pProjectCategoriesStaticCtrl->SetLabel(
-        _("Categories:")
-    );
-
-    m_pProjectsStaticCtrl->SetLabel(
-        _("Projects:")
-    );
-
-    m_pProjectDetailsStaticCtrl->SetLabel(
-        _("Project details")
-    );
-
-    m_pProjectDetailsResearchAreaStaticCtrl->SetLabel(
-        _("Research area:")
-    );
-
-    m_pProjectDetailsOrganizationStaticCtrl->SetLabel(
-        _("Organization:")
-    );
-
-    m_pProjectDetailsURLStaticCtrl->SetLabel(
-        _("Web site:")
-    );
-
-    m_pProjectDetailsSupportedPlatformsStaticCtrl->SetLabel(
-        _("Supported systems:")
-    );
-
-    m_pProjectURLStaticCtrl->SetLabel(
-        _("Project URL:")
-    );
-
-
     // Populate the ProjectInfo data structure with the list of projects we want to show and
     // any other activity we need to prep the page.
     if (!m_bProjectListPopulated) {
diff --git a/clientgui/ProjectWelcomePage.cpp b/clientgui/ProjectWelcomePage.cpp
new file mode 100644
index 0000000..24ae4ef
--- /dev/null
+++ b/clientgui/ProjectWelcomePage.cpp
@@ -0,0 +1,291 @@
+// This file is part of BOINC.
+// http://boinc.berkeley.edu
+// Copyright (C) 2008 University of California
+//
+// BOINC is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation,
+// either version 3 of the License, or (at your option) any later version.
+//
+// BOINC is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
+//
+#if defined(__GNUG__) && !defined(__APPLE__)
+#pragma implementation "ProjectWelcomePage.h"
+#endif
+
+#include "stdwx.h"
+#include "diagnostics.h"
+#include "util.h"
+#include "mfile.h"
+#include "miofile.h"
+#include "parse.h"
+#include "error_numbers.h"
+#include "wizardex.h"
+#include "error_numbers.h"
+#include "BOINCGUIApp.h"
+#include "SkinManager.h"
+#include "MainDocument.h"
+#include "BOINCBaseWizard.h"
+#include "WizardAttach.h"
+#include "ProjectWelcomePage.h"
+
+////@begin XPM images
+////@end XPM images
+
+
+/*!
+ * CProjectWelcomePage type definition
+ */
+ 
+IMPLEMENT_DYNAMIC_CLASS( CProjectWelcomePage, wxWizardPageEx )
+ 
+/*!
+ * CProjectWelcomePage event table definition
+ */
+ 
+BEGIN_EVENT_TABLE( CProjectWelcomePage, wxWizardPageEx )
+ 
+////@begin CProjectWelcomePage event table entries
+    EVT_WIZARDEX_PAGE_CHANGED( -1, CProjectWelcomePage::OnPageChanged )
+    EVT_WIZARDEX_CANCEL( -1, CProjectWelcomePage::OnCancel )
+////@end CProjectWelcomePage event table entries
+ 
+END_EVENT_TABLE()
+
+/*!
+ * CProjectWelcomePage constructors
+ */
+ 
+CProjectWelcomePage::CProjectWelcomePage( )
+{
+}
+ 
+CProjectWelcomePage::CProjectWelcomePage( CBOINCBaseWizard* parent )
+{
+    Create( parent );
+}
+ 
+/*!
+ * WizardPage creator
+ */
+ 
+bool CProjectWelcomePage::Create( CBOINCBaseWizard* parent )
+{
+////@begin CProjectWelcomePage member initialisation
+
+////@end CProjectWelcomePage member initialisation
+
+	((CWizardAttach*)parent)->IsFirstPass = false;
+ 
+////@begin CProjectWelcomePage creation
+    wxWizardPageEx::Create( parent, ID_PROJECTWELCOMEPAGE );
+
+    CreateControls();
+    GetSizer()->Fit(this);
+////@end CProjectWelcomePage creation
+
+	return TRUE;
+}
+ 
+/*!
+ * Control creation for WizardPage
+ */
+ 
+void CProjectWelcomePage::CreateControls()
+{    
+////@begin CWelcomePage content construction
+    CProjectWelcomePage* itemWizardPage2 = this;
+
+    wxBoxSizer* itemBoxSizer3 = new wxBoxSizer(wxVERTICAL);
+    itemWizardPage2->SetSizer(itemBoxSizer3);
+
+    title_ctrl = new wxStaticText;
+    title_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    title_ctrl->SetFont(wxFont(12, wxSWISS, wxNORMAL, wxBOLD, FALSE, _T("Verdana")));
+    itemBoxSizer3->Add(title_ctrl, 0, wxALIGN_LEFT|wxALL, 5);
+
+    intro_ctrl = new wxStaticText;
+    intro_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    itemBoxSizer3->Add(intro_ctrl, 0, wxALIGN_LEFT|wxALL, 5);
+
+    itemBoxSizer3->Add(5, 5, 0, wxALIGN_LEFT|wxALL, 5);
+
+    wxFlexGridSizer* grid = new wxFlexGridSizer(5, 2, 0, 0);
+    grid->AddGrowableCol(1);
+    grid->SetFlexibleDirection(wxBOTH);
+    itemBoxSizer3->Add(grid, 0, wxEXPAND|wxALL, 5);
+
+    project_name1_ctrl = new wxStaticText;
+    project_name1_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    grid->Add(project_name1_ctrl, 0, wxALIGN_LEFT|wxALL, 5);
+
+    project_name2_ctrl = new wxStaticText;
+    project_name2_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    grid->Add(project_name2_ctrl, 0, wxALIGN_LEFT|wxALL|wxEXPAND, 5);
+
+    project_inst1_ctrl = new wxStaticText;
+    project_inst1_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    grid->Add(project_inst1_ctrl, 0, wxALIGN_LEFT|wxALL, 5);
+
+    project_inst2_ctrl = new wxStaticText;
+    project_inst2_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    grid->Add(project_inst2_ctrl, 0, wxALIGN_LEFT|wxALL|wxEXPAND, 5);
+
+    project_desc1_ctrl = new wxStaticText;
+    project_desc1_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    grid->Add(project_desc1_ctrl, 0, wxALIGN_LEFT|wxALL, 5);
+
+    project_desc2_ctrl = new wxStaticText;
+    project_desc2_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    grid->Add(project_desc2_ctrl, 0, wxALIGN_LEFT|wxALL|wxEXPAND, 5);
+
+    project_url1_ctrl = new wxStaticText;
+    project_url1_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    grid->Add(project_url1_ctrl, 0, wxALIGN_LEFT|wxALL, 5);
+
+    project_url2_ctrl = new wxStaticText;
+    project_url2_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    grid->Add(project_url2_ctrl, 0, wxALIGN_LEFT|wxALL|wxEXPAND, 5);
+
+    user_name1_ctrl = new wxStaticText;
+    user_name1_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    grid->Add(user_name1_ctrl, 0, wxALIGN_LEFT|wxALL, 5);
+
+    user_name2_ctrl = new wxStaticText;
+    user_name2_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    grid->Add(user_name2_ctrl, 0, wxALIGN_LEFT|wxALL|wxEXPAND, 5);
+
+    warning_ctrl = new wxStaticText;
+    warning_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    itemBoxSizer3->Add(warning_ctrl, 0, wxALIGN_LEFT|wxALL|wxEXPAND, 5);
+
+    continue_ctrl = new wxStaticText;
+    continue_ctrl->Create( itemWizardPage2, wxID_STATIC, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+    itemBoxSizer3->Add(continue_ctrl, 0, wxALIGN_LEFT|wxALL, 5);
+
+    itemWizardPage2->SetSizer(itemBoxSizer3);
+
+////@end CWelcomePage content construction
+}
+
+/*
+ * Gets the previous page.
+ */
+ 
+wxWizardPageEx* CProjectWelcomePage::GetPrev() const
+{
+    return NULL;
+}
+ 
+/*!
+ * Gets the next page.
+ */
+ 
+wxWizardPageEx* CProjectWelcomePage::GetNext() const
+{
+    CWizardAttach* pWA  = ((CWizardAttach*)GetParent());
+
+    wxASSERT(pWA);
+
+    if (CHECK_CLOSINGINPROGRESS()) {
+        // Cancel Event Detected
+        return PAGE_TRANSITION_NEXT(ID_COMPLETIONERRORPAGE);
+    } else if (pWA->GetProjectAuthenticator().IsEmpty()) {
+        return PAGE_TRANSITION_NEXT(ID_ACCOUNTINFOPAGE);
+    } else {
+        return PAGE_TRANSITION_NEXT(ID_PROJECTPROCESSINGPAGE);
+    }
+    return NULL;
+}
+ 
+/*!
+ * Should we show tooltips?
+ */
+ 
+bool CProjectWelcomePage::ShowToolTips()
+{
+    return TRUE;
+}
+ 
+/*!
+ * Get bitmap resources
+ */
+ 
+wxBitmap CProjectWelcomePage::GetBitmapResource( const wxString& WXUNUSED(name) )
+{
+    // Bitmap retrieval
+////@begin CWelcomePage bitmap retrieval
+    return wxNullBitmap;
+////@end CWelcomePage bitmap retrieval
+}
+ 
+/*!
+ * Get icon resources
+ */
+ 
+wxIcon CProjectWelcomePage::GetIconResource( const wxString& WXUNUSED(name) )
+{
+    // Icon retrieval
+////@begin CWelcomePage icon retrieval
+    return wxNullIcon;
+////@end CWelcomePage icon retrieval
+}
+
+/*!
+ * wxEVT_WIZARD_PAGE_CHANGED event handler for ID_PROJECTWELCOMEPAGE
+ */
+ 
+void CProjectWelcomePage::OnPageChanged( wxWizardExEvent& event ) {
+    if (event.GetDirection() == false) return;
+    wxLogTrace(wxT("Function Start/End"), wxT("CProjectWelcomePage::OnPageChanged - Function Begin"));
+
+    CWizardAttach* pWA  = ((CWizardAttach*)GetParent());
+
+    wxString buf;
+    buf.Printf(_("Welcome to %s."), pWA->GetProjectName().c_str());
+    title_ctrl->SetLabel(buf);
+
+    intro_ctrl->SetLabel(_("You have volunteered to compute for this project:"));
+    project_name1_ctrl->SetLabel(_("Name:"));
+    project_name2_ctrl->SetLabel(pWA->GetProjectName());
+    if (!pWA->project_inst.IsEmpty()) {
+        project_inst1_ctrl->SetLabel(_("Home:"));
+        project_inst2_ctrl->SetLabel(pWA->project_inst);
+    }
+    if (!pWA->project_desc.IsEmpty()) {
+        project_desc1_ctrl->SetLabel(_("Description:"));
+        project_desc2_ctrl->SetLabel(pWA->project_desc);
+    }
+    project_url1_ctrl->SetLabel(_("URL:"));
+    project_url2_ctrl->SetLabel(pWA->GetProjectURL());
+    if (!pWA->user_name.IsEmpty()) {
+        user_name1_ctrl->SetLabel(_("User:"));
+        user_name2_ctrl->SetLabel(pWA->user_name);
+    }
+
+    if (!pWA->known) {
+        warning_ctrl->SetLabel(_("WARNING: This project is not registered with BOINC.  Make sure you trust it before continuing."));
+
+    }
+    continue_ctrl->SetLabel(
+        _("To continue, click Next.")
+    );
+
+    Layout();
+    wxLogTrace(wxT("Function Start/End"), wxT("CProjectWelcomePage::OnPageChanged - Function End"));
+}
+
+
+/*!
+ * wxEVT_WIZARD_CANCEL event handler for ID_PROJECTWELCOMEPAGE
+ */
+ 
+void CProjectWelcomePage::OnCancel( wxWizardExEvent& event ) {
+    PROCESS_CANCELEVENT(event);
+}
diff --git a/clientgui/ProjectWelcomePage.h b/clientgui/ProjectWelcomePage.h
new file mode 100644
index 0000000..2aa24e1
--- /dev/null
+++ b/clientgui/ProjectWelcomePage.h
@@ -0,0 +1,95 @@
+// This file is part of BOINC.
+// http://boinc.berkeley.edu
+// Copyright (C) 2008 University of California
+//
+// BOINC is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation,
+// either version 3 of the License, or (at your option) any later version.
+//
+// BOINC is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
+//
+#ifndef _WIZ_PROJECTWELCOMEPAGE_H_
+#define _WIZ_PROJECTWELCOMEPAGE_H_
+
+#if defined(__GNUG__) && !defined(__APPLE__)
+#pragma interface "ProjectWelcomePage.cpp"
+#endif
+
+/*!
+ * CWelcomePage class declaration
+ */
+
+class CProjectWelcomePage: public wxWizardPageEx
+{    
+    DECLARE_DYNAMIC_CLASS( CProjectWelcomePage )
+    DECLARE_EVENT_TABLE()
+
+public:
+    /// Constructors
+    CProjectWelcomePage( );
+
+    CProjectWelcomePage( CBOINCBaseWizard* parent );
+
+    /// Creation
+    bool Create( CBOINCBaseWizard* parent );
+
+    /// Creates the controls and sizers
+    void CreateControls();
+
+////@begin CProjectWelcomePage event handler declarations
+
+    /// wxEVT_WIZARD_PAGE_CHANGED event handler for ID_PROJECTWELCOMEPAGE
+    void OnPageChanged( wxWizardExEvent& event );
+
+    /// wxEVT_WIZARD_CANCEL event handler for ID_PROJECTWELCOMEPAGE
+    void OnCancel( wxWizardExEvent& event );
+
+////@end CProjectWelcomePage event handler declarations
+
+////@begin CProjectWelcomePage member function declarations
+
+    /// Gets the previous page.
+    virtual wxWizardPageEx* GetPrev() const;
+
+    /// Gets the next page.
+    virtual wxWizardPageEx* GetNext() const;
+
+    /// Retrieves bitmap resources
+    wxBitmap GetBitmapResource( const wxString& name );
+
+    /// Retrieves icon resources
+    wxIcon GetIconResource( const wxString& name );
+
+////@end CProjectWelcomePage member function declarations
+
+    /// Should we show tooltips?
+    static bool ShowToolTips();
+
+////@begin CProjectWelcomePage member variables
+    wxStaticText* title_ctrl;
+    wxStaticText* intro_ctrl;
+    wxStaticText* project_name1_ctrl;
+    wxStaticText* project_name2_ctrl;
+    wxStaticText* project_inst1_ctrl;
+    wxStaticText* project_inst2_ctrl;
+    wxStaticText* project_desc1_ctrl;
+    wxStaticText* project_desc2_ctrl;
+    wxStaticText* project_url1_ctrl;
+    wxStaticText* project_url2_ctrl;
+    wxStaticText* user_name1_ctrl;
+    wxStaticText* user_name2_ctrl;
+
+    wxStaticText* warning_ctrl;
+    wxStaticText* continue_ctrl;
+
+////@end CProjectWelcomePage member variables
+};
+
+#endif // _WIZ_WELCOMEPAGE_H_
diff --git a/clientgui/WizardAttach.cpp b/clientgui/WizardAttach.cpp
index 7beaa8a..6adc0df 100644
--- a/clientgui/WizardAttach.cpp
+++ b/clientgui/WizardAttach.cpp
@@ -39,6 +39,7 @@
 #include "ProjectInfoPage.h"
 #include "ProjectPropertiesPage.h"
 #include "ProjectProcessingPage.h"
+#include "ProjectWelcomePage.h"
 #include "AccountManagerInfoPage.h"
 #include "AccountManagerPropertiesPage.h"
 #include "AccountManagerProcessingPage.h"
@@ -96,6 +97,7 @@ bool CWizardAttach::Create( wxWindow* parent, wxWindowID id, const wxString& /*
     m_ProjectInfoPage = NULL;
     m_ProjectPropertiesPage = NULL;
     m_ProjectProcessingPage = NULL;
+    m_ProjectWelcomePage = NULL;
     m_AccountManagerInfoPage = NULL;
     m_AccountManagerPropertiesPage = NULL;
     m_AccountManagerProcessingPage = NULL;
@@ -130,7 +132,7 @@ bool CWizardAttach::Create( wxWindow* parent, wxWindowID id, const wxString& /*
     account_out.clear();
     account_created_successfully = false;
     attached_to_project_successfully = false;
-    close_when_completed = false;
+    m_bCloseWhenCompleted = false;
     m_strProjectName.Empty();
     m_strProjectUrl.Empty();
     m_strProjectAuthenticator.Empty();
@@ -206,6 +208,10 @@ void CWizardAttach::CreateControls()
     m_ProjectProcessingPage->Create( itemWizard1 );
     GetPageAreaSizer()->Add(m_ProjectProcessingPage);
 
+    m_ProjectWelcomePage = new CProjectWelcomePage;
+    m_ProjectWelcomePage->Create( itemWizard1 );
+    GetPageAreaSizer()->Add(m_ProjectWelcomePage);
+
     m_AccountInfoPage = new CAccountInfoPage;
     m_AccountInfoPage->Create( itemWizard1 );
     GetPageAreaSizer()->Add(m_AccountInfoPage);
@@ -276,6 +282,11 @@ void CWizardAttach::CreateControls()
     );
     wxLogTrace(
         wxT("Function Status"),
+        wxT("CWizardAttach::CreateControls -     m_ProjectWelcomePage = id: '%d', location: '%p', height: '%d', width: '%d'"),
+        m_ProjectWelcomePage->GetId(), m_ProjectWelcomePage, m_ProjectWelcomePage->GetBestSize().GetHeight(), m_ProjectWelcomePage->GetBestSize().GetWidth()
+    );
+    wxLogTrace(
+        wxT("Function Status"),
         wxT("CWizardAttach::CreateControls -     m_AccountManagerInfoPage = id: '%d', location: '%p', height: '%d', width: '%d'"),
         m_AccountManagerInfoPage->GetId(), m_AccountManagerInfoPage, m_AccountManagerInfoPage->GetBestSize().GetHeight(), m_AccountManagerInfoPage->GetBestSize().GetWidth()
     );
@@ -353,8 +364,7 @@ void CWizardAttach::CreateControls()
  * Runs the wizard.
  */
 bool CWizardAttach::Run(
-    wxString& WXUNUSED(strName), wxString& strURL, wxString& strTeamName,
-    bool bCredentialsCached
+    wxString WXUNUSED(strName), wxString strURL, wxString strTeamName, bool bCredentialsCached
 ) {
     m_strTeamName = strTeamName;
 
@@ -373,7 +383,7 @@ bool CWizardAttach::Run(
 
         if (detect_setup_authenticator(url, authenticator)) {
             m_bCredentialsDetected = true;
-            close_when_completed = true;
+            m_bCloseWhenCompleted = true;
             SetProjectAuthenticator(wxString(authenticator.c_str(), wxConvUTF8));
         }
     }
@@ -388,6 +398,33 @@ bool CWizardAttach::Run(
 }
 
 
+/*!
+ * Runs the wizard.
+ */
+bool CWizardAttach::RunSimpleProjectAttach() {
+    std::string name, url, authenticator, institution, description, _known;
+    wxString strName, strURL, strAuthenticator;
+
+    if (detect_simple_account_credentials(name, url, authenticator, institution, description, _known)) {
+        strName = wxURI::Unescape(wxString(name.c_str(), wxConvUTF8));
+        strURL = wxURI::Unescape(wxString(url.c_str(), wxConvUTF8));
+        strAuthenticator = wxURI::Unescape(wxString(authenticator.c_str(), wxConvUTF8));
+
+        SetProjectName(strName);
+        SetProjectURL(strURL);
+        if (authenticator.size()) {
+            SetProjectAuthenticator(strAuthenticator);
+        }
+        project_inst = wxURI::Unescape(wxString(institution.c_str(), wxConvUTF8));
+        project_desc = wxURI::Unescape(wxString(description.c_str(), wxConvUTF8));
+
+        known = _known.length() > 0;
+    }
+
+    return RunWizard(m_ProjectWelcomePage);
+}
+
+
 bool CWizardAttach::SyncToAccountManager() {
     ACCT_MGR_INFO ami;
     CMainDocument* pDoc = wxGetApp().GetDocument();
@@ -505,7 +542,7 @@ bool CWizardAttach::HasNextPage( wxWizardPageEx* page )
  
 bool CWizardAttach::HasPrevPage( wxWizardPageEx* page )
 {
-    if ((page == m_WelcomePage) || (page == m_CompletionPage) || (page == m_CompletionErrorPage))
+    if ((page == m_WelcomePage) || (page == m_ProjectWelcomePage) || (page == m_CompletionPage) || (page == m_CompletionErrorPage))
         return false;
     return true;
 }
@@ -568,6 +605,9 @@ wxWizardPageEx* CWizardAttach::_PushPageTransition( wxWizardPageEx* pCurrentPage
         if (ID_PROJECTPROCESSINGPAGE == ulPageID)
             pPage = m_ProjectProcessingPage;
  
+        if (ID_PROJECTWELCOMEPAGE == ulPageID)
+            pPage = m_ProjectWelcomePage;
+ 
         if (ID_ACCOUNTMANAGERINFOPAGE == ulPageID)
             pPage = m_AccountManagerInfoPage;
  
@@ -652,7 +692,8 @@ void CWizardAttach::_ProcessCancelEvent( wxWizardExEvent& event ) {
         bCancelWithoutNextPage |= (page == m_ErrAlreadyExistsPage);
     } else {
         bCancelWithoutNextPage |= (page == m_WelcomePage);
-    }
+        bCancelWithoutNextPage |= (page == m_ProjectWelcomePage);
+   }
 
     if (wxYES != iRetVal) {
         event.Veto();
diff --git a/clientgui/WizardAttach.h b/clientgui/WizardAttach.h
index ba0f07a..14ed1b4 100644
--- a/clientgui/WizardAttach.h
+++ b/clientgui/WizardAttach.h
@@ -50,6 +50,7 @@
 #define ID_PROJECTINFOPAGE 10200
 #define ID_PROJECTPROPERTIESPAGE 10201
 #define ID_PROJECTPROCESSINGPAGE 10202
+#define ID_PROJECTWELCOMEPAGE 10203
 
 // Account Manager Wizard Pages
 #define ID_ACCOUNTMANAGERINFOPAGE 10300
@@ -149,6 +150,7 @@ class CErrUserDisagreesPage;
 class CProjectInfoPage;
 class CProjectPropertiesPage;
 class CProjectProcessingPage;
+class CProjectWelcomePage;
 class CAccountManagerInfoPage;
 class CAccountManagerPropertiesPage;
 class CAccountManagerProcessingPage;
@@ -220,13 +222,16 @@ public:
 
     /// Runs the wizard.
     bool Run(
-        wxString& strName,
-        wxString& strURL,
-        wxString& wxString,
+        wxString strName,
+        wxString strURL,
+        wxString strTeamName,
         bool bCredentialsCached = true
     );
     
-    // Synchronize to Account Manager
+    /// Runs the wizard.
+    bool RunSimpleProjectAttach();
+    
+    /// Synchronize to Account Manager
     bool SyncToAccountManager();
 
     /// Retrieves bitmap resources
@@ -234,7 +239,8 @@ public:
 
     /// Retrieves icon resources
     wxIcon GetIconResource( const wxString& name );
-////@end CWizardAttachProject member function declarations
+
+    ////@end CWizardAttachProject member function declarations
 
     /// Overrides
     virtual bool HasNextPage( wxWizardPageEx* page );
@@ -271,6 +277,7 @@ public:
     CProjectInfoPage* m_ProjectInfoPage;
     CProjectPropertiesPage* m_ProjectPropertiesPage;
     CProjectProcessingPage* m_ProjectProcessingPage;
+    CProjectWelcomePage* m_ProjectWelcomePage;
     CAccountManagerInfoPage* m_AccountManagerInfoPage;
     CAccountManagerPropertiesPage* m_AccountManagerPropertiesPage;
     CAccountManagerProcessingPage* m_AccountManagerProcessingPage;
@@ -302,7 +309,7 @@ public:
     ACCOUNT_OUT         account_out;
     bool                account_created_successfully;
     bool                attached_to_project_successfully;
-    bool                close_when_completed;
+    bool                m_bCloseWhenCompleted;
     bool                m_bCredentialsCached;
     bool                m_bCredentialsDetected;
     wxString            m_strProjectName;
@@ -312,6 +319,10 @@ public:
     wxString            m_strReturnURL;
     bool                m_bCookieRequired;
     wxString            m_strCookieFailureURL;
+    wxString            project_inst;   // institution
+    wxString            project_desc;   // description
+    wxString            user_name;
+    bool                known;
 };
 
 #endif // _WIZ_ATTACH_H_
diff --git a/clientgui/browser.cpp b/clientgui/browser.cpp
index 4ee63a8..38b3747 100644
--- a/clientgui/browser.cpp
+++ b/clientgui/browser.cpp
@@ -42,6 +42,7 @@ BOOL WINAPI InternetGetCookieA( LPCSTR lpszUrl, LPCSTR lpszCookieName, LPSTR lps
 #include <time.h>
 #endif
 
+#include <sqlite3.h>
 #include "error_numbers.h"
 #include "mfile.h"
 #include "miofile.h"
@@ -593,10 +594,6 @@ bool detect_cookie_mozilla_v3(
     int         rc;
     MOZILLA_COOKIE_SQL cookie;
 
-#if defined(__APPLE__)
-    // sqlite3 is not available on Mac OS 10.3.9
-    if (sqlite3_open == NULL) return false;
-#endif
 
     // determine the project hostname using the project url
     parse_hostname_mozilla_compatible(project_url, hostname);
@@ -805,10 +802,6 @@ bool detect_cookie_chrome(
     int         rc;
     CHROME_COOKIE_SQL cookie;
 
-#if defined(__APPLE__)
-    // sqlite3 is not available on Mac OS 10.3.9
-    if (sqlite3_open == NULL) return false;
-#endif
 
     // determine the project hostname using the project url
     parse_hostname_chrome_compatible(project_url, hostname);
@@ -819,7 +812,12 @@ bool detect_cookie_chrome(
     rc = sqlite3_open(tmp.c_str(), &db);
     if ( rc ) {
         sqlite3_close(db);
-        return false;
+        tmp = profile_root + "Safe Browsing Cookies";
+        rc = sqlite3_open(tmp.c_str(), &db);
+        if ( rc ) {
+            sqlite3_close(db);
+            return false;
+        }
     }
     
     // construct SQL query to extract the desired cookie
@@ -923,7 +921,7 @@ bool detect_cookie_ie_supported(std::string& project_url, std::string& name, std
         strCookieName = strCookieFragment.substr(0, uiDelimeterLocation);
         strCookieValue = strCookieFragment.substr(uiDelimeterLocation + 1);
 
-        if (name == strCookieName) {
+        if (0 == strcmp(name.c_str(), strCookieName.c_str())) {
             // Now we found it!  Yea - auto attach!
             value = strCookieValue;
             bReturnValue = true;
@@ -1011,7 +1009,7 @@ bool detect_cookie_ie_supported_uac(std::string& project_url, std::string& name,
         strCookieName = strCookieFragment.substr(0, uiDelimeterLocation);
         strCookieValue = strCookieFragment.substr(uiDelimeterLocation + 1);
 
-        if (name_w == strCookieName) {
+        if (0 == wcscmp(name_w.c_str(), strCookieName.c_str())) {
             // Now we found it!  Yea - auto attach!
             value = W2A(strCookieValue);
             bReturnValue = true;
@@ -1271,6 +1269,86 @@ END:
 
 //
 // walk through the various browsers looking up the
+// various cookies that make up the simple account creation scheme.
+//
+// give preference to the default platform specific browers first before going
+// to the platform independant browsers since most people don't switch from
+// the default.
+// 
+bool detect_simple_account_credentials(
+    std::string& project_name, std::string& project_url, std::string& authenticator, 
+    std::string& project_institution, std::string& project_description, std::string& known
+) {
+    bool retval = false;
+    std::string strCookieServer("http://boinc.berkeley.edu");
+    std::string strCookieProjectName("attach_project_name");
+    std::string strCookieProjectURL("attach_master_url");
+    std::string strCookieAuthenticator("attach_auth");
+    std::string strCookieProjectInstitution("attach_project_inst");
+    std::string strCookieProjectDescription("attach_project_desc");
+    std::string strCookieKnown("attach_known");
+
+#ifdef _WIN32
+    if ( detect_cookie_ie(strCookieServer, strCookieProjectName, project_name) &&
+         detect_cookie_ie(strCookieServer, strCookieProjectURL, project_url)
+    ){
+        detect_cookie_ie(strCookieServer, strCookieAuthenticator, authenticator);
+        detect_cookie_ie(strCookieServer, strCookieProjectInstitution, project_institution);
+        detect_cookie_ie(strCookieServer, strCookieProjectDescription, project_description);
+        detect_cookie_ie(strCookieServer, strCookieKnown, known);
+        goto END;
+    }
+#endif
+#ifdef __APPLE__
+    if ( detect_cookie_safari(strCookieServer, strCookieProjectName, project_name) &&
+         detect_cookie_safari(strCookieServer, strCookieProjectURL, project_url)
+    ){
+        detect_cookie_safari(strCookieServer, strCookieAuthenticator, authenticator);
+        detect_cookie_safari(strCookieServer, strCookieProjectInstitution, project_institution);
+        detect_cookie_safari(strCookieServer, strCookieProjectDescription, project_description);
+        detect_cookie_safari(strCookieServer, strCookieKnown, known);
+        goto END;
+    }
+#endif
+    if ( detect_cookie_chrome(strCookieServer, strCookieProjectName, project_name) &&
+         detect_cookie_chrome(strCookieServer, strCookieProjectURL, project_url)
+    ){
+        detect_cookie_chrome(strCookieServer, strCookieAuthenticator, authenticator);
+        detect_cookie_chrome(strCookieServer, strCookieProjectInstitution, project_institution);
+        detect_cookie_chrome(strCookieServer, strCookieProjectDescription, project_description);
+        detect_cookie_chrome(strCookieServer, strCookieKnown, known);
+        goto END;
+    }
+    if ( detect_cookie_firefox_3(strCookieServer, strCookieProjectName, project_name) &&
+         detect_cookie_firefox_3(strCookieServer, strCookieProjectURL, project_url)
+    ){
+        detect_cookie_firefox_3(strCookieServer, strCookieAuthenticator, authenticator);
+        detect_cookie_firefox_3(strCookieServer, strCookieProjectInstitution, project_institution);
+        detect_cookie_firefox_3(strCookieServer, strCookieProjectDescription, project_description);
+        detect_cookie_firefox_3(strCookieServer, strCookieKnown, known);
+        goto END;
+    }
+    if ( detect_cookie_firefox_2(strCookieServer, strCookieProjectName, project_name) &&
+         detect_cookie_firefox_2(strCookieServer, strCookieProjectURL, project_url)
+    ){
+        detect_cookie_firefox_2(strCookieServer, strCookieAuthenticator, authenticator);
+        detect_cookie_firefox_2(strCookieServer, strCookieProjectInstitution, project_institution);
+        detect_cookie_firefox_2(strCookieServer, strCookieProjectDescription, project_description);
+        detect_cookie_firefox_2(strCookieServer, strCookieKnown, known);
+        goto END;
+    }
+
+END:
+    if (!project_name.empty() && !project_url.empty()) {
+        retval = true;
+    }
+
+    return retval;
+}
+
+
+//
+// walk through the various browsers looking up the
 // account manager cookies until the account manager's 'Login' and 'Password_Hash'
 // cookies are found.
 //
diff --git a/clientgui/browser.h b/clientgui/browser.h
index dac9ac3..587a589 100644
--- a/clientgui/browser.h
+++ b/clientgui/browser.h
@@ -18,8 +18,6 @@
 #ifndef _BROWSER_
 #define _BROWSER_
 
-#include <sqlite3.h>
-
 //
 // The BOINC client now supports the ability to lookup a users
 //   authenticator during automatic attachments via a browser
@@ -27,7 +25,17 @@
 //
 
 bool detect_setup_authenticator(std::string& project_url, std::string& authenticator);
-bool detect_account_manager_credentials(std::string& project_url, std::string& login, std::string& password_hash, std::string& return_url);
+
+bool detect_simple_account_credentials(
+    std::string& project_name, std::string& project_url, std::string& authenticator, 
+    std::string& project_institution, std::string& project_description, std::string& known
+);
+
+bool detect_account_manager_credentials(
+    std::string& project_url, std::string& login, std::string& password_hash,
+    std::string& return_url
+);
+
 bool is_authenticator_valid(const std::string authenticator);
 
 // platform specific browsers
@@ -45,19 +53,4 @@ bool detect_cookie_firefox_2(std::string& project_url, std::string& name, std::s
 bool detect_cookie_firefox_3(std::string& project_url, std::string& name, std::string& value);
 bool detect_cookie_chrome(std::string& project_url, std::string& name, std::string& value);
 
-#if 0 //defined(__APPLE__)
-#undef sqlite3_free
-#ifdef __cplusplus
-extern "C" {
-#endif
-    // sqlite3 is not available on Mac OS 10.3.9
-    extern int sqlite3_open(const char *filename, sqlite3 **ppDb) __attribute__((weak_import));
-    extern int sqlite3_close(sqlite3 *) __attribute__((weak_import));
-    extern int sqlite3_exec(sqlite3*,  const char *sql, sqlite3_callback, void *, char **errmsg) __attribute__((weak_import));
-    extern void sqlite3_free(void *z) __attribute__((weak_import));
-#ifdef __cplusplus
-}
-#endif
-#endif
-
 #endif
diff --git a/configure.ac b/configure.ac
index de4f12e..76ca094 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,7 +6,7 @@ dnl not sure exactly what the minimum version is (but 2.13 wont work)
 AC_PREREQ(2.58)
 
 dnl Set the BOINC version here.  You can also use the set-version script.
-AC_INIT(BOINC, 7.4.23)
+AC_INIT(BOINC, 7.4.27)
 AC_CONFIG_MACRO_DIR([m4])
 LIBBOINC_VERSION=`echo ${PACKAGE_VERSION} | sed 's/\./:/g'`
 AC_SUBST([LIBBOINC_VERSION])
diff --git a/html/user/get_project_config.php b/html/user/get_project_config.php
index 793bf58..0dbfc3c 100644
--- a/html/user/get_project_config.php
+++ b/html/user/get_project_config.php
@@ -107,6 +107,10 @@ if (file_exists($tou_file)) {
     }
 }
 
+if (LDAP_HOST) {
+    echo "<ldap_auth/>\n";
+}
+
 echo "</project_config>";
 
 ?>
diff --git a/html/user/lookup_account.php b/html/user/lookup_account.php
index f86a83a..67e53c6 100644
--- a/html/user/lookup_account.php
+++ b/html/user/lookup_account.php
@@ -22,45 +22,65 @@ require_once("../inc/boinc_db.inc");
 require_once("../inc/util.inc");
 require_once("../inc/email.inc");
 require_once("../inc/xml.inc");
+require_once("../inc/ldap.inc");
 
 xml_header();
 $retval = db_init_xml();
 if ($retval) xml_error($retval);
 
-$email_addr = get_str("email_addr");
-$passwd_hash = get_str("passwd_hash", true);
+$ldap_auth = get_str("ldap_auth", true);
 
-$email_addr = BoincDb::escape_string($email_addr);
-$user = BoincUser::lookup("email_addr='$email_addr'");
-if (!$user) {
-    xml_error(ERR_DB_NOT_FOUND);
-}
+if (LDAP_HOST && $ldap_auth) {
+    // LDAP case.
+    //
+    $ldap_uid = get_str("ldap_uid");
+    $passwd = get_str("passwd");
+    list ($ldap_user, $error_msg) = ldap_auth($ldap_uid, $passwd);
+    if ($error_msg) {
+        xml_error(ERR_BAD_USER_NAME, $error_msg);
+    }
+    $x = ldap_email_string($ldap_uid);
+    $user = BoincUser::lookup_email_addr($x);
+    if (!$user) {
+        $user = make_user_ldap($x, $ldap_user->name);
+        if (!$user) {
+            xml_error(-1, "user record creation failed");
+        }
+    }
+} else {
+    // normal (non-LDAP) case
+    $email_addr = get_str("email_addr");
+    $passwd_hash = get_str("passwd_hash", true);
 
-if (!$passwd_hash) {
-    echo "<account_out>
-    <success/>
-</account_out>
-";
-    exit();
-}
+    $email_addr = BoincDb::escape_string($email_addr);
+    $user = BoincUser::lookup("email_addr='$email_addr'");
+    if (!$user) {
+        xml_error(ERR_DB_NOT_FOUND);
+    }
 
-$auth_hash = md5($user->authenticator.$user->email_addr);
+    if (!$passwd_hash) {
+        echo "<account_out>\n";
+        echo "   <success/>\n";
+        echo "</account_out>\n";
+        exit();
+    }
 
-// if no password set, set password to account key
-//
-if (!strlen($user->passwd_hash)) {
-    $user->passwd_hash = $auth_hash;
-    $user->update("passwd_hash='$user->passwd_hash'");
-}
+    $auth_hash = md5($user->authenticator.$user->email_addr);
 
-// if the given password hash matches (auth+email), accept it
-//
-if ($user->passwd_hash == $passwd_hash || $auth_hash == $passwd_hash) {
-    echo "<account_out>\n";
-    echo "<authenticator>$user->authenticator</authenticator>\n";
-    echo "</account_out>\n";
-} else {
-    xml_error(ERR_BAD_PASSWD);
-}
+    // if no password set, set password to account key
+    //
+    if (!strlen($user->passwd_hash)) {
+        $user->passwd_hash = $auth_hash;
+        $user->update("passwd_hash='$user->passwd_hash'");
+    }
 
+    // if the given password hash matches (auth+email), accept it
+    //
+    if ($user->passwd_hash != $passwd_hash && $auth_hash != $passwd_hash) {
+        xml_error(ERR_BAD_PASSWD);
+    }
+}
+echo "<account_out>\n";
+echo "<authenticator>$user->authenticator</authenticator>\n";
+echo "</account_out>\n";
 ?>
diff --git a/lib/app_ipc.cpp b/lib/app_ipc.cpp
index 737e45c..1a7a290 100644
--- a/lib/app_ipc.cpp
+++ b/lib/app_ipc.cpp
@@ -35,7 +35,7 @@
 
 #include "app_ipc.h"
 
-#if !defined(HAVE_STRDUP) && defined(HAVE__STRDUP)
+#if (!defined(HAVE_STRDUP) && defined(HAVE__STRDUP)) || defined(_MSC_VER)
 #define strdup _strdup
 #endif
 
diff --git a/lib/cc_config.cpp b/lib/cc_config.cpp
index efe486a..2aa644d 100644
--- a/lib/cc_config.cpp
+++ b/lib/cc_config.cpp
@@ -205,10 +205,10 @@ void CC_CONFIG::defaults() {
     client_new_version_text = "";
     client_version_check_url = "http://boinc.berkeley.edu/download.php?xml=1";
     config_coprocs.clear();
-    data_dir[0] = 0;
     disallow_attach = false;
     dont_check_file_sizes = false;
     dont_contact_ref_site = false;
+    dont_use_vbox = false;
     exclude_gpus.clear();
     exclusive_apps.clear();
     exclusive_gpu_apps.clear();
@@ -335,12 +335,10 @@ int CC_CONFIG::parse_options(XML_PARSER& xp) {
             config_coprocs.add(c);
             continue;
         }
-        if (xp.parse_str("data_dir", data_dir, sizeof(data_dir))) {
-            continue;
-        }
         if (xp.parse_bool("disallow_attach", disallow_attach)) continue;
         if (xp.parse_bool("dont_check_file_sizes", dont_check_file_sizes)) continue;
         if (xp.parse_bool("dont_contact_ref_site", dont_contact_ref_site)) continue;
+        if (xp.parse_bool("dont_use_vbox", dont_use_vbox)) continue;
         if (xp.match_tag("exclude_gpu")) {
             EXCLUDE_GPU eg;
             retval = eg.parse(xp);
@@ -539,19 +537,15 @@ int CC_CONFIG::write(MIOFILE& out, LOG_FLAGS& log_flags) {
         );
     }
     
-    // Older versions of BOINC choke on empty data_dir string 
-    //
-    if (strlen(data_dir)) {
-        out.printf("        <data_dir>%s</data_dir>\n", data_dir);
-    }
-    
     out.printf(
         "        <disallow_attach>%d</disallow_attach>\n"
         "        <dont_check_file_sizes>%d</dont_check_file_sizes>\n"
-        "        <dont_contact_ref_site>%d</dont_contact_ref_site>\n",
+        "        <dont_contact_ref_site>%d</dont_contact_ref_site>\n"
+        "        <dont_use_vbox>%d</dont_use_vbox>\n",
         disallow_attach,
         dont_check_file_sizes,
-        dont_contact_ref_site
+        dont_contact_ref_site,
+        dont_use_vbox
     );
     
     for (i=0; i<exclude_gpus.size(); i++) {
diff --git a/lib/cc_config.h b/lib/cc_config.h
index 5b1eb50..be8406c 100644
--- a/lib/cc_config.h
+++ b/lib/cc_config.h
@@ -146,11 +146,10 @@ struct CC_CONFIG {
     std::string client_new_version_text;
     std::string client_version_check_url;
     COPROCS config_coprocs;
-    char data_dir[256];
     bool disallow_attach;
     bool dont_check_file_sizes;
-    int max_event_log_lines;
     bool dont_contact_ref_site;
+    bool dont_use_vbox;
     std::vector<EXCLUDE_GPU> exclude_gpus;
     std::vector<std::string> exclusive_apps;
     std::vector<std::string> exclusive_gpu_apps;
@@ -164,6 +163,7 @@ struct CC_CONFIG {
     int http_transfer_timeout_bps;
     int http_transfer_timeout;
     std::vector<int> ignore_gpu_instance[NPROC_TYPES];
+    int max_event_log_lines;
     int max_file_xfers;
     int max_file_xfers_per_project;
     int max_stderr_file_size;
diff --git a/lib/coproc.h b/lib/coproc.h
index 2d699df..3db9b22 100644
--- a/lib/coproc.h
+++ b/lib/coproc.h
@@ -89,31 +89,18 @@
 #include "cl_boinc.h"
 #include "opencl_boinc.h"
 
-#define DEFER_ON_GPU_AVAIL_RAM  0
-
 #define MAX_COPROC_INSTANCES 64
 #define MAX_RSC 8
     // max # of processing resources types
 
 // arguments to proc_type_name() and proc_type_name_xml().
 //
-enum {
-    PROC_TYPE_CPU=0,
-    PROC_TYPE_NVIDIA_GPU,
-    PROC_TYPE_AMD_GPU,
-    PROC_TYPE_INTEL_GPU,
-    PROC_TYPE_A,
-    PROC_TYPE_B,
-    PROC_TYPE_C,
-    PROC_TYPE_D,
-    PROC_TYPE_E,
-    PROC_TYPE_F,
-    PROC_TYPE_G,
-    NPROC_TYPES
-};
-
-extern const char* proc_type_names_xml[NPROC_TYPES];
-extern const char* proc_type_names[NPROC_TYPES];
+#define PROC_TYPE_CPU        0
+#define PROC_TYPE_NVIDIA_GPU 1
+#define PROC_TYPE_AMD_GPU    2
+#define PROC_TYPE_INTEL_GPU  3
+#define PROC_TYPE_MINER_ASIC 4
+#define NPROC_TYPES          5
 
 extern const char* proc_type_name(int);
     // user-readable name
@@ -125,13 +112,6 @@ extern int coproc_type_name_to_num(const char* name);
 #define GPU_TYPE_NVIDIA proc_type_name_xml(PROC_TYPE_NVIDIA_GPU)
 #define GPU_TYPE_ATI proc_type_name_xml(PROC_TYPE_AMD_GPU)
 #define GPU_TYPE_INTEL proc_type_name_xml(PROC_TYPE_INTEL_GPU)
-#define COPROC_TYPE_A proc_type_name_xml(PROC_TYPE_A)
-#define COPROC_TYPE_B proc_type_name_xml(PROC_TYPE_B)
-#define COPROC_TYPE_C proc_type_name_xml(PROC_TYPE_C)
-#define COPROC_TYPE_D proc_type_name_xml(PROC_TYPE_D)
-#define COPROC_TYPE_E proc_type_name_xml(PROC_TYPE_E)
-#define COPROC_TYPE_F proc_type_name_xml(PROC_TYPE_F)
-#define COPROC_TYPE_G proc_type_name_xml(PROC_TYPE_G)
 
 // represents a requirement for a coproc.
 // This is a parsed version of the <coproc> elements in an <app_version>
@@ -202,10 +182,6 @@ struct COPROC {
 
     bool running_graphics_app[MAX_COPROC_INSTANCES];
         // is this GPU running a graphics app (NVIDIA only)
-#if DEFER_ON_GPU_AVAIL_RAM
-    double available_ram_temp[MAX_COPROC_INSTANCES];
-        // used during job scheduling
-#endif
 
     double last_print_time;
 
diff --git a/lib/error_numbers.h b/lib/error_numbers.h
index fc7d14e..e4c76c2 100644
--- a/lib/error_numbers.h
+++ b/lib/error_numbers.h
@@ -203,6 +203,7 @@
 #define ERR_PROC_PARSE      -235
 #define ERR_STATFS          -236
 #define ERR_PIPE            -237
+#define ERR_NEED_HTTPS      -238
 
 // PLEASE: add a text description of your error to 
 // the text description function boincerror() in str_util.cpp.
diff --git a/lib/gui_rpc_client.h b/lib/gui_rpc_client.h
index 1ac988c..e9c11b1 100644
--- a/lib/gui_rpc_client.h
+++ b/lib/gui_rpc_client.h
@@ -559,6 +559,8 @@ struct PROJECT_CONFIG {
         // before allowing attachment to continue.
     std::vector<std::string> platforms;
         // platforms supported by project, or empty
+    bool ldap_auth;
+        // project supports LDAP authentication
 
     PROJECT_CONFIG();
     ~PROJECT_CONFIG();
@@ -572,7 +574,7 @@ struct ACCOUNT_IN {
     std::string url;
         // URL prefix for web RPCs
     std::string email_addr;
-        // the account identifier (email address or user name)
+        // the account identifier (email address, user name, or LDAP uid)
     std::string user_name;
     std::string passwd;
     std::string team_name;
diff --git a/lib/gui_rpc_client_ops.cpp b/lib/gui_rpc_client_ops.cpp
index 9528830..63b8c82 100644
--- a/lib/gui_rpc_client_ops.cpp
+++ b/lib/gui_rpc_client_ops.cpp
@@ -2303,9 +2303,16 @@ int RPC_CLIENT::lookup_account(ACCOUNT_IN& ai) {
     SET_LOCALE sl;
     char buf[1024];
     RPC rpc(this);
+    string passwd_hash;
 
-    downcase_string(ai.email_addr);
-    string passwd_hash = get_passwd_hash(ai.passwd, ai.email_addr);
+    if (strchr(ai.email_addr.c_str(), '@')) {
+        downcase_string(ai.email_addr);
+        passwd_hash = get_passwd_hash(ai.passwd, ai.email_addr);
+    } else {
+        // LDAP case
+        //
+        passwd_hash = ai.passwd;
+    }
     snprintf(buf, sizeof(buf),
         "<lookup_account>\n"
         "   <url>%s</url>\n"
diff --git a/lib/notice.cpp b/lib/notice.cpp
index 5f907e9..f26e591 100644
--- a/lib/notice.cpp
+++ b/lib/notice.cpp
@@ -49,10 +49,7 @@ int NOTICE::parse(XML_PARSER& xp) {
         }
         if (xp.parse_int("seqno", seqno)) continue;
         if (xp.parse_str("title", title, sizeof(title))) continue;
-        if (xp.parse_string("description", description)) {
-            xml_unescape(description);   // 2nd pass
-            continue;
-        }
+        if (xp.parse_string("description", description)) continue;
         if (xp.parse_double("create_time", create_time)) continue;
         if (xp.parse_double("arrival_time", arrival_time)) continue;
         if (xp.parse_bool("is_private", is_private)) continue;
diff --git a/lib/opencl_boinc.cpp b/lib/opencl_boinc.cpp
index 12a1aaa..4efd376 100644
--- a/lib/opencl_boinc.cpp
+++ b/lib/opencl_boinc.cpp
@@ -84,16 +84,16 @@ void OPENCL_DEVICE_PROP::write_xml(MIOFILE& f, const char* tag, bool temp_file)
     );
     if (temp_file) {
         f.printf(
-            "      <is_used>%d</is_used>\n"
             "      <device_num>%d</device_num>\n"
             "      <peak_flops>%f</peak_flops>\n"
             "      <opencl_available_ram>%f</opencl_available_ram>\n"
-            "      <opencl_device_index>%d</opencl_device_index>\n",
-            is_used,
+            "      <opencl_device_index>%d</opencl_device_index>\n"
+            "      <warn_bad_cuda>%d</warn_bad_cuda>\n",
             device_num,
             peak_flops,
             opencl_available_ram,
-            opencl_device_index
+            opencl_device_index,
+            warn_bad_cuda
         );
     }
     f.printf("   </%s>\n", tag);
@@ -182,10 +182,6 @@ int OPENCL_DEVICE_PROP::parse(XML_PARSER& xp, const char* end_tag) {
         
         // The following are used only in the
         // COPROC_INFO_FILENAME temporary file
-        if (xp.parse_int("is_used", n)) {
-            is_used = (COPROC_USAGE)n;
-            continue;
-        }
         if (xp.parse_int("device_num", n)) {
             device_num = n;
             continue;
@@ -196,6 +192,7 @@ int OPENCL_DEVICE_PROP::parse(XML_PARSER& xp, const char* end_tag) {
             opencl_device_index = n;
             continue;
         }
+        if (xp.parse_bool("warn_bad_cuda", warn_bad_cuda)) continue;
     }
     return ERR_XML_PARSE;
 }
diff --git a/lib/opencl_boinc.h b/lib/opencl_boinc.h
index 3041552..aaf490e 100644
--- a/lib/opencl_boinc.h
+++ b/lib/opencl_boinc.h
@@ -64,6 +64,7 @@ struct OPENCL_DEVICE_PROP {
     COPROC_USAGE is_used;               // temp used in scan process
     double opencl_available_ram;        // temp used in scan process
     int opencl_device_index;            // zero-based device number within this OpenCL platform
+    bool warn_bad_cuda;                 // If true, warn we can't use GPU due to CUDA version
 
     void write_xml(MIOFILE&, const char* tag, bool temp_file=false);
     int parse(XML_PARSER&, const char* end_tag);
diff --git a/lib/procinfo_mac.cpp b/lib/procinfo_mac.cpp
index 639a463..e9ddec6 100644
--- a/lib/procinfo_mac.cpp
+++ b/lib/procinfo_mac.cpp
@@ -137,6 +137,11 @@ int procinfo_setup(PROC_MAP& pm) {
         p.swap_size = (double)virtual_mem * 1024.;
         p.user_time += 60. * (float)hours;
         p.is_boinc_app = (p.id == pid || strcasestr(p.command, "boinc"));
+        // Ideally, we should count ScreenSaverEngine.app as a BOINC process
+        // only if BOINC is set as the screensaver.  We could set a flag in
+        // the client when the get_screensaver_tasks rpc is called, but that
+        // would not be 100% reliable for several reasons.
+        if (strcasestr(p.command, "screensaverengine")) p.is_boinc_app = true;
         p.is_low_priority = (priority <= 12);
 
         switch (iBrandID) {
diff --git a/lib/str_util.cpp b/lib/str_util.cpp
index 041a57b..fc75e53 100644
--- a/lib/str_util.cpp
+++ b/lib/str_util.cpp
@@ -528,6 +528,7 @@ const char* boincerror(int which_error) {
         case ERR_ABORTED_ON_EXIT: return "job was aborted on client exit";
         case ERR_PROC_PARSE: return "a /proc entry was not parsed correctly";
         case ERR_PIPE: return "pipe() failed";
+        case ERR_NEED_HTTPS: return "HTTPS needed";
         case 404: return "HTTP file not found";
         case 407: return "HTTP proxy authentication failure";
         case 416: return "HTTP range request error";
diff --git a/lib/url.cpp b/lib/url.cpp
index 6b093bf..a1a2e35 100644
--- a/lib/url.cpp
+++ b/lib/url.cpp
@@ -290,3 +290,6 @@ void escape_project_url(char *in, char* out) {
     }
 }
 
+bool is_https(const char* url) {
+    return (strncmp(url, "https://", 8) == 0);
+}
diff --git a/lib/url.h b/lib/url.h
index 32b715b..c687554 100644
--- a/lib/url.h
+++ b/lib/url.h
@@ -45,5 +45,6 @@ extern void escape_project_url(char *in, char* out);
 extern bool valid_master_url(char*);
 extern void canonicalize_master_url(char *url, int len);
 extern void canonicalize_master_url(std::string&);
+extern bool is_https(const char*);
 
 #endif
diff --git a/py/Boinc/boinc_db.py b/py/Boinc/boinc_db.py
index c70d29e..36efe47 100644
--- a/py/Boinc/boinc_db.py
+++ b/py/Boinc/boinc_db.py
@@ -77,6 +77,12 @@ MODE_BLANKSCREEN = 4
 MODE_REREAD_PREFS = 5
 MODE_QUIT = 6
 NGRAPHICS_MSGS = 7
+PROCESS_PRIORITY_UNSPECIFIED = 0
+PROCESS_PRIORITY_LOWEST = 1
+PROCESS_PRIORITY_LOW = 2
+PROCESS_PRIORITY_NORMAL = 3
+PROCESS_PRIORITY_HIGH = 4
+PROCESS_PRIORITY_HIGHEST = 5
 MSG_INFO = 1
 MSG_USER_ALERT = 2
 MSG_INTERNAL_ERROR = 3
diff --git a/sched/script_validator.cpp b/sched/script_validator.cpp
new file mode 100644
index 0000000..43b80de
--- /dev/null
+++ b/sched/script_validator.cpp
@@ -0,0 +1,128 @@
+// This file is part of BOINC.
+// http://boinc.berkeley.edu
+// Copyright (C) 2014 University of California
+//
+// BOINC is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License
+// as published by the Free Software Foundation,
+// either version 3 of the License, or (at your option) any later version.
+//
+// BOINC is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
+
+// A validator that runs scripts to check and compare results
+// cmdline args:
+// --init_script scriptname
+// --compare_script scriptname
+//
+// the init script is called as
+// scriptname f1 ... fn
+// where f1 ... fn are the output files of a job (there may be just one)
+// It returns zero if the files are valid
+//
+// the compare script is called as
+// scriptname f1 ... fn g1 ... gn
+// where f1 ... fn are the output files of one job,
+// and g1 ... gn are the output files are another job.
+// It returns zero if the files are equivalent.
+
+#include <sys/param.h>
+
+#include "validate_util2.h"
+#include "error_numbers.h"
+#include "boinc_db.h"
+#include "sched_util.h"
+#include "validate_util.h"
+#include "validator.h"
+
+using std::string;
+using std::vector;
+
+bool first = true;
+char init_script[MAXPATHLEN], compare_script[MAXPATHLEN];
+
+void parse_cmdline() {
+    strcpy(init_script, "");
+    strcpy(compare_script, "");
+    for (int i=1; i<g_argc; i++) {
+        if (!strcmp(g_argv[i], "--init_script")) {
+            sprintf(init_script, "../bin/%s", g_argv[++i]);
+        } else if (!strcmp(g_argv[i], "--compare_script")) {
+            sprintf(compare_script, "../bin/%s", g_argv[++i]);
+        }
+    }
+    if (!strlen(init_script) && !strlen(compare_script)) {
+        log_messages.printf(MSG_CRITICAL,
+            "script names missing from command line\n"
+        );
+        exit(1);
+    }
+}
+
+int init_result(RESULT& result, void*&) {
+    if (!strlen(init_script)) return 0;
+    vector<string> paths;
+    int retval;
+    retval = get_output_file_paths(result, paths);
+    if (retval) {
+        fprintf(stderr, "get_output_file_paths() returned %d\n", retval);
+        return retval;
+    }
+    char cmd[4096];
+    strcpy(cmd, init_script);
+    for (unsigned int i=0; i<paths.size(); i++) {
+        strcat(cmd, " ");
+        strcat(cmd, paths[i].c_str());
+    }
+    retval = system(cmd);
+    if (retval) {
+        return retval;
+    }
+    return 0;
+}
+
+int compare_results(RESULT& r1, void*, RESULT const& r2, void*, bool& match) {
+    if (!strlen(compare_script)) {
+        match = true;
+        return 0;
+    }
+    vector<string> paths1, paths2;
+    int retval;
+    retval = get_output_file_paths(r1, paths1);
+    if (retval) {
+        fprintf(stderr, "get_output_file_paths() returned %d\n", retval);
+        return retval;
+    }
+    retval = get_output_file_paths(r2, paths2);
+    if (retval) {
+        fprintf(stderr, "get_output_file_paths() returned %d\n", retval);
+        return retval;
+    }
+    char cmd[4096];
+    strcpy(cmd, compare_script);
+    for (unsigned int i=0; i<paths1.size(); i++) {
+        strcat(cmd, " ");
+        strcat(cmd, paths1[i].c_str());
+    }
+    for (unsigned int i=0; i<paths2.size(); i++) {
+        strcat(cmd, " ");
+        strcat(cmd, paths2[i].c_str());
+    }
+    retval = system(cmd);
+    if (retval) {
+        match = false;
+    } else {
+        match = true;
+    }
+    return 0;
+}
+
+int cleanup_result(RESULT const&, void*) {
+    return 0;
+}
+
diff --git a/version.log b/version.log
index fac714a..76ae95b 100644
--- a/version.log
+++ b/version.log
@@ -1 +1 @@
-6.11.1
+7.4.27

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-boinc/boinc.git



More information about the pkg-boinc-commits mailing list