diff --git a/aclocal.m4 b/aclocal.m4 index 2c80f1df64..5413b12d72 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1104,8 +1104,8 @@ AC_DEFUN([SAH_HEADER_STDCXX],[ # # Revision Log: # $Log$ -# Revision 1.62 2004/01/17 18:47:03 davea -# version 2.19 +# Revision 1.63 2004/01/21 07:07:05 davea +# *** empty log message *** # # Revision 1.1 2003/12/11 18:38:24 korpela # Added checked macro files into boinc diff --git a/checkin_notes b/checkin_notes index 5fbf21464c..1756080d5c 100755 --- a/checkin_notes +++ b/checkin_notes @@ -9393,3 +9393,33 @@ David Jan 19 2004 (from Rom Walton) win_build/ boinc_cli.vcproj boinc_ss.vcproj + +David Jan 20 2004 + - added basic support for GUI RPCs in the core client. + This allows GUIs to be implemented in a separate process. + There can be multiple GUIs looking at the same core client. + New classes: + GUI_RPC_CONN: represents a connection to a GUI program + GUI_RPC_CONN_SET: represents the set of all such connections + has the usual poll() function. + This is currently implemented only for UNIX, using UNIX domain sockets. + Should be straightforward to do in Win using named pipes. + - The isspace() macro crashes if called with a non-ASCII arg, + so use it only after isascii() + - reimplement strip_whitespace() to do the above, + have it trim at both start and end, + and make a version for string + NOTE: the real problem is that user-supplied text is being + kept in XML elements in the state file. + Should escape it. + + client/ + Makefile.am,in + client_state.C,h + gui_rpc_client.C,h (new) + gui_rpc_server.C,h (new) + gui_test.C + + lib/ + parse.C + util.C,h diff --git a/client/Makefile.am b/client/Makefile.am index 74c56582a5..85a46b1371 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -26,6 +26,7 @@ boinc_client_SOURCES = \ cs_statefile.C \ file_names.C \ file_xfer.C \ + gui_rpc_server.C \ hostinfo.C \ hostinfo_unix.C \ http.C \ diff --git a/client/Makefile.in b/client/Makefile.in index e1069d85f6..5afed19a46 100644 --- a/client/Makefile.in +++ b/client/Makefile.in @@ -193,6 +193,7 @@ boinc_client_SOURCES = \ cs_statefile.C \ file_names.C \ file_xfer.C \ + gui_rpc_server.C \ hostinfo.C \ hostinfo_unix.C \ http.C \ @@ -256,6 +257,7 @@ am_boinc_client_OBJECTS = boinc_client-app.$(OBJEXT) \ boinc_client-cs_statefile.$(OBJEXT) \ boinc_client-file_names.$(OBJEXT) \ boinc_client-file_xfer.$(OBJEXT) \ + boinc_client-gui_rpc_server.$(OBJEXT) \ boinc_client-hostinfo.$(OBJEXT) \ boinc_client-hostinfo_unix.$(OBJEXT) \ boinc_client-http.$(OBJEXT) boinc_client-log_flags.$(OBJEXT) \ @@ -301,6 +303,7 @@ am__depfiles_maybe = depfiles @AMDEP_TRUE@ ./$(DEPDIR)/boinc_client-file_names.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/boinc_client-file_xfer.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/boinc_client-filesys.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/boinc_client-gui_rpc_server.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/boinc_client-hostinfo.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/boinc_client-hostinfo_unix.Po \ @AMDEP_TRUE@ ./$(DEPDIR)/boinc_client-http.Po \ @@ -385,6 +388,7 @@ boinc_client-cs_scheduler.$(OBJEXT): cs_scheduler.C boinc_client-cs_statefile.$(OBJEXT): cs_statefile.C boinc_client-file_names.$(OBJEXT): file_names.C boinc_client-file_xfer.$(OBJEXT): file_xfer.C +boinc_client-gui_rpc_server.$(OBJEXT): gui_rpc_server.C boinc_client-hostinfo.$(OBJEXT): hostinfo.C boinc_client-hostinfo_unix.$(OBJEXT): hostinfo_unix.C boinc_client-http.$(OBJEXT): http.C @@ -440,6 +444,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/boinc_client-file_names.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/boinc_client-file_xfer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/boinc_client-filesys.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/boinc_client-gui_rpc_server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/boinc_client-hostinfo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/boinc_client-hostinfo_unix.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/boinc_client-http.Po@am__quote@ @@ -839,6 +844,28 @@ boinc_client-file_xfer.obj: file_xfer.C @AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(boinc_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o boinc_client-file_xfer.obj `if test -f 'file_xfer.C'; then $(CYGPATH_W) 'file_xfer.C'; else $(CYGPATH_W) '$(srcdir)/file_xfer.C'` +boinc_client-gui_rpc_server.o: gui_rpc_server.C +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(boinc_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT boinc_client-gui_rpc_server.o -MD -MP -MF "$(DEPDIR)/boinc_client-gui_rpc_server.Tpo" \ +@am__fastdepCXX_TRUE@ -c -o boinc_client-gui_rpc_server.o `test -f 'gui_rpc_server.C' || echo '$(srcdir)/'`gui_rpc_server.C; \ +@am__fastdepCXX_TRUE@ then mv "$(DEPDIR)/boinc_client-gui_rpc_server.Tpo" "$(DEPDIR)/boinc_client-gui_rpc_server.Po"; \ +@am__fastdepCXX_TRUE@ else rm -f "$(DEPDIR)/boinc_client-gui_rpc_server.Tpo"; exit 1; \ +@am__fastdepCXX_TRUE@ fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='gui_rpc_server.C' object='boinc_client-gui_rpc_server.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/boinc_client-gui_rpc_server.Po' tmpdepfile='$(DEPDIR)/boinc_client-gui_rpc_server.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(boinc_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o boinc_client-gui_rpc_server.o `test -f 'gui_rpc_server.C' || echo '$(srcdir)/'`gui_rpc_server.C + +boinc_client-gui_rpc_server.obj: gui_rpc_server.C +@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(boinc_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT boinc_client-gui_rpc_server.obj -MD -MP -MF "$(DEPDIR)/boinc_client-gui_rpc_server.Tpo" \ +@am__fastdepCXX_TRUE@ -c -o boinc_client-gui_rpc_server.obj `if test -f 'gui_rpc_server.C'; then $(CYGPATH_W) 'gui_rpc_server.C'; else $(CYGPATH_W) '$(srcdir)/gui_rpc_server.C'`; \ +@am__fastdepCXX_TRUE@ then mv "$(DEPDIR)/boinc_client-gui_rpc_server.Tpo" "$(DEPDIR)/boinc_client-gui_rpc_server.Po"; \ +@am__fastdepCXX_TRUE@ else rm -f "$(DEPDIR)/boinc_client-gui_rpc_server.Tpo"; exit 1; \ +@am__fastdepCXX_TRUE@ fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='gui_rpc_server.C' object='boinc_client-gui_rpc_server.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ depfile='$(DEPDIR)/boinc_client-gui_rpc_server.Po' tmpdepfile='$(DEPDIR)/boinc_client-gui_rpc_server.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(boinc_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o boinc_client-gui_rpc_server.obj `if test -f 'gui_rpc_server.C'; then $(CYGPATH_W) 'gui_rpc_server.C'; else $(CYGPATH_W) '$(srcdir)/gui_rpc_server.C'` + boinc_client-hostinfo.o: hostinfo.C @am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(boinc_client_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT boinc_client-hostinfo.o -MD -MP -MF "$(DEPDIR)/boinc_client-hostinfo.Tpo" \ @am__fastdepCXX_TRUE@ -c -o boinc_client-hostinfo.o `test -f 'hostinfo.C' || echo '$(srcdir)/'`hostinfo.C; \ diff --git a/client/client_state.C b/client/client_state.C index 152e16eadd..3fc5f2cbe9 100644 --- a/client/client_state.C +++ b/client/client_state.C @@ -272,6 +272,8 @@ int CLIENT_STATE::init() { // Just to be on the safe side; something may have been modified // set_client_state_dirty("init"); + + gui_rpcs.init("gui_rpc"); return 0; } @@ -338,6 +340,7 @@ bool CLIENT_STATE::do_something() { POLL_ACTION(scheduler_rpc , scheduler_rpc_poll ); POLL_ACTION(garbage_collect , garbage_collect ); POLL_ACTION(update_results , update_results ); + POLL_ACTION(gui_rpc , gui_rpcs.poll ); } else { net_stats.poll(*net_xfers); // Call these functions in bottom to top order with @@ -354,6 +357,7 @@ bool CLIENT_STATE::do_something() { POLL_ACTION(handle_pers_file_xfers , handle_pers_file_xfers ); POLL_ACTION(garbage_collect , garbage_collect ); POLL_ACTION(update_results , update_results ); + POLL_ACTION(gui_rpc , gui_rpcs.poll ); } retval = write_state_file_if_needed(); diff --git a/client/client_state.h b/client/client_state.h index 1045aa141b..54ef3a8ae3 100644 --- a/client/client_state.h +++ b/client/client_state.h @@ -26,6 +26,7 @@ #include "app.h" #include "client_types.h" #include "file_xfer.h" +#include "gui_rpc_server.h" #include "hostinfo.h" #include "http.h" #include "language.h" @@ -73,6 +74,7 @@ public: GLOBAL_PREFS global_prefs; NET_STATS net_stats; SS_LOGIC ss_logic; + GUI_RPC_CONN_SET gui_rpcs; LANGUAGE language; int core_client_major_version; diff --git a/client/client_types.C b/client/client_types.C index 0372eab260..b3038277af 100644 --- a/client/client_types.C +++ b/client/client_types.C @@ -941,7 +941,7 @@ int RESULT::write(FILE* out, bool to_server) { if (to_server) { fprintf(out, "%d\n", wup->version_num); fprintf(out, - "%d.%0.2d\n", + "%d.%.2d\n", gstate.core_client_major_version, gstate.core_client_minor_version ); diff --git a/client/file_xfer.C b/client/file_xfer.C index ca12a2a029..e6bf0d9edb 100644 --- a/client/file_xfer.C +++ b/client/file_xfer.C @@ -209,15 +209,26 @@ bool FILE_XFER_SET::poll() { remove(fxp); i--; - // Restart the upload, using the newly obtained upload_offset - fxp->file_xfer_retval = fxp->init_upload(*fxp->fip); + // if the server's file size is bigger than ours, + // something bad has happened (like a result + // got sent to multiple users). + // Pretend the file was successfully uploaded + // + if (fxp->fip->upload_offset >= fxp->fip->nbytes) { + fxp->file_xfer_done = true; + fxp->file_xfer_retval = 0; + } else { + // Restart the upload, using the newly obtained + // upload_offset + fxp->file_xfer_retval = fxp->init_upload(*fxp->fip); - if (!fxp->file_xfer_retval) { - fxp->file_xfer_retval = insert(fxp); if (!fxp->file_xfer_retval) { - fxp->file_xfer_done = false; - fxp->file_xfer_retval = 0; - fxp->http_op_retval = 0; + fxp->file_xfer_retval = insert(fxp); + if (!fxp->file_xfer_retval) { + fxp->file_xfer_done = false; + fxp->file_xfer_retval = 0; + fxp->http_op_retval = 0; + } } } } diff --git a/client/gui_rpc_client.C b/client/gui_rpc_client.C new file mode 100644 index 0000000000..21857f0d52 --- /dev/null +++ b/client/gui_rpc_client.C @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include + +#include "parse.h" +#include "error_numbers.h" +#include "gui_rpc_client.h" + +int RPC_CLIENT::init(char* path) { + int sock, retval; + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, path); + sock = socket(AF_UNIX, SOCK_STREAM, 0); + retval = connect(sock, (const sockaddr*)(&addr), sizeof(addr)); + if (retval) { + perror("connect"); + exit(1); + } + fin = fdopen(dup(sock), "r"); + fout = fdopen(sock, "w"); +} + +RPC_CLIENT::~RPC_CLIENT() { + fclose(fin); + fclose(fout); +} + +int RPC_CLIENT::get_projects(vector& projects) { + char buf[256]; + int retval; + + fprintf(fout, "\n"); + fflush(fout); + while (fgets(buf, 256, fin)) { + if (match_tag(buf, "")) continue; + else if (match_tag(buf, "")) return 0; + else if (match_tag(buf, "")) { + PROJECT project; + retval = project.parse_state(fin); + if (!retval) { + projects.push_back(project); + } + } else { + fprintf(stderr, "unrecognized: %s", buf); + } + } + return ERR_XML_PARSE; +} + +int PROJECT::parse_state(FILE* in) { + char buf[256]; + STRING256 string; + + strcpy(project_name, ""); + strcpy(user_name, ""); + strcpy(team_name, ""); + resource_share = 100; + exp_avg_cpu = 0; + exp_avg_mod_time = 0; + min_rpc_time = 0; + min_report_min_rpc_time = 0; + nrpc_failures = 0; + master_url_fetch_pending = false; + sched_rpc_pending = false; + scheduler_urls.clear(); + while (fgets(buf, 256, in)) { + if (match_tag(buf, "")) return 0; + else if (parse_str(buf, "", string.text, sizeof(string.text))) { + scheduler_urls.push_back(string); + continue; + } + else if (parse_str(buf, "", master_url, sizeof(master_url))) continue; + else if (parse_str(buf, "", project_name, sizeof(project_name))) continue; + else if (parse_str(buf, "", user_name, sizeof(user_name))) continue; + else if (parse_str(buf, "", team_name, sizeof(team_name))) continue; + else if (parse_double(buf, "", user_total_credit)) continue; + else if (parse_double(buf, "", user_expavg_credit)) continue; + else if (parse_int(buf, "", (int &)user_create_time)) continue; + else if (parse_int(buf, "", rpc_seqno)) continue; + else if (parse_int(buf, "", hostid)) continue; + else if (parse_double(buf, "", host_total_credit)) continue; + else if (parse_double(buf, "", host_expavg_credit)) continue; + else if (parse_int(buf, "", (int &)host_create_time)) continue; + else if (parse_double(buf, "", exp_avg_cpu)) continue; + else if (parse_int(buf, "", exp_avg_mod_time)) continue; + else if (match_tag(buf, "")) { + copy_element_contents( + in, + "", + code_sign_key, + sizeof(code_sign_key) + ); + } + else if (parse_int(buf, "", nrpc_failures)) continue; + else if (parse_int(buf, "", master_fetch_failures)) continue; + else if (parse_int(buf, "", (int&)min_rpc_time)) continue; + else if (match_tag(buf, "")) master_url_fetch_pending = true; + else if (match_tag(buf, "")) sched_rpc_pending = true; + else printf("PROJECT::parse_state(): unrecognized: %s\n", buf); + } + return ERR_XML_PARSE; +} + +PROJECT::PROJECT() { + init(); +} + +void PROJECT::init() { + strcpy(master_url, ""); + strcpy(authenticator, ""); + project_specific_prefs = ""; + resource_share = 100; + strcpy(project_name, ""); + strcpy(user_name, ""); + strcpy(team_name, ""); + user_total_credit = 0; + user_expavg_credit = 0; + user_create_time = 0; + rpc_seqno = 0; + hostid = 0; + host_total_credit = 0; + host_expavg_credit = 0; + host_create_time = 0; + exp_avg_cpu = 0; + exp_avg_mod_time = 0; + strcpy(code_sign_key, ""); + nrpc_failures = 0; + min_rpc_time = 0; + min_report_min_rpc_time = 0; + master_fetch_failures = 0; + resource_debt = 0; + debt_order = 0; + master_url_fetch_pending = false; + sched_rpc_pending = false; + tentative = false; +} + +PROJECT::~PROJECT() { +} + diff --git a/client/gui_rpc_client.h b/client/gui_rpc_client.h new file mode 100644 index 0000000000..ae4a18349b --- /dev/null +++ b/client/gui_rpc_client.h @@ -0,0 +1,14 @@ +#include + +#include "client_types.h" + +class RPC_CLIENT { + int sock; + FILE* fin; + FILE* fout; +public: + ~RPC_CLIENT(); + int init(char*); + int get_projects(vector&); +}; + diff --git a/client/gui_rpc_server.C b/client/gui_rpc_server.C new file mode 100644 index 0000000000..e6ddaf7f88 --- /dev/null +++ b/client/gui_rpc_server.C @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include + +#include "parse.h" +#include "client_state.h" + +GUI_RPC_CONN::GUI_RPC_CONN(int s) { + sock = s; +#ifndef _WIN32 + fout = fdopen(dup(sock), "w"); +#endif +} + +GUI_RPC_CONN::~GUI_RPC_CONN() { + close(sock); + fclose(fout); +} + +int GUI_RPC_CONN::handle_rpc() { + char buf[256]; + int n; + unsigned int i; + + n = read(sock, buf, 256); + if (n <= 0) return -1; + buf[n] = 0; + printf("got %s\n", buf); + if (match_tag(buf, "\n"); + for (i=0; iwrite_state(fout); + } + fprintf(fout, "\n"); + } else { + fprintf(fout, "\n"); + } + fflush(fout); + return 0; +} + +int GUI_RPC_CONN_SET::insert(GUI_RPC_CONN* p) { + gui_rpcs.push_back(p); + return 0; +} + +void GUI_RPC_CONN_SET::init(char* path) { + sockaddr_un addr; + int retval; + +#ifndef _WIN32 + unlink(path); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, path); + lsock = socket(AF_UNIX, SOCK_STREAM, 0); + if (lsock < 0) { + perror("socket"); + exit(1); + } + retval = bind(lsock, (const sockaddr*)(&addr), sizeof(addr)); + if (retval) { + perror("bind"); + exit(1); + } + retval = listen(lsock, 999); + if (retval) { + perror("listen"); + exit(1); + } +#endif +} + +bool GUI_RPC_CONN_SET::poll() { +#ifdef _WIN32 + return false; +#else + unsigned int i; + fd_set read_fds, error_fds; + int sock, n, retval; + vector::iterator iter; + GUI_RPC_CONN* gr; + struct timeval tv; + + FD_ZERO(&read_fds); + FD_ZERO(&error_fds); + FD_SET(lsock, &read_fds); + for (i=0; isock, &read_fds); + FD_SET(gr->sock, &error_fds); + } + + memset(&tv, 0, sizeof(tv)); + n = select(FD_SETSIZE, &read_fds, 0, &error_fds, &tv); + if (FD_ISSET(lsock, &read_fds)) { + sock = accept(lsock, 0, 0); + GUI_RPC_CONN* gr = new GUI_RPC_CONN(sock); + insert(gr); + } + iter = gui_rpcs.begin(); + while (iter != gui_rpcs.end()) { + gr = *iter; + if (FD_ISSET(gr->sock, &error_fds)) { + delete gr; + gui_rpcs.erase(iter); + } else { + iter++; + } + } + iter = gui_rpcs.begin(); + while (iter != gui_rpcs.end()) { + gr = *iter; + if (FD_ISSET(gr->sock, &read_fds)) { + retval = gr->handle_rpc(); + if (retval) { + delete gr; + gui_rpcs.erase(iter); + continue; + } + } + iter++; + } + return (n != 0); +#endif +} diff --git a/client/gui_rpc_server.h b/client/gui_rpc_server.h new file mode 100644 index 0000000000..c23ca5a8fd --- /dev/null +++ b/client/gui_rpc_server.h @@ -0,0 +1,18 @@ +class GUI_RPC_CONN { +public: + int sock; + FILE* fout; + GUI_RPC_CONN(int); + ~GUI_RPC_CONN(); + int handle_rpc(); +}; + +class GUI_RPC_CONN_SET { + vector gui_rpcs; + int insert(GUI_RPC_CONN*); + int lsock; +public: + bool poll(); + void init(char*); +}; + diff --git a/client/gui_test.C b/client/gui_test.C new file mode 100644 index 0000000000..7f09299051 --- /dev/null +++ b/client/gui_test.C @@ -0,0 +1,16 @@ +#include + +#include "gui_rpc_client.h" + +main() { + RPC_CLIENT rpc; + vectorprojects; + unsigned int i; + + rpc.init("gui_rpc"); + rpc.get_projects(projects); + for (i=0; i1) Get file size

The request message has the form: -

-<data_server_request>
-    <core_client_major_version>1</core_client_major_version>
-    <core_client_minor_version>1</core_client_minor_version>
-    <get_file_size>filename</get_file_size>
-</data_server_request>
-
+
 ", htmlspecialchars("
+
+    1
+    1
+    filename
+
+"), "

The reply message has the form: -

-<data_server_reply>
-    <status>x</status>
-    [ <message>text</message>
-    | <file_size>nbytes</file_size> ]
-</data_server_reply>
-
-Status is -
    -
  • 0 on success. -Nbytes is 0 if the file doesn't exist. -
  • 1 on transient error. +
     ", htmlspecialchars("
    +
    +    x
    +    [ text&
    +    | nbytes ]
    +
    +"), "
    +Status is"; +list_start(); +list_item("0", "Success. Nbytes is 0 if the file doesn't exist."); +list_item("1", "Transient error. The client should try another data server, or try this one later. -
  • -1 on permanent error. -The client should give up on the result. -
+"); +list_item("-1", "Permanent error. The client should give up on the result."); +list_end(); +echo " In the error cases, the <file_size> element is omitted and the <message> element gives an explanation.

2) Upload file

Request message format: -

-<data_server_request>
-<core_client_major_version>1</core_client_major_version>
-<core_client_minor_version>1</core_client_minor_version>
-<file_upload>
-<file_info>
+
 ", htmlspecialchars("
+
+1
+1
+
+
    ...
-<xml_signature>
+
    ...
-</xml_signature>
-</file_info>
-<nbytes>x</nbytes>
-<offset>x</offset>
-<data>
+
+
+x
+x
+
 ... (nbytes bytes of data; may include non-ASCII data)
-</data>
-
+ +"), "

The <file_info> element is the exact text sent from the scheduling server to the client. It includes a signature based on the project's file upload authentication key pair. -<nbytes> is the amount of data being uploaded. +<nbytes> is the size of the file. <offset> is the offset within the file.

Reply message format: -

-<data_server_reply>
-    <status>x</status>
-    <message>text</message>
-</data_server_reply>
-
-Status is -
    -
  • 0 on success. -
  • 1 on transient error. +
     ", htmlspecialchars("
    +
    +    x
    +    text
    +
    +"), "
    +Status is "; + +list_start(); +list_item("0", "success"); +list_item("1", "transient error; The client should try another data server, or try this one later. -
  • -1 on permanent error. +"); +list_item("-1", "Permanent error. The client should give up on the result. +"); +list_end(); +echo "
In the error cases, the <message> element gives an explanation.

diff --git a/lib/parse.C b/lib/parse.C index b14d028a22..e1d56aaa41 100644 --- a/lib/parse.C +++ b/lib/parse.C @@ -70,6 +70,7 @@ bool parse_double(const char* buf, const char* tag, double& x) { // parse a string of the form ...string...; // returns the "string" part. +// "string" may not include '<' // Strips white space from ends. // Use "", if there might be attributes // @@ -78,14 +79,13 @@ bool parse_str(const char* buf, const char* tag, char* dest, int len) { if (!p) return false; p = strchr(p, '>'); ++p; - while (isspace(*p)) ++p; char* q = strchr(p, '<'); if (!q) return false; - while (isspace(*(q-1))) --q; char save_q = *q; *q = 0; safe_strncpy(dest, p, len); *q = save_q; + strip_whitespace(dest); return true; } @@ -96,15 +96,15 @@ bool parse_str(const char* buf, const char* tag, string& dest) { if (!p) return false; p = strchr(p, '>'); ++p; - while (isspace(*p)) ++p; char const* q = strchr(p, '<'); if (!q) return false; - while (isspace(*(q-1))) --q; dest.assign(p, q-p); + strip_whitespace(dest); return true; } -// parse a string of the form name="string" +// parse a string of the form name="string"; +// returns string in dest // void parse_attr(const char* buf, const char* name, char* dest, int len) { char* p, *q; diff --git a/lib/util.C b/lib/util.C index 27930e8f10..c1a20dc05f 100755 --- a/lib/util.C +++ b/lib/util.C @@ -289,15 +289,34 @@ void c2x(char *what) { strcpy(what, buf); } +// remove whitespace from start and end of a string +// void strip_whitespace(char *str) { - int read_pos=0, write_pos=0; - while (str[read_pos]) { - if (!isspace(str[read_pos])) { - str[write_pos++] = str[read_pos]; - } - read_pos++; + int n; + while (isascii(str[0]) && isspace(str[0])) { + strcpy(str, str+1); + } + while (1) { + n = strlen(str); + if (n == 0) break; + if (!isascii(str[n-1])) break; + if (!isspace(str[n-1])) break; + str[n-1] = 0; + } +} + +void strip_whitespace(string& str) { + int n; + while (isascii(str[0]) && isspace(str[0])) { + str.erase(0, 1); + } + while (1) { + n = str.length(); + if (n == 0) break; + if (!isascii(str[n-1])) break; + if (!isspace(str[n-1])) break; + str.erase(n-1, 1); } - str[write_pos] = 0; } void unescape_url(char *url) { diff --git a/lib/util.h b/lib/util.h index dea729dd1c..21b71744b2 100755 --- a/lib/util.h +++ b/lib/util.h @@ -40,6 +40,7 @@ extern int parse_command_line( char *, char ** ); extern int lock_file(char*); extern void c2x(char *what); extern void strip_whitespace(char *str); +extern void strip_whitespace(string&); extern void unescape_url(char *url); extern void escape_url(char *in, char*out); extern void escape_url_readable(char* in, char* out); @@ -62,19 +63,16 @@ inline bool starts_with(string const& s, string const& prefix) { } // http://lists.debian.org/debian-gcc/2002/debian-gcc-200204/msg00092.html -inline void downcase_string(string::iterator begin, string::iterator end, - string::iterator src) -{ +inline void downcase_string( + string::iterator begin, string::iterator end, string::iterator src +) { std::transform(begin, end, src, (int(*)(int))tolower); } -inline void downcase_string(string& w) -{ +inline void downcase_string(string& w) { downcase_string(w.begin(), w.end(), w.begin()); } - - // NOTE: use #include to get max,min // the __attribute((format...)) tags are GCC extensions that let the compiler