build: add typecast to avoid warning
[platform/upstream/curl.git] / lib / pingpong.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2010, 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
36 #define _MPRINTF_REPLACE /* use our functions only */
37 #include <curl/mprintf.h>
38
39 #include "curl_memory.h"
40 /* The last #include file should be: */
41 #include "memdebug.h"
42
43 #ifdef USE_PINGPONG
44
45 /* Returns timeout in ms. 0 or negative number means the timeout has already
46    triggered */
47 long Curl_pp_state_timeout(struct pingpong *pp)
48 {
49   struct connectdata *conn = pp->conn;
50   struct SessionHandle *data=conn->data;
51   long timeout_ms; /* in milliseconds */
52   long timeout2_ms; /* in milliseconds */
53   long response_time= (data->set.server_response_timeout)?
54     data->set.server_response_timeout: pp->response_time;
55
56   /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
57      remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
58      supposed to govern the response for any given server response, not for
59      the time from connect to the given server response. */
60
61   /* Without a requested timeout, we only wait 'response_time' seconds for the
62      full response to arrive before we bail out */
63   timeout_ms = response_time -
64     Curl_tvdiff(Curl_tvnow(), pp->response); /* spent time */
65
66   if(data->set.timeout) {
67     /* if timeout is requested, find out how much remaining time we have */
68     timeout2_ms = data->set.timeout - /* timeout time */
69       Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
70
71     /* pick the lowest number */
72     timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
73   }
74
75   return timeout_ms;
76 }
77
78
79 /*
80  * Curl_pp_multi_statemach()
81  *
82  * called repeatedly until done when the multi interface is used.
83  */
84 CURLcode Curl_pp_multi_statemach(struct pingpong *pp)
85 {
86   struct connectdata *conn = pp->conn;
87   curl_socket_t sock = conn->sock[FIRSTSOCKET];
88   int rc;
89   struct SessionHandle *data=conn->data;
90   CURLcode result = CURLE_OK;
91   long timeout_ms = Curl_pp_state_timeout(pp);
92
93   if(timeout_ms <= 0) {
94     failf(data, "server response timeout");
95     return CURLE_OPERATION_TIMEDOUT;
96   }
97
98   rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
99                          pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
100                          0);
101
102   if(rc == -1) {
103     failf(data, "select/poll error");
104     return CURLE_OUT_OF_MEMORY;
105   }
106   else if(rc != 0)
107     result = pp->statemach_act(conn);
108
109   /* if rc == 0, then select() timed out */
110
111   return result;
112 }
113
114 /*
115  * Curl_pp_easy_statemach()
116  *
117  * called repeatedly until done when the easy interface is used.
118  */
119 CURLcode Curl_pp_easy_statemach(struct pingpong *pp)
120 {
121   struct connectdata *conn = pp->conn;
122   curl_socket_t sock = conn->sock[FIRSTSOCKET];
123   int rc;
124   long interval_ms;
125   long timeout_ms = Curl_pp_state_timeout(pp);
126   struct SessionHandle *data=conn->data;
127   CURLcode result;
128
129   if(timeout_ms <=0 ) {
130     failf(data, "server response timeout");
131     return CURLE_OPERATION_TIMEDOUT; /* already too little time */
132   }
133
134   interval_ms = 1000;  /* use 1 second timeout intervals */
135   if(timeout_ms < interval_ms)
136     interval_ms = timeout_ms;
137
138   rc = Curl_socket_ready(pp->sendleft?CURL_SOCKET_BAD:sock, /* reading */
139                          pp->sendleft?sock:CURL_SOCKET_BAD, /* writing */
140                          (int)interval_ms);
141
142   if(Curl_pgrsUpdate(conn))
143     result = CURLE_ABORTED_BY_CALLBACK;
144   else
145     result = Curl_speedcheck(data, Curl_tvnow());
146
147   if(result)
148     ;
149   else if(rc == -1) {
150     failf(data, "select/poll error");
151     result = CURLE_OUT_OF_MEMORY;
152   }
153   else if(rc)
154     result = pp->statemach_act(conn);
155
156   return result;
157 }
158
159 /* initialize stuff to prepare for reading a fresh new response */
160 void Curl_pp_init(struct pingpong *pp)
161 {
162   struct connectdata *conn = pp->conn;
163   pp->nread_resp = 0;
164   pp->linestart_resp = conn->data->state.buffer;
165   pp->pending_resp = TRUE;
166   pp->response = Curl_tvnow(); /* start response time-out now! */
167 }
168
169
170
171 /***********************************************************************
172  *
173  * Curl_pp_sendfv()
174  *
175  * Send the formated string as a command to a pingpong server. Note that
176  * the string should not have any CRLF appended, as this function will
177  * append the necessary things itself.
178  *
179  * NOTE: we build the command in a fixed-length buffer, which sets length
180  * restrictions on the command!
181  *
182  * made to never block
183  */
184 CURLcode Curl_pp_vsendf(struct pingpong *pp,
185                         const char *fmt,
186                         va_list args)
187 {
188   ssize_t bytes_written;
189 /* may still not be big enough for some krb5 tokens */
190 #define SBUF_SIZE 1024
191   char s[SBUF_SIZE];
192   size_t write_len;
193   char *sptr=s;
194   CURLcode res = CURLE_OK;
195   struct connectdata *conn = pp->conn;
196   struct SessionHandle *data = conn->data;
197
198 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
199   enum protection_level data_sec = conn->data_prot;
200 #endif
201
202   vsnprintf(s, SBUF_SIZE-3, fmt, args);
203
204   strcat(s, "\r\n"); /* append a trailing CRLF */
205
206   bytes_written=0;
207   write_len = strlen(s);
208
209   Curl_pp_init(pp);
210
211 #ifdef CURL_DOES_CONVERSIONS
212   res = Curl_convert_to_network(data, s, write_len);
213   /* Curl_convert_to_network calls failf if unsuccessful */
214   if(res != CURLE_OK) {
215     return res;
216   }
217 #endif /* CURL_DOES_CONVERSIONS */
218
219 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
220   conn->data_prot = prot_cmd;
221 #endif
222   res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
223                    &bytes_written);
224 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
225   conn->data_prot = data_sec;
226 #endif
227
228   if(CURLE_OK != res)
229     return res;
230
231   if(conn->data->set.verbose)
232     Curl_debug(conn->data, CURLINFO_HEADER_OUT,
233                sptr, (size_t)bytes_written, conn);
234
235   if(bytes_written != (ssize_t)write_len) {
236     /* the whole chunk was not sent, store the rest of the data */
237     write_len -= bytes_written;
238     sptr += bytes_written;
239     pp->sendthis = malloc(write_len);
240     if(pp->sendthis) {
241       memcpy(pp->sendthis, sptr, write_len);
242       pp->sendsize = pp->sendleft = write_len;
243     }
244     else {
245       failf(data, "out of memory");
246       res = CURLE_OUT_OF_MEMORY;
247     }
248   }
249   else
250     pp->response = Curl_tvnow();
251
252   return res;
253 }
254
255
256 /***********************************************************************
257  *
258  * Curl_pp_sendf()
259  *
260  * Send the formated string as a command to a pingpong server. Note that
261  * the string should not have any CRLF appended, as this function will
262  * append the necessary things itself.
263  *
264  * NOTE: we build the command in a fixed-length buffer, which sets length
265  * restrictions on the command!
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
335       conn->data_prot = 0;
336 #endif
337       DEBUGASSERT((ptr+BUFSIZE-pp->nread_resp) <= (buf+BUFSIZE+1));
338       res = Curl_read(conn, sockfd, ptr, BUFSIZE-pp->nread_resp,
339                       &gotbytes);
340 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
341       conn->data_prot = prot;
342 #endif
343       if(res == CURLE_AGAIN)
344         return CURLE_OK; /* return */
345
346 #ifdef CURL_DOES_CONVERSIONS
347       if((res == CURLE_OK) && (gotbytes > 0)) {
348         /* convert from the network encoding */
349         res = Curl_convert_from_network(data, ptr, gotbytes);
350         /* Curl_convert_from_network calls failf if unsuccessful */
351       }
352 #endif /* CURL_DOES_CONVERSIONS */
353
354       if(CURLE_OK != res) {
355         result = (CURLcode)res; /* Set outer result variable to this error. */
356         keepon = FALSE;
357       }
358     }
359
360     if(!keepon)
361       ;
362     else if(gotbytes <= 0) {
363       keepon = FALSE;
364       result = CURLE_RECV_ERROR;
365       failf(data, "FTP response reading failed");
366     }
367     else {
368       /* we got a whole chunk of data, which can be anything from one
369        * byte to a set of lines and possible just a piece of the last
370        * line */
371       ssize_t i;
372       ssize_t clipamount = 0;
373       bool restart = FALSE;
374
375       data->req.headerbytecount += (long)gotbytes;
376
377       pp->nread_resp += gotbytes;
378       for(i = 0; i < gotbytes; ptr++, i++) {
379         perline++;
380         if(*ptr=='\n') {
381           /* a newline is CRLF in ftp-talk, so the CR is ignored as
382              the line isn't really terminated until the LF comes */
383
384           /* output debug output if that is requested */
385 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
386           if(!conn->sec_complete)
387 #endif
388             if(data->set.verbose)
389               Curl_debug(data, CURLINFO_HEADER_IN,
390                          pp->linestart_resp, (size_t)perline, conn);
391
392           /*
393            * We pass all response-lines to the callback function registered
394            * for "headers". The response lines can be seen as a kind of
395            * headers.
396            */
397           result = Curl_client_write(conn, CLIENTWRITE_HEADER,
398                                      pp->linestart_resp, perline);
399           if(result)
400             return result;
401
402           if(pp->endofresp(pp, code)) {
403             /* This is the end of the last line, copy the last line to the
404                start of the buffer and zero terminate, for old times sake (and
405                krb4)! */
406             char *meow;
407             int n;
408             for(meow=pp->linestart_resp, n=0; meow<ptr; meow++, n++)
409               buf[n] = *meow;
410             *meow=0; /* zero terminate */
411             keepon=FALSE;
412             pp->linestart_resp = ptr+1; /* advance pointer */
413             i++; /* skip this before getting out */
414
415             *size = pp->nread_resp; /* size of the response */
416             pp->nread_resp = 0; /* restart */
417             break;
418           }
419           perline=0; /* line starts over here */
420           pp->linestart_resp = ptr+1;
421         }
422       }
423
424       if(!keepon && (i != gotbytes)) {
425         /* We found the end of the response lines, but we didn't parse the
426            full chunk of data we have read from the server. We therefore need
427            to store the rest of the data to be checked on the next invoke as
428            it may actually contain another end of response already! */
429         clipamount = gotbytes - i;
430         restart = TRUE;
431       }
432       else if(keepon) {
433
434         if((perline == gotbytes) && (gotbytes > BUFSIZE/2)) {
435           /* We got an excessive line without newlines and we need to deal
436              with it. We keep the first bytes of the line then we throw
437              away the rest. */
438           infof(data, "Excessive server response line length received, %zd bytes."
439                 " Stripping\n", gotbytes);
440           restart = TRUE;
441
442           /* we keep 40 bytes since all our pingpong protocols are only
443              interested in the first piece */
444           clipamount = 40;
445         }
446         else if(pp->nread_resp > BUFSIZE/2) {
447           /* We got a large chunk of data and there's potentially still trailing
448              data to take care of, so we put any such part in the "cache", clear
449              the buffer to make space and restart. */
450           clipamount = perline;
451           restart = TRUE;
452         }
453       }
454       else if(i == gotbytes)
455         restart = TRUE;
456
457       if(clipamount) {
458         pp->cache_size = clipamount;
459         pp->cache = malloc(pp->cache_size);
460         if(pp->cache)
461           memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
462         else
463           return CURLE_OUT_OF_MEMORY;
464       }
465       if(restart) {
466         /* now reset a few variables to start over nicely from the start of
467            the big buffer */
468         pp->nread_resp = 0; /* start over from scratch in the buffer */
469         ptr = pp->linestart_resp = buf;
470         perline = 0;
471       }
472
473     } /* there was data */
474
475   } /* while there's buffer left and loop is requested */
476
477   pp->pending_resp = FALSE;
478
479   return result;
480 }
481
482 int Curl_pp_getsock(struct pingpong *pp,
483                     curl_socket_t *socks,
484                     int numsocks)
485 {
486   struct connectdata *conn = pp->conn;
487
488   if(!numsocks)
489     return GETSOCK_BLANK;
490
491   socks[0] = conn->sock[FIRSTSOCKET];
492
493   if(pp->sendleft) {
494     /* write mode */
495     return GETSOCK_WRITESOCK(0);
496   }
497
498   /* read mode */
499   return GETSOCK_READSOCK(0);
500 }
501
502 CURLcode Curl_pp_flushsend(struct pingpong *pp)
503 {
504   /* we have a piece of a command still left to send */
505   struct connectdata *conn = pp->conn;
506   ssize_t written;
507   CURLcode result = CURLE_OK;
508   curl_socket_t sock = conn->sock[FIRSTSOCKET];
509
510   result = Curl_write(conn, sock, pp->sendthis + pp->sendsize -
511                       pp->sendleft, pp->sendleft, &written);
512   if(result)
513     return result;
514
515   if(written != (ssize_t)pp->sendleft) {
516     /* only a fraction was sent */
517     pp->sendleft -= written;
518   }
519   else {
520     free(pp->sendthis);
521     pp->sendthis=NULL;
522     pp->sendleft = pp->sendsize = 0;
523     pp->response = Curl_tvnow();
524   }
525   return CURLE_OK;
526 }
527
528 CURLcode Curl_pp_disconnect(struct pingpong *pp)
529 {
530   if(pp->cache) {
531     free(pp->cache);
532     pp->cache = NULL;
533   }
534   return CURLE_OK;
535 }
536
537
538
539 #endif