Revert changes relative to lib/*.[ch] recent renaming
[platform/upstream/curl.git] / lib / pingpong.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2012, 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        * pp->cache_size is cast to ssize_t here.  This should be safe, because
319        * it would have been populated with something of size int to begin
320        * with, even though its datatype may be larger than an int.
321        */
322       DEBUGASSERT((ptr+pp->cache_size) <= (buf+BUFSIZE+1));
323       memcpy(ptr, pp->cache, pp->cache_size);
324       gotbytes = (ssize_t)pp->cache_size;
325       free(pp->cache);    /* free the cache */
326       pp->cache = NULL;   /* clear the pointer */
327       pp->cache_size = 0; /* zero the size just in case */
328     }
329     else {
330       int res;
331 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
332       enum protection_level prot = conn->data_prot;
333       conn->data_prot = PROT_CLEAR;
334 #endif
335       DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
336       res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
337                       &gotbytes);
338 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
339       DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
340       conn->data_prot = prot;
341 #endif
342       if(res == CURLE_AGAIN)
343         return CURLE_OK; /* return */
344
345       if((res == CURLE_OK) && (gotbytes > 0))
346         /* convert from the network encoding */
347         res = Curl_convert_from_network(data, ptr, gotbytes);
348       /* Curl_convert_from_network calls failf if unsuccessful */
349
350       if(CURLE_OK != res) {
351         result = (CURLcode)res; /* Set outer result variable to this error. */
352         keepon = FALSE;
353       }
354     }
355
356     if(!keepon)
357       ;
358     else if(gotbytes <= 0) {
359       keepon = FALSE;
360       result = CURLE_RECV_ERROR;
361       failf(data, "response reading failed");
362     }
363     else {
364       /* we got a whole chunk of data, which can be anything from one
365        * byte to a set of lines and possible just a piece of the last
366        * line */
367       ssize_t i;
368       ssize_t clipamount = 0;
369       bool restart = FALSE;
370
371       data->req.headerbytecount += (long)gotbytes;
372
373       pp->nread_resp += gotbytes;
374       for(i = 0; i < gotbytes; ptr++, i++) {
375         perline++;
376         if(*ptr=='\n') {
377           /* a newline is CRLF in pp-talk, so the CR is ignored as
378              the line isn't really terminated until the LF comes */
379
380           /* output debug output if that is requested */
381 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
382           if(!conn->sec_complete)
383 #endif
384             if(data->set.verbose)
385               Curl_debug(data, CURLINFO_HEADER_IN,
386                          pp->linestart_resp, (size_t)perline, conn);
387
388           /*
389            * We pass all response-lines to the callback function registered
390            * for "headers". The response lines can be seen as a kind of
391            * headers.
392            */
393           result = Curl_client_write(conn, CLIENTWRITE_HEADER,
394                                      pp->linestart_resp, perline);
395           if(result)
396             return result;
397
398           if(pp->endofresp(pp, code)) {
399             /* This is the end of the last line, copy the last line to the
400                start of the buffer and zero terminate, for old times sake (and
401                krb4)! */
402             char *meow;
403             int n;
404             for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++)
405               buf[n] = *meow;
406             *meow=0; /* zero terminate */
407             keepon=FALSE;
408             pp->linestart_resp = ptr+1; /* advance pointer */
409             i++; /* skip this before getting out */
410
411             *size = pp->nread_resp; /* size of the response */
412             pp->nread_resp = 0; /* restart */
413             break;
414           }
415           perline=0; /* line starts over here */
416           pp->linestart_resp = ptr+1;
417         }
418       }
419
420       if(!keepon && (i != gotbytes)) {
421         /* We found the end of the response lines, but we didn't parse the
422            full chunk of data we have read from the server. We therefore need
423            to store the rest of the data to be checked on the next invoke as
424            it may actually contain another end of response already! */
425         clipamount = gotbytes - i;
426         restart = TRUE;
427         DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
428                      "server response left\n",
429                      (int)clipamount));
430       }
431       else if(keepon) {
432
433         if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
434           /* We got an excessive line without newlines and we need to deal
435              with it. We keep the first bytes of the line then we throw
436              away the rest. */
437           infof(data, "Excessive server response line length received, "
438                 "%zd bytes. Stripping\n", gotbytes);
439           restart = TRUE;
440
441           /* we keep 40 bytes since all our pingpong protocols are only
442              interested in the first piece */
443           clipamount = 40;
444         }
445         else if(pp->nread_resp > BUFSIZE/2) {
446           /* We got a large chunk of data and there's potentially still
447              trailing data to take care of, so we put any such part in the
448              "cache", clear the buffer to make space and restart. */
449           clipamount = perline;
450           restart = TRUE;
451         }
452       }
453       else if(i == gotbytes)
454         restart = TRUE;
455
456       if(clipamount) {
457         pp->cache_size = clipamount;
458         pp->cache = malloc(pp->cache_size);
459         if(pp->cache)
460           memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
461         else
462           return CURLE_OUT_OF_MEMORY;
463       }
464       if(restart) {
465         /* now reset a few variables to start over nicely from the start of
466            the big buffer */
467         pp->nread_resp = 0; /* start over from scratch in the buffer */
468         ptr = pp->linestart_resp = buf;
469         perline = 0;
470       }
471
472     } /* there was data */
473
474   } /* while there's buffer left and loop is requested */
475
476   pp->pending_resp = FALSE;
477
478   return result;
479 }
480
481 int Curl_pp_getsock(struct pingpong *pp,
482                     curl_socket_t *socks,
483                     int numsocks)
484 {
485   struct connectdata *conn = pp->conn;
486
487   if(!numsocks)
488     return GETSOCK_BLANK;
489
490   socks[0] = conn->sock[FIRSTSOCKET];
491
492   if(pp->sendleft) {
493     /* write mode */
494     return GETSOCK_WRITESOCK(0);
495   }
496
497   /* read mode */
498   return GETSOCK_READSOCK(0);
499 }
500
501 CURLcode Curl_pp_flushsend(struct pingpong *pp)
502 {
503   /* we have a piece of a command still left to send */
504   struct connectdata *conn = pp->conn;
505   ssize_t written;
506   CURLcode result = CURLE_OK;
507   curl_socket_t sock = conn->sock[FIRSTSOCKET];
508
509   result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
510                       pp->sendleft, pp->sendleft, &written);
511   if(result)
512     return result;
513
514   if(written != (ssize_t)pp->sendleft) {
515     /* only a fraction was sent */
516     pp->sendleft -= written;
517   }
518   else {
519     free(pp->sendthis);
520     pp->sendthis=NULL;
521     pp->sendleft = pp->sendsize = 0;
522     pp->response = Curl_tvnow();
523   }
524   return CURLE_OK;
525 }
526
527 CURLcode Curl_pp_disconnect(struct pingpong *pp)
528 {
529   if(pp->cache) {
530     free(pp->cache);
531     pp->cache = NULL;
532   }
533   return CURLE_OK;
534 }
535
536
537
538 #endif