/* return 0 on success, non-zero on failure */ static int get_request(curl_socket_t sock, struct httprequest *req) { int error; int fail = 0; int done_processing = 0; char *reqbuf = req->reqbuf; ssize_t got = 0; char *pipereq = NULL; size_t pipereq_length = 0; if(req->pipelining) { pipereq = reqbuf + req->checkindex; pipereq_length = req->offset - req->checkindex; } /*** Init the httprequest structure properly for the upcoming request ***/ req->checkindex = 0; req->offset = 0; req->testno = DOCNUMBER_NOTHING; req->partno = 0; req->open = TRUE; req->auth_req = FALSE; req->auth = FALSE; req->cl = 0; req->digest = FALSE; req->ntlm = FALSE; req->pipe = 0; req->skip = 0; req->rcmd = RCMD_NORMALREQ; req->protocol = RPROT_NONE; req->prot_version = 0; req->pipelining = FALSE; req->rtp_buffer = NULL; req->rtp_buffersize = 0; /*** end of httprequest init ***/ while(!done_processing && (req->offset < REQBUFSIZ-1)) { if(pipereq_length && pipereq) { memmove(reqbuf, pipereq, pipereq_length); got = curlx_uztosz(pipereq_length); pipereq_length = 0; } else { if(req->skip) /* we are instructed to not read the entire thing, so we make sure to only read what we're supposed to and NOT read the enire thing the client wants to send! */ got = sread(sock, reqbuf + req->offset, req->cl); else got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset); } if(got_exit_signal) return 1; if(got == 0) { logmsg("Connection closed by client"); fail = 1; } else if(got < 0) { error = SOCKERRNO; logmsg("recv() returned error: (%d) %s", error, strerror(error)); fail = 1; } if(fail) { /* dump the request received so far to the external file */ reqbuf[req->offset] = '\0'; storerequest(reqbuf, req->offset); return 1; } logmsg("Read %zd bytes", got); req->offset += (size_t)got; reqbuf[req->offset] = '\0'; done_processing = ProcessRequest(req); if(got_exit_signal) return 1; if(done_processing && req->pipe) { logmsg("Waiting for another piped request"); done_processing = 0; req->pipe--; } } if((req->offset == REQBUFSIZ-1) && (got > 0)) { logmsg("Request would overflow buffer, closing connection"); /* dump request received so far to external file anyway */ reqbuf[REQBUFSIZ-1] = '\0'; fail = 1; } else if(req->offset > REQBUFSIZ-1) { logmsg("Request buffer overflow, closing connection"); /* dump request received so far to external file anyway */ reqbuf[REQBUFSIZ-1] = '\0'; fail = 1; } else reqbuf[req->offset] = '\0'; /* dump the request to an external file */ storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset); if(got_exit_signal) return 1; return fail; /* return 0 on success */ }
static CURLcode gopher_do(struct connectdata *conn, bool *done) { CURLcode result=CURLE_OK; struct SessionHandle *data=conn->data; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; curl_off_t *bytecount = &data->req.bytecount; char *path = data->state.path; char *sel; char *sel_org = NULL; ssize_t amount, k; int len; *done = TRUE; /* unconditionally */ /* Create selector. Degenerate cases: / and /1 => convert to "" */ if(strlen(path) <= 2) { sel = (char *)""; len = (int)strlen(sel); } else { char *newp; size_t j, i; /* Otherwise, drop / and the first character (i.e., item type) ... */ newp = path; newp+=2; /* ... then turn ? into TAB for search servers, Veronica, etc. ... */ j = strlen(newp); for(i=0; i<j; i++) if(newp[i] == '?') newp[i] = '\x09'; /* ... and finally unescape */ sel = curl_easy_unescape(data, newp, 0, &len); if(!sel) return CURLE_OUT_OF_MEMORY; sel_org = sel; } /* We use Curl_write instead of Curl_sendf to make sure the entire buffer is sent, which could be sizeable with long selectors. */ k = curlx_uztosz(len); for(;;) { result = Curl_write(conn, sockfd, sel, k, &amount); if(!result) { /* Which may not have written it all! */ result = Curl_client_write(conn, CLIENTWRITE_HEADER, sel, amount); if(result) { free(sel_org); return result; } k -= amount; sel += amount; if(k < 1) break; /* but it did write it all */ } else { failf(data, "Failed sending Gopher request"); free(sel_org); return result; } /* Don't busyloop. The entire loop thing is a work-around as it causes a BLOCKING behavior which is a NO-NO. This function should rather be split up in a do and a doing piece where the pieces that aren't possible to send now will be sent in the doing function repeatedly until the entire request is sent. Wait a while for the socket to be writable. Note that this doesn't acknowledge the timeout. */ Curl_socket_ready(CURL_SOCKET_BAD, sockfd, 100); } free(sel_org); /* We can use Curl_sendf to send the terminal \r\n relatively safely and save allocing another string/doing another _write loop. */ result = Curl_sendf(sockfd, conn, "\r\n"); if(result) { failf(data, "Failed sending Gopher request"); return result; } result = Curl_client_write(conn, CLIENTWRITE_HEADER, (char *)"\r\n", 2); if(result) return result; Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, -1, NULL); /* no upload */ return CURLE_OK; }