Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
comparison src/gt_xfer_obj.c @ 0:d39e1d0d75b6
initial add
author | paulo@hit-nxdomain.opendns.com |
---|---|
date | Sat, 20 Feb 2010 21:18:28 -0800 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:d1ff8b32cdb4 |
---|---|
1 /* | |
2 * $Id: gt_xfer_obj.c,v 1.48 2004/12/19 12:38:42 mkern Exp $ | |
3 * | |
4 * acts as a gateway between giFT and the HTTP implementation | |
5 * | |
6 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net) | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify it | |
9 * under the terms of the GNU General Public License as published by the | |
10 * Free Software Foundation; either version 2, or (at your option) any | |
11 * later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, but | |
14 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 * General Public License for more details. | |
17 */ | |
18 | |
19 #include "gt_gnutella.h" | |
20 | |
21 #include "gt_xfer_obj.h" | |
22 #include "gt_xfer.h" | |
23 #include "gt_http_server.h" | |
24 #include "gt_http_client.h" | |
25 #include "gt_share.h" | |
26 | |
27 #include "encoding/url.h" | |
28 | |
29 #include "transfer/source.h" | |
30 | |
31 /*****************************************************************************/ | |
32 | |
33 static List *upload_connections = NULL; | |
34 static List *download_connections = NULL; | |
35 | |
36 /*****************************************************************************/ | |
37 /* HTTP COMMUNICATION */ | |
38 | |
39 static BOOL header_read_timeout (GtTransfer *xfer) | |
40 { | |
41 gt_transfer_status (xfer, SOURCE_TIMEOUT, "Timed out"); | |
42 gt_transfer_close (xfer, TRUE); | |
43 return FALSE; | |
44 } | |
45 | |
46 GtTransfer *gt_transfer_new (GtTransferType type, Source *source, | |
47 in_addr_t ip, in_port_t port, | |
48 off_t start, off_t stop) | |
49 { | |
50 GtTransfer *xfer; | |
51 GtTransferCB cb; | |
52 | |
53 if (!(xfer = malloc (sizeof (GtTransfer)))) | |
54 return NULL; | |
55 | |
56 memset (xfer, 0, sizeof (GtTransfer)); | |
57 | |
58 if (type == GT_TRANSFER_UPLOAD) | |
59 cb = gt_upload; | |
60 else if (type == GT_TRANSFER_DOWNLOAD) | |
61 cb = gt_download; | |
62 else | |
63 abort (); | |
64 | |
65 xfer->type = type; | |
66 xfer->callback = cb; | |
67 xfer->source = source; | |
68 | |
69 /* parsed information about the source */ | |
70 xfer->ip = ip; | |
71 xfer->port = port; | |
72 | |
73 xfer->start = start; | |
74 xfer->stop = stop; | |
75 | |
76 xfer->shared = TRUE; | |
77 | |
78 xfer->detach_timer = TIMER_NONE; | |
79 xfer->detach_msgtxt = NULL; | |
80 | |
81 /* set the size of this http request */ | |
82 xfer->remaining_len = xfer->stop - xfer->start; | |
83 | |
84 /* give this GtTransfer a maximum amount of time before cancelling */ | |
85 xfer->header_timer = timer_add (1 * MINUTES, | |
86 (TimerCallback)header_read_timeout, | |
87 xfer); | |
88 | |
89 return xfer; | |
90 } | |
91 | |
92 static void gt_transfer_free (GtTransfer *xfer) | |
93 { | |
94 if (!xfer) | |
95 return; | |
96 | |
97 free (xfer->command); | |
98 free (xfer->request); | |
99 free (xfer->request_path); | |
100 free (xfer->content_urns); | |
101 free (xfer->detach_msgtxt); | |
102 | |
103 if (xfer->header) | |
104 dataset_clear (xfer->header); | |
105 | |
106 #if 0 | |
107 if (xfer->share_authd && xfer->shared_authd_free) | |
108 share_free (xfer->share_authd); | |
109 #endif | |
110 | |
111 timer_remove (xfer->detach_timer); | |
112 timer_remove (xfer->header_timer); | |
113 | |
114 /* uploads use these */ | |
115 free (xfer->open_path); | |
116 free (xfer->hash); | |
117 free (xfer->version); | |
118 | |
119 if (xfer->f) | |
120 fclose (xfer->f); | |
121 | |
122 /* whee */ | |
123 free (xfer); | |
124 } | |
125 | |
126 /*****************************************************************************/ | |
127 | |
128 void gt_transfer_set_tcpc (GtTransfer *xfer, TCPC *c) | |
129 { | |
130 assert (c->udata == NULL); | |
131 assert (xfer->c == NULL); | |
132 | |
133 c->udata = xfer; | |
134 xfer->c = c; | |
135 } | |
136 | |
137 void gt_transfer_set_chunk (GtTransfer *xfer, Chunk *chunk) | |
138 { | |
139 assert (chunk->udata == NULL); | |
140 assert (xfer->chunk == NULL); | |
141 | |
142 xfer->chunk = chunk; | |
143 chunk->udata = xfer; | |
144 } | |
145 | |
146 Chunk *gt_transfer_get_chunk (GtTransfer *xfer) | |
147 { | |
148 assert (xfer->chunk != NULL); | |
149 assert (xfer->chunk->udata == xfer); | |
150 | |
151 return xfer->chunk; | |
152 } | |
153 | |
154 TCPC *gt_transfer_get_tcpc (GtTransfer *xfer) | |
155 { | |
156 assert (xfer->c != NULL); | |
157 assert (xfer->c->udata == xfer); | |
158 | |
159 return xfer->c; | |
160 } | |
161 | |
162 /*****************************************************************************/ | |
163 | |
164 GtSource *gt_transfer_get_source (GtTransfer *xfer) | |
165 { | |
166 Source *source = xfer->source; | |
167 | |
168 /* could be null for uploads */ | |
169 if (!source) | |
170 return NULL; | |
171 | |
172 assert (source->udata != NULL); | |
173 return source->udata; | |
174 } | |
175 | |
176 /*****************************************************************************/ | |
177 | |
178 /* | |
179 * giftd may change the Chunk size. This routine will reset the range of the | |
180 * transfer we'll ask the remote end for. It should be called before we've | |
181 * transmitted the HTTP headers that include the Range: request. | |
182 */ | |
183 void gt_transfer_set_length (GtTransfer *xfer, Chunk *chunk) | |
184 { | |
185 TCPC *c; | |
186 off_t old_start; | |
187 off_t old_stop; | |
188 off_t old_len; | |
189 | |
190 c = gt_transfer_get_tcpc (xfer); | |
191 | |
192 /* don't call me if you've already transmitted headers */ | |
193 assert (!xfer->transmitted_hdrs); | |
194 | |
195 old_start = xfer->start; | |
196 old_stop = xfer->stop; | |
197 old_len = xfer->remaining_len; | |
198 | |
199 xfer->start = chunk->start + chunk->transmit; | |
200 xfer->stop = chunk->stop; | |
201 | |
202 xfer->remaining_len = xfer->stop - xfer->start; | |
203 | |
204 /* i believe this is true even for push downloads... */ | |
205 assert (xfer->start == old_start); | |
206 | |
207 if (xfer->stop != old_stop) | |
208 { | |
209 assert (xfer->remaining_len != old_len); | |
210 | |
211 GT->DBGSOCK (GT, c, "(%s) old chunk range: [%lu,%lu) " | |
212 "new range: [%lu,%lu) old len: %lu new len: %lu", | |
213 xfer->request, (long)old_start,(long)old_stop, | |
214 (long)xfer->start, (long)xfer->stop, | |
215 (long)old_len, (long)xfer->remaining_len); | |
216 } | |
217 } | |
218 | |
219 /*****************************************************************************/ | |
220 | |
221 /* | |
222 * Cancels a possibly active HTTP transfer | |
223 * | |
224 * NOTE: | |
225 * This function may be called recursively if you don't do what giFT expects | |
226 * of you. This is a feature, not a bug, trust me. | |
227 */ | |
228 static void gt_transfer_cancel (Chunk *chunk, TransferType type) | |
229 { | |
230 GtTransfer *xfer; | |
231 BOOL force_close = FALSE; | |
232 | |
233 if (!chunk) | |
234 return; | |
235 | |
236 xfer = chunk->udata; | |
237 | |
238 /* each time this function is called we _MUST_ uncouple the transfer | |
239 * and the chunk or the code to follow will eventually call this func | |
240 * again!! */ | |
241 if (!xfer) | |
242 return; | |
243 | |
244 /* do not emit a callback signal */ | |
245 xfer->callback = NULL; | |
246 gt_transfer_close (xfer, force_close); | |
247 } | |
248 | |
249 /*****************************************************************************/ | |
250 | |
251 static void close_http_connection (TCPC *c, BOOL force_close, | |
252 GtTransferType type, GtSource *gt_src) | |
253 { | |
254 if (!c) | |
255 return; | |
256 | |
257 /* | |
258 * If this is an incoming indirect download that we sent a push out | |
259 * for, then don't store the connection in the HTTP connection cache, | |
260 * store it in the separate push connection cache that uses the client | |
261 * id as well as the IP address to identify the node. | |
262 */ | |
263 if (!force_close && type == GT_TRANSFER_DOWNLOAD && !c->outgoing) | |
264 { | |
265 if (gt_src != NULL) | |
266 { | |
267 if (HTTP_DEBUG) | |
268 GT->DBGSOCK (GT, c, "Keeping push connection"); | |
269 | |
270 /* nullify the previous data on this connection */ | |
271 c->udata = NULL; | |
272 | |
273 gt_push_source_add_conn (gt_src->guid, gt_src->user_ip, c); | |
274 return; | |
275 } | |
276 | |
277 /* | |
278 * This could happen if the chunk has no source url to parse | |
279 * at the moment. Argh, GtTransfer should always have a GtSource | |
280 * if xfer->type == GT_TRANSFER_DOWNLOAD. | |
281 */ | |
282 if (HTTP_DEBUG) | |
283 GT->DBGSOCK (GT, c, "Closing pushed connection! ARGH!"); | |
284 | |
285 force_close = TRUE; | |
286 } | |
287 | |
288 gt_http_connection_close (type, c, force_close); | |
289 } | |
290 | |
291 /* | |
292 * This function is very critical to OpenFT's transfer system. It is called | |
293 * anytime either the client or server HTTP implementations need to "cleanup" | |
294 * all data associated. This includes disconnecting the socket, unlinking | |
295 * itself from the chunk system and registering this status with giFT, just | |
296 * in case this is premature. If anything is leaking or fucking up, blame | |
297 * this :) | |
298 */ | |
299 void gt_transfer_close (GtTransfer *xfer, BOOL force_close) | |
300 { | |
301 TCPC *c; | |
302 Chunk *chunk; | |
303 GtSource *gt_src = NULL; | |
304 char *conn_hdr; | |
305 | |
306 if (!xfer) | |
307 return; | |
308 | |
309 c = xfer->c; | |
310 chunk = xfer->chunk; | |
311 | |
312 assert (xfer != NULL); | |
313 | |
314 /* remove the xfer from the indirect src list */ | |
315 gt_push_source_remove_xfer (xfer); | |
316 | |
317 /* get this source if this was a download */ | |
318 if (xfer->type == GT_TRANSFER_DOWNLOAD && chunk && chunk->source) | |
319 gt_src = gt_source_unserialize (chunk->source->url); | |
320 | |
321 /* if we have associated a chunk with this transfer we need to make sure | |
322 * we remove cleanly detach */ | |
323 if (chunk) | |
324 { | |
325 chunk->udata = NULL; | |
326 | |
327 /* | |
328 * notify the transfer callback that we have terminated this | |
329 * connection. let giFT handle the rest | |
330 * | |
331 * NOTE: | |
332 * see gt_transfer_cancel for some warnings about this code | |
333 */ | |
334 if (xfer->callback) | |
335 (*xfer->callback) (chunk, NULL, 0); | |
336 | |
337 /* WARNING: chunk is free'd in the depths of xfer->callback! */ | |
338 } | |
339 | |
340 /* HTTP/1.0 does not support persist connections or something...i dunno */ | |
341 if (!STRCASECMP (xfer->version, "HTTP/1.0")) | |
342 force_close = TRUE; | |
343 | |
344 /* older gnutella clients send a plain "HTTP" version, that is | |
345 * not persistent */ | |
346 if (!STRCASECMP (xfer->version, "HTTP")) | |
347 force_close = TRUE; | |
348 | |
349 /* | |
350 * We must force a socket close if there is still data waiting to | |
351 * be read on this transfer. | |
352 */ | |
353 if (!xfer->transmitted_hdrs || xfer->remaining_len != 0) | |
354 force_close = TRUE; | |
355 | |
356 /* close the connection if "Connection: close" was supplied */ | |
357 conn_hdr = dataset_lookupstr (xfer->header, "connection"); | |
358 if (!STRCASECMP (conn_hdr, "close")) | |
359 force_close = TRUE; | |
360 | |
361 close_http_connection (c, force_close, xfer->type, gt_src); | |
362 | |
363 gt_source_free (gt_src); | |
364 | |
365 gt_transfer_free (xfer); | |
366 } | |
367 | |
368 void gt_transfer_status (GtTransfer *xfer, SourceStatus status, char *text) | |
369 { | |
370 Chunk *chunk; | |
371 GtSource *gt_src; | |
372 char *next_status; | |
373 | |
374 if (!xfer || !text) | |
375 return; | |
376 | |
377 /* Added this on 2004-12-19 to track observed assertion failure in | |
378 * gt_transfer_get_chunk. -- mkern | |
379 */ | |
380 if (!xfer->chunk || xfer->chunk->udata != xfer) | |
381 { | |
382 GT->DBGFN (GT, "This is where we say good bye. status = %d, " | |
383 "text = %s, xfer->request_path = %s, xfer->ip = %s", | |
384 status, text, xfer->request_path, net_ip_str (xfer->ip)); | |
385 } | |
386 | |
387 chunk = gt_transfer_get_chunk (xfer); | |
388 | |
389 GT->source_status (GT, chunk->source, status, text); | |
390 | |
391 /* | |
392 * HACK: Store the status message on the GtSource, | |
393 * so we can reuse it sometimes. | |
394 */ | |
395 if (!xfer->source || !(gt_src = xfer->source->udata)) | |
396 return; | |
397 | |
398 /* allocate first so it's ok to call this function with an old value of | |
399 * gt_src->status_txt */ | |
400 next_status = STRDUP (text); | |
401 free (gt_src->status_txt); | |
402 gt_src->status_txt = next_status; | |
403 } | |
404 | |
405 /*****************************************************************************/ | |
406 /* PERSISTENT HTTP HANDLING */ | |
407 | |
408 struct conn_info | |
409 { | |
410 in_addr_t ip; | |
411 in_port_t port; | |
412 size_t count; | |
413 }; | |
414 | |
415 static int conn_cmp (TCPC *c, struct conn_info *info) | |
416 { | |
417 if (info->port != c->port) | |
418 return -1; | |
419 | |
420 if (net_peer (c->fd) != info->ip) | |
421 return 1; | |
422 | |
423 return 0; | |
424 } | |
425 | |
426 TCPC *gt_http_connection_lookup (GtTransferType type, in_addr_t ip, | |
427 in_port_t port) | |
428 { | |
429 List *link; | |
430 List **connlist_ptr; | |
431 TCPC *c = NULL; | |
432 struct conn_info info; | |
433 | |
434 info.ip = ip; | |
435 info.port = port; | |
436 | |
437 if (type == GT_TRANSFER_DOWNLOAD) | |
438 connlist_ptr = &download_connections; | |
439 else | |
440 connlist_ptr = &upload_connections; | |
441 | |
442 link = list_find_custom (*connlist_ptr, &info, (CompareFunc)conn_cmp); | |
443 | |
444 if (link) | |
445 { | |
446 c = link->data; | |
447 | |
448 GT->DBGFN (GT, "using previous connection to %s:%hu", | |
449 net_ip_str (ip), port); | |
450 | |
451 /* remove from the open list */ | |
452 *connlist_ptr = list_remove_link (*connlist_ptr, link); | |
453 input_remove_all (c->fd); | |
454 } | |
455 | |
456 return c; | |
457 } | |
458 | |
459 /* | |
460 * Handles outgoing HTTP connections. This function is capable of | |
461 * retrieving an already connected socket that was left over from a previous | |
462 * transfer. | |
463 */ | |
464 TCPC *gt_http_connection_open (GtTransferType type, in_addr_t ip, | |
465 in_port_t port) | |
466 { | |
467 TCPC *c; | |
468 | |
469 if (!(c = gt_http_connection_lookup (type, ip, port))) | |
470 c = tcp_open (ip, port, FALSE); | |
471 | |
472 return c; | |
473 } | |
474 | |
475 static BOOL count_open (TCPC *c, struct conn_info *info) | |
476 { | |
477 if (info->ip == net_peer (c->fd)) | |
478 info->count++; | |
479 | |
480 return FALSE; | |
481 } | |
482 | |
483 size_t gt_http_connection_length (GtTransferType type, in_addr_t ip) | |
484 { | |
485 struct conn_info info; | |
486 List *list; | |
487 | |
488 info.ip = ip; | |
489 info.port = 0; | |
490 info.count = 0; | |
491 | |
492 assert (type == GT_TRANSFER_DOWNLOAD || type == GT_TRANSFER_UPLOAD); | |
493 list = (type == GT_TRANSFER_DOWNLOAD ? download_connections : | |
494 upload_connections); | |
495 | |
496 list_foreach (list, (ListForeachFunc)count_open, &info); | |
497 | |
498 return info.count; | |
499 } | |
500 | |
501 void gt_http_connection_close (GtTransferType type, TCPC *c, BOOL force_close) | |
502 { | |
503 List **connlist_ptr; | |
504 | |
505 if (!c) | |
506 return; | |
507 | |
508 switch (type) | |
509 { | |
510 case GT_TRANSFER_DOWNLOAD: | |
511 { | |
512 gt_http_client_reset (c); | |
513 connlist_ptr = &download_connections; | |
514 } | |
515 break; | |
516 | |
517 case GT_TRANSFER_UPLOAD: | |
518 { | |
519 gt_http_server_reset (c); | |
520 connlist_ptr = &upload_connections; | |
521 } | |
522 break; | |
523 | |
524 default: | |
525 abort (); | |
526 } | |
527 | |
528 if (force_close) | |
529 { | |
530 *connlist_ptr = list_remove (*connlist_ptr, c); | |
531 | |
532 if (HTTP_DEBUG) | |
533 GT->DBGSOCK (GT, c, "force closing"); | |
534 | |
535 tcp_close (c); | |
536 return; | |
537 } | |
538 | |
539 /* remove the data associated with this connection */ | |
540 c->udata = NULL; | |
541 | |
542 /* | |
543 * This condition will happen because the server doesn't remove the | |
544 * connection from the persistent list until the connection fails. | |
545 */ | |
546 if (list_find (*connlist_ptr, c)) | |
547 { | |
548 assert (type == GT_TRANSFER_UPLOAD); | |
549 return; | |
550 } | |
551 | |
552 /* track it */ | |
553 *connlist_ptr = list_prepend (*connlist_ptr, c); | |
554 } | |
555 | |
556 /*****************************************************************************/ | |
557 | |
558 static char *localize_request (GtTransfer *xfer, BOOL *authorized) | |
559 { | |
560 char *open_path; | |
561 char *s_path; | |
562 int auth = FALSE; | |
563 int need_free = FALSE; | |
564 | |
565 if (!xfer || !xfer->request) | |
566 return NULL; | |
567 | |
568 /* dont call secure_path if they dont even care if it's a secure | |
569 * lookup */ | |
570 s_path = | |
571 (authorized ? file_secure_path (xfer->request) : xfer->request); | |
572 | |
573 if (authorized) | |
574 need_free = TRUE; | |
575 | |
576 open_path = gt_localize_request (xfer, s_path, &auth); | |
577 | |
578 if (need_free || !open_path) | |
579 free (s_path); | |
580 | |
581 if (authorized) | |
582 *authorized = auth; | |
583 | |
584 /* we need a unix style path for authorization */ | |
585 return open_path; | |
586 } | |
587 | |
588 /* | |
589 * request is expected in the form: | |
590 * /shared/Fuck%20Me%20Hard.mpg | |
591 */ | |
592 BOOL gt_transfer_set_request (GtTransfer *xfer, char *request) | |
593 { | |
594 #if 0 | |
595 FileShare *file; | |
596 char *shared_path; | |
597 #endif | |
598 | |
599 free (xfer->request); | |
600 xfer->request = NULL; | |
601 | |
602 /* lets keep this sane shall we */ | |
603 if (!request || *request != '/') | |
604 return FALSE; | |
605 | |
606 xfer->request = STRDUP (request); | |
607 xfer->request_path = gt_url_decode (request); /* storing here for opt */ | |
608 | |
609 return TRUE; | |
610 } | |
611 | |
612 /* attempts to open the requested file locally. | |
613 * NOTE: this handles verification */ | |
614 FILE *gt_transfer_open_request (GtTransfer *xfer, int *code) | |
615 { | |
616 FILE *f; | |
617 char *shared_path; | |
618 char *full_path = NULL; | |
619 char *host_path; | |
620 int auth = FALSE; | |
621 int code_l = 200; | |
622 | |
623 if (code) | |
624 *code = 404; /* Not Found */ | |
625 | |
626 if (!xfer || !xfer->request) | |
627 return NULL; | |
628 | |
629 if (!(shared_path = localize_request (xfer, &auth))) | |
630 { | |
631 /* | |
632 * If we havent finished syncing shares, that may be why the | |
633 * request failed. If so, return 503 here. | |
634 */ | |
635 if (!gt_share_local_sync_is_done () && code != NULL) | |
636 *code = 503; | |
637 | |
638 return NULL; | |
639 } | |
640 | |
641 /* needs more work for virtual requests */ | |
642 #if 0 | |
643 /* check to see if we matched a special OpenFT condition. If we did, the | |
644 * localized path is in the wrong order, so convert it (just to be | |
645 * converted back again...it's easier to maintain this way :) */ | |
646 if (auth) | |
647 { | |
648 xfer->shared = FALSE; | |
649 full_path = file_unix_path (shared_path); | |
650 } | |
651 else | |
652 #endif | |
653 { | |
654 Share *share; | |
655 int reason = UPLOAD_AUTH_NOTSHARED; | |
656 upload_auth_t cond; | |
657 | |
658 /* | |
659 * NOTE: the user string is not consistent with the one | |
660 * echoed through search results, which prefixes the IP | |
661 * with the GUID, if this node is firewalled. | |
662 */ | |
663 if ((share = GT->share_lookup (GT, SHARE_LOOKUP_HPATH, shared_path))) | |
664 reason = GT->upload_auth (GT, net_ip_str (xfer->ip), share, &cond); | |
665 | |
666 xfer->share_authd = share; | |
667 | |
668 switch (reason) | |
669 { | |
670 case UPLOAD_AUTH_STALE: | |
671 code_l = 500; /* Internal Server Error */ | |
672 break; | |
673 case UPLOAD_AUTH_NOTSHARED: | |
674 code_l = 404; /* Not Found */ | |
675 break; | |
676 case UPLOAD_AUTH_ALLOW: | |
677 code_l = 200; /* OK */ | |
678 xfer->open_path_size = share->size; | |
679 xfer->content_type = share->mime; | |
680 full_path = STRDUP (share->path); | |
681 break; | |
682 case UPLOAD_AUTH_MAX: | |
683 case UPLOAD_AUTH_MAX_PERUSER: | |
684 case UPLOAD_AUTH_HIDDEN: | |
685 default: | |
686 code_l = 503; /* Service Unavailable */ | |
687 xfer->queue_pos = cond.queue_pos; | |
688 xfer->queue_ttl = cond.queue_ttl; | |
689 break; | |
690 } | |
691 } | |
692 | |
693 if (code) | |
694 *code = code_l; | |
695 | |
696 /* error of some kind, get out of here */ | |
697 if (code_l != 200) | |
698 return NULL; | |
699 | |
700 /* figure out the actual filename that we should be opening */ | |
701 host_path = file_host_path (full_path); | |
702 free (full_path); | |
703 | |
704 /* needs more work for virtual requests */ | |
705 #if 0 | |
706 /* complete the rest of the data required */ | |
707 if (auth) | |
708 { | |
709 struct stat st; | |
710 | |
711 if (!file_stat (host_path, &st)) | |
712 { | |
713 free (host_path); | |
714 return NULL; | |
715 } | |
716 | |
717 xfer->open_path_size = st.st_size; | |
718 xfer->content_type = mime_type (host_path); | |
719 } | |
720 #endif | |
721 | |
722 if (!(f = fopen (host_path, "rb"))) | |
723 { | |
724 *code = 500; | |
725 return NULL; | |
726 } | |
727 | |
728 /* NOTE: gt_transfer_close will be responsible for freeing this now */ | |
729 xfer->open_path = host_path; | |
730 | |
731 if (code) | |
732 *code = 200; /* OK */ | |
733 | |
734 return f; | |
735 } | |
736 | |
737 /*****************************************************************************/ | |
738 | |
739 /* | |
740 * The callbacks here are from within the HTTP system, centralized for | |
741 * maintainability. | |
742 */ | |
743 | |
744 /* report back the progress of this download chunk */ | |
745 void gt_download (Chunk *chunk, unsigned char *segment, size_t len) | |
746 { | |
747 GT->chunk_write (GT, chunk->transfer, chunk, chunk->source, | |
748 segment, len); | |
749 } | |
750 | |
751 /* report back the progress of this upload chunk */ | |
752 void gt_upload (Chunk *chunk, unsigned char *segment, size_t len) | |
753 { | |
754 GT->chunk_write (GT, chunk->transfer, chunk, chunk->source, | |
755 segment, len); | |
756 } | |
757 | |
758 void gt_transfer_write (GtTransfer *xfer, Chunk *chunk, | |
759 unsigned char *segment, size_t len) | |
760 { | |
761 /* | |
762 * Cap the data at the remaining size of the xfer. Note that this is | |
763 * the size of the HTTP request issued, _NOT_ the chunk size, which may | |
764 * have been altered by giFT in splitting up chunks. I think giFT | |
765 * handles that case properly, but we also have to guard against | |
766 * remaining_len becoming less than 0. Note that p->chunk_write | |
767 * will cancel the transfer if remaining_len goes to 0. | |
768 * | |
769 * TODO: remaining_len is off_t, make sure this is handled right wrt | |
770 * negative file sizes (do big files become negative sizes?) | |
771 */ | |
772 if (len > xfer->remaining_len) | |
773 len = xfer->remaining_len; | |
774 | |
775 xfer->remaining_len -= len; | |
776 (*xfer->callback) (chunk, segment, len); | |
777 } | |
778 | |
779 /*****************************************************************************/ | |
780 | |
781 static BOOL throttle_suspend (Chunk *chunk, int s_opt, float factor) | |
782 { | |
783 GtTransfer *xfer; | |
784 | |
785 if (!chunk) | |
786 return FALSE; | |
787 | |
788 xfer = chunk->udata; | |
789 | |
790 if (!xfer || !xfer->c) | |
791 { | |
792 GT->DBGFN (GT, "no connection found to suspend"); | |
793 return FALSE; | |
794 } | |
795 | |
796 input_suspend_all (xfer->c->fd); | |
797 | |
798 if (factor) | |
799 net_sock_adj_buf (xfer->c->fd, s_opt, factor); | |
800 | |
801 return TRUE; | |
802 } | |
803 | |
804 static BOOL throttle_resume (Chunk *chunk, int s_opt, float factor) | |
805 { | |
806 GtTransfer *xfer = NULL; | |
807 | |
808 if (!chunk) | |
809 return FALSE; | |
810 | |
811 xfer = chunk->udata; | |
812 | |
813 if (!xfer || !xfer->c) | |
814 { | |
815 GT->DBGFN (GT, "no connection found to resume"); | |
816 return FALSE; | |
817 } | |
818 | |
819 input_resume_all (xfer->c->fd); | |
820 | |
821 #if 0 | |
822 if (factor) | |
823 net_sock_adj_buf (xfer->c->fd, s_opt, factor); | |
824 #endif | |
825 | |
826 return TRUE; | |
827 } | |
828 | |
829 /*****************************************************************************/ | |
830 | |
831 void gt_download_cancel (Chunk *chunk, void *data) | |
832 { | |
833 gt_transfer_cancel (chunk, TRANSFER_DOWNLOAD); | |
834 } | |
835 | |
836 /* cancel the transfer associate with the chunk giFT gave us */ | |
837 void gt_upload_cancel (Chunk *chunk, void *data) | |
838 { | |
839 gt_transfer_cancel (chunk, TRANSFER_UPLOAD); | |
840 } | |
841 | |
842 static int throttle_sopt (Transfer *transfer) | |
843 { | |
844 int sopt = 0; | |
845 | |
846 switch (transfer_direction (transfer)) | |
847 { | |
848 case TRANSFER_DOWNLOAD: | |
849 sopt = SO_RCVBUF; | |
850 break; | |
851 case TRANSFER_UPLOAD: | |
852 sopt = SO_SNDBUF; | |
853 break; | |
854 } | |
855 | |
856 return sopt; | |
857 } | |
858 | |
859 BOOL gt_chunk_suspend (Chunk *chunk, Transfer *transfer, void *data) | |
860 { | |
861 return throttle_suspend (chunk, throttle_sopt (transfer), 0.0); | |
862 } | |
863 | |
864 BOOL gt_chunk_resume (Chunk *chunk, Transfer *transfer, void *data) | |
865 { | |
866 return throttle_resume (chunk, throttle_sopt (transfer), 0.0); | |
867 } |