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