1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
17 
18 #if defined(__GNUG__) && !defined(__APPLE__)
19 #pragma implementation "MainDocument.h"
20 #endif
21 
22 #include "stdwx.h"
23 #include <wx/numformatter.h>
24 
25 #include "error_numbers.h"
26 #include "str_replace.h"
27 #include "util.h"
28 #ifdef __WXMAC__
29 #include "mac_util.h"
30 #endif
31 #ifdef _WIN32
32 #include "proc_control.h"
33 #endif
34 
35 #include "BOINCGUIApp.h"
36 #include "MainDocument.h"
37 #include "BOINCBaseFrame.h"
38 #include "AdvancedFrame.h"
39 #include "BOINCClientManager.h"
40 #include "BOINCTaskBar.h"
41 #include "DlgEventLog.h"
42 #include "Events.h"
43 #include "SkinManager.h"
44 
45 #ifndef _WIN32
46 #include <sys/wait.h>
47 #endif
48 
49 #ifdef SANDBOX
50 #include <grp.h>
51 #endif
52 
53 #define USE_CACHE_TIMEOUTS 0
54 
55 // If get_results RPC takes x seconds, do it no more often than
56 // once every (x * GET_RESULTS_FREQUENCY_FACTOR) seconds
57 #define GET_RESULTS_FREQUENCY_FACTOR 10
58 
59 // *** RPC update intervals in seconds ***
60 // m_dtCachedCCStatusTimestamp
61 #define CCSTATUS_RPC_INTERVAL 1
62 
63 //m_dtCachedStateTimestamp
64 #define STATERPC_INTERVAL 3600
65 
66 //m_dtNoticesTimeStamp
67 #define NOTICESBACKGROUNDRPC_INTERVAL 60
68 
69 //m_dtProjectsStatusTimestamp
70 #define PROJECTSTATUSRPC_INTERVAL 1
71 
72 //m_dtResultsTimestamp
73 #define RESULTSRPC_INTERVAL 1
74 
75 //m_dtFileTransfersTimestamp
76 #define FILETRANSFERSRPC_INTERVAL 1
77 
78 // m_dtStatisticsStatusTimestamp
79 #define STATISTICSSTATUSRPC_INTERVAL 60
80 
81 // m_dtDiskUsageTimestamp
82 #define DISKUSAGERPC_INTERVAL 60
83 
84 // m_dtCachedSimpleGUITimestamp
85 #define CACHEDSIMPLEGUIRPC_INTERVAL 1
86 
87 // m_dtCachedAcctMgrInfoTimestamp
88 #define CACHEDACCTMGRINFORPC_INTERVAL 600
89 
90 // m_dtLasAsyncRPCDlgTime
91 #define DELAYAFTERASYNCRPC_DLG 1
92 
93 bool g_use_sandbox = false;
94 
95 extern bool s_bSkipExitConfirmation;
96 
97 
98 using std::string;
99 
CNetworkConnection(CMainDocument * pDocument)100 CNetworkConnection::CNetworkConnection(CMainDocument* pDocument) :
101     wxObject() {
102     m_pDocument = pDocument;
103 
104     m_strConnectedComputerName = wxEmptyString;
105     m_strConnectedComputerPassword = wxEmptyString;
106     m_strNewComputerName = wxEmptyString;
107     m_strNewComputerPassword = wxEmptyString;
108     m_bFrameShutdownDetected = false;
109     m_bConnectEvent = false;
110     m_bConnected = false;
111     m_bReconnecting = false;
112     m_bForceReconnect = false;
113     m_bReconnectOnError = false;
114     m_bNewConnection = false;
115     m_bUseDefaultPassword = false;
116     m_bUsedDefaultPassword = false;
117     m_iPort = GUI_RPC_PORT,
118     m_iReadGUIRPCAuthFailure = 0;
119 }
120 
121 
~CNetworkConnection()122 CNetworkConnection::~CNetworkConnection() {
123 }
124 
125 
GetLocalPassword(wxString & strPassword)126 int CNetworkConnection::GetLocalPassword(wxString& strPassword){
127     char buf[256];
128     safe_strcpy(buf, "");
129 
130     FILE* f = fopen("gui_rpc_auth.cfg", "r");
131     if (!f) return errno;
132     fgets(buf, 256, f);
133     fclose(f);
134     int n = (int)strlen(buf);
135     if (n) {
136         n--;
137         if (buf[n]=='\n') {
138             buf[n] = 0;
139         }
140     }
141 
142     strPassword = wxString(buf, wxConvUTF8);
143     return 0;
144 }
145 
146 
Poll()147 void CNetworkConnection::Poll() {
148     int retval;
149     wxString strComputer = wxEmptyString;
150     wxString strComputerPassword = wxEmptyString;
151 
152     if (IsReconnecting()) {
153         wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - Reconnection Detected"));
154         retval = m_pDocument->rpcClient.init_poll();
155         if (!retval) {
156             wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - init_poll() returned ERR_CONNECT, now authorizing..."));
157 
158             // Wait until we can establish a connection to the core client before reading
159             //   the password so that the client has time to create one when it needs to.
160             if (m_bUseDefaultPassword) {
161                 m_iReadGUIRPCAuthFailure = 0;
162                 m_bUseDefaultPassword = FALSE;
163                 m_bUsedDefaultPassword = true;
164 
165                 m_iReadGUIRPCAuthFailure = GetLocalPassword(m_strNewComputerPassword);
166             }
167 
168             retval = m_pDocument->rpc.authorize(m_strNewComputerPassword.mb_str());
169             if (!retval) {
170                 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - Connection Success"));
171                 SetStateSuccess(m_strNewComputerName, m_strNewComputerPassword);
172             } else if (ERR_AUTHENTICATOR == retval) {
173                 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - RPC Authorization - ERR_AUTHENTICATOR"));
174                 SetStateErrorAuthentication();
175             } else {
176                 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - RPC Authorization Failed '%d'"), retval);
177                 SetStateError();
178             }
179             m_bUsedDefaultPassword = false;
180         } else if (ERR_RETRY != retval) {
181             wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - RPC Connection Failed '%d'"), retval);
182             SetStateError();
183         }
184     } else if (IsConnectEventSignaled() || m_bReconnectOnError) {
185         if ((m_bForceReconnect) || (!IsConnected() && m_bReconnectOnError)) {
186             wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - Resetting Document State"));
187             m_pDocument->ResetState();
188             wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - Setting connection state to reconnecting"));
189             SetStateReconnecting();
190         }
191 
192         if (!IsConnected()) {
193             // determine computer name and password to use.
194             // NOTE: Initial connection case.
195             if (!m_strNewComputerName.empty()) {
196                 strComputer = m_strNewComputerName;
197                 strComputerPassword = m_strNewComputerPassword;
198             } else {
199                 // NOTE: Reconnect after a disconnect case.
200                 //       Values are stored after the first successful connect to the host.
201                 //       See: SetStateSuccess()
202                 if (!m_strConnectedComputerName.empty()) {
203                     strComputer = m_strConnectedComputerName;
204                     strComputerPassword = m_strConnectedComputerPassword;
205                 }
206             }
207 
208             // a host value of NULL is special cased as binding to the localhost and
209             //   if we are connecting to the localhost we need to retry the connection
210             //   for awhile so that the users can respond to firewall prompts.
211             //
212             // use a timeout of 60 seconds so that slow machines do not get a
213             //   timeout event right after boot-up.
214             //
215             if (IsComputerNameLocal(strComputer)) {
216                 retval = m_pDocument->rpcClient.init_asynch(NULL, 60.0, true, m_iPort);
217             } else {
218                 retval = m_pDocument->rpcClient.init_asynch(strComputer.mb_str(), 60.0, false, m_iPort);
219             }
220 
221             if (!retval) {
222                 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - RPC Initialization Called"));
223             } else {
224                 wxLogTrace(wxT("Function Status"), wxT("CNetworkConnection::Poll - RPC Initialization Failed '%d'"), retval);
225                 SetStateError();
226             }
227         }
228     }
229 }
230 
231 
FrameShutdownDetected()232 int CNetworkConnection::FrameShutdownDetected() {
233     m_bFrameShutdownDetected = true;
234     return 0;
235 }
236 
GetConnectedComputerName(wxString & strMachine)237 int CNetworkConnection::GetConnectedComputerName(wxString& strMachine) {
238     strMachine = m_strConnectedComputerName;
239     return 0;
240 }
241 
242 
GetConnectedComputerVersion(wxString & strVersion)243 int CNetworkConnection::GetConnectedComputerVersion(wxString& strVersion) {
244     strVersion = m_strConnectedComputerVersion;
245     return 0;
246 }
247 
248 
GetConnectingComputerName(wxString & strMachine)249 int CNetworkConnection::GetConnectingComputerName(wxString& strMachine) {
250     strMachine = m_strNewComputerName;
251     return 0;
252 }
253 
254 
IsComputerNameLocal(const wxString & strMachine)255 bool CNetworkConnection::IsComputerNameLocal(const wxString& strMachine) {
256     static wxString strHostName = wxEmptyString;
257     static wxString strFullHostName = wxEmptyString;
258 
259     if (strHostName.empty()) {
260         strHostName = ::wxGetHostName().Lower();
261     }
262     if (strFullHostName.empty()) {
263         strFullHostName = ::wxGetFullHostName().Lower();
264     }
265 
266     if (strMachine.empty()) {
267         return true;
268     } else if (wxT("localhost") == strMachine.Lower()) {
269         return true;
270     } else if (wxT("localhost.localdomain") == strMachine.Lower()) {
271         return true;
272     } else if (strHostName == strMachine.Lower()) {
273         return true;
274     } else if (strFullHostName == strMachine.Lower()) {
275         return true;
276     }
277     return false;
278 }
279 
280 
SetComputer(const wxString & szComputer,const int iPort,const wxString & szPassword,const bool bUseDefaultPassword)281 int CNetworkConnection::SetComputer(
282     const wxString& szComputer, const int iPort, const wxString& szPassword,
283     const bool bUseDefaultPassword
284 ) {
285     m_strNewComputerName.Empty();
286     m_strNewComputerPassword.Empty();
287     m_bUseDefaultPassword = FALSE;
288 
289     m_bNewConnection = true;
290     m_strNewComputerName = szComputer;
291     m_iPort = iPort;
292     m_strNewComputerPassword = szPassword;
293     m_bUseDefaultPassword = bUseDefaultPassword;
294     return 0;
295 }
296 
297 
SetStateErrorAuthentication()298 void CNetworkConnection::SetStateErrorAuthentication() {
299     CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
300     if (pFrame && !m_bFrameShutdownDetected) {
301         wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
302         m_bConnected = false;
303         m_bReconnecting = false;
304         m_bReconnectOnError = false;
305 
306         m_bConnectEvent = false;
307 
308         pFrame->ShowConnectionBadPasswordAlert(m_bUsedDefaultPassword, m_iReadGUIRPCAuthFailure);
309     }
310 }
311 
312 
SetStateError()313 void CNetworkConnection::SetStateError() {
314     CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
315     if (pFrame && !m_bFrameShutdownDetected) {
316         wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
317         m_bConnected = false;
318         m_bReconnecting = false;
319         m_bReconnectOnError = false;
320 
321         m_bConnectEvent = false;
322 
323         pFrame->ShowConnectionFailedAlert();
324     }
325 }
326 
327 
SetStateReconnecting()328 void CNetworkConnection::SetStateReconnecting() {
329     CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
330     if (pFrame && !m_bFrameShutdownDetected) {
331         wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
332         m_bConnected = false;
333         m_bReconnectOnError = false;
334         m_bForceReconnect = false;
335         m_bReconnecting = true;
336         if (!m_bNewConnection) {
337             m_strNewComputerName = m_strConnectedComputerName;
338             m_strNewComputerPassword = m_strConnectedComputerPassword;
339         }
340         pFrame->FireRefreshView();
341     }
342 }
343 
344 
SetStateSuccess(wxString & strComputer,wxString & strComputerPassword)345 void CNetworkConnection::SetStateSuccess(wxString& strComputer, wxString& strComputerPassword) {
346     CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
347     if (pFrame && !m_bFrameShutdownDetected) {
348         wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
349 
350         m_bConnected = true;
351         m_bReconnecting = false;
352         m_bReconnectOnError = true;
353         m_strConnectedComputerName = strComputer;
354         m_strConnectedComputerPassword = strComputerPassword;
355         m_strNewComputerName = wxEmptyString;
356         m_strNewComputerPassword = wxEmptyString;
357         m_bNewConnection = false;
358 
359         // Prevent a race condition where OnFrameRender() causes SetStateDisconnected()
360         // to be called due to a previous RPC error before we reconnected.
361         m_pDocument->RefreshRPCs(true);
362 
363         // Get the version of the client and cache it
364         VERSION_INFO vi;
365         m_pDocument->rpc.exchange_versions(vi);
366         m_strConnectedComputerVersion.Printf(
367             wxT("%d.%d.%d"),
368             vi.major, vi.minor, vi.release
369         );
370 
371         m_bConnectEvent = false;
372         m_pDocument->ResetMessageState();
373 
374         pFrame->FireConnect();
375     }
376 }
377 
378 
SetStateDisconnected()379 void CNetworkConnection::SetStateDisconnected() {
380     CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
381     if (pFrame && !m_bFrameShutdownDetected) {
382         wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
383         m_bConnected = false;
384         m_bReconnecting = false;
385         m_pDocument->results.clear();
386     }
387 }
388 
389 
IMPLEMENT_DYNAMIC_CLASS(CMainDocument,wxObject)390 IMPLEMENT_DYNAMIC_CLASS(CMainDocument, wxObject)
391 
392 
393 CMainDocument::CMainDocument() : rpc(this) {
394 
395 #ifdef __WIN32__
396     int retval;
397     WSADATA wsdata;
398 
399     retval = WSAStartup(MAKEWORD(1, 1), &wsdata);
400     if (retval) {
401         wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CMainDocument - Winsock Initialization Failure '%d'"), retval);
402     }
403 #endif
404 
405     safe_strcpy(m_szLanguage, "");
406 
407     m_bClientStartCheckCompleted = false;
408 
409     m_ActiveTasksOnly = false;
410 
411     m_fProjectTotalResourceShare = 0.0;
412 
413     m_iLastMessageSequenceNumber = 0;
414     m_iFirstMessageSequenceNumber = -1;
415 
416     m_iNoticeSequenceNumber = 0;
417     m_iLastReadNoticeSequenceNumber = -1;
418     m_dLastReadNoticeArrivalTime = 0.0;
419     m_bWaitingForGetNoticesRPC = false;
420 
421     m_dtCachedStateTimestamp = wxDateTime((time_t)0);
422     m_iGet_state_rpc_result = 0;
423 
424     m_dtCachedCCStatusTimestamp = wxDateTime((time_t)0);
425     m_iGet_status_rpc_result = 0;
426 
427     m_dtNoticesTimeStamp = wxDateTime((time_t)0);;
428     m_iGet_notices_rpc_result = -1;
429 
430     m_dtProjectsStatusTimestamp = wxDateTime((time_t)0);
431     m_iGet_project_status1_rpc_result = -1;
432 
433     m_dtResultsTimestamp = wxDateTime((time_t)0);
434     m_iGet_results_rpc_result = -1;
435 
436     m_fResultsRPCExecutionTime = 0;
437 
438     m_dtKillInactiveGfxTimestamp = wxDateTime((time_t)0);
439     m_dtFileTransfersTimestamp = wxDateTime((time_t)0);
440     m_iGet_file_transfers_rpc_result = 0;
441 
442     m_iGet_messages_rpc_result = -1;
443 
444     m_dtDiskUsageTimestamp = wxDateTime((time_t)0);
445     m_iGet_dsk_usage_rpc_result = -1;
446 
447     m_dtStatisticsStatusTimestamp = wxDateTime((time_t)0);
448     m_iGet_statistics_rpc_result = -1;
449 
450     m_dtCachedSimpleGUITimestamp = wxDateTime((time_t)0);
451     m_iGet_simple_gui2_rpc_result = -1;
452 
453     m_dtCachedAcctMgrInfoTimestamp = wxDateTime((time_t)0);
454     m_iAcct_mgr_info_rpc_result = -1;
455 
456     m_dtLasAsyncRPCDlgTime = wxDateTime((time_t)0);
457     m_dtLastFrameViewRefreshRPCTime = wxDateTime((time_t)0);
458 
459     status.max_event_log_lines = 0;
460 }
461 
462 
~CMainDocument()463 CMainDocument::~CMainDocument() {
464     KillAllRunningGraphicsApps();
465 #ifdef __WIN32__
466     WSACleanup();
467 #endif
468 }
469 
470 
OnInit()471 int CMainDocument::OnInit() {
472     int iRetVal = -1;
473 
474     m_pNetworkConnection = new CNetworkConnection(this);
475     wxASSERT(m_pNetworkConnection);
476 
477     m_pClientManager = new CBOINCClientManager();
478     wxASSERT(m_pClientManager);
479 
480     m_RPCWaitDlg = NULL;
481     m_bWaitingForRPC = false;
482     m_bNeedRefresh = false;
483     m_bNeedTaskBarRefresh = false;
484     m_bRPCThreadIsReady = false;
485     m_bShutDownRPCThread = false;
486     current_rpc_request.clear();
487 
488     m_pRPC_Thread_Mutex = new BOINC_Mutex();
489     wxASSERT(m_pRPC_Thread_Mutex);
490 
491     m_pRPC_Thread_Condition = new BOINC_Condition(*m_pRPC_Thread_Mutex);
492     wxASSERT(m_pRPC_Thread_Condition);
493 
494     m_pRPC_Request_Mutex = new BOINC_Mutex();
495     wxASSERT(m_pRPC_Request_Mutex);
496 
497     m_pRPC_Request_Condition = new BOINC_Condition(*m_pRPC_Request_Mutex);
498     wxASSERT(m_pRPC_Request_Condition);
499 
500     m_RPCThread = new RPCThread(this,
501         m_pRPC_Thread_Mutex,
502         m_pRPC_Thread_Condition,
503         m_pRPC_Request_Mutex,
504         m_pRPC_Request_Condition
505     );
506     wxASSERT(m_RPCThread);
507 
508     iRetVal = m_RPCThread->Create();
509     wxASSERT(!iRetVal);
510 
511     m_RPCThread->Run();
512     for (int i=0; i<100; i++) {
513         if (!m_bRPCThreadIsReady) {
514             boinc_sleep(0.01);  // Allow RPC thread to initialize itself
515         }
516     }
517 
518     return iRetVal;
519 }
520 
521 
OnExit()522 int CMainDocument::OnExit() {
523     int              iRetVal = 0;
524 
525     if (m_pClientManager) {
526         if (wxGetApp().ShouldShutdownCoreClient()) {
527             // Shut down only local clients on Manager exit
528             if (!wxGetApp().IsMgrMultipleInstance()) {
529                 m_pClientManager->ShutdownBOINCCore(true);
530             }
531         }
532 
533         delete m_pClientManager;
534         m_pClientManager = NULL;
535     }
536 
537     if (m_RPCThread) {
538         KillRPCThread();
539         m_RPCThread = NULL;
540     }
541 
542     delete m_pRPC_Thread_Mutex;
543     m_pRPC_Thread_Mutex = NULL;
544 
545     delete m_pRPC_Thread_Condition;
546     m_pRPC_Thread_Condition = NULL;
547 
548     delete m_pRPC_Request_Mutex;
549     m_pRPC_Request_Mutex = NULL;
550 
551     delete m_pRPC_Request_Condition;
552     m_pRPC_Request_Condition = NULL;
553 
554     rpcClient.close();
555 
556     if (m_pNetworkConnection) {
557         delete m_pNetworkConnection;
558         m_pNetworkConnection = NULL;
559     }
560 
561     return iRetVal;
562 }
563 
564 
OnPoll()565 int CMainDocument::OnPoll() {
566     CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
567     int iRetVal = 0;
568     wxString hostName = wxGetApp().GetClientHostNameArg();
569     wxString password = wxGetApp().GetClientPasswordArg();
570     int portNum = wxGetApp().GetClientRPCPortArg();
571 
572     wxASSERT(wxDynamicCast(m_pClientManager, CBOINCClientManager));
573     wxASSERT(wxDynamicCast(m_pNetworkConnection, CNetworkConnection));
574 
575     if (!m_bClientStartCheckCompleted && pFrame) {
576         m_bClientStartCheckCompleted = true;
577 
578         if (IsComputerNameLocal(hostName)) {
579             if (wxGetApp().IsAnotherInstanceRunning()) {
580                 if (!pFrame->SelectComputer(hostName, portNum, password, true)) {
581                     s_bSkipExitConfirmation = true;
582                     wxCommandEvent event;
583                     pFrame->OnExit(event); // Exit if Select Computer dialog cancelled
584                 }
585             }
586         }
587 
588         if (wxGetApp().GetNeedRunDaemon()) {
589             if (IsComputerNameLocal(hostName)) {
590                 if (m_pClientManager->StartupBOINCCore()) {
591                     Connect(wxT("localhost"), portNum, password, TRUE, TRUE);
592                 } else {
593                     m_pNetworkConnection->ForceDisconnect();
594                     pFrame->ShowDaemonStartFailedAlert();
595                 }
596             } else {
597                 Connect(hostName, portNum, password, TRUE, password.IsEmpty());
598             }
599         }
600     }
601 
602     // Check connection state, connect if needed.
603     m_pNetworkConnection->Poll();
604 
605     // Every 10 seconds, kill any running graphics apps
606     // whose associated worker tasks are no longer running
607     wxTimeSpan ts(wxDateTime::Now() - m_dtKillInactiveGfxTimestamp);
608     if (ts.GetSeconds() > 10) {
609         m_dtKillInactiveGfxTimestamp = wxDateTime::Now();
610         KillInactiveGraphicsApps();
611     }
612 
613     return iRetVal;
614 }
615 
616 
OnRefreshState()617 int CMainDocument::OnRefreshState() {
618     if (IsConnected()) {
619         CachedStateUpdate();
620     }
621     return 0;
622 }
623 
624 
CachedStateUpdate()625 int CMainDocument::CachedStateUpdate() {
626     // Most of this is now handled by RunPeriodicRPCs() and ForceCacheUpdate()
627     int     retval = 0;
628 
629     if (m_iGet_state_rpc_result) retval = m_iGet_state_rpc_result;
630 
631     if (retval) m_pNetworkConnection->SetStateDisconnected();
632 
633     return retval;
634 }
635 
636 
ResetState()637 int CMainDocument::ResetState() {
638     rpcClient.close();
639     state.clear();
640     results.clear();
641     ft.clear();
642     statistics_status.clear();
643     disk_usage.clear();
644     proxy_info.clear();
645 
646     ForceCacheUpdate();
647     return 0;
648 }
649 
650 
Connect(const wxString & szComputer,int iPort,const wxString & szComputerPassword,const bool bDisconnect,const bool bUseDefaultPassword)651 int CMainDocument::Connect(const wxString& szComputer, int iPort, const wxString& szComputerPassword, const bool bDisconnect, const bool bUseDefaultPassword) {
652     if (IsComputerNameLocal(szComputer)) {
653         // Restart client if not already running
654         m_pClientManager->AutoRestart();
655     }
656 
657     if (bDisconnect) {
658         m_pNetworkConnection->ForceReconnect();
659     }
660 
661     m_pNetworkConnection->SetComputer(szComputer, iPort, szComputerPassword, bUseDefaultPassword);
662     m_pNetworkConnection->FireReconnectEvent();
663 
664     ResetMessageState();
665     ResetNoticeState();
666     return 0;
667 }
668 
669 
Reconnect()670 int CMainDocument::Reconnect() {
671     m_pNetworkConnection->ForceReconnect();
672     m_pNetworkConnection->FireReconnectEvent();
673     ResetMessageState();
674     ResetNoticeState();
675     return 0;
676 }
677 
678 
GetConnectedComputerName(wxString & strMachine)679 int CMainDocument::GetConnectedComputerName(wxString& strMachine) {
680     m_pNetworkConnection->GetConnectedComputerName(strMachine);
681     return 0;
682 }
683 
684 
GetConnectedComputerVersion(wxString & strVersion)685 int CMainDocument::GetConnectedComputerVersion(wxString& strVersion) {
686     m_pNetworkConnection->GetConnectedComputerVersion(strVersion);
687     return 0;
688 }
689 
690 
GetConnectingComputerName(wxString & strMachine)691 int CMainDocument::GetConnectingComputerName(wxString& strMachine) {
692     m_pNetworkConnection->GetConnectingComputerName(strMachine);
693     return 0;
694 }
695 
696 
IsComputerNameLocal(const wxString & strMachine)697 bool CMainDocument::IsComputerNameLocal(const wxString& strMachine) {
698     return m_pNetworkConnection->IsComputerNameLocal(strMachine);
699 }
700 
701 
IsConnected()702 bool CMainDocument::IsConnected() {
703     return m_pNetworkConnection->IsConnected();
704 }
705 
706 
IsReconnecting()707 bool CMainDocument::IsReconnecting() {
708     return m_pNetworkConnection->IsReconnecting();
709 }
710 
711 
ForceDisconnect()712 void CMainDocument::ForceDisconnect() {
713     return m_pNetworkConnection->ForceDisconnect();
714 }
715 
716 
FrameShutdownDetected()717 int CMainDocument::FrameShutdownDetected() {
718     return m_pNetworkConnection->FrameShutdownDetected();
719 }
720 
721 
722 // It is _not_ enough to just reset m_dtCachedCCStatusTimestamp
723 // and let RunPeriodicRPCs() update the state for these routines
724 // (which need immediate results):
725 //      CMainDocument::SetActivityRunMode()
726 //      CMainDocument::SetNetworkRunMode()
727 // Otherwise the Snooze task bar menu item and SimpleGUI Pause
728 // button do not work properly.
729 //
GetCoreClientStatus(CC_STATUS & ccs,bool bForce)730 int CMainDocument::GetCoreClientStatus(CC_STATUS& ccs, bool bForce) {
731     wxString         strMachine = wxEmptyString;
732     int              iRetVal = 0;
733 
734     if (IsConnected()) {
735         if (!m_bWaitingForRPC) {    // Prevent recursive entry of RequestRPC()
736 #if USE_CACHE_TIMEOUTS
737             wxTimeSpan ts(wxDateTime::Now() - m_dtCachedCCStatusTimestamp);
738             if (ts.GetSeconds() >= (10 * CCSTATUS_RPC_INTERVAL)) bForce = true;
739 #endif
740             if (m_dtCachedCCStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) bForce = true;
741         }
742         if (bForce) {
743             m_dtCachedCCStatusTimestamp = wxDateTime::Now();
744 
745             m_iGet_status_rpc_result = rpc.get_cc_status(ccs);
746             if (0 == iRetVal) {
747                 status = ccs;
748             } else {
749                 iRetVal = m_iGet_status_rpc_result;
750             }
751         } else {
752             ccs = status;
753             iRetVal = m_iGet_status_rpc_result;
754         }
755 
756         if (m_iGet_status_rpc_result) {
757             m_pNetworkConnection->SetStateDisconnected();
758         } else {
759             if (ccs.manager_must_quit) {
760                 GetConnectedComputerName(strMachine);
761                 if (IsComputerNameLocal(strMachine)) {
762                     CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
763                     wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
764                     pFrame->Close(true);
765                 }
766             }
767         }
768     } else {
769         iRetVal = -1;
770     }
771 
772     return iRetVal;
773 }
774 
775 
SetActivityRunMode(int iMode,int iTimeout)776 int CMainDocument::SetActivityRunMode(int iMode, int iTimeout) {
777     int       iRetVal = 0;
778     CC_STATUS ccs;
779 
780     if (IsConnected()) {
781         iRetVal = rpc.set_run_mode(iMode, iTimeout);
782         if (0 == iRetVal) {
783             if (RUN_MODE_RESTORE == iMode) {
784                 GetCoreClientStatus(ccs, true);
785             } else {
786                 status.task_mode = iMode;
787             }
788         }
789     }
790 
791     return iRetVal;
792 }
793 
794 
SetGPURunMode(int iMode,int iTimeout)795 int CMainDocument::SetGPURunMode(int iMode, int iTimeout) {
796     CC_STATUS ccs;
797 
798     if (IsConnected()) {
799         int retval = rpc.set_gpu_mode(iMode, iTimeout);
800         if (retval) return retval;
801         if (iMode == RUN_MODE_RESTORE) {
802             GetCoreClientStatus(ccs, true);
803         } else {
804             status.gpu_mode = iMode;
805         }
806     }
807     return 0;
808 }
809 
SetNetworkRunMode(int iMode,int iTimeout)810 int CMainDocument::SetNetworkRunMode(int iMode, int iTimeout) {
811     int       iRetVal = 0;
812     CC_STATUS ccs;
813 
814     if (IsConnected()) {
815         iRetVal = rpc.set_network_mode(iMode, iTimeout);
816         if (0 == iRetVal) {
817             if (RUN_MODE_RESTORE == iMode) {
818                 GetCoreClientStatus(ccs, true);
819             } else {
820                 status.network_mode = iMode;
821             }
822         }
823     }
824 
825     return iRetVal;
826 }
827 
828 
829 // We use 0 to indicate that the RPC has never been called yet, so
830 // set last update time to (time_t)1 here rather than to (time_t)0,
831 // and only if it is currently not zero.
RefreshRPCs(bool fullReset)832 void CMainDocument::RefreshRPCs(bool fullReset) {
833     wxDateTime t = fullReset ? wxDateTime((time_t)0) : wxDateTime((time_t)1);
834 
835     if (!m_dtCachedCCStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
836         m_dtCachedCCStatusTimestamp = t;
837 //      m_iGet_status_rpc_result = -1;
838     }
839 
840     if (!m_dtProjectsStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
841         m_dtProjectsStatusTimestamp = t;
842 //      m_iGet_project_status1_rpc_result = -1;
843     }
844 
845     if (!m_dtResultsTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
846         m_dtResultsTimestamp = t;
847 //      m_iGet_results_rpc_result = -1;
848     }
849     m_fResultsRPCExecutionTime = 0;
850 
851     if (!m_dtFileTransfersTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
852         m_dtFileTransfersTimestamp = t;
853 //      m_iGet_file_transfers_rpc_result = 0;
854     }
855 
856 //  m_iGet_messages_rpc_result = -1;
857 
858     if (!m_dtDiskUsageTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
859         m_dtDiskUsageTimestamp = t;
860 //      m_iGet_dsk_usage_rpc_result = -1;
861     }
862 
863     if (!m_dtStatisticsStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
864         m_dtStatisticsStatusTimestamp = t;
865 //      m_iGet_statistics_rpc_result = -1;
866     }
867 
868     if (!m_dtCachedSimpleGUITimestamp.IsEqualTo(wxDateTime((time_t)0))) {
869         m_dtCachedSimpleGUITimestamp = t;
870 //      m_iGet_simple_gui2_rpc_result = -1;
871     }
872 
873     if (!m_dtCachedAcctMgrInfoTimestamp.IsEqualTo(wxDateTime((time_t)0))) {
874         m_dtCachedAcctMgrInfoTimestamp = t;
875         m_iAcct_mgr_info_rpc_result = -1;
876     }
877 
878 //  m_iGet_state_rpc_result = -1;
879 }
880 
881 
RunPeriodicRPCs(int frameRefreshRate)882 void CMainDocument::RunPeriodicRPCs(int frameRefreshRate) {
883     ASYNC_RPC_REQUEST request;
884     wxTimeSpan ts;
885 
886     // Timer events are handled while the RPC Wait dialog is shown
887     // which may cause unintended recursion and repeatedly posting
888     // the same RPC requests from timer routines.
889     if (WaitingForRPC()) return;
890 
891     CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
892     if (!pFrame) return;
893 
894     int currentTabView = pFrame->GetCurrentViewPage();
895 
896     // If the client is heavily loaded (e.g, very many tasks), the
897     // RPC Wait dialog could appear continuously.  To prevent this,
898     // delay periodic RPCs for 1 second after the dialog closes.
899     wxDateTime dtNow(wxDateTime::Now());
900     if ((currentTabView & (VW_STAT | VW_DISK)) == 0) {
901         ts = dtNow - m_dtLasAsyncRPCDlgTime;
902         if (ts.GetSeconds()<= DELAYAFTERASYNCRPC_DLG) {
903             return;
904         }
905     }
906 
907     wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
908 
909     if (!IsConnected()) {
910         CFrameEvent event(wxEVT_FRAME_REFRESHVIEW, pFrame);
911         pFrame->GetEventHandler()->AddPendingEvent(event);
912         CTaskBarIcon* pTaskbar = wxGetApp().GetTaskBarIcon();
913         if (pTaskbar) {
914             CTaskbarEvent event(wxEVT_TASKBAR_REFRESH, pTaskbar);
915             pTaskbar->AddPendingEvent(event);
916         }
917         CDlgEventLog* eventLog = wxGetApp().GetEventLog();
918         if (eventLog) {
919             eventLog->OnRefresh();
920         }
921         return;
922     }
923 
924     // Several functions (such as Abort, Reset, Detach) display an
925     // "Are you sure?" dialog before passing a pointer to a result
926     // or project in a demand RPC call.  If Periodic RPCs continue
927     // to run during these dialogs, that pointer may no longer be
928     // valid by the time the demand RPC is executed.  So we suspend
929     // periodic RPCs during certain modal dialogs.
930     //
931     // Note that this depends on using wxGetApp().SafeMessageBox()
932     // instead of wxMessageBox in all tab views and anywhere else
933     // where a periodic RPC could cause a similar problem.
934     if (wxGetApp().IsSafeMesageBoxDisplayed()) {
935         return;
936     }
937 
938     // SET_LANGUAGE
939 
940     static bool first = true;
941     if (first) {
942         first = false;
943         safe_strcpy(m_szLanguage, wxGetApp().GetISOLanguageCode().mb_str());
944         request.clear();
945         request.which_rpc = RPC_SET_LANGUAGE;
946         request.arg1 = (void*)(const char*)&m_szLanguage;
947         request.rpcType = RPC_TYPE_ASYNC_NO_REFRESH;
948         RequestRPC(request);
949     }
950 
951     // *********** RPC_GET_CC_STATUS **************
952 
953     ts = dtNow - m_dtCachedCCStatusTimestamp;
954     if (ts.GetSeconds() >= CCSTATUS_RPC_INTERVAL) {
955         request.clear();
956         request.which_rpc = RPC_GET_CC_STATUS;
957         request.arg1 = &async_status_buf;
958         request.exchangeBuf = &status;
959         request.rpcType = RPC_TYPE_ASYNC_WITH_UPDATE_TASKBAR_ICON_AFTER;
960         request.completionTime = &m_dtCachedCCStatusTimestamp;
961         request.resultPtr = &m_iGet_status_rpc_result;
962 
963         RequestRPC(request);
964     }
965 
966     // *********** RPC_GET_MESSAGES **************
967 
968     // We must keep getting messages even if the Event Log is not open
969     // due to the limited size of the client's buffer, or some may be
970     // lost, causing gaps when the Event Log is later opened.
971     //
972     request.clear();
973     request.which_rpc = RPC_GET_MESSAGES;
974     // m_iLastMessageSequenceNumber could change between request and execution
975     // of RPC, so pass in a pointer rather than its value
976     request.arg1 = &m_iLastMessageSequenceNumber;
977     request.arg2 = &messages;
978     static bool _true = true;
979     request.arg3 = &_true;
980     request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_EVENT_LOG_AFTER;
981     request.completionTime = NULL;
982     request.resultPtr = &m_iGet_messages_rpc_result;
983 
984     RequestRPC(request);
985 
986     // *********** RPC_GET_NOTICES **************
987 
988     // We must keep getting notices even if the Notices Tab is not open
989     // so we can notify the user when new notices become available.
990     ts = dtNow - m_dtNoticesTimeStamp;
991     if ((currentTabView & VW_NOTIF) ||
992         (ts.GetSeconds() >= NOTICESBACKGROUNDRPC_INTERVAL)) {
993         // Don't request another get_notices RPC until we have
994         // updated m_iNoticeSequenceNumber from the previous
995         // one; otherwise we will get duplicate notices
996         if (!m_bWaitingForGetNoticesRPC) {
997             m_bWaitingForGetNoticesRPC =  true;
998             request.clear();
999             request.which_rpc = RPC_GET_NOTICES;
1000             // m_iNoticeSequenceNumber could change between request and execution
1001             // of RPC, so pass in a pointer rather than its value
1002             request.arg1 = &m_iNoticeSequenceNumber;
1003             request.arg2 = &notices;
1004             request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1005             if (!pFrame->IsShown()
1006 #ifdef __WXMAC__
1007                 || (!wxGetApp().IsApplicationVisible())
1008 #endif
1009             ) {
1010                 request.rpcType = RPC_TYPE_ASYNC_NO_REFRESH;
1011             }
1012 
1013             request.completionTime = &m_dtNoticesTimeStamp;
1014             request.resultPtr = &m_iGet_notices_rpc_result;
1015 
1016             RequestRPC(request);
1017         }
1018     }
1019 
1020     ts = dtNow - m_dtCachedStateTimestamp;
1021     if (ts.GetSeconds() >= STATERPC_INTERVAL) {
1022 
1023         // *********** RPC_GET_STATE **************
1024 
1025         request.clear();
1026         request.which_rpc = RPC_GET_STATE;
1027         request.arg1 = &async_state_buf;
1028         request.exchangeBuf = &state;
1029         request.rpcType = RPC_TYPE_ASYNC_NO_REFRESH;
1030         request.completionTime = &m_dtCachedStateTimestamp;
1031         request.resultPtr = &m_iGet_state_rpc_result;
1032 
1033         RequestRPC(request);
1034     }
1035 
1036     // **** All periodic RPCs after this point are used only
1037     // **** when refreshing Advanced Frame or Simple Frame views.
1038     // **** If the Event Log is shown, the Periodic RPC Timer is
1039     // **** set for 1 second even though the Frame View may need
1040     // **** less frequent update.
1041     // **** The argument frameRefreshRate is 0 if an immediate
1042     // **** update is needed due to some user action, etc.
1043     // **** Otherwise frameRefreshRate is the rate at which the
1044     // **** the current Frame View should be updated.
1045     ts = dtNow - m_dtLastFrameViewRefreshRPCTime;
1046     if (ts.GetMilliseconds() < (frameRefreshRate - 500)) return;
1047 
1048     // Don't do periodic RPC calls when hidden / minimized
1049     if (!pFrame->IsShown()) return;
1050 #ifdef __WXMAC__
1051     if (!wxGetApp().IsApplicationVisible()) return;
1052 #endif
1053 
1054     m_dtLastFrameViewRefreshRPCTime = dtNow;
1055 
1056     // *********** RPC_GET_PROJECT_STATUS1 **************
1057 
1058     if (currentTabView & VW_PROJ) {
1059         ts = dtNow - m_dtProjectsStatusTimestamp;
1060         if (ts.GetSeconds() >= PROJECTSTATUSRPC_INTERVAL) {
1061             request.clear();
1062             request.which_rpc = RPC_GET_PROJECT_STATUS1;
1063             request.arg1 = &async_projects_update_buf;
1064             request.arg2 = &state;
1065             request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1066             request.completionTime = &m_dtProjectsStatusTimestamp;
1067             request.resultPtr = &m_iGet_project_status1_rpc_result;
1068 
1069             RequestRPC(request);
1070         }
1071     }
1072 
1073     // *********** RPC_GET_RESULTS **************
1074 
1075     if (currentTabView & VW_TASK) {
1076         ts = dtNow - m_dtResultsTimestamp;
1077         wxLongLong secondsSinceLastRPC = ts.GetSeconds();
1078         if (secondsSinceLastRPC >= RESULTSRPC_INTERVAL) {
1079             if (secondsSinceLastRPC >= (m_fResultsRPCExecutionTime * GET_RESULTS_FREQUENCY_FACTOR)) {
1080                 request.clear();
1081                 request.which_rpc = RPC_GET_RESULTS;
1082                 request.arg1 = &async_results_buf;
1083                 request.arg2 = &m_ActiveTasksOnly;
1084                 request.exchangeBuf = &results;
1085                 request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1086                 request.completionTime = &m_dtResultsTimestamp;
1087                 request.RPCExecutionTime = &m_fResultsRPCExecutionTime;
1088                 request.resultPtr = &m_iGet_results_rpc_result;
1089 
1090                 RequestRPC(request);
1091             }
1092         }
1093     }
1094 
1095     // *********** RPC_GET_FILE_TRANSFERS **************
1096 
1097     if (currentTabView & VW_XFER) {
1098         ts = dtNow - m_dtFileTransfersTimestamp;
1099         if (ts.GetSeconds() >= FILETRANSFERSRPC_INTERVAL) {
1100             request.clear();
1101             request.which_rpc = RPC_GET_FILE_TRANSFERS;
1102             request.arg1 = &async_ft_buf;
1103             request.exchangeBuf = &ft;
1104             request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1105             request.completionTime = &m_dtFileTransfersTimestamp;
1106             request.resultPtr = &m_iGet_file_transfers_rpc_result;
1107 
1108             RequestRPC(request);
1109         }
1110     }
1111 
1112     // *********** RPC_GET_STATISTICS **************
1113 
1114     if (currentTabView & VW_STAT) {
1115         ts = dtNow - m_dtStatisticsStatusTimestamp;
1116         if (ts.GetSeconds() >= STATISTICSSTATUSRPC_INTERVAL) {
1117             request.clear();
1118             request.which_rpc = RPC_GET_STATISTICS;
1119             request.arg1 = &async_statistics_status_buf;
1120             request.exchangeBuf = &statistics_status;
1121             request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1122             request.completionTime = &m_dtStatisticsStatusTimestamp;
1123             request.resultPtr = &m_iGet_statistics_rpc_result;
1124 
1125             RequestRPC(request);
1126         }
1127     }
1128 
1129     // *********** RPC_GET_DISK_USAGE **************
1130 
1131     if (currentTabView & VW_DISK) {
1132         ts = dtNow - m_dtDiskUsageTimestamp;
1133         if ((ts.GetSeconds() >= DISKUSAGERPC_INTERVAL) || disk_usage.projects.empty()) {
1134             request.clear();
1135             request.which_rpc = RPC_GET_DISK_USAGE;
1136             request.arg1 = &async_disk_usage_buf;
1137             request.exchangeBuf = &disk_usage;
1138             request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1139             request.completionTime = &m_dtDiskUsageTimestamp;
1140             request.resultPtr = &m_iGet_dsk_usage_rpc_result;
1141 
1142             RequestRPC(request);
1143         }
1144     }
1145 
1146     // *********** GET_SIMPLE_GUI_INFO2 **************
1147     if (currentTabView & VW_SGUI) {
1148         ts = dtNow - m_dtCachedSimpleGUITimestamp;
1149         if (ts.GetSeconds() >= CACHEDSIMPLEGUIRPC_INTERVAL) {
1150             request.clear();
1151             request.which_rpc = RPC_GET_SIMPLE_GUI_INFO2;
1152             request.arg1 = &async_projects_update_buf;
1153             request.arg2 = &state;
1154             request.arg3 = &async_results_buf;
1155             request.arg4 = &m_ActiveTasksOnly;
1156             request.exchangeBuf = &results;
1157             request.rpcType = RPC_TYPE_ASYNC_WITH_REFRESH_AFTER;
1158             request.completionTime = &m_dtCachedSimpleGUITimestamp;
1159             request.resultPtr = &m_iGet_simple_gui2_rpc_result;
1160 
1161             RequestRPC(request);
1162         }
1163     }
1164     // *********** RPC_ACCT_MGR_INFO **************
1165 
1166     if (currentTabView & VW_SGUI) {
1167         ts = dtNow - m_dtCachedAcctMgrInfoTimestamp;
1168         if (ts.GetSeconds() >= CACHEDACCTMGRINFORPC_INTERVAL) {
1169             request.clear();
1170             request.which_rpc = RPC_ACCT_MGR_INFO;
1171             request.arg1 = &async_ami_buf;
1172             request.exchangeBuf = &ami;
1173             request.rpcType = RPC_TYPE_ASYNC_NO_REFRESH;
1174             request.completionTime = &m_dtCachedAcctMgrInfoTimestamp;
1175             request.resultPtr = &m_iAcct_mgr_info_rpc_result;
1176 
1177             RequestRPC(request);
1178         }
1179     }
1180 }
1181 
1182 
1183 // TODO: Is it enough to just reset m_dtCachedStateTimestamp
1184 // and let RunPeriodicRPCs() update the state?  This would avoid
1185 // displaying the "Please wait" dialog on multi-processor computers.
1186 // Possible exceptions might be when ForceCacheUpdate() is called
1187 // from these routines (which may need immediate results):
1188 //      CAdvancedFrame::OnConnect()
1189 //      CDlgItemProperties::FormatApplicationName()
1190 //      WorkunitNotebook::AddTab()
1191 //      CMainDocument::CachedProjectStatusUpdate()
1192 //      CMainDocument::CachedSimpleGUIUpdate()
1193 //
1194 // Currently, no calls to ForceCacheUpdate pass false as the arg.
1195 //
ForceCacheUpdate(bool immediate)1196 int CMainDocument::ForceCacheUpdate(bool immediate) {
1197     wxLogTrace(wxT("Function Start/End"), wxT("CMainDocument::ForceCacheUpdate - Function Begin"));
1198 
1199     if (!immediate) {
1200         m_dtCachedStateTimestamp = wxDateTime((time_t)0);
1201         return m_iGet_state_rpc_result;
1202     }
1203 
1204     int     retval = 0;
1205 
1206     if (IsConnected()) {
1207         m_dtCachedStateTimestamp = wxDateTime::Now();
1208         m_iGet_state_rpc_result = rpc.get_state(state);
1209         if (m_iGet_state_rpc_result) {
1210             retval = m_iGet_state_rpc_result;
1211             wxLogTrace(wxT("Function Status"), wxT("CMainDocument::ForceCacheUpdate - Get State Failed '%d'"), retval);
1212             m_pNetworkConnection->SetStateDisconnected();
1213         }
1214 
1215     } else {
1216         retval = -1;
1217     }
1218 
1219     wxLogTrace(wxT("Function Start/End"), wxT("CMainDocument::ForceCacheUpdate - Function End"));
1220     return retval;
1221 }
1222 
1223 
RunBenchmarks()1224 int CMainDocument::RunBenchmarks() {
1225     return rpc.run_benchmarks();
1226 }
1227 
1228 
CoreClientQuit()1229 int CMainDocument::CoreClientQuit() {
1230     return rpc.quit();
1231 }
1232 
1233 
IsUserAuthorized()1234 bool CMainDocument::IsUserAuthorized() {
1235 #ifndef _WIN32
1236 #ifdef SANDBOX
1237 #ifndef __WXMAC__   // Currently unauthorized users can't run Manager, so this would be redundant
1238     static bool         sIsAuthorized = false;
1239     group               *grp;
1240     gid_t               rgid, boinc_master_gid;
1241     char                *userName, *groupMember;
1242     int                 i;
1243 
1244     if (g_use_sandbox) {
1245         if (sIsAuthorized)
1246             return true;            // We already checked and OK'd current user
1247 
1248         grp = getgrnam(BOINC_MASTER_GROUP_NAME);
1249         if (grp) {
1250             boinc_master_gid = grp->gr_gid;
1251 
1252             rgid = getgid();
1253             if (rgid == boinc_master_gid) {
1254                 sIsAuthorized = true;           // User's primary group is boinc_master
1255                 return true;
1256             }
1257 
1258             userName = getlogin();
1259             if (userName) {
1260                 for (i=0; ; i++) {              // Step through all users in group boinc_master
1261                     groupMember = grp->gr_mem[i];
1262                     if (groupMember == NULL)
1263                         break;                  // User is not a member of group boinc_master
1264                     if (strcmp(userName, groupMember) == 0) {
1265                         sIsAuthorized = true;   // User is a member of group boinc_master
1266                         return true;
1267                     }
1268                 }       // for (i)
1269             }           // if (userName)
1270         }               // if grp
1271 
1272 #ifdef __WXMAC__
1273         if (Mac_Authorize()) {          // Run Mac Authentication dialog
1274             sIsAuthorized = true;       // Authenticated by password
1275             return true;
1276         }
1277 #endif      // __WXMAC__
1278 
1279         return false;
1280 
1281     }       // if (g_use_sandbox)
1282 #endif      // SANDBOX
1283 #endif      // #ifndef _WIN32
1284 #endif      // #ifndef __WXMAC__
1285 
1286     return true;
1287 }
1288 
CheckForVersionUpdate(bool showMessage)1289 void CMainDocument::CheckForVersionUpdate(bool showMessage) {
1290     std::string version, url;
1291     wxString message, title;
1292     title.Printf(_("Version Update"));
1293     wxString applicationName = wxGetApp().GetSkinManager()->GetAdvanced()->GetApplicationName();
1294     if (IsConnected()) {
1295         rpc.get_newer_version(version, url);
1296 
1297         if (!showMessage)
1298             return;
1299 
1300         if (!version.empty() && !url.empty()) {
1301             message.Printf(_("A new version of %s is available. You can download it here: %s"), applicationName, url);
1302         }
1303         else {
1304             message.Printf(_("There is no new version of %s available for download."), applicationName);
1305         }
1306     }
1307     else {
1308         message.Printf(_("%s is not connected to the client"), applicationName);
1309     }
1310     if (showMessage) {
1311         wxGetApp().SafeMessageBox(message, title);
1312     }
1313 }
1314 
CachedProjectStatusUpdate(bool bForce)1315 int CMainDocument::CachedProjectStatusUpdate(bool bForce) {
1316     int     i = 0;
1317 
1318     if (! IsConnected()) return -1;
1319 
1320 #if USE_CACHE_TIMEOUTS
1321     wxTimeSpan ts(wxDateTime::Now() - m_dtProjecStatusTimestamp);
1322     if (ts.GetSeconds() >= (2 * PROJECTSTATUSRPC_INTERVAL)) bForce = true;
1323 #endif
1324     if (m_dtProjectsStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) bForce = true;
1325 
1326     if (bForce) {
1327         m_dtProjectsStatusTimestamp = wxDateTime::Now();
1328         m_iGet_project_status1_rpc_result = rpc.get_project_status(async_projects_update_buf, state);
1329     }
1330 
1331     if (m_iGet_project_status1_rpc_result) {
1332         wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedProjectStatusUpdate - Get Project Status Failed '%d'"), m_iGet_project_status1_rpc_result);
1333         ForceCacheUpdate();
1334 //        return m_iGet_project_status1_rpc_result;
1335     }
1336 
1337     m_fProjectTotalResourceShare = 0.0;
1338     for (i=0; i < (long)state.projects.size(); i++) {
1339         m_fProjectTotalResourceShare += state.projects.at(i)->resource_share;
1340     }
1341     if (!m_fProjectTotalResourceShare) m_fProjectTotalResourceShare = 1;
1342 
1343     return m_iGet_project_status1_rpc_result;
1344 }
1345 
1346 
project(unsigned int i)1347 PROJECT* CMainDocument::project(unsigned int i) {
1348     PROJECT* pProject = NULL;
1349 
1350     try {
1351         if (!state.projects.empty())
1352             pProject = state.projects.at(i);
1353     }
1354     catch (std::out_of_range e) {
1355         pProject = NULL;
1356     }
1357 
1358     return pProject;
1359 }
1360 
1361 
project(char * url)1362 PROJECT* CMainDocument::project(char* url) {
1363     for (unsigned int i=0; i< state.projects.size(); i++) {
1364         PROJECT* tp = state.projects[i];
1365         if (!strcmp(url, tp->master_url)) return tp;
1366     }
1367     return NULL;
1368 }
1369 
1370 
GetProjectCount()1371 int CMainDocument::GetProjectCount() {
1372     int iCount = -1;
1373 
1374     CachedProjectStatusUpdate();
1375     CachedStateUpdate();
1376 
1377     if (!state.projects.empty())
1378         iCount = (int)state.projects.size();
1379 
1380     return iCount;
1381 }
1382 
1383 
ProjectDetach(int iIndex)1384 int CMainDocument::ProjectDetach(int iIndex) {
1385     PROJECT* pProject = NULL;
1386     int iRetVal = -1;
1387 
1388     pProject = project(iIndex);
1389 
1390     if (pProject)
1391         iRetVal = rpc.project_op((*pProject), "detach");
1392 
1393     return iRetVal;
1394 }
1395 
ProjectUpdate(int iIndex)1396 int CMainDocument::ProjectUpdate(int iIndex) {
1397     PROJECT* pProject = NULL;
1398     int iRetVal = -1;
1399 
1400     pProject = project(iIndex);
1401 
1402     if (pProject)
1403         iRetVal = rpc.project_op((*pProject), "update");
1404 
1405     return iRetVal;
1406 }
1407 
ProjectReset(int iIndex)1408 int CMainDocument::ProjectReset(int iIndex) {
1409     PROJECT* pProject = NULL;
1410     int iRetVal = -1;
1411 
1412     pProject = project(iIndex);
1413 
1414     if (pProject)
1415         iRetVal = rpc.project_op((*pProject), "reset");
1416 
1417     return iRetVal;
1418 }
1419 
ProjectSuspend(int iIndex)1420 int CMainDocument::ProjectSuspend(int iIndex) {
1421     PROJECT* pProject = NULL;
1422     int iRetVal = -1;
1423 
1424     pProject = project(iIndex);
1425 
1426     if (pProject)
1427         iRetVal = rpc.project_op((*pProject), "suspend");
1428 
1429     return iRetVal;
1430 }
1431 
ProjectResume(int iIndex)1432 int CMainDocument::ProjectResume(int iIndex) {
1433     PROJECT* pProject = NULL;
1434     int iRetVal = -1;
1435 
1436     pProject = project(iIndex);
1437 
1438     if (pProject)
1439         iRetVal = rpc.project_op((*pProject), "resume");
1440 
1441     return iRetVal;
1442 }
1443 
ProjectNoMoreWork(int iIndex)1444 int CMainDocument::ProjectNoMoreWork(int iIndex) {
1445     PROJECT* pProject = NULL;
1446     int iRetVal = -1;
1447 
1448     pProject = project(iIndex);
1449 
1450     if (pProject)
1451         iRetVal = rpc.project_op((*pProject), "nomorework");
1452 
1453     return iRetVal;
1454 }
1455 
ProjectAllowMoreWork(int iIndex)1456 int CMainDocument::ProjectAllowMoreWork(int iIndex) {
1457     PROJECT* pProject = NULL;
1458     int iRetVal = -1;
1459 
1460     pProject = project(iIndex);
1461 
1462     if (pProject)
1463         iRetVal = rpc.project_op((*pProject), "allowmorework");
1464 
1465     return iRetVal;
1466 }
1467 
CachedResultsStatusUpdate()1468 int CMainDocument::CachedResultsStatusUpdate() {
1469     if (! IsConnected()) return -1;
1470     bool active_tasks_only = false;
1471     bool immediate = false;
1472 
1473     CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
1474     if (pFrame) {
1475         wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
1476 
1477         if (pFrame->GetCurrentViewPage() & VW_TASK) {
1478             active_tasks_only = m_ActiveTasksOnly;
1479         }
1480     }
1481 
1482 #if USE_CACHE_TIMEOUTS
1483     wxTimeSpan ts(wxDateTime::Now() - m_dtResultsTimestamp);
1484     if (ts.GetSeconds() >= (2 * RESULTSRPC_INTERVAL)) immediate = true;
1485 #endif
1486     if (m_dtResultsTimestamp.IsEqualTo(wxDateTime((time_t)0))) immediate = true;
1487 
1488     if (immediate) {
1489         m_dtResultsTimestamp = wxDateTime::Now();
1490         m_iGet_results_rpc_result = rpc.get_results(results, active_tasks_only);
1491     }
1492 
1493     if (m_iGet_results_rpc_result) {
1494         wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedResultsStatusUpdate - Get Result Status Failed '%d'"), m_iGet_results_rpc_result);
1495         ForceCacheUpdate();
1496     }
1497 
1498     return m_iGet_results_rpc_result;
1499 }
1500 
1501 
result(unsigned int i)1502 RESULT* CMainDocument::result(unsigned int i) {
1503     RESULT* pResult = NULL;
1504 
1505     try {
1506         if (!results.results.empty())
1507             pResult = results.results.at(i);
1508     }
1509     catch (std::out_of_range e) {
1510         pResult = NULL;
1511     }
1512 
1513     return pResult;
1514 }
1515 
1516 // get the result not by index, but by name
1517 //
result(const wxString & name,const wxString & project_url)1518 RESULT* CMainDocument::result(const wxString& name, const wxString& project_url) {
1519     RESULT* pResult = NULL;
1520 
1521     try {
1522         if (!results.results.empty()) {
1523             //iterating over the vector and find the right result
1524             for(unsigned int i=0; i< results.results.size();i++) {
1525                 RESULT* tResult = results.results.at(i);
1526                 wxString resname(tResult->name, wxConvUTF8);
1527                 if(resname.IsSameAs(name)){
1528                     wxString resurl(tResult->project_url, wxConvUTF8);
1529                     if(resurl.IsSameAs(project_url)){
1530                         pResult = tResult;
1531                         break;
1532                     }
1533                 }
1534             }
1535         }
1536     }
1537     catch (std::out_of_range e) {
1538         pResult = NULL;
1539     }
1540 
1541     return pResult;
1542 }
1543 
GetWorkCount()1544 int CMainDocument::GetWorkCount() {
1545     int iCount = -1;
1546 
1547     CachedResultsStatusUpdate();
1548     CachedStateUpdate();
1549 
1550     if (!results.results.empty())
1551         iCount = (int)results.results.size();
1552 
1553     return iCount;
1554 }
1555 
1556 
WorkSuspend(char * url,char * name)1557 int CMainDocument::WorkSuspend(char* url, char* name) {
1558     int iRetVal = 0;
1559 
1560     RESULT* pStateResult = state.lookup_result(url, name);
1561     if (pStateResult) {
1562         iRetVal = rpc.result_op((*pStateResult), "suspend");
1563     } else {
1564         ForceCacheUpdate();
1565     }
1566 
1567     return iRetVal;
1568 }
1569 
1570 
WorkResume(char * url,char * name)1571 int CMainDocument::WorkResume(char* url, char* name) {
1572     int iRetVal = 0;
1573 
1574     RESULT* pStateResult = state.lookup_result(url, name);
1575     if (pStateResult) {
1576         iRetVal = rpc.result_op((*pStateResult), "resume");
1577     } else {
1578         ForceCacheUpdate();
1579     }
1580 
1581     return iRetVal;
1582 }
1583 
1584 
1585 // If the graphics application for the current task is already
1586 // running, return a pointer to its RUNNING_GFX_APP struct.
1587 //
GetRunningGraphicsApp(RESULT * rp,int slot)1588 RUNNING_GFX_APP* CMainDocument::GetRunningGraphicsApp(
1589     RESULT* rp, int slot
1590 ) {
1591     bool exited = false;
1592     std::vector<RUNNING_GFX_APP>::iterator gfx_app_iter;
1593 
1594     for( gfx_app_iter = m_running_gfx_apps.begin();
1595         gfx_app_iter != m_running_gfx_apps.end();
1596         ++gfx_app_iter
1597     ) {
1598         if ((slot >= 0) && ((*gfx_app_iter).slot != slot)) continue;
1599 
1600 #ifdef _WIN32
1601         unsigned long exit_code;
1602         if (GetExitCodeProcess((*gfx_app_iter).pid, &exit_code)) {
1603             if (exit_code != STILL_ACTIVE) {
1604                 exited = true;
1605             }
1606         }
1607 #else
1608         if (waitpid((*gfx_app_iter).pid, 0, WNOHANG) == (*gfx_app_iter).pid) {
1609             exited = true;
1610         }
1611 #endif
1612         if (! exited) {
1613             if ((rp->name == (*gfx_app_iter).name) &&
1614                 (rp->project_url == (*gfx_app_iter).project_url)
1615             ) {
1616                 return &(*gfx_app_iter);
1617             }
1618 
1619             // Graphics app is still running but the slot now has a different task
1620             KillGraphicsApp((*gfx_app_iter).pid);
1621         }
1622 
1623         // Either the graphics app had already exited or we just killed it
1624         (*gfx_app_iter).name.clear();
1625         (*gfx_app_iter).project_url.clear();
1626         m_running_gfx_apps.erase(gfx_app_iter);
1627         return NULL;
1628     }
1629     return NULL;
1630 }
1631 
1632 
1633 // Kill any running graphics apps whose worker tasks aren't running
KillInactiveGraphicsApps()1634 void CMainDocument::KillInactiveGraphicsApps() {
1635 /*
1636     std::vector<RUNNING_GFX_APP>::iterator gfx_app_iter;
1637     unsigned int i;
1638     bool bStillRunning;
1639 
1640     if (m_running_gfx_apps.size() <= 0) return;
1641 
1642     // If none of the Tasks displays are visible, we need to update
1643     // the results vector.  This call does nothing if recently updated
1644     // by a call from CViewWork or CViewTabPage.
1645     CachedResultsStatusUpdate();
1646 
1647     gfx_app_iter = m_running_gfx_apps.begin();
1648     while (gfx_app_iter != m_running_gfx_apps.end()) {
1649         bStillRunning = false;
1650 
1651         for (i=0; i<results.results.size(); i++) {
1652             if ((results.results.at(i))->state != RESULT_FILES_DOWNLOADED) continue;
1653             if (!(results.results.at(i))->active_task) continue;
1654             if ((results.results.at(i))->scheduler_state != CPU_SCHED_SCHEDULED) continue;
1655             if ((results.results.at(i))->name != (*gfx_app_iter).name) continue;
1656             if ((results.results.at(i))->project_url != (*gfx_app_iter).project_url) continue;
1657             bStillRunning =  true;
1658             break;
1659         }
1660 
1661         if (!bStillRunning) {
1662             KillGraphicsApp((*gfx_app_iter).pid);
1663             gfx_app_iter = m_running_gfx_apps.erase(gfx_app_iter);
1664         } else {
1665             gfx_app_iter++;
1666         }
1667     }
1668 */
1669 }
1670 
1671 
KillAllRunningGraphicsApps()1672 void CMainDocument::KillAllRunningGraphicsApps()
1673 {
1674     size_t i, n;
1675     std::vector<RUNNING_GFX_APP>::iterator gfx_app_iter;
1676 
1677     n = m_running_gfx_apps.size();
1678     for (i=0; i<n; i++) {
1679         gfx_app_iter = m_running_gfx_apps.begin();
1680         KillGraphicsApp((*gfx_app_iter).pid);
1681         (*gfx_app_iter).name.clear();
1682         (*gfx_app_iter).project_url.clear();
1683         m_running_gfx_apps.erase(gfx_app_iter);
1684     }
1685 }
1686 
1687 
1688 #ifdef _WIN32
KillGraphicsApp(HANDLE pid)1689 void CMainDocument::KillGraphicsApp(HANDLE pid) {
1690     kill_program(pid);
1691 }
1692 #else
KillGraphicsApp(int pid)1693 void CMainDocument::KillGraphicsApp(int pid) {
1694     char* argv[6];
1695     char currentDir[1024];
1696     char thePIDbuf[20];
1697     int id, iRetVal;
1698 
1699 
1700     if (g_use_sandbox) {
1701         snprintf(thePIDbuf, sizeof(thePIDbuf), "%d", pid);
1702         argv[0] = "switcher";
1703         argv[1] = "/bin/kill";
1704         argv[2] =  "kill";
1705         argv[3] = "-KILL";
1706         argv[4] = thePIDbuf;
1707         argv[5] = 0;
1708 
1709         iRetVal = run_program(
1710             getcwd(currentDir, sizeof(currentDir)),
1711             "./switcher/switcher",
1712             5,
1713             argv,
1714             0,
1715             id
1716         );
1717     } else {
1718         kill_program(pid);
1719     }
1720 }
1721 #endif
1722 
WorkShowGraphics(RESULT * rp)1723 int CMainDocument::WorkShowGraphics(RESULT* rp) {
1724     int iRetVal = 0;
1725 
1726     if (strlen(rp->web_graphics_url)) {
1727         wxString url(rp->web_graphics_url, wxConvUTF8);
1728         wxLaunchDefaultBrowser(url);
1729         return 0;
1730     }
1731     if (strlen(rp->graphics_exec_path)) {
1732         // V6 Graphics
1733         RUNNING_GFX_APP gfx_app;
1734         RUNNING_GFX_APP* previous_gfx_app;
1735         char *p;
1736         int slot;
1737 #ifdef __WXMSW__
1738         HANDLE   id;
1739 #else
1740         int      id;
1741 #endif
1742 
1743         p = strrchr((char*)rp->slot_path, '/');
1744         if (!p) return ERR_INVALID_PARAM;
1745         slot = atoi(p+1);
1746 
1747         // See if we are already running the graphics application for this task
1748         previous_gfx_app = GetRunningGraphicsApp(rp, slot);
1749 
1750 #ifndef __WXMSW__
1751         char* argv[4];
1752 
1753         if (previous_gfx_app) {
1754 #ifdef __WXMAC__
1755             // If this graphics app is already running,
1756             // just bring it to the front
1757             //
1758             BringAppWithPidToFront(previous_gfx_app->pid);
1759 #endif
1760             // If graphics app is already running, don't launch a second instance
1761             //
1762             return 0;
1763         }
1764         argv[0] = "switcher";
1765         // For unknown reasons on Macs, the graphics application
1766         // exits with "RegisterProcess failed (error = -50)" unless
1767         // we pass its full path twice in the argument list to execv.
1768         //
1769         argv[1] = (char *)rp->graphics_exec_path;
1770         argv[2] = (char *)rp->graphics_exec_path;
1771         argv[3] = 0;
1772 
1773         if (g_use_sandbox) {
1774             iRetVal = run_program(
1775                 rp->slot_path,
1776                 "../../switcher/switcher",
1777                 3,
1778                 argv,
1779                 0,
1780                 id
1781             );
1782         } else {
1783             iRetVal = run_program(
1784                 rp->slot_path,
1785                 rp->graphics_exec_path,
1786                 1,
1787                 &argv[2],
1788                 0,
1789                 id
1790             );
1791         }
1792 #else
1793         char* argv[2];
1794 
1795         // If graphics app is already running, don't launch a second instance
1796         //
1797         if (previous_gfx_app) return 0;
1798         argv[0] = 0;
1799 
1800         iRetVal = run_program(
1801             rp->slot_path,
1802             rp->graphics_exec_path,
1803             0,
1804             argv,
1805             0,
1806             id
1807         );
1808 #endif
1809         if (!iRetVal) {
1810             gfx_app.slot = slot;
1811             gfx_app.project_url = rp->project_url;
1812             gfx_app.name = rp->name;
1813             gfx_app.pid = id;
1814             m_running_gfx_apps.push_back(gfx_app);
1815         }
1816     }
1817     return iRetVal;
1818 }
1819 
1820 
WorkShowVMConsole(RESULT * res)1821 int CMainDocument::WorkShowVMConsole(RESULT* res) {
1822     int iRetVal = 0;
1823 
1824     if (strlen(res->remote_desktop_addr)) {
1825         wxString strConnection(res->remote_desktop_addr, wxConvUTF8);
1826         wxString strCommand;
1827 
1828 #if   defined(__WXMSW__)
1829         strCommand = wxT("mstsc.exe /v:") + strConnection;
1830         wxExecute(strCommand);
1831 #elif defined(__WXGTK__)
1832         strCommand = wxT("rdesktop-vrdp ") + strConnection;
1833         wxExecute(strCommand);
1834 #elif defined(__WXMAC__)
1835         OSStatus status = noErr;
1836         char pathToCoRD[MAXPATHLEN];
1837 
1838         // I have found no reliable way to pass the IP address and port to Microsoft's
1839         // Remote Desktop Connection application for the Mac, so I'm using CoRD.
1840         // Unfortunately, CoRD does not seem as reliable as I would like either.
1841         //
1842         // First try to find the CoRD application by Bundle ID and Creator Code
1843         status = GetPathToAppFromID('RDC#', CFSTR("net.sf.cord"), pathToCoRD, MAXPATHLEN);
1844         if (status != noErr) {
1845             CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
1846             if (pFrame) {
1847                 pFrame->ShowAlert(
1848                     _("Missing application"),
1849                     _("Please download and install the CoRD application from http://cord.sourceforge.net"),
1850                     wxOK | wxICON_INFORMATION,
1851                     false
1852                 );
1853             }
1854             return ERR_FILE_MISSING;
1855         }
1856 
1857         strCommand = wxT("osascript -e 'tell application \"CoRD\"' -e 'activate' -e 'open location \"rdp://") + strConnection + wxT("\"' -e 'end tell'");
1858         strCommand.Replace(wxT("localhost"), wxT("127.0.0.1"));
1859         system(strCommand.char_str());
1860 #endif
1861     }
1862 
1863     return iRetVal;
1864 }
1865 
1866 
WorkAbort(char * url,char * name)1867 int CMainDocument::WorkAbort(char* url, char* name) {
1868     int iRetVal = 0;
1869 
1870     RESULT* pStateResult = state.lookup_result(url, name);
1871     if (pStateResult) {
1872         iRetVal = rpc.result_op((*pStateResult), "abort");
1873     } else {
1874         ForceCacheUpdate();
1875     }
1876 
1877     return iRetVal;
1878 }
1879 
1880 
1881 // Call this only when notice buffer is stable
1882 // Note: This must not call any rpcs.
1883 // This is now called after each get_notices RPC from
1884 //   CMainDocument::HandleCompletedRPC() .
CachedNoticeUpdate()1885 int CMainDocument::CachedNoticeUpdate() {
1886     static bool in_this_func = false;
1887 
1888     if (in_this_func) return 0;
1889     in_this_func = true;
1890 
1891     if (IsConnected()) {
1892         // Can't look up previous last read message until we know machine name
1893         if (!strlen(state.host_info.domain_name)) {
1894             goto done;
1895         }
1896 
1897         // rpc.get_notices is now called from RunPeriodicRPCs()
1898         if (m_iGet_notices_rpc_result) {
1899             wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedNoticeUpdate - Get Notices Failed '%d'"), m_iGet_notices_rpc_result);
1900             m_pNetworkConnection->SetStateDisconnected();
1901             goto done;
1902         }
1903 
1904         if (notices.notices.size() > 0) {
1905             m_iNoticeSequenceNumber = notices.notices[0]->seqno;
1906 
1907             if (m_iLastReadNoticeSequenceNumber < 0) {
1908                 m_iLastReadNoticeSequenceNumber = 0;    // Do this only once
1909                 RestoreUnreadNoticeInfo();
1910             }
1911         }
1912     }
1913 
1914 done:
1915     in_this_func = false;
1916     return 0;
1917 }
1918 
1919 
SaveUnreadNoticeInfo()1920 void CMainDocument::SaveUnreadNoticeInfo() {
1921     static double   dLastSavedArrivalTime = 0.0;
1922 
1923     if (dLastSavedArrivalTime != m_dLastReadNoticeArrivalTime) {
1924         wxString        strBaseConfigLocation = wxString(wxT("/Notices/"));
1925         wxConfigBase*   pConfig = wxConfigBase::Get(FALSE);
1926         wxString        strDomainName = wxString(state.host_info.domain_name, wxConvUTF8, strlen(state.host_info.domain_name));
1927         wxString        strArrivalTime = wxEmptyString;
1928 
1929         pConfig->SetPath(strBaseConfigLocation + strDomainName);
1930         // wxConfigBase::Write(const wxString& key, double value) has
1931         // insufficient precision so write a wxString.
1932         strArrivalTime.Printf(wxT("%f"), m_dLastReadNoticeArrivalTime);
1933         pConfig->Write(wxT("lastReadNoticeTime"), strArrivalTime);
1934         dLastSavedArrivalTime = m_dLastReadNoticeArrivalTime;
1935     }
1936 }
1937 
1938 
RestoreUnreadNoticeInfo()1939 void CMainDocument::RestoreUnreadNoticeInfo() {
1940     wxString        strBaseConfigLocation = wxString(wxT("/Notices/"));
1941     wxConfigBase*   pConfig = wxConfigBase::Get(FALSE);
1942     wxString        strDomainName = wxString(state.host_info.domain_name, wxConvUTF8, strlen(state.host_info.domain_name));
1943     double          dLastReadNoticeTime;
1944     wxString        strArrivalTime = wxEmptyString;
1945     int             i, n = (int)notices.notices.size();
1946 
1947     pConfig->SetPath(strBaseConfigLocation + strDomainName);
1948 
1949     if (pConfig->Read(wxT("LastReadNoticeTime"), &strArrivalTime)) {
1950         strArrivalTime.ToDouble(&dLastReadNoticeTime);
1951         // To avoid problems caused by rounding in save & restore operation, test in
1952         // reverse order (oldest first) and for arrival time <= dLastReadNoticeTime
1953         for (i=n-1; i>=0; --i) {
1954             if (notices.notices[i]->arrival_time <= dLastReadNoticeTime) {
1955                 m_iLastReadNoticeSequenceNumber = notices.notices[i]->seqno;
1956                 m_dLastReadNoticeArrivalTime = notices.notices[i]->arrival_time;
1957             }
1958         }
1959     }
1960 }
1961 
1962 
notice(unsigned int i)1963 NOTICE* CMainDocument::notice(unsigned int i) {
1964     NOTICE* pNotice = NULL;
1965 
1966     try {
1967         if (!notices.notices.empty())
1968             pNotice = notices.notices.at(i);
1969     }
1970     catch (std::out_of_range e) {
1971         pNotice = NULL;
1972     }
1973 
1974     return pNotice;
1975 }
1976 
1977 
GetNoticeCount()1978 int CMainDocument::GetNoticeCount() {
1979     int iCount = -1;
1980 
1981     // CachedNoticeUpdate() is now called from CMainDocument::OnRPCComplete()
1982     // only after a get_notices RPC completes so notices buffer is stable.
1983     CachedStateUpdate();
1984 
1985     if (notices.received) {
1986         iCount = (int)notices.notices.size();
1987     }
1988 
1989     return iCount;
1990 }
1991 
1992 
GetUnreadNoticeCount()1993 int CMainDocument::GetUnreadNoticeCount() {
1994     int iCount = 0;
1995     if (!notices.notices.empty()) {
1996         for (unsigned int i = 0; i < notices.notices.size(); i++) {
1997             if (notices.notices[i]->arrival_time > m_dLastReadNoticeArrivalTime) {
1998                 iCount++;
1999             }
2000         }
2001     }
2002     return iCount;
2003 }
2004 
2005 
UpdateUnreadNoticeState()2006 void CMainDocument::UpdateUnreadNoticeState() {
2007     if (!notices.notices.empty()) {
2008         m_iLastReadNoticeSequenceNumber = notices.notices[0]->seqno;
2009         m_dLastReadNoticeArrivalTime = notices.notices[0]->arrival_time;
2010         SaveUnreadNoticeInfo();
2011     }
2012 }
2013 
2014 
ResetNoticeState()2015 int CMainDocument::ResetNoticeState() {
2016     notices.clear();
2017     m_iNoticeSequenceNumber = 0;
2018     m_iLastReadNoticeSequenceNumber = -1;
2019     m_dLastReadNoticeArrivalTime = 0.0;
2020     return 0;
2021 }
2022 
2023 
2024 // Replace CRLFs and LFs with HTML breaks.
2025 //
eol_to_br(wxString & strMessage)2026 void eol_to_br(wxString& strMessage) {
2027     strMessage.Replace(wxT("\r\n"), wxT("<BR>"));
2028     strMessage.Replace(wxT("\n"), wxT("<BR>"));
2029 }
2030 
2031 // Remove CRLFs and LFs
2032 //
remove_eols(wxString & strMessage)2033 void remove_eols(wxString& strMessage) {
2034     strMessage.Replace(wxT("\r\n"), wxT(""));
2035     strMessage.Replace(wxT("\n"), wxT(""));
2036 }
2037 
2038 // Replace https:// with http://
2039 //
https_to_http(wxString & strMessage)2040 void https_to_http(wxString& strMessage) {
2041     strMessage.Replace(wxT("https://"), wxT("http://"));
2042 }
2043 
2044 // replace substrings of the form _("X") with the translation of X
2045 //
localize(wxString & strMessage)2046 void localize(wxString& strMessage) {
2047     wxString strBuffer = wxEmptyString;
2048     wxString strStart = wxString(wxT("_(\""));
2049     wxString strEnd = wxString(wxT("\")"));
2050 
2051     while (strMessage.Find(strStart.c_str()) != wxNOT_FOUND) {
2052         strBuffer = strMessage.SubString(
2053             strMessage.Find(strStart.c_str()) + strStart.Length(),
2054             strMessage.Find(strEnd.c_str()) - (strEnd.Length() - 1)
2055         );
2056         strMessage.Replace(
2057             wxString(strStart + strBuffer + strEnd).c_str(),
2058             wxGetTranslation(strBuffer.c_str())
2059         );
2060     }
2061 }
2062 
2063 
2064 // Call this only when message buffer is stable
2065 // Note: This must not call any rpcs.
2066 // This is now called after each get_messages RPC from
2067 //   CMainDocument::HandleCompletedRPC() .
2068 //
CachedMessageUpdate()2069 int CMainDocument::CachedMessageUpdate() {
2070     static bool in_this_func = false;
2071     size_t last_ind;
2072 
2073     if (in_this_func) return 0;
2074     in_this_func = true;
2075 
2076     if (IsConnected()) {
2077         // rpc.get_messages is now called from RunPeriodicRPCs()
2078         // retval = rpc.get_messages(m_iLastMessageSequenceNumber, messages);
2079         if (m_iGet_messages_rpc_result) {
2080             wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedMessageUpdate - Get Messages Failed '%d'"), m_iGet_messages_rpc_result);
2081             m_pNetworkConnection->SetStateDisconnected();
2082             goto done;
2083         }
2084         if (messages.messages.size() != 0) {
2085             last_ind = messages.messages.size()-1;
2086             m_iLastMessageSequenceNumber = messages.messages[last_ind]->seqno;
2087 
2088             // status.max_event_log_lines <= 0 means no limit
2089             if ((status.max_event_log_lines > 0) &&
2090                     (last_ind >= (unsigned)status.max_event_log_lines)
2091             ) {
2092                 // Remove oldest messages if we have too many
2093                 while (messages.messages.size() > (unsigned)status.max_event_log_lines) {
2094                     delete messages.messages.front();
2095                     messages.messages.pop_front();
2096                 }
2097                 m_iFirstMessageSequenceNumber = messages.messages[0]->seqno;
2098             } else if (m_iFirstMessageSequenceNumber < 0) {
2099                 m_iFirstMessageSequenceNumber = messages.messages[0]->seqno;
2100             }
2101         }
2102     }
2103 
2104 done:
2105     in_this_func = false;
2106     return 0;
2107 }
2108 
2109 
message(unsigned int i)2110 MESSAGE* CMainDocument::message(unsigned int i) {
2111     MESSAGE* pMessage = NULL;
2112 
2113     try {
2114         if (!messages.messages.empty())
2115             pMessage = messages.messages.at(i);
2116     }
2117     catch (std::out_of_range e) {
2118         pMessage = NULL;
2119     }
2120 
2121     return pMessage;
2122 }
2123 
2124 
GetMessageCount()2125 int CMainDocument::GetMessageCount() {
2126     int iCount = -1;
2127 
2128     // CachedMessageUpdate() is now called from CMainDocument::OnRPCComplete()
2129     // only after a get_messages RPC completes so messages buffer is stable.
2130     CachedStateUpdate();
2131 
2132     if (!messages.messages.empty()) {
2133         iCount = (int)messages.messages.size();
2134     }
2135 
2136     return iCount;
2137 }
2138 
2139 
ResetMessageState()2140 int CMainDocument::ResetMessageState() {
2141     messages.clear();
2142     m_iLastMessageSequenceNumber = 0;
2143     m_iFirstMessageSequenceNumber = -1;
2144     return 0;
2145 }
2146 
2147 
CachedFileTransfersUpdate()2148 int CMainDocument::CachedFileTransfersUpdate() {
2149     bool immediate = false;
2150 
2151     if (! IsConnected()) return -1;
2152 
2153 #if USE_CACHE_TIMEOUTS
2154     wxTimeSpan ts(wxDateTime::Now() - m_dtFileTransfersTimestamp);
2155     if (ts.GetSeconds() >= (2* FILETRANSFERSRPC_INTERVAL)) immediate = true;
2156 #endif
2157     if (m_dtFileTransfersTimestamp.IsEqualTo(wxDateTime((time_t)0))) immediate = true;
2158 
2159     if (immediate) {
2160         m_dtFileTransfersTimestamp = wxDateTime::Now();
2161         m_iGet_file_transfers_rpc_result = rpc.get_file_transfers(ft);
2162     }
2163 
2164     if (m_iGet_file_transfers_rpc_result) {
2165         wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedFileTransfersUpdate - Get File Transfers Failed '%d'"), m_iGet_file_transfers_rpc_result);
2166         ForceCacheUpdate();
2167     }
2168 
2169     return m_iGet_file_transfers_rpc_result;
2170 }
2171 
2172 
file_transfer(unsigned int i)2173 FILE_TRANSFER* CMainDocument::file_transfer(unsigned int i) {
2174     FILE_TRANSFER* pFT = NULL;
2175 
2176     try {
2177         if (!ft.file_transfers.empty())
2178             pFT = ft.file_transfers.at(i);
2179     }
2180     catch (std::out_of_range e) {
2181         pFT = NULL;
2182     }
2183 
2184     return pFT;
2185 }
2186 
file_transfer(const wxString & fileName,const wxString & project_url)2187 FILE_TRANSFER* CMainDocument::file_transfer(const wxString& fileName, const wxString& project_url) {
2188     FILE_TRANSFER* pFT = NULL;
2189 
2190     try {
2191         if (!ft.file_transfers.empty()) {
2192             for(unsigned int i=0; i< ft.file_transfers.size();i++) {
2193                 FILE_TRANSFER* tFT = ft.file_transfers.at(i);
2194                 wxString fname(tFT->name.c_str(),wxConvUTF8);
2195                 if(fname.IsSameAs(fileName)) {
2196                     wxString furl(tFT->project_url.c_str(),wxConvUTF8);
2197                     if(furl.IsSameAs(project_url)){
2198                         pFT = tFT;
2199                         break;
2200                     }
2201                 }
2202             }
2203         }
2204     }
2205     catch (std::out_of_range e) {
2206         pFT = NULL;
2207     }
2208 
2209     return pFT;
2210 }
2211 
2212 
GetTransferCount()2213 int CMainDocument::GetTransferCount() {
2214     int iCount = 0;
2215 
2216     CachedFileTransfersUpdate();
2217     CachedStateUpdate();
2218 
2219     if (!ft.file_transfers.empty())
2220         iCount = (int)ft.file_transfers.size();
2221 
2222     return iCount;
2223 }
2224 
2225 
TransferRetryNow(int iIndex)2226 int CMainDocument::TransferRetryNow(int iIndex) {
2227     FILE_TRANSFER* pFT = NULL;
2228     int iRetVal = 0;
2229 
2230     pFT = file_transfer(iIndex);
2231 
2232     if (pFT)
2233         iRetVal = rpc.file_transfer_op((*pFT), "retry");
2234 
2235     return iRetVal;
2236 }
2237 
TransferRetryNow(const wxString & fileName,const wxString & project_url)2238 int CMainDocument::TransferRetryNow(const wxString& fileName, const wxString& project_url) {
2239     FILE_TRANSFER* pFT = NULL;
2240     int iRetVal = 0;
2241 
2242     pFT = file_transfer(fileName, project_url);
2243 
2244     if (pFT)
2245         iRetVal = rpc.file_transfer_op((*pFT), "retry");
2246 
2247     return iRetVal;
2248 }
2249 
2250 
TransferAbort(int iIndex)2251 int CMainDocument::TransferAbort(int iIndex) {
2252     FILE_TRANSFER* pFT = NULL;
2253     int iRetVal = 0;
2254 
2255     pFT = file_transfer(iIndex);
2256 
2257     if (pFT)
2258         iRetVal = rpc.file_transfer_op((*pFT), "abort");
2259 
2260     return iRetVal;
2261 }
2262 
TransferAbort(const wxString & fileName,const wxString & project_url)2263 int CMainDocument::TransferAbort(const wxString& fileName, const wxString& project_url) {
2264     FILE_TRANSFER* pFT = NULL;
2265     int iRetVal = 0;
2266 
2267     pFT = file_transfer(fileName, project_url);
2268 
2269     if (pFT)
2270         iRetVal = rpc.file_transfer_op((*pFT), "abort");
2271 
2272     return iRetVal;
2273 }
2274 
CachedDiskUsageUpdate()2275 int CMainDocument::CachedDiskUsageUpdate() {
2276     bool immediate = true;
2277 
2278     if (!IsConnected()) return -1;
2279 
2280     // don't get disk usage more than once per minute
2281     // unless we just connected to a client
2282     //
2283 #if USE_CACHE_TIMEOUTS
2284     wxTimeSpan ts(wxDateTime::Now() - m_dtDiskUsageTimestamp);
2285     if (ts.GetSeconds() >= (2 * DISKUSAGERPC_INTERVAL)) immediate = true;
2286 #endif
2287     if (disk_usage.projects.empty()) immediate = true;
2288     if (m_dtDiskUsageTimestamp.IsEqualTo(wxDateTime((time_t)0))) immediate = true;
2289 
2290     if (immediate) {
2291         m_dtDiskUsageTimestamp = wxDateTime::Now();
2292         m_iGet_dsk_usage_rpc_result = rpc.get_disk_usage(disk_usage);
2293     }
2294 
2295     if (m_iGet_dsk_usage_rpc_result) {
2296         wxLogTrace(wxT("Function Status"), wxT("Get Disk Usage Failed '%d'"), m_iGet_dsk_usage_rpc_result);
2297         ForceCacheUpdate();
2298     }
2299 
2300     return m_iGet_dsk_usage_rpc_result;
2301 }
2302 
2303 
DiskUsageProject(unsigned int i)2304 PROJECT* CMainDocument::DiskUsageProject(unsigned int i) {
2305     PROJECT* pProject = NULL;
2306 
2307     try {
2308         if (!disk_usage.projects.empty()) {
2309             pProject = disk_usage.projects.at(i);
2310         }
2311     }
2312     catch (std::out_of_range e) {
2313         pProject = NULL;
2314     }
2315 
2316     return pProject;
2317 }
2318 
CachedStatisticsStatusUpdate()2319 int CMainDocument::CachedStatisticsStatusUpdate() {
2320     bool immediate = false;
2321 
2322     if (! IsConnected()) return -1;
2323 
2324 #if USE_CACHE_TIMEOUTS
2325     wxTimeSpan ts(wxDateTime::Now() - m_dtStatisticsStatusTimestamp);
2326     if (ts.GetSeconds() >= (2 * STATISTICSSTATUSRPC_INTERVAL)) immediate = true;
2327 #endif
2328     if (statistics_status.projects.empty()) immediate = true;
2329     if (m_dtStatisticsStatusTimestamp.IsEqualTo(wxDateTime((time_t)0))) immediate = true;
2330 
2331     if (immediate) {
2332         m_dtStatisticsStatusTimestamp = wxDateTime::Now();
2333         m_dtStatisticsStatusTimestamp = rpc.get_statistics(statistics_status);
2334     }
2335 
2336     if (m_iGet_statistics_rpc_result) {
2337         wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedStatisticsStatusUpdate - Get Statistics Failed '%d'"), m_iGet_statistics_rpc_result);
2338         ForceCacheUpdate();
2339     }
2340 
2341     return m_iGet_state_rpc_result;
2342 }
2343 
2344 
statistic(unsigned int i)2345 PROJECT* CMainDocument::statistic(unsigned int i) {
2346     PROJECT* pProject = NULL;
2347 
2348 
2349     try {
2350         if (!statistics_status.projects.empty())
2351             pProject = statistics_status.projects.at(i);
2352     }
2353     catch (std::out_of_range e) {
2354         pProject = NULL;
2355     }
2356 
2357 
2358     return pProject;
2359 }
2360 
2361 
GetStatisticsCount()2362 int CMainDocument::GetStatisticsCount() {
2363     int iCount = -1;
2364 
2365     CachedStatisticsStatusUpdate();
2366     CachedStateUpdate();
2367 
2368     if (!statistics_status.projects.empty())
2369         iCount = (int)statistics_status.projects.size();
2370 
2371     return iCount;
2372 }
2373 
2374 
GetProxyConfiguration()2375 int CMainDocument::GetProxyConfiguration() {
2376     int     iRetVal = 0;
2377 
2378     iRetVal = rpc.get_proxy_settings(proxy_info);
2379     if (iRetVal) {
2380         wxLogTrace(wxT("Function Status"), wxT("CMainDocument::GetProxyInfo - Get Proxy Info Failed '%d'"), iRetVal);
2381     }
2382 
2383     return iRetVal;
2384 }
2385 
2386 
SetProxyConfiguration()2387 int CMainDocument::SetProxyConfiguration() {
2388     int     iRetVal = 0;
2389 
2390     if (!proxy_info.http_user_name.empty() || !proxy_info.http_user_passwd.empty())
2391         proxy_info.use_http_authentication = true;
2392 
2393     iRetVal = rpc.set_proxy_settings(proxy_info);
2394     if (iRetVal) {
2395         wxLogTrace(wxT("Function Status"), wxT("CMainDocument::SetProxyInfo - Set Proxy Info Failed '%d'"), iRetVal);
2396     }
2397 
2398     return iRetVal;
2399 }
2400 
2401 
CachedSimpleGUIUpdate(bool bForce)2402 int CMainDocument::CachedSimpleGUIUpdate(bool bForce) {
2403     int     i = 0;
2404 
2405     if (! IsConnected()) return -1;
2406 
2407 #if USE_CACHE_TIMEOUTS
2408     wxTimeSpan ts(wxDateTime::Now() - m_dtCachedSimpleGUITimestamp);
2409     if (ts.GetSeconds() >= (2 * CACHEDSIMPLEGUIRPC_INTERVAL)) bForce = true;
2410 #endif
2411     if (m_dtCachedSimpleGUITimestamp.IsEqualTo(wxDateTime((time_t)0))) bForce = true;
2412     if (bForce) {
2413         m_dtCachedSimpleGUITimestamp = wxDateTime::Now();
2414         m_iGet_simple_gui2_rpc_result = rpc.get_simple_gui_info(async_projects_update_buf, state, results, m_ActiveTasksOnly);
2415     }
2416 
2417     if (m_iGet_simple_gui2_rpc_result) {
2418         wxLogTrace(wxT("Function Status"), wxT("CMainDocument::CachedSimpleGUIUpdate - Get Simple GUI Failed '%d'"), m_iGet_simple_gui2_rpc_result);
2419         ForceCacheUpdate();
2420     }
2421 
2422     m_fProjectTotalResourceShare = 0.0;
2423     for (i=0; i < (long)state.projects.size(); i++) {
2424         m_fProjectTotalResourceShare += state.projects.at(i)->resource_share;
2425     }
2426     if (!m_fProjectTotalResourceShare) m_fProjectTotalResourceShare = 1;
2427 
2428     return m_iGet_simple_gui2_rpc_result;
2429 }
2430 
2431 
GetSimpleProjectCount()2432 int CMainDocument::GetSimpleProjectCount() {
2433     int iCount = -1;
2434 
2435     CachedSimpleGUIUpdate();
2436     CachedStateUpdate();
2437 
2438     if (!state.projects.empty())
2439         iCount = (int)state.projects.size();
2440 
2441     return iCount;
2442 }
2443 
2444 
GetSimpleGUIWorkCount()2445 int CMainDocument::GetSimpleGUIWorkCount() {
2446     int iCount = 0;
2447     unsigned int i = 0;
2448 
2449     CachedSimpleGUIUpdate();
2450     CachedStateUpdate();
2451 
2452     for(i=0; i<results.results.size(); i++) {
2453         if (results.results[i]->active_task) {
2454             iCount++;
2455         }
2456     }
2457     return iCount;
2458 }
2459 
suspend_reason_wxstring(int reason)2460 wxString suspend_reason_wxstring(int reason) {
2461     switch (reason) {
2462     case SUSPEND_REASON_BATTERIES: return _("on batteries");
2463     case SUSPEND_REASON_USER_ACTIVE: return _("computer is in use");
2464     case SUSPEND_REASON_USER_REQ: return _("user request");
2465     case SUSPEND_REASON_TIME_OF_DAY: return _("time of day");
2466     case SUSPEND_REASON_BENCHMARKS: return _("CPU benchmarks in progress");
2467     case SUSPEND_REASON_DISK_SIZE: return _("need disk space - check preferences");
2468     case SUSPEND_REASON_NO_RECENT_INPUT: return _("computer is not in use");
2469     case SUSPEND_REASON_INITIAL_DELAY: return _("starting up");
2470     case SUSPEND_REASON_EXCLUSIVE_APP_RUNNING: return _("an exclusive app is running");
2471     case SUSPEND_REASON_CPU_USAGE: return _("CPU is busy");
2472     case SUSPEND_REASON_NETWORK_QUOTA_EXCEEDED: return _("network bandwidth limit exceeded");
2473     case SUSPEND_REASON_OS: return _("requested by operating system");
2474     }
2475     return _("unknown reason");
2476 }
2477 
uses_gpu(RESULT * r)2478 bool uses_gpu(RESULT* r) {
2479     // kludge.  But r->avp isn't populated.
2480     return (strstr(r->resources, "GPU") != NULL);
2481 }
2482 
result_description(RESULT * result,bool show_resources)2483 wxString result_description(RESULT* result, bool show_resources) {
2484     CMainDocument* doc = wxGetApp().GetDocument();
2485     PROJECT* project;
2486     CC_STATUS       status;
2487     int             retval;
2488     wxString strBuffer= wxEmptyString;
2489 
2490     strBuffer.Clear();
2491     retval = doc->GetCoreClientStatus(status);
2492     if (retval || !result) {
2493         return strBuffer;
2494     }
2495 
2496     if (result->coproc_missing) {
2497         strBuffer += _("GPU missing, ");
2498     }
2499 
2500     project = doc->state.lookup_project(result->project_url);
2501     int throttled = status.task_suspend_reason & SUSPEND_REASON_CPU_THROTTLE;
2502     switch(result->state) {
2503     case RESULT_NEW:
2504         strBuffer += _("New");
2505         break;
2506     case RESULT_FILES_DOWNLOADING:
2507         if (result->ready_to_report) {
2508             strBuffer += _("Download failed");
2509         } else {
2510             strBuffer += _("Downloading");
2511             if (status.network_suspend_reason) {
2512                 strBuffer += _(" (suspended - ");
2513                 strBuffer += suspend_reason_wxstring(status.network_suspend_reason);
2514                 strBuffer += _(")");
2515             }
2516         }
2517         break;
2518     case RESULT_FILES_DOWNLOADED:
2519         if (result->project_suspended_via_gui) {
2520             strBuffer += _("Project suspended by user");
2521         } else if (result->suspended_via_gui) {
2522             strBuffer += _("Task suspended by user");
2523         } else if (status.task_suspend_reason && !throttled && result->active_task_state != PROCESS_EXECUTING) {
2524             // an NCI process can be running even though computation is suspended
2525             // (because of <dont_suspend_nci>
2526             //
2527             strBuffer += _("Suspended - ");
2528             strBuffer += suspend_reason_wxstring(status.task_suspend_reason);
2529         } else if (status.gpu_suspend_reason && uses_gpu(result)) {
2530             strBuffer += _("GPU suspended - ");
2531             strBuffer += suspend_reason_wxstring(status.gpu_suspend_reason);
2532         } else if (result->active_task) {
2533             if (result->too_large) {
2534                 strBuffer += _("Waiting for memory");
2535             } else if (result->needs_shmem) {
2536                 strBuffer += _("Waiting for shared memory");
2537             } else if (result->scheduler_state == CPU_SCHED_SCHEDULED) {
2538                 strBuffer += _("Running");
2539                 if (project && project->non_cpu_intensive) {
2540                     strBuffer += _(" (non-CPU-intensive)");
2541                 }
2542             } else if (result->scheduler_state == CPU_SCHED_PREEMPTED) {
2543                 strBuffer += _("Waiting to run");
2544             } else if (result->scheduler_state == CPU_SCHED_UNINITIALIZED) {
2545                 strBuffer += _("Ready to start");
2546             }
2547         } else {
2548             strBuffer += _("Ready to start");
2549         }
2550         if (result->scheduler_wait) {
2551             if (strlen(result->scheduler_wait_reason)) {
2552                 strBuffer = _("Postponed: ");
2553                 strBuffer += wxString(result->scheduler_wait_reason, wxConvUTF8);
2554             } else {
2555                 strBuffer = _("Postponed");
2556             }
2557         }
2558         if (result->network_wait) {
2559             strBuffer = _("Waiting for network access");
2560         }
2561         break;
2562     case RESULT_COMPUTE_ERROR:
2563         strBuffer += _("Computation error");
2564         break;
2565     case RESULT_FILES_UPLOADING:
2566         if (result->ready_to_report) {
2567             strBuffer += _("Upload failed");
2568         } else {
2569             strBuffer += _("Uploading");
2570             if (status.network_suspend_reason) {
2571                 strBuffer += _(" (suspended - ");
2572                 strBuffer += suspend_reason_wxstring(status.network_suspend_reason);
2573                 strBuffer += _(")");
2574             }
2575         }
2576         break;
2577     case RESULT_ABORTED:
2578         switch(result->exit_status) {
2579         case EXIT_ABORTED_VIA_GUI:
2580             strBuffer += _("Aborted by user");
2581             break;
2582         case EXIT_ABORTED_BY_PROJECT:
2583             strBuffer += _("Aborted by project");
2584             break;
2585         case EXIT_UNSTARTED_LATE:
2586             strBuffer += _("Aborted: not started by deadline");
2587             break;
2588         case EXIT_DISK_LIMIT_EXCEEDED:
2589             strBuffer += _("Aborted: task disk limit exceeded");
2590             break;
2591         case EXIT_TIME_LIMIT_EXCEEDED:
2592             strBuffer += _("Aborted: run time limit exceeded");
2593             break;
2594         case EXIT_MEM_LIMIT_EXCEEDED:
2595             strBuffer += _("Aborted: memory limit exceeded");
2596             break;
2597         default:
2598             strBuffer += _("Aborted");
2599         }
2600         break;
2601     default:
2602         if (result->got_server_ack) {
2603             strBuffer += _("Acknowledged");
2604         } else if (result->ready_to_report) {
2605             strBuffer += _("Ready to report");
2606         } else {
2607             strBuffer.Format(_("Error: invalid state '%d'"), result->state);
2608         }
2609         break;
2610     }
2611     if (strlen(result->resources)>1 && show_resources) {
2612         strBuffer += wxString(wxT(" (")) + wxString(result->resources, wxConvUTF8) + wxString(wxT(")"));
2613     }
2614     return strBuffer;
2615 }
2616 
hsv2rgb(double h,double s,double v,double & r,double & g,double & b)2617 static void hsv2rgb(
2618     double h, double s, double v, double& r, double& g, double& b
2619 ) {
2620     double m, n, f;
2621     int i = floor(h);
2622     f = h - i;
2623     if (!(i&1)) f = 1 - f;
2624     m = v * (1 - s);
2625     n = v * (1 - s*f);
2626     switch (i) {
2627     case 6:
2628     case 0: r = v; g = n; b = m; return;
2629     case 1: r = n; g = v; b = m; return;
2630     case 2: r = m; g = v; b = n; return;
2631     case 3: r = m; g = n; b = v; return;
2632     case 4: r = n; g = m; b = v; return;
2633     case 5: r = v; g = m; b = n; return;
2634     }
2635 }
2636 
2637 // return the ith out of n maximally distinct colors
2638 //
color_cycle(int i,int n,wxColour & color)2639 void color_cycle(int i, int n, wxColour& color) {
2640     double h = (double)i/(double)n;
2641     double r, g, b;
2642     double v = .75;
2643     if (n > 6) v = .6 + (i % 3)*.1;
2644         // cycle through 3 different brightnesses
2645     hsv2rgb(h*6, .5, v, r, g, b);
2646     unsigned char cr = (unsigned char) (r*256);
2647     unsigned char cg = (unsigned char) (g*256);
2648     unsigned char cb = (unsigned char) (b*256);
2649     color = wxColour(cr, cg, cb);
2650 }
2651 
2652 #ifdef __WXMSW__
2653 static double XDPIScaleFactor = 0.0;
2654 static double YDPIScaleFactor = 0.0;
2655 
GetDPIScaling()2656 void GetDPIScaling() {
2657     XDPIScaleFactor = 1.0;
2658     YDPIScaleFactor = 1.0;
2659     // SetProcessDPIAware() requires Windows Vista or later
2660     HMODULE hUser32 = LoadLibrary(_T("user32.dll"));
2661     typedef BOOL (*SetProcessDPIAwareFunc)();
2662     SetProcessDPIAwareFunc setDPIAware = (SetProcessDPIAwareFunc)GetProcAddress(hUser32, "SetProcessDPIAware");
2663     if (setDPIAware) {
2664         setDPIAware();
2665         HWND hWnd = GetForegroundWindow();
2666         HDC hdc = GetDC(hWnd);
2667         XDPIScaleFactor = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f;
2668         YDPIScaleFactor = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0f;
2669         ReleaseDC(hWnd, hdc);
2670     }
2671     FreeLibrary(hUser32);
2672 }
2673 
GetXDPIScaling()2674 double GetXDPIScaling() {
2675     if (XDPIScaleFactor == 0.0) {
2676         GetDPIScaling();
2677     }
2678     return XDPIScaleFactor;
2679 }
2680 
GetYDPIScaling()2681 double GetYDPIScaling() {
2682     if (YDPIScaleFactor == 0.0) {
2683         GetDPIScaling();
2684     }
2685     return YDPIScaleFactor;
2686 }
2687 #endif
2688 
2689 // TODO: Choose from multiple size images if provided, else resize the closest one
GetScaledBitmapFromXPMData(const char ** XPMData)2690 wxBitmap GetScaledBitmapFromXPMData(const char** XPMData) {
2691 #ifdef __WXMSW__
2692     if ((GetXDPIScaling() > 1.05) || (GetYDPIScaling() > 1.05)) {
2693         wxImage img = wxImage(XPMData);
2694         img.Rescale((int) (img.GetWidth()*GetXDPIScaling()),
2695                     (int) (img.GetHeight()*GetYDPIScaling()),
2696                     wxIMAGE_QUALITY_BILINEAR
2697                 );
2698         wxBitmap *bm = new wxBitmap(img);
2699         return *bm;
2700     }
2701 #endif
2702     return wxBitmap(XPMData);
2703 }
2704 
FormatTime(double secs)2705 wxString FormatTime(double secs) {
2706     if (secs <= 0) {
2707         return wxT("---");
2708     }
2709     wxInt32 iHour = (wxInt32)(secs / (60 * 60));
2710     wxInt32 iMin  = (wxInt32)(secs / 60) % 60;
2711     wxInt32 iSec  = (wxInt32)(secs) % 60;
2712     wxTimeSpan ts = wxTimeSpan(iHour, iMin, iSec);
2713     return ts.Format((secs>=86400)?"%Dd %H:%M:%S":"%H:%M:%S");
2714 }
2715 
format_number(double x,int nprec)2716 wxString format_number(double x, int nprec) {
2717     return wxNumberFormatter::ToString(x, nprec);
2718 
2719 }
2720