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(_WIN32) && !defined(__STDWX_H__)
19 #include "boinc_win.h"
20 #elif defined(_WIN32) && defined(__STDWX_H__)
21 #include "stdwx.h"
22 #endif
23 #ifdef _WIN32
24 #include "win_util.h"
25 #endif
26 
27 #ifndef _WIN32
28 #include "config.h"
29 #include <sstream>
30 #include <string>
31 #include <cmath>
32 #include <string.h>
33 #include <time.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #endif
37 
38 #ifdef _USING_FCGI_
39 #include "boinc_fcgi.h"
40 #endif
41 
42 #include "error_numbers.h"
43 #include "common_defs.h"
44 #include "filesys.h"
45 #include "str_replace.h"
46 #include "str_util.h"
47 
48 using std::string;
49 using std::stringstream;
50 using std::vector;
51 
52 // Use this instead of strncpy().
53 // Result will always be null-terminated, and it's faster.
54 // see http://www.gratisoft.us/todd/papers/strlcpy.html
55 //
56 #if !HAVE_STRLCPY
strlcpy(char * dst,const char * src,size_t size)57 size_t strlcpy(char *dst, const char *src, size_t size) {
58     size_t ret = strlen(src);
59 
60     if (size) {
61         size_t len = (ret >= size) ? size-1 : ret;
62         memcpy(dst, src, len);
63         dst[len] = '\0';
64     }
65 
66     return ret;
67 }
68 #endif
69 
70 #if !HAVE_STRLCAT
strlcat(char * dst,const char * src,size_t size)71 size_t strlcat(char *dst, const char *src, size_t size) {
72     size_t dst_len = strlen(dst);
73     size_t src_len = strlen(src);
74 
75     if (size) {
76         size_t len = (src_len >= size-dst_len) ? (size-dst_len-1) : src_len;
77         memcpy(&dst[dst_len], src, len);
78         dst[dst_len + len] = '\0';
79     }
80 
81     return dst_len + src_len;
82 }
83 #endif // !HAVE_STRLCAT
84 
85 #if !HAVE_STRCASESTR
86 // BOINC only uses strcasestr() for short strings,
87 // so the following will suffice
88 //
strcasestr(const char * s1,const char * s2)89 const char *strcasestr(const char *s1, const char *s2) {
90     char needle[1024], haystack[1024], *p=NULL;
91     strlcpy(haystack, s1, sizeof(haystack));
92     strlcpy(needle, s2, sizeof(needle));
93     // convert both strings to lower case
94     p = haystack;
95     while (*p) {
96         *p = tolower(*p);
97         p++;
98     }
99     p = needle;
100     while (*p) {
101         *p = tolower(*p);
102         p++;
103     }
104     // find the substring
105     p = strstr(haystack, needle);
106     // correct the pointer to point to the substring within s1
107     if (p) {
108         p = const_cast<char *>(s1)+(p-haystack);
109     }
110     return p;
111 }
112 #endif
113 
114 // version of strcpy that works even if strings overlap (p < q)
115 //
strcpy_overlap(char * p,const char * q)116 void strcpy_overlap(char* p, const char* q) {
117     while (1) {
118         *p++ = *q;
119         if (!*q) break;
120         q++;
121     }
122 }
123 
124 // Converts a double precision time (where the value of 1 represents
125 // a day) into a string.  smallest_timescale determines the smallest
126 // unit of time division used
127 // smallest_timescale: 0=seconds, 1=minutes, 2=hours, 3=days, 4=years
128 //
ndays_to_string(double x,int smallest_timescale,char * buf)129 int ndays_to_string (double x, int smallest_timescale, char *buf) {
130     double years, days, hours, minutes, seconds;
131     char year_buf[64], day_buf[16], hour_buf[16], min_buf[16], sec_buf[16];
132 
133     if (x < 0 || buf == NULL) return ERR_NULL;
134 
135     years = x / 365.25;
136     days = fmod(x, 365.25);
137     hours = fmod(x*24, 24);
138     minutes = fmod(x*24*60, 60);
139     seconds = fmod(x*24*60*60, 60);
140 
141     if (smallest_timescale==4) {
142         sprintf( year_buf, "%.3f yr ", years );
143     } else if (years > 1 && smallest_timescale < 4) {
144         sprintf( year_buf, "%d yr ", (int)years );
145     } else {
146         safe_strcpy( year_buf, "" );
147     }
148 
149     if (smallest_timescale==3) {
150         sprintf( day_buf, "%.2f day%s ", days, (days>1?"s":"") );
151     } else if (days > 1 && smallest_timescale < 3) {
152         sprintf( day_buf, "%d day%s ", (int)days, (days>1?"s":"") );
153     } else {
154         safe_strcpy( day_buf, "" );
155     }
156 
157     if (smallest_timescale==2) {
158         sprintf( hour_buf, "%.2f hr ", hours );
159     } else if (hours > 1 && smallest_timescale < 2) {
160         sprintf( hour_buf, "%d hr ", (int)hours );
161     } else {
162         safe_strcpy( hour_buf, "" );
163     }
164 
165     if (smallest_timescale==1) {
166         sprintf( min_buf, "%.2f min ", minutes );
167     } else if (minutes > 1 && smallest_timescale < 1) {
168         sprintf( min_buf, "%d min ", (int)minutes );
169     } else {
170         safe_strcpy( min_buf, "" );
171     }
172 
173     if (smallest_timescale==0) {
174         sprintf( sec_buf, "%.2f sec ", seconds );
175     } else if (seconds > 1 && smallest_timescale < 0) {
176         sprintf( sec_buf, "%d sec ", (int)seconds );
177     } else {
178         safe_strcpy( sec_buf, "" );
179     }
180     // the "-0.05" below is to prevent it from printing 60.0 sec
181     // when the real value is e.g. 59.91
182     //
183     sprintf(buf, "%s%s%s%s%s", year_buf, day_buf, hour_buf, min_buf, sec_buf);
184 
185     return 0;
186 }
187 
188 // convert seconds into a string "0h00m00s00"
189 //
secs_to_hmsf(double secs,char * buf)190 void secs_to_hmsf(double secs, char* buf) {
191     int s = secs;
192     int f = (secs - s) * 100.0;
193     int h = s / 3600;
194     s -= h * 3600;
195     int m = s / 60;
196     s -= m * 60;
197     sprintf(buf, "%uh%02um%02us%02u", h, m, s, f);
198 }
199 
200 // Convert nbytes into a string.  If total_bytes is non-zero,
201 // convert the two into a fractional display (i.e. 4/16 KB)
202 //
nbytes_to_string(double nbytes,double total_bytes,char * str,int len)203 void nbytes_to_string(double nbytes, double total_bytes, char* str, int len) {
204     char buf[256];
205     double xTera = (1024.0*1024.0*1024.0*1024.0);
206     double xGiga = (1024.0*1024.0*1024.0);
207     double xMega = (1024.0*1024.0);
208     double xKilo = (1024.0);
209 
210     if (total_bytes != 0) {
211         if (total_bytes >= xTera) {
212             sprintf(buf, "%0.2f/%0.2f TB", nbytes/xTera, total_bytes/xTera);
213         } else if (total_bytes >= xGiga) {
214             sprintf(buf, "%0.2f/%0.2f GB", nbytes/xGiga, total_bytes/xGiga);
215         } else if (total_bytes >= xMega) {
216             sprintf(buf, "%0.2f/%0.2f MB", nbytes/xMega, total_bytes/xMega);
217         } else if (total_bytes >= xKilo) {
218             sprintf(buf, "%0.2f/%0.2f KB", nbytes/xKilo, total_bytes/xKilo);
219         } else {
220             sprintf(buf, "%0.0f/%0.0f bytes", nbytes, total_bytes);
221         }
222     } else {
223         if (nbytes >= xTera) {
224             sprintf(buf, "%0.2f TB", nbytes/xTera);
225         } else if (nbytes >= xGiga) {
226             sprintf(buf, "%0.2f GB", nbytes/xGiga);
227         } else if (nbytes >= xMega) {
228             sprintf(buf, "%0.2f MB", nbytes/xMega);
229         } else if (nbytes >= xKilo) {
230             sprintf(buf, "%0.2f KB", nbytes/xKilo);
231         } else {
232             sprintf(buf, "%0.0f bytes", nbytes);
233         }
234     }
235 
236     strlcpy(str, buf, len);
237 }
238 
239 // take a string containing some space separated words.
240 // return an array of pointers to the null-terminated words.
241 // Modifies the string arg.
242 // Returns argc
243 // TODO: use strtok here
244 
245 #define NOT_IN_TOKEN                0
246 #define IN_SINGLE_QUOTED_TOKEN      1
247 #define IN_DOUBLE_QUOTED_TOKEN      2
248 #define IN_UNQUOTED_TOKEN           3
249 
parse_command_line(char * p,char ** argv)250 int parse_command_line(char* p, char** argv) {
251     int state = NOT_IN_TOKEN;
252     int argc=0;
253 
254     while (*p) {
255         switch(state) {
256         case NOT_IN_TOKEN:
257             if (isspace(*p)) {
258             } else if (*p == '\'') {
259                 p++;
260                 argv[argc++] = p;
261                 state = IN_SINGLE_QUOTED_TOKEN;
262                 break;
263             } else if (*p == '\"') {
264                 p++;
265                 argv[argc++] = p;
266                 state = IN_DOUBLE_QUOTED_TOKEN;
267                 break;
268             } else {
269                 argv[argc++] = p;
270                 state = IN_UNQUOTED_TOKEN;
271             }
272             break;
273         case IN_SINGLE_QUOTED_TOKEN:
274             if (*p == '\'') {
275                 *p = 0;
276                 state = NOT_IN_TOKEN;
277             }
278             break;
279         case IN_DOUBLE_QUOTED_TOKEN:
280             if (*p == '\"') {
281                 *p = 0;
282                 state = NOT_IN_TOKEN;
283             }
284             break;
285         case IN_UNQUOTED_TOKEN:
286             if (isspace(*p)) {
287                 *p = 0;
288                 state = NOT_IN_TOKEN;
289             }
290             break;
291         }
292         p++;
293     }
294     argv[argc] = 0;
295     return argc;
296 }
297 
298 // remove whitespace from start and end of a string
299 //
strip_whitespace(string & str)300 void strip_whitespace(string& str) {
301     while (1) {
302         if (str.length() == 0) break;
303         if (!isascii(str[0])) break;
304         if (!isspace(str[0])) break;
305         str.erase(0, 1);
306     }
307 
308     int n = (int) str.length();
309     while (n>0) {
310         if (!isascii(str[n-1])) break;
311         if (!isspace(str[n-1])) break;
312         n--;
313     }
314     str.erase(n, str.length()-n);
315 }
316 
strip_whitespace(char * str)317 void strip_whitespace(char *str) {
318     string s = str;
319     strip_whitespace(s);
320     strcpy(str, s.c_str());
321 }
322 
323 // remove whitespace and quotes from start and end of a string
324 //
strip_quotes(string & str)325 void strip_quotes(string& str) {
326     while (1) {
327         if (str.length() == 0) break;
328         if (str[0] == '"' || str[0] == '\'') {
329             str.erase(0, 1);
330             continue;
331         }
332         if (!isascii(str[0])) break;
333         if (!isspace(str[0])) break;
334         str.erase(0, 1);
335     }
336 
337     int n = (int) str.length();
338     while (n>0) {
339         if (str[n-1] == '"' || str[n-1] == '\'') {
340             if (str[n-2] != '\\') {
341                 n--;
342                 continue;
343             }
344         }
345         if (!isascii(str[n-1])) break;
346         if (!isspace(str[n-1])) break;
347         n--;
348     }
349     str.erase(n, str.length()-n);
350 }
351 
strip_quotes(char * str)352 void strip_quotes(char *str) {
353     string s = str;
354     strip_quotes(s);
355     strcpy(str, s.c_str());
356 }
357 
358 // This only unescapes some special shell characters used in /etc/os-release
359 // see https://www.freedesktop.org/software/systemd/man/os-release.html
unescape_os_release(char * buf)360 void unescape_os_release(char* buf) {
361     char* out = buf;
362     char* in = buf;
363     while (*in) {
364         if (*in != '\\') {
365             *out++ = *in++;
366         } else if (*(in+1) == '$') {
367             *out++ = '$';
368             in += 2;
369         } else if (*(in+1) == '\'') {
370             *out++ = '\'';
371             in += 2;
372         } else if (*(in+1) == '"') {
373             *out++ = '"';
374             in += 2;
375         } else if (*(in+1) == '\\') {
376             *out++ = '\\';
377             in += 2;
378         } else if (*(in+1) == '`') {
379             *out++ = '`';
380             in += 2;
381         } else {
382             *out++ = *in++;
383         }
384     }
385     *out = 0;
386 }
387 
388 // collapse multiple whitespace into one (will not strip_whitespace)
389 //
collapse_whitespace(string & str)390 void collapse_whitespace(string& str) {
391     int n = (int) str.length();
392     if (n<2) return;
393     for (int i=1; i<n; i++) {
394         if (isspace(str[i-1]) && isspace(str[i])) {
395             str.erase(i, 1);
396             n--; i--;
397         }
398     }
399 }
400 
collapse_whitespace(char * str)401 void collapse_whitespace(char *str) {
402     string s = str;
403     collapse_whitespace(s);
404     strcpy(str, s.c_str());
405 }
406 
time_to_string(double t)407 char* time_to_string(double t) {
408     static char buf[100];
409     if (!t) {
410         safe_strcpy(buf, "---");
411     } else {
412         time_t x = (time_t)t;
413         struct tm* tm = localtime(&x);
414         strftime(buf, sizeof(buf)-1, "%d-%b-%Y %H:%M:%S", tm);
415     }
416     return buf;
417 }
418 
precision_time_to_string(double t)419 char* precision_time_to_string(double t) {
420     static char buf[100];
421     char finer[16];
422     int hundreds_of_microseconds=(int)(10000*(t-(int)t));
423     if (hundreds_of_microseconds == 10000) {
424         // paranoia -- this should never happen!
425         //
426         hundreds_of_microseconds=0;
427         t+=1.0;
428     }
429     time_t x = (time_t)t;
430     struct tm* tm = localtime(&x);
431 
432     strftime(buf, sizeof(buf)-1, "%Y-%m-%d %H:%M:%S", tm);
433     sprintf(finer, ".%04d", hundreds_of_microseconds);
434     safe_strcat(buf, finer);
435     return buf;
436 }
437 
timediff_format(double diff)438 string timediff_format(double diff) {
439     char buf[256];
440     int tdiff = (int)diff;
441 
442     int sex = tdiff % 60;
443     tdiff /= 60;
444     if (!tdiff) {
445         sprintf(buf, "00:00:%02d", sex);
446         return buf;
447     }
448 
449     int min = tdiff % 60;
450     tdiff /= 60;
451     if (!tdiff) {
452         sprintf(buf, "00:%02d:%02d", min, sex);
453         return buf;
454     }
455 
456     int hours = tdiff % 24;
457     tdiff /= 24;
458     if (!tdiff) {
459         sprintf(buf, "%02d:%02d:%02d", hours, min, sex);
460         return buf;
461     }
462 
463     sprintf(buf, "%d days %02d:%02d:%02d", tdiff, hours, min, sex);
464     return buf;
465 
466 }
467 
mysql_timestamp(double dt,char * p)468 void mysql_timestamp(double dt, char* p) {
469     struct tm* tmp;
470     time_t t = (time_t)dt;
471     tmp = localtime(&t);     // MySQL timestamps are in local time
472     sprintf(p, "%4d%02d%02d%02d%02d%02d",
473         tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
474         tmp->tm_hour, tmp->tm_min, tmp->tm_sec
475     );
476 }
477 
478 // Return a text-string description of a given error.
479 // Must be kept consistent with error_numbers.h
480 //
boincerror(int which_error)481 const char* boincerror(int which_error) {
482     switch (which_error) {
483         case BOINC_SUCCESS: return "Success";
484         case ERR_SELECT: return "select() failed";
485         case ERR_MALLOC: return "malloc() failed";
486         case ERR_READ: return "read() failed";
487         case ERR_WRITE: return "write() failed";
488         case ERR_FREAD: return "fread() failed";
489         case ERR_FWRITE: return "fwrite() failed";
490         case ERR_IO: return "system I/O error";
491         case ERR_CONNECT: return "connect() failed";
492         case ERR_FOPEN: return "fopen() failed";
493         case ERR_RENAME: return "rename() failed";
494         case ERR_UNLINK: return "unlink() failed";
495         case ERR_OPENDIR: return "opendir() failed";
496         case ERR_XML_PARSE: return "unexpected XML tag or syntax";
497         case ERR_GETHOSTBYNAME: return "can't resolve hostname";
498         case ERR_GIVEUP_DOWNLOAD: return "file download timed out";
499         case ERR_GIVEUP_UPLOAD: return "file upload timed out";
500         case ERR_NULL: return "unexpected null pointer";
501         case ERR_NEG: return "unexpected negative value";
502         case ERR_BUFFER_OVERFLOW: return "buffer overflow";
503         case ERR_MD5_FAILED: return "md5 checksum failed for file";
504         case ERR_RSA_FAILED: return "RSA key check failed for file";
505         case ERR_OPEN: return "open() failed";
506         case ERR_DUP2: return "dup() failed";
507         case ERR_NO_SIGNATURE: return "no signature";
508         case ERR_THREAD: return "thread failure";
509         case ERR_SIGNAL_CATCH: return "caught signal";
510         case ERR_BAD_FORMAT: return "bad file format";
511         case ERR_UPLOAD_TRANSIENT: return "transient upload error";
512         case ERR_UPLOAD_PERMANENT: return "permanent upload error";
513         case ERR_IDLE_PERIOD: return "user preferences say can't start work";
514         case ERR_ALREADY_ATTACHED: return "already attached to project";
515         case ERR_FILE_TOO_BIG: return "file size too big";
516         case ERR_GETRUSAGE: return "getrusage() failed";
517         case ERR_BENCHMARK_FAILED: return "benchmark failed";
518         case ERR_BAD_HEX_FORMAT: return "hex format key data bad";
519         case ERR_GETADDRINFO: return "getaddrinfo() failed";
520         case ERR_DB_NOT_FOUND: return "no database rows found in lookup/enumerate";
521         case ERR_DB_NOT_UNIQUE: return "database lookup not unique";
522         case ERR_DB_CANT_CONNECT: return "can't connect to database";
523         case ERR_GETS: return "gets()/fgets() failedj";
524         case ERR_SCANF: return "scanf()/fscanf() failed";
525         case ERR_READDIR: return "readdir() failed";
526         case ERR_SHMGET: return "shmget() failed";
527         case ERR_SHMCTL: return "shmctl() failed";
528         case ERR_SHMAT: return "shmat() failed";
529         case ERR_FORK: return "fork() failed";
530         case ERR_EXEC: return "exec() failed";
531         case ERR_NOT_EXITED: return "process didn't exit";
532         case ERR_NOT_IMPLEMENTED: return "system call not implemented";
533         case ERR_GETHOSTNAME: return "gethostname() failed";
534         case ERR_NETOPEN: return "netopen() failed";
535         case ERR_SOCKET: return "socket() failed";
536         case ERR_FCNTL: return "fcntl() failed";
537         case ERR_AUTHENTICATOR: return "authentication error";
538         case ERR_SCHED_SHMEM: return "scheduler shared memory contents bad";
539         case ERR_ASYNCSELECT: return "async select() failed";
540         case ERR_BAD_RESULT_STATE: return "bad result state";
541         case ERR_DB_CANT_INIT: return "can't init database";
542         case ERR_NOT_UNIQUE: return "state files have redundant entries";
543         case ERR_NOT_FOUND: return "not found";
544         case ERR_NO_EXIT_STATUS: return "no exit status in scheduler request";
545         case ERR_FILE_MISSING: return "file missing";
546         case ERR_KILL: return "kill() or TerminateProcess() failed";
547         case ERR_SEMGET: return "semget() failed";
548         case ERR_SEMCTL: return "semctl() failed";
549         case ERR_SEMOP: return "semop() failed";
550         case ERR_FTOK: return "ftok() failed";
551         case ERR_SOCKS_UNKNOWN_FAILURE: return "SOCKS: unknown error";
552         case ERR_SOCKS_REQUEST_FAILED: return "SOCKS: request failed";
553         case ERR_SOCKS_BAD_USER_PASS: return "SOCKS: bad user password";
554         case ERR_SOCKS_UNKNOWN_SERVER_VERSION: return "SOCKS: unknown server version";
555         case ERR_SOCKS_UNSUPPORTED: return "SOCKS: unsupported";
556         case ERR_SOCKS_CANT_REACH_HOST: return "SOCKS: can't reach host";
557         case ERR_SOCKS_CONN_REFUSED: return "SOCKS: connection refused";
558         case ERR_TIMER_INIT: return "timer init";
559         case ERR_INVALID_PARAM: return "invalid parameter";
560         case ERR_SIGNAL_OP: return "signal op";
561         case ERR_BIND: return "bind() failed";
562         case ERR_LISTEN: return "listen() failed";
563         case ERR_TIMEOUT: return "timeout";
564         case ERR_PROJECT_DOWN: return "project down";
565         case ERR_RESULT_START: return "result start failed";
566         case ERR_RESULT_DOWNLOAD: return "result download failed";
567         case ERR_RESULT_UPLOAD: return "result upload failed";
568         case ERR_BAD_USER_NAME: return "bad username";
569         case ERR_INVALID_URL: return "invalid URL";
570         case ERR_MAJOR_VERSION: return "bad major version";
571         case ERR_NO_OPTION: return "no option";
572         case ERR_MKDIR: return "mkdir() failed";
573         case ERR_INVALID_EVENT: return "invalid event";
574         case ERR_ALREADY_RUNNING: return "already running";
575         case ERR_NO_APP_VERSION: return "no app version";
576         case ERR_WU_USER_RULE: return "user already did result for this workunit";
577         case ERR_ABORTED_VIA_GUI: return "result aborted via GUI";
578         case ERR_INSUFFICIENT_RESOURCE: return "insufficient resources";
579         case ERR_RETRY: return "retry";
580         case ERR_WRONG_SIZE: return "wrong size";
581         case ERR_USER_PERMISSION: return "user permission";
582         case ERR_BAD_EMAIL_ADDR: return "bad email address";
583         case ERR_BAD_PASSWD: return "bad password";
584         case ERR_SHMEM_NAME: return "can't get shared mem segment name";
585         case ERR_NO_NETWORK_CONNECTION: return "no available network connection";
586         case ERR_IN_PROGRESS: return "operation in progress";
587         case ERR_NONUNIQUE_EMAIL: return "email already registered";
588         case ERR_ACCT_CREATION_DISABLED: return "account creation disabled";
589         case ERR_ATTACH_FAIL_INIT: return "Couldn't start master page download";
590         case ERR_ATTACH_FAIL_DOWNLOAD: return "Couldn't download master page";
591         case ERR_ATTACH_FAIL_PARSE: return "Couldn't parse master page";
592         case ERR_ATTACH_FAIL_BAD_KEY: return "Invalid account key";
593         case ERR_ATTACH_FAIL_FILE_WRITE: return "Couldn't write account file";
594         case ERR_ATTACH_FAIL_SERVER_ERROR: return "Couldn't attach because of server error";
595         case ERR_SIGNING_KEY: return "signing key failure";
596         case ERR_FFLUSH: return "fflush() failed";
597         case ERR_FSYNC: return "fsync() failed";
598         case ERR_TRUNCATE: return "truncate() failed";
599         case ERR_WRONG_URL: return "wrong URL";
600         case ERR_DUP_NAME: return "coprocs with duplicate names detected";
601         case ERR_GETGRNAM: return "getgrnam() failed";
602         case ERR_CHOWN: return "chown() failed";
603         case ERR_HTTP_PERMANENT: return "permanent HTTP error";
604         case ERR_HTTP_TRANSIENT: return "transient HTTP error";
605         case ERR_BAD_FILENAME: return "file name is empty or has '..'";
606         case ERR_TOO_MANY_EXITS: return "application exited too many times";
607         case ERR_RMDIR: return "rmdir() failed";
608         case ERR_SYMLINK: return "symlink() failed";
609         case ERR_DB_CONN_LOST: return "DB connection lost during enumeration";
610         case ERR_CRYPTO: return "encryption error";
611         case ERR_ABORTED_ON_EXIT: return "job was aborted on client exit";
612         case ERR_PROC_PARSE: return "a /proc entry was not parsed correctly";
613         case ERR_STATFS: return "statfs() failed";
614         case ERR_PIPE: return "pipe() failed";
615         case ERR_NEED_HTTPS: return "HTTPS needed";
616         case ERR_CHMOD : return "chmod() failed";
617         case ERR_STAT : return "stat() failed";
618         case ERR_FCLOSE : return "fclose() failed";
619         case HTTP_STATUS_NOT_FOUND: return "HTTP file not found";
620         case HTTP_STATUS_PROXY_AUTH_REQ: return "HTTP proxy authentication failure";
621         case HTTP_STATUS_RANGE_REQUEST_ERROR: return "HTTP range request error";
622         case HTTP_STATUS_EXPECTATION_FAILED: return "HTTP expectation failed";
623         case HTTP_STATUS_INTERNAL_SERVER_ERROR: return "HTTP internal server error";
624         case HTTP_STATUS_NOT_IMPLEMENTED: return "HTTP not implemented";
625         case HTTP_STATUS_BAD_GATEWAY: return "HTTP bad gateway";
626         case HTTP_STATUS_SERVICE_UNAVAILABLE: return "HTTP service unavailable";
627         case HTTP_STATUS_GATEWAY_TIMEOUT: return "HTTP gateway timeout";
628     }
629     static char buf[128];
630     sprintf(buf, "Error %d", which_error);
631     return buf;
632 }
633 
network_status_string(int n)634 const char* network_status_string(int n) {
635     switch (n) {
636     case NETWORK_STATUS_ONLINE: return "online";
637     case NETWORK_STATUS_WANT_CONNECTION: return "need connection";
638     case NETWORK_STATUS_WANT_DISCONNECT: return "don't need connection";
639     case NETWORK_STATUS_LOOKUP_PENDING: return "reference site lookup pending";
640     default: return "unknown";
641     }
642 }
643 
rpc_reason_string(int reason)644 const char* rpc_reason_string(int reason) {
645     switch (reason) {
646     case RPC_REASON_USER_REQ: return "Requested by user";
647     case RPC_REASON_NEED_WORK: return "To fetch work";
648     case RPC_REASON_RESULTS_DUE: return "To report completed tasks";
649     case RPC_REASON_TRICKLE_UP: return "To send trickle-up message";
650     case RPC_REASON_ACCT_MGR_REQ: return "Requested by account manager";
651     case RPC_REASON_INIT: return "Project initialization";
652     case RPC_REASON_PROJECT_REQ: return "Requested by project";
653     default: return "Unknown reason";
654     }
655 }
656 
suspend_reason_string(int reason)657 const char* suspend_reason_string(int reason) {
658     switch (reason) {
659     case SUSPEND_REASON_BATTERIES: return "on batteries";
660     case SUSPEND_REASON_USER_ACTIVE: return "computer is in use";
661     case SUSPEND_REASON_USER_REQ: return "user request";
662     case SUSPEND_REASON_TIME_OF_DAY: return "time of day";
663     case SUSPEND_REASON_BENCHMARKS: return "CPU benchmarks in progress";
664     case SUSPEND_REASON_DISK_SIZE: return "need disk space - check preferences";
665     case SUSPEND_REASON_NO_RECENT_INPUT: return "no recent user activity";
666     case SUSPEND_REASON_INITIAL_DELAY: return "initial delay";
667     case SUSPEND_REASON_EXCLUSIVE_APP_RUNNING: return "an exclusive app is running";
668     case SUSPEND_REASON_CPU_USAGE: return "CPU is busy";
669     case SUSPEND_REASON_NETWORK_QUOTA_EXCEEDED: return "network transfer limit exceeded";
670     case SUSPEND_REASON_OS: return "requested by operating system";
671     case SUSPEND_REASON_WIFI_STATE: return "not connected to WiFi network";
672     case SUSPEND_REASON_BATTERY_CHARGING: return "battery low";
673     case SUSPEND_REASON_BATTERY_OVERHEATED: return "battery thermal protection";
674     case SUSPEND_REASON_NO_GUI_KEEPALIVE: return "GUI not active";
675     }
676     return "unknown reason";
677 }
678 
run_mode_string(int mode)679 const char* run_mode_string(int mode) {
680     switch (mode) {
681     case RUN_MODE_ALWAYS: return "always";
682     case RUN_MODE_AUTO: return "according to prefs";
683     case RUN_MODE_NEVER: return "never";
684     }
685     return "unknown";
686 }
687 
battery_state_string(int state)688 const char* battery_state_string(int state) {
689     switch (state) {
690     case BATTERY_STATE_DISCHARGING: return "discharging";
691     case BATTERY_STATE_CHARGING: return "charging";
692     case BATTERY_STATE_FULL: return "full";
693     case BATTERY_STATE_OVERHEATED: return "overheated";
694     }
695     return "unknown";
696 }
697 
result_client_state_string(int state)698 const char* result_client_state_string(int state) {
699     switch (state) {
700     case RESULT_NEW: return "new";
701     case RESULT_FILES_DOWNLOADING: return "downloading";
702     case RESULT_FILES_DOWNLOADED: return "downloaded";
703     case RESULT_COMPUTE_ERROR: return "compute error";
704     case RESULT_FILES_UPLOADING: return "uploading";
705     case RESULT_FILES_UPLOADED: return "uploaded";
706     case RESULT_ABORTED: return "aborted";
707     case RESULT_UPLOAD_FAILED: return "upload failed";
708     }
709     return "unknown";
710 }
711 
result_scheduler_state_string(int state)712 const char* result_scheduler_state_string(int state) {
713     switch (state) {
714     case CPU_SCHED_UNINITIALIZED: return "uninitialized";
715     case CPU_SCHED_PREEMPTED: return "preempted";
716     case CPU_SCHED_SCHEDULED: return "scheduled";
717     }
718     return "unknown";
719 }
720 
active_task_state_string(int state)721 const char* active_task_state_string(int state) {
722     switch (state) {
723     case PROCESS_UNINITIALIZED: return "UNINITIALIZED";
724     case PROCESS_EXECUTING: return "EXECUTING";
725     case PROCESS_SUSPENDED: return "SUSPENDED";
726     case PROCESS_ABORT_PENDING: return "ABORT_PENDING";
727     case PROCESS_EXITED: return "EXITED";
728     case PROCESS_WAS_SIGNALED: return "WAS_SIGNALED";
729     case PROCESS_EXIT_UNKNOWN: return "EXIT_UNKNOWN";
730     case PROCESS_ABORTED: return "ABORTED";
731     case PROCESS_COULDNT_START: return "COULDNT_START";
732     case PROCESS_QUIT_PENDING: return "QUIT_PENDING";
733     case PROCESS_COPY_PENDING: return "COPY_PENDING";
734     }
735     return "Unknown";
736 }
737 
batch_state_string(int state)738 const char* batch_state_string(int state) {
739     switch (state) {
740     case BATCH_STATE_INIT: return "uninitialized";
741     case BATCH_STATE_IN_PROGRESS: return "in progress";
742     case BATCH_STATE_COMPLETE: return "completed";
743     case BATCH_STATE_ABORTED: return "aborted";
744     case BATCH_STATE_RETIRED: return "retired";
745     }
746     return "unknown";
747 }
748 
749 // string substitution:
750 // haystack is the input string
751 // out is the output buffer
752 // out_len is the length of the output buffer
753 // needle is string to search for within the haystack
754 // target is string to replace with
755 //
string_substitute(const char * haystack,char * out,int out_len,const char * needle,const char * target)756 int string_substitute(
757     const char* haystack, char* out, int out_len,
758     const char* needle, const char* target
759 ) {
760     int i=0, j=0;
761     int needle_len = (int)strlen(needle);
762     int target_len = (int)strlen(target);
763     int retval = 0;
764 
765     while (haystack[i]) {
766         if (j+target_len >= out_len-1) {
767             retval = ERR_BUFFER_OVERFLOW;
768             break;
769         }
770         if (!strncmp(&haystack[i], needle, needle_len)){
771             strlcpy(out+j, target, out_len-((out+j)-out));
772             i += needle_len;
773             j += target_len;
774         } else {
775             out[j++] = haystack[i++];
776         }
777     }
778     out[j] = 0;
779     return retval;
780 }
781 
remove_str(char * p,const char * str)782 inline void remove_str(char* p, const char* str) {
783     size_t n = strlen(str);
784     while (1) {
785         p = strstr(p, str);
786         if (!p) break;
787         strcpy_overlap(p, p+n);
788     }
789 }
790 
791 // remove _(" and ") from string
792 //
strip_translation(char * p)793 void strip_translation(char* p) {
794     remove_str(p, "_(\"");
795     remove_str(p, "\")");
796 }
797 
lf_terminate(char * p)798 char* lf_terminate(char* p) {
799     int n = (int)strlen(p);
800     if (p[n-1] == '\n') {
801         return p;
802     }
803     p = (char*)realloc(p, n+2);
804     p[n] = '\n';
805     p[n+1] = 0;
806     return p;
807 }
808 
parse_serialnum(char * in,char * boinc,char * vbox,char * coprocs)809 void parse_serialnum(char* in, char* boinc, char* vbox, char* coprocs) {
810     strcpy(boinc, "");
811     strcpy(vbox, "");
812     strcpy(coprocs, "");
813     while (*in) {
814         if (*in != '[') break;      // format error
815         char* p = strchr(in, ']');
816         if (!p) break;              // format error
817         p++;
818         char c = *p;
819         *p = 0;
820         if (strstr(in, "BOINC")) {
821             strcpy(boinc, in);
822         } else if (strstr(in, "vbox")) {
823             strcpy(vbox, in);
824         } else {
825             strcat(coprocs, in);
826         }
827         *p = c;
828         in = p;
829     }
830 }
831 
split(string s,char delim)832 vector<string> split(string s, char delim) {
833     vector<string> result;
834     stringstream ss(s);
835     string item;
836     while (getline(ss, item, delim)) {
837         result.push_back(item);
838     }
839     return result;
840 }
841 
842 // check whether filename is legit
843 // - can't start with /
844 // - can't have control chars
845 // - can't have ..
846 //
is_valid_filename(const char * name)847 bool is_valid_filename(const char* name) {
848     size_t n = strlen(name);
849     for (size_t i=0; i<n; i++) {
850         if (iscntrl(name[i])) {
851             return false;
852         }
853     }
854     if (strstr(name, "..")) {
855         return false;
856     }
857     if (name[0] == '/') {
858         return false;
859     }
860     return true;
861 }
862 
863 // get the name part of a filepath
864 // returns:
865 //   0 on success
866 //  -1 when fpath is empty
867 //  -2 when fpath is a directory
path_to_filename(string fpath,string & fname)868 int path_to_filename(string fpath, string& fname) {
869     std::string::size_type n;
870     if (fpath.size() == 0) {
871         return -1;
872     }
873     n = fpath.rfind("/");
874     if (n == std::string::npos) {
875         fname = fpath;
876     } else if (n == fpath.size()-1) {
877         return -2;
878     } else {
879         fname = fpath.substr(n+1);
880     }
881     return 0;
882 }
883 
884 // get the name part of a filepath
885 //
886 // wrapper for path_to_filename(string, string&)
path_to_filename(string fpath,char * & fname)887 int path_to_filename(string fpath, char* &fname) {
888     string name;
889     int retval = path_to_filename(fpath, name);
890     if (retval) {
891         return retval;
892     }
893     fname = new char[name.size()+1];
894     strcpy(fname, name.c_str());
895     return 0;
896 }
897