Merge pull request #25 from trtom/master
[platform/upstream/curl.git] / lib / pingpong.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  *   'pingpong' is for generic back-and-forth support functions used by FTP,
22  *   IMAP, POP3, SMTP and whatever more that likes them.
23  *
24  ***************************************************************************/
25
26 #include "setup.h"
27
28 #include "urldata.h"
29 #include "sendf.h"
30 #include "select.h"
31 #include "progress.h"
32 #include "speedcheck.h"
33 #include "pingpong.h"
34 #include "multiif.h"
35 #include "non-ascii.h"
36
37 #define _MPRINTF_REPLACE /* use our functions only */
38 #include <curl/mprintf.h>
39
40 #include "curl_memory.h"
41 /* The last #include file should be: */
42 #include "memdebug.h"
43
44 #ifdef USE_PINGPONG
45
46 /* Returns timeout in ms. 0 or negative number means the timeout has already
47    triggered */
48 long Curl_pp_state_timeout(struct pingpong *pp)
49 {
50   struct connectdata *conn = pp->conn;
51   struct SessionHandle *data=conn->data;
52   long timeout_ms; /* in milliseconds */
53   long timeout2_ms; /* in milliseconds */
54   long response_time= (data->set.server_response_timeout)?
55     data->set.server_response_timeout: pp->response_time;
56
57   /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
58      remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
59      supposed to govern the response for any given server response, not for
60      the time from connect to the given server response. */
61
62   /* Without a requested timeout, we only wait 'response_time' seconds for the
63      full response to arrive before we bail out */
64   timeout_ms = response_time -
65     Curl_tvdiff(Curl_tvnow(), pp->response); /* spent time */
66
67   if(data->set.timeout) {
68     /* if timeout is requested, find out how much remaining time we have */
69     timeout2_ms = data->set.timeout - /* timeout time */
70       Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
71
72     /* pick the lowest number */
73     timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
74   }
75
76   return timeout_ms;
77 }
78
79
80 /*
81  * Curl_pp_multi_statemach()
82  *
83  * called repeatedly until done when the multi interface is used.
84  */
85 CURLcode Curl_pp_multi_statemach(struct pingpong *pp)
86 {
87   struct connectdata *conn = pp->conn;
88   curl_socket_t sock = conn->sock[FIRSTSOCKET];
89   int rc;
90   struct SessionHandle *data=conn->data;
91   CURLcode result = CURLE_OK;
92   long timeout_ms = Curl_pp_state_timeout(pp);
93
94   if(timeout_ms <= 0) {
95     failf(data, "server response timeout");
96     return CURLE_OPERATION_TIMEDOUT;
97   }
98
99   rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
100                          pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
101                          0);
102
103   if(rc == -1) {
104     failf(data, "select/poll error");
105     return CURLE_OUT_OF_MEMORY;
106   }
107   else if(rc != 0)
108     result = pp->statemach_act(conn);
109
110   /* if rc == 0, then select() timed out */
111
112   return result;
113 }
114
115 /*
116  * Curl_pp_easy_statemach()
117  *
118  * called repeatedly until done when the easy interface is used.
119  */
120 CURLcode Curl_pp_easy_statemach(struct pingpong *pp)
121 {
122   struct connectdata *conn = pp->conn;
123   curl_socket_t sock = conn->sock[FIRSTSOCKET];
124   int rc;
125   long interval_ms;
126   long timeout_ms = Curl_pp_state_timeout(pp);
127   struct SessionHandle *data=conn->data;
128   CURLcode result;
129
130   if(timeout_ms <=0 ) {
131     failf(data, "server response timeout");
132     return CURLE_OPERATION_TIMEDOUT; /* already too little time */
133   }
134
135   interval_ms = 1000;  /* use 1 second timeout intervals */
136   if(timeout_ms < interval_ms)
137     interval_ms = timeout_ms;
138
139   rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
140                          pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
141                          interval_ms);
142
143   if(Curl_pgrsUpdate(conn))
144     result = CURLE_ABORTED_BY_CALLBACK;
145   else
146     result = Curl_speedcheck(data, Curl_tvnow());
147
148   if(result)
149     ;
150   else if(rc == -1) {
151     failf(data, "select/poll error");
152     result = CURLE_OUT_OF_MEMORY;
153   }
154   else if(rc)
155     result = pp->statemach_act(conn);
156
157   return result;
158 }
159
160 /* initialize stuff to prepare for reading a fresh new response */
161 void Curl_pp_init(struct pingpong *pp)
162 {
163   struct connectdata *conn = pp->conn;
164   pp->nread_resp = 0;
165   pp->linestart_resp = conn->data->state.buffer;
166   pp->pending_resp = TRUE;
167   pp->response = Curl_tvnow(); /* start response time-out now! */
168 }
169
170
171
172 /***********************************************************************
173  *
174  * Curl_pp_vsendf()
175  *
176  * Send the formated string as a command to a pingpong server. Note that
177  * the string should not have any CRLF appended, as this function will
178  * append the necessary things itself.
179  *
180  * made to never block
181  */
182 CURLcode Curl_pp_vsendf(struct pingpong *pp,
183                         const char *fmt,
184                         va_list args)
185 {
186   ssize_t bytes_written;
187   size_t write_len;
188   char *fmt_crlf;
189   char *s;
190   CURLcode error;
191   struct connectdata *conn = pp->conn;
192   struct SessionHandle *data = conn->data;
193
194 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
195   enum protection_level data_sec = conn->data_prot;
196 #endif
197
198   DEBUGASSERT(pp->sendleft == 0);
199   DEBUGASSERT(pp->sendsize == 0);
200   DEBUGASSERT(pp->sendthis == NULL);
201
202   fmt_crlf = aprintf("%s\r\n", fmt); /* append a trailing CRLF */
203   if(!fmt_crlf)
204     return CURLE_OUT_OF_MEMORY;
205
206   s = vaprintf(fmt_crlf, args); /* trailing CRLF appended */
207   free(fmt_crlf);
208   if(!s)
209     return CURLE_OUT_OF_MEMORY;
210
211   bytes_written = 0;
212   write_len = strlen(s);
213
214   Curl_pp_init(pp);
215
216   error = Curl_convert_to_network(data, s, write_len);
217   /* Curl_convert_to_network calls failf if unsuccessful */
218   if(error) {
219     free(s);
220     return error;
221   }
222
223 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
224   conn->data_prot = PROT_CMD;
225 #endif
226   error = Curl_write(conn, conn->sock[FIRSTSOCKET], s, write_len,
227                      &bytes_written);
228 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
229   DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
230   conn->data_prot = data_sec;
231 #endif
232
233   if(error) {
234     free(s);
235     return error;
236   }
237
238   if(conn->data->set.verbose)
239     Curl_debug(conn->data, CURLINFO_HEADER_OUT,
240                s, (size_t)bytes_written, conn);
241
242   if(bytes_written != (ssize_t)write_len) {
243     /* the whole chunk was not sent, keep it around and adjust sizes */
244     pp->sendthis = s;
245     pp->sendsize = write_len;
246     pp->sendleft = write_len - bytes_written;
247   }
248   else {
249     free(s);
250     pp->sendthis = NULL;
251     pp->sendleft = pp->sendsize = 0;
252     pp->response = Curl_tvnow();
253   }
254
255   return CURLE_OK;
256 }
257
258
259 /***********************************************************************
260  *
261  * Curl_pp_sendf()
262  *
263  * Send the formated string as a command to a pingpong server. Note that
264  * the string should not have any CRLF appended, as this function will
265  * append the necessary things itself.
266  *
267  * made to never block
268  */
269 CURLcode Curl_pp_sendf(struct pingpong *pp,
270                        const char *fmt, ...)
271 {
272   CURLcode res;
273   va_list ap;
274   va_start(ap, fmt);
275
276   res = Curl_pp_vsendf(pp, fmt, ap);
277
278   va_end(ap);
279
280   return res;
281 }
282
283 /*
284  * Curl_pp_readresp()
285  *
286  * Reads a piece of a server response.
287  */
288 CURLcode Curl_pp_readresp(curl_socket_t sockfd,
289                           struct pingpong *pp,
290                           int *code, /* return the server code if done */
291                           size_t *size) /* size of the response */
292 {
293   ssize_t perline; /* count bytes per line */
294   bool keepon=TRUE;
295   ssize_t gotbytes;
296   char *ptr;
297   struct connectdata *conn = pp->conn;
298   struct SessionHandle *data = conn->data;
299   char * const buf = data->state.buffer;
300   CURLcode result = CURLE_OK;
301
302   *code = 0; /* 0 for errors or not done */
303   *size = 0;
304
305   ptr=buf + pp->nread_resp;
306
307   /* number of bytes in the current line, so far */
308   perline = (ssize_t)(ptr-pp->linestart_resp);
309
310   keepon=TRUE;
311
312   while((pp->nread_resp<BUFSIZE) && (keepon && !result)) {
313
314     if(pp->cache) {
315       /* we had data in the "cache", copy that instead of doing an actual
316        * read
317        *
318        * ftp->cache_size is cast to int here.  This should be safe,
319        * because it would have been populated with something of size
320        * int to begin with, even though its datatype may be larger
321        * than an int.
322        */
323       DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1));
324       memcpy(ptr, pp->cache, pp->cache_size);
325       gotbytes = (ssize_t)pp->cache_size;
326       free(pp->cache);    /* free the cache */
327       pp->cache = NULL;   /* clear the pointer */
328       pp->cache_size = 0; /* zero the size just in case */
329     }
330     else {
331       int res;
332 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
333       enum protection_level prot = conn->data_prot;
334       conn->data_prot = PROT_CLEAR;
335 #endif
336       DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
337       res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
338                       &gotbytes);
339 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
340       DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
341       conn->data_prot = prot;
342 #endif
343       if(res == CURLE_AGAIN)
344         return CURLE_OK; /* return */
345
346       if((res == CURLE_OK) && (gotbytes > 0))
347         /* convert from the network encoding */
348         res = Curl_convert_from_network(data, ptr, gotbytes);
349       /* Curl_convert_from_network calls failf if unsuccessful */
350
351       if(CURLE_OK != res) {
352         result = (CURLcode)res; /* Set outer result variable to this error. */
353         keepon = FALSE;
354       }
355     }
356
357     if(!keepon)
358       ;
359     else if(gotbytes <= 0) {
360       keepon = FALSE;
361       result = CURLE_RECV_ERROR;
362       failf(data, "response reading failed");
363     }
364     else {
365       /* we got a whole chunk of data, which can be anything from one
366        * byte to a set of lines and possible just a piece of the last
367        * line */
368       ssize_t i;
369       ssize_t clipamount = 0;
370       bool restart = FALSE;
371
372       data->req.headerbytecount += (long)gotbytes;
373
374       pp->nread_resp += gotbytes;
375       for(i = 0; i < gotbytes; ptr++, i++) {
376         perline++;
377         if(*ptr=='\n') {
378           /* a newline is CRLF in ftp-talk, so the CR is ignored as
379              the line isn't really terminated until the LF comes */
380
381           /* output debug output if that is requested */
382 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
383           if(!conn->sec_complete)
384 #endif
385             if(data->set.verbose)
386               Curl_debug(data, CURLINFO_HEADER_IN,
387                          pp->linestart_resp, (size_t)perline, conn);
388
389           /*
390            * We pass all response-lines to the callback function registered
391            * for "headers". The response lines can be seen as a kind of
392            * headers.
393            */
394           result = Curl_client_write(conn, CLIENTWRITE_HEADER,
395                                      pp->linestart_resp, perline);
396           if(result)
397             return result;
398
399           if(pp->endofresp(pp, code)) {
400             /* This is the end of the last line, copy the last line to the
401                start of the buffer and zero terminate, for old times sake (and
402                krb4)! */
403             char *meow;
404             int n;
405             for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++)
406               buf[n] = *meow;
407             *meow=0; /* zero terminate */
408             keepon=FALSE;
409             pp->linestart_resp = ptr+1; /* advance pointer */
410             i++; /* skip this before getting out */
411
412             *size = pp->nread_resp; /* size of the response */
413             pp->nread_resp = 0; /* restart */
414             break;
415           }
416           perline=0; /* line starts over here */
417           pp->linestart_resp = ptr+1;
418         }
419       }
420
421       if(!keepon && (i != gotbytes)) {
422         /* We found the end of the response lines, but we didn't parse the
423            full chunk of data we have read from the server. We therefore need
424            to store the rest of the data to be checked on the next invoke as
425            it may actually contain another end of response already! */
426         clipamount = gotbytes - i;
427         restart = TRUE;
428       }
429       else if(keepon) {
430
431         if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
432           /* We got an excessive line without newlines and we need to deal
433              with it. We keep the first bytes of the line then we throw
434              away the rest. */
435           infof(data, "Excessive server response line length received, "
436                 "%zd bytes. Stripping\n", gotbytes);
437           restart = TRUE;
438
439           /* we keep 40 bytes since all our pingpong protocols are only
440              interested in the first piece */
441           clipamount = 40;
442         }
443         else if(pp->nread_resp > BUFSIZE/2) {
444           /* We got a large chunk of data and there's potentially still
445              trailing data to take care of, so we put any such part in the
446              "cache", clear the buffer to make space and restart. */
447           clipamount = perline;
448           restart = TRUE;
449         }
450       }
451       else if(i == gotbytes)
452         restart = TRUE;
453
454       if(clipamount) {
455         pp->cache_size = clipamount;
456         pp->cache = malloc(pp->cache_size);
457         if(pp->cache)
458           memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
459         else
460           return CURLE_OUT_OF_MEMORY;
461       }
462       if(restart) {
463         /* now reset a few variables to start over nicely from the start of
464            the big buffer */
465         pp->nread_resp = 0; /* start over from scratch in the buffer */
466         ptr = pp->linestart_resp = buf;
467         perline = 0;
468       }
469
470     } /* there was data */
471
472   } /* while there's buffer left and loop is requested */
473
474   pp->pending_resp = FALSE;
475
476   return result;
477 }
478
479 int Curl_pp_getsock(struct pingpong *pp,
480                     curl_socket_t *socks,
481                     int numsocks)
482 {
483   struct connectdata *conn = pp->conn;
484
485   if(!numsocks)
486     return GETSOCK_BLANK;
487
488   socks[0] = conn->sock[FIRSTSOCKET];
489
490   if(pp->sendleft) {
491     /* write mode */
492     return GETSOCK_WRITESOCK(0);
493   }
494
495   /* read mode */
496   return GETSOCK_READSOCK(0);
497 }
498
499 CURLcode Curl_pp_flushsend(struct pingpong *pp)
500 {
501   /* we have a piece of a command still left to send */
502   struct connectdata *conn = pp->conn;
503   ssize_t written;
504   CURLcode result = CURLE_OK;
505   curl_socket_t sock = conn->sock[FIRSTSOCKET];
506
507   result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
508                       pp->sendleft, pp->sendleft, &written);
509   if(result)
510     return result;
511
512   if(written != (ssize_t)pp->sendleft) {
513     /* only a fraction was sent */
514     pp->sendleft -= written;
515   }
516   else {
517     free(pp->sendthis);
518     pp->sendthis=NULL;
519     pp->sendleft = pp->sendsize = 0;
520     pp->response = Curl_tvnow();
521   }
522   return CURLE_OK;
523 }
524
525 CURLcode Curl_pp_disconnect(struct pingpong *pp)
526 {
527   if(pp->cache) {
528     free(pp->cache);
529     pp->cache = NULL;
530   }
531   return CURLE_OK;
532 }
533
534
535
536 #endif