Mercurial > audlegacy-plugins
comparison src/neon/neon.c @ 1719:29c35cb8873e
- Add neon HTTP transport plugin sources (for real)
author | Ralf Ertzinger <ralf@skytale.net> |
---|---|
date | Mon, 17 Sep 2007 21:46:53 +0200 |
parents | |
children | 2e33cfa6a872 |
comparison
equal
deleted
inserted
replaced
1718:892deefba58d | 1719:29c35cb8873e |
---|---|
1 #include <audacious/vfs.h> | |
2 #include <audacious/plugin.h> | |
3 | |
4 #include <ne_socket.h> | |
5 #include <ne_utils.h> | |
6 #include <ne_redirect.h> | |
7 #include <ne_request.h> | |
8 | |
9 #include "debug.h" | |
10 #include "neon.h" | |
11 #include "rb.h" | |
12 | |
13 #define NBUFSIZ (128u*1024u) | |
14 #define NETBLKSIZ (4096u) | |
15 | |
16 DECLARE_PLUGIN(neon, init, fini) | |
17 | |
18 VFSConstructor neon_http_const = { | |
19 "http://", | |
20 neon_vfs_fopen_impl, | |
21 neon_vfs_fclose_impl, | |
22 neon_vfs_fread_impl, | |
23 neon_vfs_fwrite_impl, | |
24 neon_vfs_getc_impl, | |
25 neon_vfs_ungetc_impl, | |
26 neon_vfs_fseek_impl, | |
27 neon_vfs_rewind_impl, | |
28 neon_vfs_ftell_impl, | |
29 neon_vfs_feof_impl, | |
30 neon_vfs_truncate_impl, | |
31 neon_vfs_fsize_impl, | |
32 neon_vfs_metadata_impl | |
33 }; | |
34 | |
35 /* | |
36 * ======== | |
37 */ | |
38 | |
39 static struct neon_handle* handle_init(void) { | |
40 | |
41 struct neon_handle* h; | |
42 | |
43 _ENTER; | |
44 | |
45 if (NULL == (h = malloc(sizeof(struct neon_handle)))) { | |
46 _ERROR("Could not allocate memory for handle"); | |
47 _LEAVE NULL; | |
48 } | |
49 | |
50 if (0 != init_rb(&(h->rb), NBUFSIZ)) { | |
51 _ERROR("Could not initialize buffer"); | |
52 free(h); | |
53 _LEAVE NULL; | |
54 } | |
55 | |
56 h->url = NULL; | |
57 h->purl = &purl; | |
58 memset(h->purl, 0, sizeof(ne_uri)); | |
59 h->session = NULL; | |
60 h->request = NULL; | |
61 h->redircount = 0; | |
62 h->pos = 0; | |
63 h->content_length = -1; | |
64 h->can_ranges = FALSE; | |
65 h->reader = NULL; | |
66 h->reader_status.mutex = g_mutex_new(); | |
67 h->reader_status.cond = g_cond_new(); | |
68 h->reader_status.reading = FALSE; | |
69 h->reader_status.status = NEON_READER_INIT; | |
70 h->eof = FALSE; | |
71 | |
72 _LEAVE h; | |
73 } | |
74 | |
75 /* | |
76 * ----- | |
77 */ | |
78 | |
79 static void handle_free(struct neon_handle* h) { | |
80 | |
81 _ENTER; | |
82 | |
83 ne_uri_free(h->purl); | |
84 destroy_rb(&h->rb); | |
85 free(h); | |
86 | |
87 _LEAVE; | |
88 } | |
89 | |
90 /* | |
91 * ---- | |
92 */ | |
93 | |
94 static void init(void) { | |
95 | |
96 int ret; | |
97 | |
98 _ENTER; | |
99 | |
100 if (0 != (ret = ne_sock_init())) { | |
101 _ERROR("Could not initialize neon library: %d\n", ret); | |
102 _LEAVE; | |
103 } | |
104 | |
105 vfs_register_transport(&neon_http_const); | |
106 | |
107 if (0 != ne_has_support(NE_FEATURE_SSL)) { | |
108 _DEBUG("neon compiled with thread-safe SSL, enabling https:// transport"); | |
109 } | |
110 | |
111 _LEAVE; | |
112 } | |
113 | |
114 /* | |
115 * ----- | |
116 */ | |
117 | |
118 static void fini(void) { | |
119 | |
120 _ENTER; | |
121 | |
122 ne_sock_exit(); | |
123 | |
124 _LEAVE; | |
125 } | |
126 | |
127 /* | |
128 * ----- | |
129 */ | |
130 | |
131 static void kill_reader(struct neon_handle* h) { | |
132 | |
133 _ENTER; | |
134 | |
135 _DEBUG("Signaling reader thread to terminate"); | |
136 g_mutex_lock(h->reader_status.mutex); | |
137 h->reader_status.reading = FALSE; | |
138 g_cond_signal(h->reader_status.cond); | |
139 g_mutex_unlock(h->reader_status.mutex); | |
140 | |
141 _DEBUG("Waiting for reader thread to die..."); | |
142 g_thread_join(h->reader); | |
143 _DEBUG("Reader thread has died"); | |
144 h->reader = NULL; | |
145 } | |
146 | |
147 /* | |
148 * ----- | |
149 */ | |
150 | |
151 static void handle_headers(struct neon_handle* h) { | |
152 | |
153 const gchar* name; | |
154 const gchar* value; | |
155 void* cursor = NULL; | |
156 long len; | |
157 gchar* endptr; | |
158 | |
159 _ENTER; | |
160 | |
161 _DEBUG("Header responses:"); | |
162 while(NULL != (cursor = ne_response_header_iterate(h->request, cursor, &name, &value))) { | |
163 _DEBUG("HEADER: %s: %s", name, value); | |
164 if (0 == g_ascii_strncasecmp("accept-ranges", name, 13)) { | |
165 /* | |
166 * The server advertises range capability. we need "bytes" | |
167 */ | |
168 if (NULL != g_strrstr(value, "bytes")) { | |
169 _DEBUG("server can_ranges"); | |
170 h->can_ranges = TRUE; | |
171 } | |
172 } | |
173 | |
174 if (0 == g_ascii_strncasecmp("content-length", name, 14)) { | |
175 /* | |
176 * The server sent us the content length. Parse and store. | |
177 */ | |
178 len = strtol(value, &endptr, 10); | |
179 if ((*value != '\0') && (*endptr == '\0')) { | |
180 /* | |
181 * Valid data. | |
182 */ | |
183 _DEBUG("Content length as advertised by server: %d", len); | |
184 h->content_length = len; | |
185 } | |
186 } | |
187 } | |
188 | |
189 _LEAVE; | |
190 } | |
191 | |
192 /* | |
193 * ----- | |
194 */ | |
195 | |
196 static int open_request(struct neon_handle* handle, unsigned long startbyte) { | |
197 | |
198 int ret; | |
199 | |
200 _ENTER; | |
201 | |
202 handle->request = ne_request_create(handle->session, "GET", handle->purl->path); | |
203 ne_print_request_header(handle->request, "Range", "bytes=%ld-", startbyte); | |
204 | |
205 /* | |
206 * Try to connect to the server. | |
207 */ | |
208 _DEBUG("Connecting..."); | |
209 ret = ne_begin_request(handle->request); | |
210 | |
211 switch (ret) { | |
212 case NE_OK: | |
213 /* URL opened OK */ | |
214 _DEBUG("URL opened OK"); | |
215 handle->content_start = startbyte; | |
216 handle->pos = startbyte; | |
217 handle_headers(handle); | |
218 _LEAVE 0; | |
219 break; | |
220 | |
221 case NE_REDIRECT: | |
222 /* We hit a redirect. Handle it. */ | |
223 _DEBUG("Redirect encountered"); | |
224 handle->redircount += 1; | |
225 handle->purl = ne_redirect_location(handle->session); | |
226 ne_request_destroy(handle->request); | |
227 if (NULL == handle->purl) { | |
228 _ERROR("Could not parse redirect response"); | |
229 _LEAVE -1; | |
230 } | |
231 break; | |
232 | |
233 default: | |
234 /* Something went wrong. */ | |
235 _ERROR("Could not open URL: %d", ret); | |
236 if (1 == ret) { | |
237 _ERROR("neon error string: %s", ne_get_error(handle->session)); | |
238 } | |
239 ne_request_destroy(handle->request); | |
240 _LEAVE -1; | |
241 break; | |
242 } | |
243 } | |
244 | |
245 /* | |
246 * ----- | |
247 */ | |
248 | |
249 static int open_handle(struct neon_handle* handle, unsigned long startbyte) { | |
250 | |
251 int ret; | |
252 | |
253 _ENTER; | |
254 | |
255 handle->redircount = 0; | |
256 | |
257 _DEBUG("Parsing URL"); | |
258 if (0 != ne_uri_parse(handle->url, handle->purl)) { | |
259 _ERROR("Could not parse URL '%s'", handle->url); | |
260 _LEAVE -1; | |
261 } | |
262 | |
263 if (0 == handle->purl->port) { | |
264 handle->purl->port = 80; | |
265 } | |
266 | |
267 while (handle->redircount < 10) { | |
268 | |
269 _DEBUG("Creating session"); | |
270 handle->session = ne_session_create(handle->purl->scheme, handle->purl->host, handle->purl->port); | |
271 ne_set_session_flag(handle->session, NE_SESSFLAG_ICYPROTO, 1); | |
272 ne_set_session_flag(handle->session, NE_SESSFLAG_PERSIST, 0); | |
273 ne_redirect_register(handle->session); | |
274 | |
275 _DEBUG("Creating request"); | |
276 ret = open_request(handle, startbyte); | |
277 | |
278 if (0 == ret) { | |
279 _LEAVE 0; | |
280 } else if (-1 == ret) { | |
281 ne_session_destroy(handle->session); | |
282 _LEAVE -1; | |
283 } | |
284 } | |
285 | |
286 /* | |
287 * If we get here, our redirect count exceeded | |
288 */ | |
289 | |
290 _ERROR("Redirect count exceeded for URL"); | |
291 | |
292 _LEAVE 1; | |
293 } | |
294 | |
295 /* | |
296 * ----- | |
297 */ | |
298 | |
299 static int fill_buffer(struct neon_handle* h) { | |
300 | |
301 ssize_t bsize; | |
302 char buffer[NETBLKSIZ]; | |
303 ssize_t to_read; | |
304 | |
305 _ENTER; | |
306 | |
307 bsize = free_rb(&h->rb); | |
308 to_read = MIN(bsize, NETBLKSIZ); | |
309 | |
310 _DEBUG("%d bytes free in buffer, trying to read %d bytes max", bsize, to_read); | |
311 | |
312 _DEBUG("Reading from the network...."); | |
313 if (0 >= (bsize = ne_read_response_block(h->request, buffer, to_read))) { | |
314 if (0 == bsize) { | |
315 _DEBUG("End of file encountered"); | |
316 _LEAVE 1; | |
317 } else { | |
318 _ERROR("Error while reading from the network"); | |
319 _LEAVE -1; | |
320 } | |
321 } | |
322 _DEBUG("Read %d bytes from the network", bsize); | |
323 | |
324 if (0 != write_rb(&(h->rb), buffer, bsize)) { | |
325 _ERROR("Error putting data into buffer"); | |
326 _LEAVE -1; | |
327 } | |
328 | |
329 _LEAVE 0; | |
330 } | |
331 | |
332 /* | |
333 * ----- | |
334 */ | |
335 | |
336 static int fill_buffer_limit(struct neon_handle* h, unsigned int maxfree) { | |
337 | |
338 ssize_t bfree; | |
339 int ret; | |
340 | |
341 _ENTER; | |
342 | |
343 bfree = free_rb(&h->rb); | |
344 _DEBUG("Filling buffer up to max %d bytes free, %d bytes free now", maxfree, bfree); | |
345 | |
346 while (bfree > maxfree) { | |
347 ret = fill_buffer(h); | |
348 if (-1 == ret) { | |
349 _ERROR("Error while filling buffer"); | |
350 _LEAVE ret; | |
351 } else if (1 == ret) { | |
352 /* | |
353 * EOF while filling the buffer. Return what we have. | |
354 */ | |
355 _LEAVE 0; | |
356 } | |
357 | |
358 bfree = free_rb(&h->rb); | |
359 } | |
360 | |
361 _LEAVE 0; | |
362 } | |
363 | |
364 /* | |
365 * ----- | |
366 */ | |
367 | |
368 static gpointer reader_thread(void* data) { | |
369 | |
370 struct neon_handle* h = (struct neon_handle*)data; | |
371 int ret; | |
372 | |
373 _ENTER; | |
374 | |
375 g_mutex_lock(h->reader_status.mutex); | |
376 | |
377 while(h->reader_status.reading) { | |
378 g_mutex_unlock(h->reader_status.mutex); | |
379 | |
380 /* | |
381 * Hit the network only if we have more than NETBLKSIZ of free buffer | |
382 */ | |
383 if (NETBLKSIZ < free_rb(&h->rb)) { | |
384 | |
385 _DEBUG("Filling buffer..."); | |
386 ret = fill_buffer(h); | |
387 | |
388 g_mutex_lock(h->reader_status.mutex); | |
389 if (-1 == ret) { | |
390 /* | |
391 * Error encountered while reading from the network. | |
392 * Set the error flag and terminate the | |
393 * reader thread. | |
394 */ | |
395 _DEBUG("Error while reading from the network. Terminating reader thread"); | |
396 h->reader_status.status = NEON_READER_ERROR; | |
397 g_mutex_unlock(h->reader_status.mutex); | |
398 _LEAVE NULL; | |
399 } else if (1 == ret) { | |
400 /* | |
401 * EOF encountered while reading from the | |
402 * network. Set the EOF status and exit. | |
403 */ | |
404 _DEBUG("EOF encountered while reading from the network. Terminating reader thread"); | |
405 h->reader_status.status = NEON_READER_EOF; | |
406 g_mutex_unlock(h->reader_status.mutex); | |
407 _LEAVE NULL; | |
408 } | |
409 | |
410 /* | |
411 * So we actually got some data out of the stream. | |
412 */ | |
413 _DEBUG("Network read succeeded"); | |
414 } else { | |
415 /* | |
416 * Not enough free space in the buffer. | |
417 * Sleep until the main thread wakes us up. | |
418 */ | |
419 g_mutex_lock(h->reader_status.mutex); | |
420 if (h->reader_status.reading) { | |
421 _DEBUG("Reader thread going to sleep"); | |
422 g_cond_wait(h->reader_status.cond, h->reader_status.mutex); | |
423 _DEBUG("Reader thread woke up"); | |
424 } else { | |
425 /* | |
426 * Main thread has ordered termination of this thread. | |
427 * Leave the loop. | |
428 */ | |
429 break; | |
430 } | |
431 } | |
432 } | |
433 | |
434 _DEBUG("Reader thread terminating gracefully"); | |
435 h->reader_status.status = NEON_READER_TERM; | |
436 g_mutex_unlock(h->reader_status.mutex); | |
437 | |
438 _LEAVE NULL; | |
439 } | |
440 | |
441 /* | |
442 * ----- | |
443 */ | |
444 | |
445 VFSFile* neon_vfs_fopen_impl(const gchar* path, const gchar* mode) { | |
446 | |
447 VFSFile* file; | |
448 struct neon_handle* handle; | |
449 | |
450 _ENTER; | |
451 | |
452 _DEBUG("Trying to open '%s' with neon", path); | |
453 | |
454 if (NULL == (file = malloc(sizeof(VFSFile)))) { | |
455 _ERROR("Could not allocate memory for filehandle"); | |
456 _LEAVE NULL; | |
457 } | |
458 | |
459 if (NULL == (handle = handle_init())) { | |
460 _ERROR("Could not allocate memory for neon handle"); | |
461 free(file); | |
462 _LEAVE NULL; | |
463 } | |
464 | |
465 if (NULL == (handle->url = strdup(path))) { | |
466 _ERROR("Could not copy URL string"); | |
467 handle_free(handle); | |
468 free(file); | |
469 _LEAVE NULL; | |
470 } | |
471 | |
472 if (0 != open_handle(handle, 0)) { | |
473 _ERROR("Could not open URL"); | |
474 handle_free(handle); | |
475 free(file); | |
476 _LEAVE NULL; | |
477 } | |
478 | |
479 file->handle = handle; | |
480 file->base = &neon_http_const; | |
481 | |
482 _LEAVE file; | |
483 } | |
484 | |
485 /* | |
486 * ---- | |
487 */ | |
488 | |
489 gint neon_vfs_fclose_impl(VFSFile* file) { | |
490 | |
491 struct neon_handle* h = (struct neon_handle *)file->handle; | |
492 | |
493 _ENTER; | |
494 | |
495 if (NULL != h->reader) { | |
496 kill_reader(h); | |
497 } | |
498 | |
499 _DEBUG("Destroying request"); | |
500 if (NULL != h->request) { | |
501 ne_request_destroy(h->request); | |
502 } | |
503 | |
504 _DEBUG("Destroying session"); | |
505 ne_session_destroy(h->session); | |
506 | |
507 handle_free(h); | |
508 | |
509 _LEAVE 0; | |
510 } | |
511 | |
512 /* | |
513 * ----- | |
514 */ | |
515 | |
516 size_t neon_vfs_fread_impl(gpointer ptr_, size_t size, size_t nmemb, VFSFile* file) { | |
517 | |
518 struct neon_handle* h = (struct neon_handle*)file->handle; | |
519 int belem; | |
520 int ret; | |
521 | |
522 _ENTER; | |
523 | |
524 if (NULL == h->request) { | |
525 _ERROR("No request to read from, seek gone wrong?"); | |
526 _LEAVE 0; | |
527 } | |
528 | |
529 _DEBUG("Requesting %d elements of %d bytes size each (%d bytes total), to be stored at %p", | |
530 nmemb, size, (nmemb*size), ptr_); | |
531 | |
532 /* | |
533 * Look how much data is in the buffer | |
534 */ | |
535 belem = used_rb(&h->rb) / size; | |
536 | |
537 if ((NULL != h->reader) && (0 == belem)) { | |
538 /* | |
539 * There is a reader thread, but the buffer is empty. | |
540 * If we are running normally we will have to rebuffer. | |
541 * Kill the reader thread and restart. | |
542 */ | |
543 g_mutex_lock(h->reader_status.mutex); | |
544 if (NEON_READER_RUN == h->reader_status.status) { | |
545 g_mutex_unlock(h->reader_status.mutex); | |
546 _ERROR("Buffer underrun, trying rebuffering"); | |
547 kill_reader(h); | |
548 } else { | |
549 g_mutex_unlock(h->reader_status.mutex); | |
550 } | |
551 } | |
552 | |
553 if (NULL == h->reader) { | |
554 /* | |
555 * There is no reader thread yet. Read the first bytes from | |
556 * the network ourselves, and then fire up the reader thread | |
557 * to keep the buffer filled up. | |
558 */ | |
559 _DEBUG("Doing initial buffer fill"); | |
560 ret = fill_buffer_limit(h, NBUFSIZ/2); | |
561 | |
562 if (-1 == ret) { | |
563 _ERROR("Error while reading from the network"); | |
564 _LEAVE 0; | |
565 } else if (1 == ret) { | |
566 _ERROR("EOF during initial read"); | |
567 _LEAVE 0; | |
568 } | |
569 | |
570 /* | |
571 * We have some data in the buffer now. | |
572 * Start the reader thread. | |
573 */ | |
574 h->reader_status.reading = TRUE; | |
575 if (NULL == (h->reader = g_thread_create(reader_thread, h, TRUE, NULL))) { | |
576 h->reader_status.reading = FALSE; | |
577 _ERROR("Error creating reader thread!"); | |
578 _LEAVE 0; | |
579 } | |
580 g_mutex_lock(h->reader_status.mutex); | |
581 h->reader_status.status = NEON_READER_RUN; | |
582 g_mutex_unlock(h->reader_status.mutex); | |
583 } else { | |
584 /* | |
585 * There already is a reader thread. Look if it is in good | |
586 * shape. | |
587 */ | |
588 g_mutex_lock(h->reader_status.mutex); | |
589 _DEBUG("Reader thread status: %d", h->reader_status.status); | |
590 switch (h->reader_status.status) { | |
591 case NEON_READER_INIT: | |
592 case NEON_READER_RUN: | |
593 /* | |
594 * All is well, nothing to be done. | |
595 */ | |
596 break; | |
597 case NEON_READER_EOF: | |
598 /* | |
599 * If there still is data in the buffer, carry on. | |
600 * If not, terminate the reader thread and return 0. | |
601 */ | |
602 if (0 == used_rb(&h->rb)) { | |
603 _DEBUG("Reached end of stream"); | |
604 g_mutex_unlock(h->reader_status.mutex); | |
605 kill_reader(h); | |
606 h->eof = TRUE; | |
607 _LEAVE 0; | |
608 } | |
609 break; | |
610 case NEON_READER_ERROR: | |
611 /* Terminate the reader and return 0 */ | |
612 g_mutex_unlock(h->reader_status.mutex); | |
613 kill_reader(h); | |
614 _LEAVE 0; | |
615 break; | |
616 case NEON_READER_TERM: | |
617 /* | |
618 * The reader thread terminated gracefully, most | |
619 * likely on our own request. | |
620 * We should not get here. | |
621 */ | |
622 _ERROR("Reader thread terminated and fread() called. How did we get here?"); | |
623 g_mutex_unlock(h->reader_status.mutex); | |
624 kill_reader(h); | |
625 _LEAVE 0; | |
626 } | |
627 g_mutex_unlock(h->reader_status.mutex); | |
628 } | |
629 | |
630 /* | |
631 * Deliver data from the buffer | |
632 */ | |
633 belem = used_rb(&h->rb) / size; | |
634 | |
635 if (0 == belem) { | |
636 /* | |
637 * The buffer is empty, we can deliver no data! | |
638 */ | |
639 _ERROR("Buffer still underrun, fatal."); | |
640 _LEAVE 0; | |
641 } | |
642 | |
643 _DEBUG("%d elements of data in the buffer", belem); | |
644 read_rb(&h->rb, ptr_, MIN(belem, nmemb)*size); | |
645 | |
646 /* | |
647 * Signal the network thread to continue reading | |
648 */ | |
649 _DEBUG("Waking up reader thread"); | |
650 g_mutex_lock(h->reader_status.mutex); | |
651 g_cond_signal(h->reader_status.cond); | |
652 g_mutex_unlock(h->reader_status.mutex); | |
653 | |
654 h->pos += (MIN(belem, nmemb)*size); | |
655 | |
656 _DEBUG("Returning %d elements", MIN(belem, nmemb)); | |
657 | |
658 _LEAVE MIN(belem, nmemb); | |
659 } | |
660 | |
661 | |
662 /* | |
663 * ----- | |
664 */ | |
665 | |
666 size_t neon_vfs_fwrite_impl(gconstpointer ptr, size_t size, size_t nmemb, VFSFile* file) { | |
667 | |
668 _ENTER; | |
669 | |
670 _ERROR("NOT IMPLEMENTED"); | |
671 | |
672 _LEAVE 0; | |
673 } | |
674 | |
675 /* | |
676 * ----- | |
677 */ | |
678 | |
679 gint neon_vfs_getc_impl(VFSFile* file) { | |
680 | |
681 gchar c; | |
682 | |
683 _ENTER; | |
684 | |
685 if (1 != neon_vfs_fread_impl(&c, 1, 1, file)) { | |
686 _ERROR("Could not getc()!"); | |
687 _LEAVE -1; | |
688 } | |
689 | |
690 _LEAVE c; | |
691 } | |
692 | |
693 /* | |
694 * ----- | |
695 */ | |
696 | |
697 gint neon_vfs_ungetc_impl(gint c, VFSFile* stream) { | |
698 | |
699 _ENTER; | |
700 | |
701 _ERROR("NOT IMPLEMENTED"); | |
702 | |
703 _LEAVE 0; | |
704 } | |
705 | |
706 /* | |
707 * ----- | |
708 */ | |
709 | |
710 void neon_vfs_rewind_impl(VFSFile* file) { | |
711 | |
712 _ENTER; | |
713 | |
714 (void)neon_vfs_fseek_impl(file, 0L, SEEK_SET); | |
715 | |
716 _LEAVE; | |
717 } | |
718 | |
719 /* | |
720 * ----- | |
721 */ | |
722 | |
723 glong neon_vfs_ftell_impl(VFSFile* file) { | |
724 | |
725 struct neon_handle* h = (struct neon_handle *)file->handle; | |
726 | |
727 _ENTER; | |
728 | |
729 _DEBUG("Current file position: %d", h->pos); | |
730 | |
731 _LEAVE h->pos; | |
732 } | |
733 | |
734 /* | |
735 * ----- | |
736 */ | |
737 | |
738 gboolean neon_vfs_feof_impl(VFSFile* file) { | |
739 | |
740 struct neon_handle* h = (struct neon_handle*)file->handle; | |
741 | |
742 _ENTER; | |
743 | |
744 _LEAVE h->eof; | |
745 } | |
746 | |
747 /* | |
748 * ----- | |
749 */ | |
750 | |
751 gint neon_vfs_truncate_impl(VFSFile* file, glong size) { | |
752 | |
753 _ENTER; | |
754 | |
755 _ERROR("NOT IMPLEMENTED"); | |
756 | |
757 _LEAVE 0; | |
758 } | |
759 | |
760 /* | |
761 * ----- | |
762 */ | |
763 | |
764 gint neon_vfs_fseek_impl(VFSFile* file, glong offset, gint whence) { | |
765 | |
766 struct neon_handle* h = (struct neon_handle*)file->handle; | |
767 long newpos; | |
768 long content_length; | |
769 | |
770 _ENTER; | |
771 | |
772 _DEBUG("Seek requested: offset %ld, whence %d", offset, whence); | |
773 /* | |
774 * Two things must be satisfied for us to be able to seek: | |
775 * - the server must advertise a content-length | |
776 * - the server must advertise accept-ranges: bytes | |
777 */ | |
778 if ((-1 == h->content_length) || !h->can_ranges) { | |
779 _DEBUG("Can not seek due to server restrictions"); | |
780 _LEAVE -1; | |
781 } | |
782 | |
783 content_length = h->content_length + h->content_start; | |
784 | |
785 switch (whence) { | |
786 case SEEK_SET: | |
787 newpos = offset; | |
788 break; | |
789 case SEEK_CUR: | |
790 newpos = h->pos + offset; | |
791 break; | |
792 case SEEK_END: | |
793 newpos = content_length + offset; | |
794 break; | |
795 default: | |
796 _ERROR("Invalid whence specified"); | |
797 _LEAVE -1; | |
798 } | |
799 | |
800 _DEBUG("Position to seek to: %ld, current: %ld", newpos, h->pos); | |
801 if (0 > newpos) { | |
802 _ERROR("Can not seek before start of stream"); | |
803 _LEAVE -1; | |
804 } | |
805 | |
806 if (newpos > content_length) { | |
807 _ERROR("Can not seek beyond end of stream"); | |
808 _LEAVE -1; | |
809 } | |
810 | |
811 if (newpos == h->pos) { | |
812 _LEAVE 0; | |
813 } | |
814 | |
815 /* | |
816 * To seek to the new position we have to | |
817 * - stop the current reader thread, if there is one | |
818 * - destroy the current request | |
819 * - dump all data currently in the ringbuffer | |
820 * - create a new request starting at newpos | |
821 */ | |
822 if (NULL != h->reader) { | |
823 /* | |
824 * There may be a thread still running. | |
825 */ | |
826 kill_reader(h); | |
827 } | |
828 | |
829 ne_request_destroy(h->request); | |
830 ne_session_destroy(h->session); | |
831 reset_rb(&h->rb); | |
832 | |
833 if (0 != open_handle(h, newpos)) { | |
834 /* | |
835 * Something went wrong while creating the new request. | |
836 * There is not much we can do now, we'll set the request | |
837 * to NULL, so that fread() will error out on the next | |
838 * read request | |
839 */ | |
840 _ERROR("Error while creating new request!"); | |
841 h->request = NULL; | |
842 _LEAVE -1; | |
843 } | |
844 | |
845 /* | |
846 * Things seem to have worked. The next read request will start | |
847 * the reader thread again. | |
848 */ | |
849 | |
850 _LEAVE 0; | |
851 } | |
852 | |
853 /* | |
854 * ----- | |
855 */ | |
856 | |
857 gchar *neon_vfs_metadata_impl(VFSFile* file, const gchar * field) { | |
858 | |
859 _ENTER; | |
860 | |
861 _ERROR("NOT IMPLEMENTED"); | |
862 | |
863 _LEAVE NULL; | |
864 } | |
865 | |
866 /* | |
867 * ----- | |
868 */ | |
869 | |
870 off_t neon_vfs_fsize_impl(VFSFile* file) { | |
871 | |
872 struct neon_handle* h = (struct neon_handle*)file->handle; | |
873 | |
874 _ENTER; | |
875 | |
876 if (-1 == h->content_length) { | |
877 _DEBUG("Unknown content length"); | |
878 _LEAVE 0; | |
879 } | |
880 | |
881 _LEAVE (h->content_start + h->content_length); | |
882 } |