125
|
1 /* -*- tab-width: 4; indent-tabs-mode: nil -*- */
|
|
2 /* vim: set ts=4 sts=4 sw=4 expandtab number : */
|
|
3 /*
|
|
4 * http.c : GeeXboX uShare Web Server handler.
|
|
5 * Originally developped for the GeeXboX project.
|
|
6 * Parts of the code are originated from GMediaServer from Oskar Liljeblad.
|
|
7 * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
|
|
8 *
|
|
9 * This program is free software; you can redistribute it and/or modify
|
|
10 * it under the terms of the GNU General Public License as published by
|
|
11 * the Free Software Foundation; either version 2 of the License, or
|
|
12 * (at your option) any later version.
|
|
13 *
|
|
14 * This program is distributed in the hope that it will be useful,
|
|
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17 * GNU Library General Public License for more details.
|
|
18 *
|
|
19 * You should have received a copy of the GNU General Public License along
|
|
20 * with this program; if not, write to the Free Software Foundation,
|
|
21 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
22 */
|
|
23
|
|
24 #include <sys/types.h>
|
|
25 #include <sys/stat.h>
|
|
26 #include <fcntl.h>
|
|
27 #include <errno.h>
|
|
28 #include <stdio.h>
|
|
29 #include <stdlib.h>
|
|
30 #include <unistd.h>
|
|
31 #include <errno.h>
|
|
32
|
|
33 #include <upnp/upnp.h>
|
|
34 #include <upnp/upnptools.h>
|
|
35
|
|
36 #include "services.h"
|
|
37 #include "cds.h"
|
|
38 #include "cms.h"
|
|
39 #include "msr.h"
|
|
40 #include "metadata.h"
|
|
41 #include "http.h"
|
|
42 #include "minmax.h"
|
|
43 #include "trace.h"
|
|
44 #include "presentation.h"
|
|
45 #include "osdep.h"
|
|
46 #include "mime.h"
|
|
47 #include "recpt1.h"
|
|
48 #include "tssplitter_lite.h"
|
|
49
|
|
50 #define PROTOCOL_TYPE_PRE_SZ 11 /* for the str length of "http-get:*:" */
|
|
51 #define PROTOCOL_TYPE_SUFF_SZ 2 /* for the str length of ":*" */
|
|
52
|
|
53 extern thread_data tdata;
|
|
54
|
|
55 struct web_file_t {
|
|
56 char *fullpath;
|
|
57 off_t pos;
|
|
58 enum {
|
|
59 FILE_LOCAL,
|
|
60 FILE_MEMORY,
|
|
61 FILE_STREAM
|
|
62 } type;
|
|
63 union {
|
|
64 struct {
|
|
65 int fd;
|
|
66 struct upnp_entry_t *entry;
|
|
67 } local;
|
|
68 struct {
|
|
69 char *contents;
|
|
70 off_t len;
|
|
71 } memory;
|
|
72 struct {
|
|
73 int id;
|
|
74 STREAM_QUEUE_T *p_queue;
|
|
75 ARIB_STD_B25_BUFFER *qbuf;
|
|
76 off_t len;
|
|
77 } stream;
|
|
78 } detail;
|
|
79 };
|
|
80
|
|
81 static inline void
|
|
82 set_info_file (struct File_Info *info, const size_t length,
|
|
83 const char *content_type)
|
|
84 {
|
|
85 info->file_length = length;
|
|
86 info->last_modified = 0;
|
|
87 info->is_directory = 0;
|
|
88 info->is_readable = 1;
|
|
89 info->content_type = ixmlCloneDOMString (content_type);
|
|
90 }
|
|
91
|
|
92 //#define STREAM_LOCATION "/web/stream.ts"
|
|
93 static int
|
|
94 http_get_info (const char *filename, struct File_Info *info)
|
|
95 {
|
|
96 extern struct ushare_t *ut;
|
|
97 struct upnp_entry_t *entry = NULL;
|
|
98 struct stat st;
|
|
99 int upnp_id = 0;
|
|
100 char *content_type = NULL;
|
|
101 char *protocol = NULL;
|
|
102 extern thread_data *gp_tdata;
|
|
103 thread_data *tdata = gp_tdata;
|
|
104
|
|
105 if (!filename || !info)
|
|
106 return -1;
|
|
107
|
|
108 log_verbose ("http_get_info, filename : %s\n", filename);
|
|
109
|
|
110 upnp_id = atoi (strrchr (filename, '/') + 1);
|
|
111 entry = upnp_get_entry (ut, upnp_id);
|
|
112
|
|
113 #if 0
|
|
114 if ( (!strcmp (filename, STREAM_LOCATION)) ||
|
|
115 ( (entry->fullpath != NULL) &&
|
|
116 ( !strcmp(entry->fullpath, STREAM_LOCATION))) ) {
|
|
117 #endif
|
|
118 if (!strcmp (filename, STREAM_LOCATION)) {
|
|
119 log_verbose ("http_get_info, stream location found.\n");
|
|
120 info->is_readable = 1;
|
|
121 info->file_length = 100*1024*1024;
|
|
122 info->file_length = info->file_length - (info->file_length % LENGTH_PACKET);
|
|
123 info->last_modified = time(NULL);
|
|
124 info->is_directory = 0;
|
|
125 info->content_type = ixmlCloneDOMString ("video/mpeg");
|
|
126 return 0;
|
|
127 }
|
|
128 if (!strcmp (filename, CDS_LOCATION))
|
|
129 {
|
|
130 set_info_file (info, CDS_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE);
|
|
131 return 0;
|
|
132 }
|
|
133
|
|
134 if (!strcmp (filename, CMS_LOCATION))
|
|
135 {
|
|
136 set_info_file (info, CMS_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE);
|
|
137 return 0;
|
|
138 }
|
|
139
|
|
140 if (!strcmp (filename, MSR_LOCATION))
|
|
141 {
|
|
142 set_info_file (info, MSR_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE);
|
|
143 return 0;
|
|
144 }
|
|
145
|
|
146 if (ut->use_presentation && !strcmp (filename, USHARE_PRESENTATION_PAGE))
|
|
147 {
|
|
148 if (build_presentation_page (ut) < 0)
|
|
149 return -1;
|
|
150
|
|
151 set_info_file (info, ut->presentation->len, PRESENTATION_PAGE_CONTENT_TYPE);
|
|
152 return 0;
|
|
153 }
|
|
154
|
|
155 if (ut->use_presentation && !strncmp (filename, USHARE_CGI, strlen (USHARE_CGI)))
|
|
156 {
|
|
157 if (process_cgi (ut, (char *) (filename + strlen (USHARE_CGI) + 1)) < 0)
|
|
158 return -1;
|
|
159
|
|
160 set_info_file (info, ut->presentation->len, PRESENTATION_PAGE_CONTENT_TYPE);
|
|
161 return 0;
|
|
162 }
|
|
163
|
|
164 if (!entry)
|
|
165 return -1;
|
|
166 log_verbose ("http_get_info, entry found.\n");
|
|
167
|
|
168 if (!entry->fullpath)
|
|
169 return -1;
|
|
170
|
|
171 #if 0
|
|
172 if (stat (entry->fullpath, &st) < 0)
|
|
173 return -1;
|
|
174
|
|
175 if (access (entry->fullpath, R_OK) < 0)
|
|
176 {
|
|
177 if (errno != EACCES) {
|
|
178 log_verbose ("http_get_info, access() error.\n");
|
|
179 return -1;
|
|
180 }
|
|
181 info->is_readable = 0;
|
|
182 }
|
|
183 else
|
|
184 info->is_readable = 1;
|
|
185
|
|
186 /* file exist and can be read */
|
|
187 info->file_length = st.st_size;
|
|
188 info->last_modified = st.st_mtime;
|
|
189 info->is_directory = S_ISDIR (st.st_mode);
|
|
190 #endif
|
|
191
|
|
192 info->is_readable = 1;
|
|
193 info->file_length = 100*1024*1024;
|
|
194 info->file_length = info->file_length - (info->file_length % LENGTH_PACKET);
|
|
195 info->last_modified = time(NULL);
|
|
196 info->is_directory = 0;
|
|
197
|
|
198 protocol =
|
|
199 #ifdef HAVE_DLNA
|
|
200 entry->dlna_profile ?
|
|
201 dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
|
|
202 DLNA_ORG_PLAY_SPEED_NORMAL,
|
|
203 DLNA_ORG_CONVERSION_NONE,
|
|
204 DLNA_ORG_OPERATION_RANGE,
|
|
205 ut->dlna_flags, entry->dlna_profile) :
|
|
206 #endif /* HAVE_DLNA */
|
|
207 mime_get_protocol (entry->mime_type);
|
|
208
|
|
209 content_type =
|
|
210 strndup ((protocol + PROTOCOL_TYPE_PRE_SZ),
|
|
211 strlen (protocol + PROTOCOL_TYPE_PRE_SZ)
|
|
212 - PROTOCOL_TYPE_SUFF_SZ);
|
|
213 free (protocol);
|
|
214
|
|
215 if (content_type)
|
|
216 {
|
|
217 info->content_type = ixmlCloneDOMString (content_type);
|
|
218 free (content_type);
|
|
219 }
|
|
220 else
|
|
221 info->content_type = ixmlCloneDOMString ("");
|
|
222
|
|
223 return 0;
|
|
224 }
|
|
225
|
|
226 static UpnpWebFileHandle
|
|
227 get_file_memory (const char *fullpath, const char *description,
|
|
228 const size_t length)
|
|
229 {
|
|
230 struct web_file_t *file;
|
|
231
|
|
232 // log_verbose ("get_file_memory() description[%s]\n",
|
|
233 // description);
|
|
234 file = malloc (sizeof (struct web_file_t));
|
|
235 file->fullpath = strdup (fullpath);
|
|
236 file->pos = 0;
|
|
237 file->type = FILE_MEMORY;
|
|
238 file->detail.memory.contents = strdup (description);
|
|
239 if ( file->detail.memory.contents == NULL ) {
|
|
240 log_verbose ("get_file_memory() null\n");
|
|
241 }
|
|
242 file->detail.memory.len = length;
|
|
243 // log_verbose ("get_file_memory() path[%s] contents[%s]\n",
|
|
244 // file->fullpath, file->detail.memory.contents);
|
|
245
|
|
246 return ((UpnpWebFileHandle) file);
|
|
247 }
|
|
248
|
|
249 /*
|
|
250 * 2. get_file_stream() $B$G$O!"(Bopen()$B;~$NA`:n(B($B%U%!%$%k>pJs$NIU2C(B)$B$NB>!"%U%!%$%k>pJs$H(B queue $B$NI3IU$1$r<B;\(B
|
|
251 * $B"((B get_file_memory() $B$+$i$N2~B$E@$KCe4c$7$F5-:\(B
|
|
252 * 2.1 tdata->streamer->mutex $B$r(B lock $B$7$F$+$i0J2<$N(Bloop$B=hM}$r9T$&(B(loop$B:GBg?t$O(B16$B$G7h$aBG$A(B)
|
|
253 * 2.1.1 tdata->streamer->stream_session[i] $B$,(B NULL $B$G$"$k$+3NG'(B
|
|
254 * 2.1.1.2 NULL$B$G$"$k>l9g(B
|
|
255 * 2.1.1.2.1 create_queue $B$r<B;\(B
|
|
256 * 2.1.1.3 NULL $B$G$O$J$$>l9g(B
|
|
257 * 2.1.1.2.1 $BF@$K$d$k$3$HL5$7(B
|
|
258 * 2.2 tdata->streamer->mutex $B$r(B unlock
|
|
259 */
|
|
260 static UpnpWebFileHandle
|
|
261 get_file_stream (const char *fullpath, thread_data *tdata)
|
|
262 {
|
|
263 #define STREAM_QUEUE (1024)
|
|
264 struct web_file_t *file;
|
|
265 int i = 0;
|
|
266 file = malloc (sizeof (struct web_file_t));
|
|
267
|
|
268 // 2.1 tdata->streamer->mutex $B$r(B lock $B$7$F$+$i0J2<$N(Bloop$B=hM}$r9T$&(B(loop$B:GBg?t$O(B16$B$G7h$aBG$A(B)
|
|
269 pthread_mutex_lock(&tdata->streamer->mutex);
|
|
270 for( i=0; i < STREAM_MAX; i++ ) {
|
|
271 if ( tdata->streamer->stream_session[i] == NULL ) {
|
|
272 // 2.1.1 tdata->streamer->stream_session[i] $B$,(B NULL $B$G$"$k>l9g(B
|
|
273 // 2.1.1.1 $B?75,%9%H%j!<%`MQ$N%-%e!<$r:n@.(B
|
|
274 file->detail.stream.id = tdata->streamer->stream_nr;
|
|
275 tdata->streamer->stream_session[i] = malloc(sizeof(session));
|
|
276 if ( tdata->streamer->stream_session[i] == NULL ) {
|
|
277 return NULL;
|
|
278 }
|
|
279 tdata->streamer->stream_session[i]->is_valid = true;
|
|
280 tdata->streamer->stream_session[i]->p_queue = create_queue(STREAM_QUEUE);
|
|
281 if ( tdata->streamer->stream_session[i]->p_queue == NULL ) {
|
|
282 log_error ("get_file_stream(): tdata->streamer->stream_session[%d].p_queue alloc failed.\n", i);
|
|
283 return NULL;
|
|
284 }
|
|
285 pthread_mutex_init(&tdata->streamer->stream_session[i]->p_queue->mutex, NULL);
|
|
286 pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_avail, NULL);
|
|
287 pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_used, NULL);
|
|
288 tdata->streamer->stream_nr++;
|
|
289 break;
|
|
290 } else {
|
|
291 // 2.1.2 tdata->streamer->stream_session[i] $B$,(B NULL $B$G$O$J$$(B
|
|
292 if ( ! tdata->streamer->stream_session[i]->is_valid ) {
|
|
293 // 2.1.2.1 tdata->streamer->stream_session[i] $B$,L$;HMQ>uBV$G$"$k>l9g(B
|
|
294 file->detail.stream.id = i;
|
|
295 //tdata->streamer->stream_nr++;
|
|
296 tdata->streamer->stream_session[i]->is_valid = true;
|
|
297 tdata->streamer->stream_session[i]->p_queue = create_queue(STREAM_QUEUE);
|
|
298 if ( tdata->streamer->stream_session[i]->p_queue != NULL ) {
|
|
299 pthread_mutex_init(&tdata->streamer->stream_session[i]->p_queue->mutex, NULL);
|
|
300 pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_avail, NULL);
|
|
301 pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_used, NULL);
|
|
302 } else {
|
|
303 log_error ("get_file_stream(): tdata->streamer->stream_session[%d].p_queue alloc failed.\n", i);
|
|
304 return NULL;
|
|
305 }
|
|
306 break;
|
|
307 }
|
|
308 }
|
|
309 }
|
|
310 pthread_mutex_unlock(&tdata->streamer->mutex);
|
|
311 if ( i == STREAM_MAX ) {
|
|
312 log_verbose ("get_file_stream(): cannot get new file_stream.\n");
|
|
313 }
|
|
314
|
|
315 file->detail.stream.p_queue = tdata->streamer->stream_session[i]->p_queue;
|
|
316 file->fullpath = strdup (fullpath);
|
|
317 file->pos = 0;
|
|
318 file->type = FILE_STREAM;
|
|
319 file->detail.stream.len = 100*1024*1024; //$B%U%!%$%k%5%$%:(B($B;DO?2h;~4V(Bx$B%S%C%H%l!<%H(B)
|
|
320 file->detail.stream.len = file->detail.stream.len - (file->detail.stream.len % LENGTH_PACKET);
|
|
321 file->detail.stream.qbuf = stream_dequeue(file->detail.stream.p_queue);
|
|
322 if ( file->detail.stream.qbuf == NULL ) {
|
|
323 log_error ("get_file_stream(): stream_dequeue error.\n");
|
|
324 return NULL;
|
|
325 }
|
|
326 log_verbose ("get_file_stream(): finish.\n");
|
|
327
|
|
328 return ((UpnpWebFileHandle) file);
|
|
329 }
|
|
330
|
|
331 static UpnpWebFileHandle
|
|
332 http_open (const char *filename, enum UpnpOpenFileMode mode)
|
|
333 {
|
|
334 extern struct ushare_t *ut;
|
|
335 struct upnp_entry_t *entry = NULL;
|
|
336 struct web_file_t *file;
|
|
337 int fd, upnp_id = 0;
|
|
338 extern thread_data *gp_tdata;
|
|
339 thread_data *tdata = gp_tdata;
|
|
340
|
|
341 if (!filename)
|
|
342 return NULL;
|
|
343
|
|
344 if (mode != UPNP_READ)
|
|
345 return NULL;
|
|
346
|
|
347 if (!strcmp (filename, CDS_LOCATION))
|
|
348 return get_file_memory (CDS_LOCATION, CDS_DESCRIPTION, CDS_DESCRIPTION_LEN);
|
|
349
|
|
350 if (!strcmp (filename, CMS_LOCATION))
|
|
351 return get_file_memory (CMS_LOCATION, CMS_DESCRIPTION, CMS_DESCRIPTION_LEN);
|
|
352
|
|
353 if (!strcmp (filename, MSR_LOCATION))
|
|
354 return get_file_memory (MSR_LOCATION, MSR_DESCRIPTION, MSR_DESCRIPTION_LEN);
|
|
355
|
|
356 if (ut->use_presentation && ( !strcmp (filename, USHARE_PRESENTATION_PAGE)
|
|
357 || !strncmp (filename, USHARE_CGI, strlen (USHARE_CGI))))
|
|
358 return get_file_memory (USHARE_PRESENTATION_PAGE, ut->presentation->buf,
|
|
359 ut->presentation->len);
|
|
360
|
|
361 upnp_id = atoi (strrchr (filename, '/') + 1);
|
|
362 entry = upnp_get_entry (ut, upnp_id);
|
|
363 if (!entry)
|
|
364 return NULL;
|
|
365
|
|
366 if (!entry->fullpath)
|
|
367 return NULL;
|
|
368
|
|
369 /*
|
|
370 * 1. http_open() $B$G$O(B entry $B$,%9%H%j!<%`:F@8MQ$N$b$N$G$"$k>l9g$K!"(B
|
|
371 * get_file_stream()$B$r8F$S=P$7%O%s%I%i$rJV5Q$9$k(B
|
|
372 */
|
|
373 log_verbose ("Fullpath : %s\n", entry->fullpath);
|
|
374 if (!strcmp (entry->fullpath, STREAM_LOCATION))
|
|
375 return get_file_stream (STREAM_LOCATION, tdata);
|
|
376
|
|
377 fd = open (entry->fullpath, O_RDONLY | O_NONBLOCK | O_SYNC | O_NDELAY);
|
|
378 if (fd < 0)
|
|
379 return NULL;
|
|
380
|
|
381 file = malloc (sizeof (struct web_file_t));
|
|
382 file->fullpath = strdup (entry->fullpath);
|
|
383 file->pos = 0;
|
|
384 file->type = FILE_LOCAL;
|
|
385 file->detail.local.entry = entry;
|
|
386 file->detail.local.fd = fd;
|
|
387
|
|
388 return ((UpnpWebFileHandle) file);
|
|
389 }
|
|
390
|
|
391 static int
|
|
392 http_read (UpnpWebFileHandle fh, char *buf, size_t buflen)
|
|
393 {
|
|
394 struct web_file_t *file = (struct web_file_t *) fh;
|
|
395 ssize_t len = -1;
|
|
396 extern thread_data *gp_tdata;
|
|
397 thread_data *tdata = gp_tdata;
|
|
398
|
|
399 //log_verbose ("http_read file:[%s]\n", file->fullpath);
|
|
400
|
|
401 if (!file)
|
|
402 return -1;
|
|
403
|
|
404 switch (file->type)
|
|
405 {
|
|
406 case FILE_LOCAL:
|
|
407 log_verbose ("Read local file.\n");
|
|
408 len = read (file->detail.local.fd, buf, buflen);
|
|
409 break;
|
|
410 case FILE_MEMORY:
|
|
411 log_verbose ("Read file from memory.\n");
|
|
412 len = (size_t) MIN (buflen, file->detail.memory.len - file->pos);
|
|
413 memcpy (buf, file->detail.memory.contents + file->pos, (size_t) len);
|
|
414 break;
|
|
415 case FILE_STREAM:
|
|
416 //log_verbose ("Read file from stream.\n");
|
|
417 if ( file->detail.stream.qbuf->size <= file->pos ) {
|
|
418 free(file->detail.stream.qbuf->data);
|
|
419 file->detail.stream.qbuf->data = NULL;
|
|
420 free(file->detail.stream.qbuf);
|
|
421 file->detail.stream.qbuf = NULL;
|
|
422 file->detail.stream.qbuf = stream_dequeue(file->detail.stream.p_queue);
|
|
423 file->pos = 0;
|
|
424 }
|
|
425 if ( file->detail.stream.qbuf == NULL ) {
|
|
426 log_verbose ("http_read stream_dequeue error NULL\n");
|
|
427 return -1;
|
|
428 }
|
|
429 len = (size_t) MIN (buflen, file->detail.stream.qbuf->size - file->pos);
|
|
430 memcpy (buf, file->detail.stream.qbuf->data + file->pos, (size_t) len);
|
|
431 break;
|
|
432 default:
|
|
433 log_verbose ("Unknown file type.\n");
|
|
434 break;
|
|
435 }
|
|
436
|
|
437 if (len >= 0)
|
|
438 file->pos += len;
|
|
439
|
|
440 //log_verbose ("Read %zd bytes.\n", len);
|
|
441
|
|
442 return len;
|
|
443 }
|
|
444
|
|
445 static int
|
|
446 http_write (UpnpWebFileHandle fh __attribute__((unused)),
|
|
447 char *buf __attribute__((unused)),
|
|
448 size_t buflen __attribute__((unused)))
|
|
449 {
|
|
450 log_verbose ("http write\n");
|
|
451
|
|
452 return 0;
|
|
453 }
|
|
454
|
|
455 static int
|
|
456 http_seek (UpnpWebFileHandle fh, off_t offset, int origin)
|
|
457 {
|
|
458 struct web_file_t *file = (struct web_file_t *) fh;
|
|
459 off_t newpos = -1;
|
|
460
|
|
461 log_verbose ("http_seek\n");
|
|
462
|
|
463 if (!file)
|
|
464 return -1;
|
|
465
|
|
466 switch (origin)
|
|
467 {
|
|
468 case SEEK_SET:
|
|
469 log_verbose ("Attempting to seek to %lld (was at %lld) in %s\n",
|
|
470 offset, file->pos, file->fullpath);
|
|
471 newpos = offset;
|
|
472 break;
|
|
473 case SEEK_CUR:
|
|
474 log_verbose ("Attempting to seek by %lld from %lld in %s\n",
|
|
475 offset, file->pos, file->fullpath);
|
|
476 newpos = file->pos + offset;
|
|
477 break;
|
|
478 case SEEK_END:
|
|
479 log_verbose ("Attempting to seek by %lld from end (was at %lld) in %s\n",
|
|
480 offset, file->pos, file->fullpath);
|
|
481
|
|
482 if (file->type == FILE_LOCAL)
|
|
483 {
|
|
484 struct stat sb;
|
|
485 if (stat (file->fullpath, &sb) < 0)
|
|
486 {
|
|
487 log_verbose ("%s: cannot stat: %s\n",
|
|
488 file->fullpath, strerror (errno));
|
|
489 return -1;
|
|
490 }
|
|
491 newpos = sb.st_size + offset;
|
|
492 }
|
|
493 else if (file->type == FILE_MEMORY)
|
|
494 newpos = file->detail.memory.len + offset;
|
|
495 break;
|
|
496 }
|
|
497
|
|
498 switch (file->type)
|
|
499 {
|
|
500 case FILE_LOCAL:
|
|
501 /* Just make sure we cannot seek before start of file. */
|
|
502 if (newpos < 0)
|
|
503 {
|
|
504 log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (EINVAL));
|
|
505 return -1;
|
|
506 }
|
|
507
|
|
508 /* Don't seek with origin as specified above, as file may have
|
|
509 changed in size since our last stat. */
|
|
510 if (lseek (file->detail.local.fd, newpos, SEEK_SET) == -1)
|
|
511 {
|
|
512 log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (errno));
|
|
513 return -1;
|
|
514 }
|
|
515 break;
|
|
516 case FILE_MEMORY:
|
|
517 if (newpos < 0 || newpos > file->detail.memory.len)
|
|
518 {
|
|
519 log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (EINVAL));
|
|
520 return -1;
|
|
521 }
|
|
522 break;
|
|
523 case FILE_STREAM:
|
|
524 log_verbose ("%s: cannot seek: %s\n", file->fullpath, "STREAM");
|
|
525 newpos = file->pos;
|
|
526 break;
|
|
527 }
|
|
528
|
|
529 file->pos = newpos;
|
|
530
|
|
531 return 0;
|
|
532 }
|
|
533
|
|
534 static int
|
|
535 http_close (UpnpWebFileHandle fh)
|
|
536 {
|
|
537 struct web_file_t *file = (struct web_file_t *) fh;
|
|
538 extern thread_data *gp_tdata;
|
|
539 thread_data *tdata = gp_tdata;
|
|
540 STREAM_QUEUE_T *p_queue;
|
|
541 int i;
|
|
542 int id = 0;
|
|
543 int j;
|
|
544
|
|
545 if (!file)
|
|
546 return -1;
|
|
547
|
|
548 switch (file->type)
|
|
549 {
|
|
550 case FILE_LOCAL:
|
|
551 close (file->detail.local.fd);
|
|
552 break;
|
|
553 case FILE_MEMORY:
|
|
554 /* no close operation */
|
|
555 if (file->detail.memory.contents)
|
|
556 free (file->detail.memory.contents);
|
|
557 break;
|
|
558 case FILE_STREAM:
|
|
559 p_queue = file->detail.stream.p_queue;
|
|
560 if ( p_queue != NULL) {
|
|
561 id = file->detail.stream.id;
|
|
562 pthread_mutex_lock(&tdata->streamer->mutex);
|
|
563 tdata->streamer->stream_session[id]->is_valid = false;
|
|
564 pthread_mutex_unlock(&tdata->streamer->mutex);
|
|
565 pthread_mutex_lock(&p_queue->mutex);
|
|
566 while ( 0 < p_queue->num_used ) {
|
|
567 free(p_queue->buffer[p_queue->out]->data);
|
|
568 p_queue->buffer[p_queue->out]->data = NULL;
|
|
569 free(p_queue->buffer[p_queue->out]);
|
|
570 p_queue->buffer[p_queue->out] = NULL;
|
|
571
|
|
572 p_queue->out++;
|
|
573 p_queue->out %= p_queue->size;
|
|
574 p_queue->num_avail++;
|
|
575 p_queue->num_used--;
|
|
576 }
|
|
577 pthread_mutex_unlock(&p_queue->mutex);
|
|
578 destroy_stream_queue(p_queue);
|
|
579 tdata->streamer->stream_session[id]->p_queue = NULL;
|
|
580 }
|
|
581 break;
|
|
582 default:
|
|
583 log_verbose ("Unknown file type.\n");
|
|
584 break;
|
|
585 }
|
|
586
|
|
587 if (file->fullpath)
|
|
588 free (file->fullpath);
|
|
589 free (file);
|
|
590
|
|
591 return 0;
|
|
592 }
|
|
593
|
|
594 struct UpnpVirtualDirCallbacks virtual_dir_callbacks =
|
|
595 {
|
|
596 http_get_info,
|
|
597 http_open,
|
|
598 http_read,
|
|
599 http_write,
|
|
600 http_seek,
|
|
601 http_close
|
|
602 };
|