~ubuntu-branches/ubuntu/warty/curl/warty-security

« back to all changes in this revision

Viewing changes to lib/multi.c

  • Committer: Bazaar Package Importer
  • Author(s): Domenico Andreoli
  • Date: 2004-06-04 19:09:25 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040604190925-wy048bp31320r2z6
Tags: 7.12.0.is.7.11.2-1
* Reverted to version 7.11.2 (closes: #252348).
* Disabled support for libidn (closes: #252367). This is to leave
  curl in unstable as much similar as possible to the one in testing.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*****************************************************************************
 
1
/***************************************************************************
2
2
 *                                  _   _ ____  _     
3
3
 *  Project                     ___| | | |  _ \| |    
4
4
 *                             / __| | | | |_) | |    
5
5
 *                            | (__| |_| |  _ <| |___ 
6
6
 *                             \___|\___/|_| \_\_____|
7
7
 *
8
 
 * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al.
9
 
 *
10
 
 * In order to be useful for every potential user, curl and libcurl are
11
 
 * dual-licensed under the MPL and the MIT/X-derivate licenses.
12
 
 *
 
8
 * Copyright (C) 1998 - 2004, 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
 * 
13
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
14
15
 * copies of the Software, and permit persons to whom the Software is
15
 
 * furnished to do so, under the terms of the MPL or the MIT/X-derivate
16
 
 * licenses. You may pick one of these licenses.
 
16
 * furnished to do so, under the terms of the COPYING file.
17
17
 *
18
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
19
 * KIND, either express or implied.
20
20
 *
21
 
 * $Id: multi.c,v 1.8 2002/01/07 20:52:32 bumblebury Exp $
22
 
 *****************************************************************************/
 
21
 * $Id: multi.c,v 1.49 2004/03/30 13:02:31 bagder Exp $
 
22
 ***************************************************************************/
23
23
 
24
24
#include "setup.h"
25
25
#include <stdlib.h>
26
26
#include <string.h>
 
27
 
 
28
#ifdef HAVE_SYS_TYPES_H
 
29
#include <sys/types.h>
 
30
#endif
 
31
#ifdef HAVE_SYS_SOCKET_H
 
32
#include <sys/socket.h>
 
33
#endif
 
34
#ifdef HAVE_UNISTD_H
 
35
#include <unistd.h>
 
36
#endif
 
37
 
27
38
#include <curl/curl.h>
28
39
 
29
 
#include "multi.h" /* will become <curl/multi.h> soon */
30
 
 
31
40
#include "urldata.h"
32
41
#include "transfer.h"
33
42
#include "url.h"
 
43
#include "connect.h"
 
44
#include "progress.h"
 
45
 
 
46
/* The last #include file should be: */
 
47
#ifdef CURLDEBUG
 
48
#include "memdebug.h"
 
49
#endif
34
50
 
35
51
struct Curl_message {
36
52
  /* the 'CURLMsg' is the part that is visible to the external user */
40
56
 
41
57
typedef enum {
42
58
  CURLM_STATE_INIT,
43
 
  CURLM_STATE_CONNECT,
44
 
  CURLM_STATE_DO,
45
 
  CURLM_STATE_PERFORM,
46
 
  CURLM_STATE_DONE,
47
 
  CURLM_STATE_COMPLETED,
 
59
  CURLM_STATE_CONNECT,     /* resolve/connect has been sent off */
 
60
  CURLM_STATE_WAITRESOLVE, /* we're awaiting the resolve to finalize */
 
61
  CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */
 
62
  CURLM_STATE_DO,          /* send off the request (part 1) */
 
63
  CURLM_STATE_DO_MORE,     /* send off the request (part 2) */
 
64
  CURLM_STATE_PERFORM,     /* transfer data */
 
65
  CURLM_STATE_DONE,        /* post data transfer operation */
 
66
  CURLM_STATE_COMPLETED,   /* operation complete */
48
67
 
49
68
  CURLM_STATE_LAST /* not a true state, never use this */
50
69
} CURLMstate;
59
78
 
60
79
  CURLMstate state;  /* the handle's state */
61
80
  CURLcode result;   /* previous result */
 
81
 
 
82
  struct Curl_message *msg; /* A pointer to one single posted message.
 
83
                               Cleanup should be done on this pointer NOT on
 
84
                               the linked list in Curl_multi.  This message
 
85
                               will be deleted when this handle is removed
 
86
                               from the multi-handle */
 
87
  int msg_num; /* number of messages left in 'msg' to return */
62
88
};
63
89
 
64
90
 
78
104
  /* This is the amount of entries in the linked list above. */
79
105
  int num_easy;
80
106
 
81
 
  /* this is a linked list of posted messages */
82
 
  struct Curl_message *msgs;
83
 
  /* amount of messages in the queue */
84
 
  int num_msgs;
 
107
  int num_msgs; /* total amount of messages in the easy handles */
 
108
 
85
109
  /* Hostname cache */
86
110
  curl_hash *hostcache;
87
111
};
97
121
    memset(multi, 0, sizeof(struct Curl_multi));
98
122
    multi->type = CURL_MULTI_HANDLE;
99
123
  }
100
 
  
 
124
 
 
125
  multi->hostcache = Curl_mk_dnscache();
 
126
  if(!multi->hostcache) {
 
127
    /* failure, free mem and bail out */
 
128
    free(multi);
 
129
    multi = NULL;
 
130
  }
101
131
  return (CURLM *) multi;
102
132
}
103
133
 
126
156
  /* set the easy handle */
127
157
  easy->easy_handle = easy_handle;
128
158
  easy->state = CURLM_STATE_INIT;
 
159
 
 
160
  /* for multi interface connections, we share DNS cache automaticly */
 
161
  easy->easy_handle->hostcache = multi->hostcache;
129
162
  
130
163
  /* We add this new entry first in the list. We make our 'next' point to the
131
164
     previous next and our 'prev' point back to the 'first' struct */
163
196
  /* scan through the list and remove the 'curl_handle' */
164
197
  easy = multi->easy.next;
165
198
  while(easy) {
166
 
    if(easy->easy_handle == curl_handle)
 
199
    if(easy->easy_handle == (struct SessionHandle *)curl_handle)
167
200
      break;
168
201
    easy=easy->next;
169
202
  }
171
204
    /* If the 'state' is not INIT or COMPLETED, we might need to do something
172
205
       nice to put the easy_handle in a good known state when this returns. */
173
206
 
 
207
    /* clear out the usage of the shared DNS cache */
 
208
    easy->easy_handle->hostcache = NULL;
 
209
    
174
210
    /* make the previous node point to our next */
175
211
    if(easy->prev)
176
212
      easy->prev->next = easy->next;
180
216
    
181
217
    /* NOTE NOTE NOTE
182
218
       We do not touch the easy handle here! */
 
219
    if (easy->msg)
 
220
      free(easy->msg);
183
221
    free(easy);
184
222
 
185
223
    multi->num_easy--; /* one less to care about now */
211
249
    switch(easy->state) {
212
250
    default:
213
251
      break;
 
252
    case CURLM_STATE_WAITRESOLVE:
 
253
      /* waiting for a resolve to complete */
 
254
      Curl_multi_ares_fdset(easy->easy_conn, read_fd_set, write_fd_set,
 
255
                            &this_max_fd);
 
256
      if(this_max_fd > *max_fd)
 
257
        *max_fd = this_max_fd;
 
258
      break;
 
259
 
 
260
    case CURLM_STATE_WAITCONNECT:
 
261
    case CURLM_STATE_DO_MORE:
 
262
      {
 
263
        /* when we're waiting for a connect, we wait for the socket to
 
264
           become writable */
 
265
        struct connectdata *conn = easy->easy_conn;
 
266
        curl_socket_t sockfd;
 
267
 
 
268
        if(CURLM_STATE_WAITCONNECT == easy->state) {
 
269
          sockfd = conn->sock[FIRSTSOCKET];
 
270
          FD_SET(sockfd, write_fd_set);
 
271
        }
 
272
        else {
 
273
          /* When in DO_MORE state, we could be either waiting for us
 
274
             to connect to a remote site, or we could wait for that site
 
275
             to connect to us. It makes a difference in the way: if we
 
276
             connect to the site we wait for the socket to become writable, if 
 
277
             the site connects to us we wait for it to become readable */
 
278
          sockfd = conn->sock[SECONDARYSOCKET];
 
279
          FD_SET(sockfd, write_fd_set);
 
280
        }
 
281
 
 
282
        if((int)sockfd > *max_fd)
 
283
          *max_fd = (int)sockfd;
 
284
      }
 
285
      break;
214
286
    case CURLM_STATE_PERFORM:
215
287
      /* This should have a set of file descriptors for us to set.  */
216
288
      /* after the transfer is done, go DONE */
237
309
  struct Curl_one_easy *easy;
238
310
  bool done;
239
311
  CURLMcode result=CURLM_OK;
 
312
  struct Curl_message *msg = NULL;
 
313
  bool connected;
 
314
  bool async;
240
315
 
241
316
  *running_handles = 0; /* bump this once for every living handle */
242
317
 
245
320
 
246
321
  easy=multi->easy.next;
247
322
  while(easy) {
248
 
    switch(easy->state) {
249
 
    case CURLM_STATE_INIT:
250
 
      /* init this transfer. */
251
 
      easy->result=Curl_pretransfer(easy->easy_handle);
252
 
      if(CURLE_OK == easy->result) {
253
 
        /* after init, go CONNECT */
254
 
        easy->state = CURLM_STATE_CONNECT;
255
 
        result = CURLM_CALL_MULTI_PERFORM; 
256
 
      }
257
 
      break;
258
 
    case CURLM_STATE_CONNECT:
259
 
      if (Curl_global_host_cache_use(easy->easy_handle)) {
260
 
        easy->easy_handle->hostcache = Curl_global_host_cache_get();
261
 
      }
262
 
      else {
263
 
        if (multi->hostcache == NULL) {
264
 
          multi->hostcache = curl_hash_alloc(7, Curl_freeaddrinfo);
265
 
        }
266
 
 
267
 
        easy->easy_handle->hostcache = multi->hostcache;
268
 
      }
269
 
 
270
 
      /* Connect. We get a connection identifier filled in. */
271
 
      easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn);
272
 
 
273
 
      /* after connect, go DO */
274
 
      if(CURLE_OK == easy->result) {
275
 
        easy->state = CURLM_STATE_DO;
276
 
        result = CURLM_CALL_MULTI_PERFORM; 
277
 
      }
278
 
      break;
279
 
    case CURLM_STATE_DO:
280
 
      /* Do the fetch or put request */
281
 
      easy->result = Curl_do(&easy->easy_conn);
282
 
      /* after do, go PERFORM */
283
 
      if(CURLE_OK == easy->result) {
284
 
        if(CURLE_OK == Curl_readwrite_init(easy->easy_conn)) {
285
 
          easy->state = CURLM_STATE_PERFORM;
286
 
          result = CURLM_CALL_MULTI_PERFORM; 
287
 
        }
288
 
      }
289
 
      break;
290
 
    case CURLM_STATE_PERFORM:
291
 
      /* read/write data if it is ready to do so */
292
 
      easy->result = Curl_readwrite(easy->easy_conn, &done);
293
 
      /* hm, when we follow redirects, we may need to go back to the CONNECT
294
 
         state */
295
 
      /* after the transfer is done, go DONE */
296
 
      if(TRUE == done) {
297
 
        /* call this even if the readwrite function returned error */
298
 
        easy->result = Curl_posttransfer(easy->easy_handle);
299
 
        easy->state = CURLM_STATE_DONE;
300
 
        result = CURLM_CALL_MULTI_PERFORM; 
301
 
      }
302
 
      break;
303
 
    case CURLM_STATE_DONE:
304
 
      /* post-transfer command */
305
 
      easy->result = Curl_done(easy->easy_conn);
306
 
      /* after we have DONE what we're supposed to do, go COMPLETED */
307
 
      if(CURLE_OK == easy->result)
 
323
#ifdef CURLDEBUG
 
324
    fprintf(stderr, "HANDLE %p: State: %x\n",
 
325
            (char *)easy, easy->state);
 
326
#endif
 
327
    do {
 
328
      if (CURLM_STATE_WAITCONNECT <= easy->state &&
 
329
          easy->state <= CURLM_STATE_DO &&
 
330
          easy->easy_handle->change.url_changed) {
 
331
        char *gotourl;
 
332
        Curl_posttransfer(easy->easy_handle);
 
333
 
 
334
        easy->result = Curl_done(easy->easy_conn);
 
335
        if(CURLE_OK == easy->result) {
 
336
          gotourl = strdup(easy->easy_handle->change.url);
 
337
          easy->easy_handle->change.url_changed = FALSE;
 
338
          easy->result = Curl_follow(easy->easy_handle, gotourl);
 
339
          if(CURLE_OK == easy->result)
 
340
            easy->state = CURLM_STATE_CONNECT;
 
341
          else
 
342
            free(gotourl);
 
343
        }
 
344
      }
 
345
    
 
346
      easy->easy_handle->change.url_changed = FALSE;
 
347
 
 
348
      switch(easy->state) {
 
349
      case CURLM_STATE_INIT:
 
350
        /* init this transfer. */
 
351
        easy->result=Curl_pretransfer(easy->easy_handle);
 
352
 
 
353
        if(CURLE_OK == easy->result) {
 
354
          /* after init, go CONNECT */
 
355
          easy->state = CURLM_STATE_CONNECT;
 
356
          result = CURLM_CALL_MULTI_PERFORM; 
 
357
        
 
358
          easy->easy_handle->state.used_interface = Curl_if_multi;
 
359
        }
 
360
        break;
 
361
 
 
362
      case CURLM_STATE_CONNECT:
 
363
        /* Connect. We get a connection identifier filled in. */
 
364
        Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
 
365
        easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn,
 
366
                                    &async);
 
367
 
 
368
        if(CURLE_OK == easy->result) {
 
369
          if(async)
 
370
            /* We're now waiting for an asynchronous name lookup */
 
371
            easy->state = CURLM_STATE_WAITRESOLVE;
 
372
          else {
 
373
            /* after the connect has been sent off, go WAITCONNECT */
 
374
            easy->state = CURLM_STATE_WAITCONNECT;
 
375
            result = CURLM_CALL_MULTI_PERFORM;
 
376
          }
 
377
        }
 
378
        break;
 
379
 
 
380
      case CURLM_STATE_WAITRESOLVE:
 
381
        /* awaiting an asynch name resolve to complete */
 
382
      {
 
383
        struct Curl_dns_entry *dns = NULL;
 
384
 
 
385
        /* check if we have the name resolved by now */
 
386
        easy->result = Curl_is_resolved(easy->easy_conn, &dns);
 
387
 
 
388
        if(dns) {
 
389
          /* Perform the next step in the connection phase, and then move on
 
390
             to the WAITCONNECT state */
 
391
          easy->result = Curl_async_resolved(easy->easy_conn);
 
392
 
 
393
          if(CURLE_OK != easy->result)
 
394
            /* if Curl_async_resolved() returns failure, the connection struct
 
395
               is already freed and gone */
 
396
            easy->easy_conn = NULL;           /* no more connection */
 
397
 
 
398
          easy->state = CURLM_STATE_WAITCONNECT;
 
399
        }
 
400
        
 
401
        if(CURLE_OK != easy->result) {
 
402
          /* failure detected */
 
403
          Curl_disconnect(easy->easy_conn); /* disconnect properly */
 
404
          easy->easy_conn = NULL;           /* no more connection */
 
405
          break;
 
406
        }
 
407
      }
 
408
      break;
 
409
 
 
410
      case CURLM_STATE_WAITCONNECT:
 
411
        /* awaiting a completion of an asynch connect */
 
412
        easy->result = Curl_is_connected(easy->easy_conn,
 
413
                                         easy->easy_conn->sock[FIRSTSOCKET],
 
414
                                         &connected);
 
415
        if(connected)
 
416
          easy->result = Curl_protocol_connect(easy->easy_conn, NULL);
 
417
 
 
418
        if(CURLE_OK != easy->result) {
 
419
          /* failure detected */
 
420
          Curl_disconnect(easy->easy_conn); /* close the connection */
 
421
          easy->easy_conn = NULL;           /* no more connection */
 
422
          break;
 
423
        }
 
424
 
 
425
        if(connected) {
 
426
          /* after the connect has completed, go DO */
 
427
          easy->state = CURLM_STATE_DO;
 
428
          result = CURLM_CALL_MULTI_PERFORM; 
 
429
        }
 
430
        break;
 
431
 
 
432
      case CURLM_STATE_DO:
 
433
        /* Do the fetch or put request */
 
434
        easy->result = Curl_do(&easy->easy_conn);
 
435
        if(CURLE_OK == easy->result) {
 
436
 
 
437
          /* after do, go PERFORM... or DO_MORE */
 
438
          if(easy->easy_conn->bits.do_more) {
 
439
            /* we're supposed to do more, but we need to sit down, relax
 
440
               and wait a little while first */
 
441
            easy->state = CURLM_STATE_DO_MORE;
 
442
            result = CURLM_OK;
 
443
          }
 
444
          else {
 
445
            /* we're done with the DO, now PERFORM */
 
446
            easy->result = Curl_readwrite_init(easy->easy_conn);
 
447
            if(CURLE_OK == easy->result) {
 
448
              easy->state = CURLM_STATE_PERFORM;
 
449
              result = CURLM_CALL_MULTI_PERFORM; 
 
450
            }
 
451
          }
 
452
        }
 
453
        break;
 
454
 
 
455
      case CURLM_STATE_DO_MORE:
 
456
        /*
 
457
         * First, check if we really are ready to do more.
 
458
         */
 
459
        easy->result =
 
460
          Curl_is_connected(easy->easy_conn,
 
461
                            easy->easy_conn->sock[SECONDARYSOCKET],
 
462
                            &connected);
 
463
        if(connected) {
 
464
          /*
 
465
           * When we are connected, DO MORE and then go PERFORM
 
466
           */
 
467
          easy->result = Curl_do_more(easy->easy_conn);
 
468
 
 
469
          if(CURLE_OK == easy->result)
 
470
            easy->result = Curl_readwrite_init(easy->easy_conn);
 
471
 
 
472
          if(CURLE_OK == easy->result) {
 
473
            easy->state = CURLM_STATE_PERFORM;
 
474
            result = CURLM_CALL_MULTI_PERFORM; 
 
475
          }
 
476
        }
 
477
        break;
 
478
 
 
479
      case CURLM_STATE_PERFORM:
 
480
        /* read/write data if it is ready to do so */
 
481
        easy->result = Curl_readwrite(easy->easy_conn, &done);
 
482
 
 
483
        if(easy->result)  {
 
484
          /* The transfer phase returned error, we mark the connection to get
 
485
           * closed to prevent being re-used. This is becasue we can't
 
486
           * possibly know if the connection is in a good shape or not now. */
 
487
          easy->easy_conn->bits.close = TRUE;
 
488
 
 
489
          if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) {
 
490
            /* if we failed anywhere, we must clean up the secondary socket if
 
491
               it was used */
 
492
            sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
 
493
            easy->easy_conn->sock[SECONDARYSOCKET]=-1;
 
494
          }
 
495
          Curl_posttransfer(easy->easy_handle);
 
496
          Curl_done(easy->easy_conn);
 
497
        }
 
498
 
 
499
        /* after the transfer is done, go DONE */
 
500
        else if(TRUE == done) {
 
501
 
 
502
          /* call this even if the readwrite function returned error */
 
503
          Curl_posttransfer(easy->easy_handle);
 
504
 
 
505
          /* When we follow redirects, must to go back to the CONNECT state */
 
506
          if(easy->easy_conn->newurl) {
 
507
            char *newurl = easy->easy_conn->newurl;
 
508
            easy->easy_conn->newurl = NULL;
 
509
            easy->result = Curl_done(easy->easy_conn);
 
510
            if(easy->result == CURLE_OK)
 
511
              easy->result = Curl_follow(easy->easy_handle, newurl);
 
512
            if(CURLE_OK == easy->result) {
 
513
              easy->state = CURLM_STATE_CONNECT;
 
514
              result = CURLM_CALL_MULTI_PERFORM;
 
515
            }
 
516
          }
 
517
          else {
 
518
            easy->state = CURLM_STATE_DONE;
 
519
            result = CURLM_CALL_MULTI_PERFORM; 
 
520
          }
 
521
        }
 
522
        break;
 
523
      case CURLM_STATE_DONE:
 
524
        /* post-transfer command */
 
525
        easy->result = Curl_done(easy->easy_conn);
 
526
 
 
527
        /* after we have DONE what we're supposed to do, go COMPLETED, and
 
528
           it doesn't matter what the Curl_done() returned! */
308
529
        easy->state = CURLM_STATE_COMPLETED;
309
 
      break;
310
 
    case CURLM_STATE_COMPLETED:
311
 
      /* this is a completed transfer, it is likely to still be connected */
312
 
 
313
 
      /* This node should be delinked from the list now and we should post
314
 
         an information message that we are complete. */
315
 
      break;
316
 
    default:
317
 
      return CURLM_INTERNAL_ERROR;
318
 
    }
319
 
 
320
 
    if((CURLM_STATE_COMPLETED != easy->state) &&
321
 
       (CURLE_OK != easy->result)) {
322
 
      /*
323
 
       * If an error was returned, and we aren't in completed now,
324
 
       * then we go to completed and consider this transfer aborted.
325
 
       */
326
 
      easy->state = CURLM_STATE_COMPLETED;
327
 
    }
328
 
    else if(CURLM_STATE_COMPLETED != easy->state)
329
 
      /* this one still lives! */
330
 
      (*running_handles)++;
 
530
        break;
 
531
 
 
532
      case CURLM_STATE_COMPLETED:
 
533
        /* this is a completed transfer, it is likely to still be connected */
 
534
 
 
535
        /* This node should be delinked from the list now and we should post
 
536
           an information message that we are complete. */
 
537
        break;
 
538
      default:
 
539
        return CURLM_INTERNAL_ERROR;
 
540
      }
 
541
 
 
542
      if(CURLM_STATE_COMPLETED != easy->state) {
 
543
        if(CURLE_OK != easy->result) {
 
544
          /*
 
545
           * If an error was returned, and we aren't in completed state now,
 
546
           * then we go to completed and consider this transfer aborted.  */
 
547
          easy->state = CURLM_STATE_COMPLETED;
 
548
        }
 
549
        else
 
550
          /* this one still lives! */
 
551
          (*running_handles)++;
 
552
      }
 
553
 
 
554
    } while (easy->easy_handle->change.url_changed);
 
555
 
 
556
    if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) {
 
557
      /* clear out the usage of the shared DNS cache */
 
558
      easy->easy_handle->hostcache = NULL;
 
559
 
 
560
      /* now add a node to the Curl_message linked list with this info */
 
561
      msg = (struct Curl_message *)malloc(sizeof(struct Curl_message));
 
562
 
 
563
      if(!msg)
 
564
        return CURLM_OUT_OF_MEMORY;
 
565
 
 
566
      msg->extmsg.msg = CURLMSG_DONE;
 
567
      msg->extmsg.easy_handle = easy->easy_handle;
 
568
      msg->extmsg.data.result = easy->result;
 
569
      msg->next=NULL;
 
570
 
 
571
      easy->msg = msg;
 
572
      easy->msg_num = 1; /* there is one unread message here */
 
573
 
 
574
      multi->num_msgs++; /* increase message counter */
 
575
    }
331
576
 
332
577
    easy = easy->next; /* operate on next handle */
333
578
  }
 
579
 
334
580
  return result;
335
581
}
336
582
 
337
583
CURLMcode curl_multi_cleanup(CURLM *multi_handle)
338
584
{
339
585
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
 
586
  struct Curl_one_easy *easy;
 
587
  struct Curl_one_easy *nexteasy;
 
588
 
340
589
  if(GOOD_MULTI_HANDLE(multi)) {
341
590
    multi->type = 0; /* not good anymore */
342
 
    curl_hash_destroy(multi->hostcache);
 
591
    Curl_hash_destroy(multi->hostcache);
 
592
 
343
593
    /* remove all easy handles */
 
594
    easy = multi->easy.next;
 
595
    while(easy) {
 
596
      nexteasy=easy->next;
 
597
      /* clear out the usage of the shared DNS cache */
 
598
      easy->easy_handle->hostcache = NULL;
 
599
 
 
600
      if (easy->msg)
 
601
        free(easy->msg);
 
602
      free(easy);
 
603
      easy = nexteasy;
 
604
    }
344
605
 
345
606
    free(multi);
346
607
 
350
611
    return CURLM_BAD_HANDLE;
351
612
}
352
613
 
353
 
CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue);
354
 
 
355
 
/*
356
 
 * local variables:
357
 
 * eval: (load-file "../curl-mode.el")
358
 
 * end:
359
 
 * vim600: fdm=marker
360
 
 * vim: et sw=2 ts=2 sts=2 tw=78
361
 
 */
 
614
CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
 
615
{
 
616
  struct Curl_multi *multi=(struct Curl_multi *)multi_handle;
 
617
 
 
618
  *msgs_in_queue = 0; /* default to none */
 
619
 
 
620
  if(GOOD_MULTI_HANDLE(multi)) {
 
621
    struct Curl_one_easy *easy;
 
622
    
 
623
    if(!multi->num_msgs)
 
624
      return NULL; /* no messages left to return */
 
625
 
 
626
    easy=multi->easy.next;
 
627
    while(easy) {
 
628
      if(easy->msg_num) {
 
629
        easy->msg_num--;
 
630
        break;
 
631
      }
 
632
      easy = easy->next;
 
633
    }
 
634
    if(!easy)
 
635
      return NULL; /* this means internal count confusion really */
 
636
 
 
637
    multi->num_msgs--;
 
638
    *msgs_in_queue = multi->num_msgs;
 
639
 
 
640
    return &easy->msg->extmsg;
 
641
  }
 
642
  else
 
643
    return NULL;
 
644
}