Mercurial > pt1.oyama
diff src/http.c @ 125:e413158cae13
Add ushare project files.
author | naoyan@johnstown.minaminoshima.org |
---|---|
date | Sun, 03 Oct 2010 11:35:19 +0900 |
parents | |
children | 5dcaf3785ebe |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http.c Sun Oct 03 11:35:19 2010 +0900 @@ -0,0 +1,602 @@ +/* -*- tab-width: 4; indent-tabs-mode: nil -*- */ +/* vim: set ts=4 sts=4 sw=4 expandtab number : */ +/* + * http.c : GeeXboX uShare Web Server handler. + * Originally developped for the GeeXboX project. + * Parts of the code are originated from GMediaServer from Oskar Liljeblad. + * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include <upnp/upnp.h> +#include <upnp/upnptools.h> + +#include "services.h" +#include "cds.h" +#include "cms.h" +#include "msr.h" +#include "metadata.h" +#include "http.h" +#include "minmax.h" +#include "trace.h" +#include "presentation.h" +#include "osdep.h" +#include "mime.h" +#include "recpt1.h" +#include "tssplitter_lite.h" + +#define PROTOCOL_TYPE_PRE_SZ 11 /* for the str length of "http-get:*:" */ +#define PROTOCOL_TYPE_SUFF_SZ 2 /* for the str length of ":*" */ + +extern thread_data tdata; + +struct web_file_t { + char *fullpath; + off_t pos; + enum { + FILE_LOCAL, + FILE_MEMORY, + FILE_STREAM + } type; + union { + struct { + int fd; + struct upnp_entry_t *entry; + } local; + struct { + char *contents; + off_t len; + } memory; + struct { + int id; + STREAM_QUEUE_T *p_queue; + ARIB_STD_B25_BUFFER *qbuf; + off_t len; + } stream; + } detail; +}; + + static inline void +set_info_file (struct File_Info *info, const size_t length, + const char *content_type) +{ + info->file_length = length; + info->last_modified = 0; + info->is_directory = 0; + info->is_readable = 1; + info->content_type = ixmlCloneDOMString (content_type); +} + +//#define STREAM_LOCATION "/web/stream.ts" + static int +http_get_info (const char *filename, struct File_Info *info) +{ + extern struct ushare_t *ut; + struct upnp_entry_t *entry = NULL; + struct stat st; + int upnp_id = 0; + char *content_type = NULL; + char *protocol = NULL; + extern thread_data *gp_tdata; + thread_data *tdata = gp_tdata; + + if (!filename || !info) + return -1; + + log_verbose ("http_get_info, filename : %s\n", filename); + + upnp_id = atoi (strrchr (filename, '/') + 1); + entry = upnp_get_entry (ut, upnp_id); + +#if 0 + if ( (!strcmp (filename, STREAM_LOCATION)) || + ( (entry->fullpath != NULL) && + ( !strcmp(entry->fullpath, STREAM_LOCATION))) ) { +#endif + if (!strcmp (filename, STREAM_LOCATION)) { + log_verbose ("http_get_info, stream location found.\n"); + info->is_readable = 1; + info->file_length = 100*1024*1024; + info->file_length = info->file_length - (info->file_length % LENGTH_PACKET); + info->last_modified = time(NULL); + info->is_directory = 0; + info->content_type = ixmlCloneDOMString ("video/mpeg"); + return 0; + } + if (!strcmp (filename, CDS_LOCATION)) + { + set_info_file (info, CDS_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE); + return 0; + } + + if (!strcmp (filename, CMS_LOCATION)) + { + set_info_file (info, CMS_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE); + return 0; + } + + if (!strcmp (filename, MSR_LOCATION)) + { + set_info_file (info, MSR_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE); + return 0; + } + + if (ut->use_presentation && !strcmp (filename, USHARE_PRESENTATION_PAGE)) + { + if (build_presentation_page (ut) < 0) + return -1; + + set_info_file (info, ut->presentation->len, PRESENTATION_PAGE_CONTENT_TYPE); + return 0; + } + + if (ut->use_presentation && !strncmp (filename, USHARE_CGI, strlen (USHARE_CGI))) + { + if (process_cgi (ut, (char *) (filename + strlen (USHARE_CGI) + 1)) < 0) + return -1; + + set_info_file (info, ut->presentation->len, PRESENTATION_PAGE_CONTENT_TYPE); + return 0; + } + + if (!entry) + return -1; + log_verbose ("http_get_info, entry found.\n"); + + if (!entry->fullpath) + return -1; + +#if 0 + if (stat (entry->fullpath, &st) < 0) + return -1; + + if (access (entry->fullpath, R_OK) < 0) + { + if (errno != EACCES) { + log_verbose ("http_get_info, access() error.\n"); + return -1; + } + info->is_readable = 0; + } + else + info->is_readable = 1; + + /* file exist and can be read */ + info->file_length = st.st_size; + info->last_modified = st.st_mtime; + info->is_directory = S_ISDIR (st.st_mode); +#endif + + info->is_readable = 1; + info->file_length = 100*1024*1024; + info->file_length = info->file_length - (info->file_length % LENGTH_PACKET); + info->last_modified = time(NULL); + info->is_directory = 0; + + protocol = +#ifdef HAVE_DLNA + entry->dlna_profile ? + dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP, + DLNA_ORG_PLAY_SPEED_NORMAL, + DLNA_ORG_CONVERSION_NONE, + DLNA_ORG_OPERATION_RANGE, + ut->dlna_flags, entry->dlna_profile) : +#endif /* HAVE_DLNA */ + mime_get_protocol (entry->mime_type); + + content_type = + strndup ((protocol + PROTOCOL_TYPE_PRE_SZ), + strlen (protocol + PROTOCOL_TYPE_PRE_SZ) + - PROTOCOL_TYPE_SUFF_SZ); + free (protocol); + + if (content_type) + { + info->content_type = ixmlCloneDOMString (content_type); + free (content_type); + } + else + info->content_type = ixmlCloneDOMString (""); + + return 0; +} + + static UpnpWebFileHandle +get_file_memory (const char *fullpath, const char *description, + const size_t length) +{ + struct web_file_t *file; + +// log_verbose ("get_file_memory() description[%s]\n", +// description); + file = malloc (sizeof (struct web_file_t)); + file->fullpath = strdup (fullpath); + file->pos = 0; + file->type = FILE_MEMORY; + file->detail.memory.contents = strdup (description); + if ( file->detail.memory.contents == NULL ) { + log_verbose ("get_file_memory() null\n"); + } + file->detail.memory.len = length; +// log_verbose ("get_file_memory() path[%s] contents[%s]\n", +// file->fullpath, file->detail.memory.contents); + + return ((UpnpWebFileHandle) file); +} + +/* + * 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 + * $B"((B get_file_memory() $B$+$i$N2~B$E@$KCe4c$7$F5-:\(B + * 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) + * 2.1.1 tdata->streamer->stream_session[i] $B$,(B NULL $B$G$"$k$+3NG'(B + * 2.1.1.2 NULL$B$G$"$k>l9g(B + * 2.1.1.2.1 create_queue $B$r<B;\(B + * 2.1.1.3 NULL $B$G$O$J$$>l9g(B + * 2.1.1.2.1 $BF@$K$d$k$3$HL5$7(B + * 2.2 tdata->streamer->mutex $B$r(B unlock + */ + static UpnpWebFileHandle +get_file_stream (const char *fullpath, thread_data *tdata) +{ +#define STREAM_QUEUE (1024) + struct web_file_t *file; + int i = 0; + file = malloc (sizeof (struct web_file_t)); + + // 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) + pthread_mutex_lock(&tdata->streamer->mutex); + for( i=0; i < STREAM_MAX; i++ ) { + if ( tdata->streamer->stream_session[i] == NULL ) { + // 2.1.1 tdata->streamer->stream_session[i] $B$,(B NULL $B$G$"$k>l9g(B + // 2.1.1.1 $B?75,%9%H%j!<%`MQ$N%-%e!<$r:n@.(B + file->detail.stream.id = tdata->streamer->stream_nr; + tdata->streamer->stream_session[i] = malloc(sizeof(session)); + if ( tdata->streamer->stream_session[i] == NULL ) { + return NULL; + } + tdata->streamer->stream_session[i]->is_valid = true; + tdata->streamer->stream_session[i]->p_queue = create_queue(STREAM_QUEUE); + if ( tdata->streamer->stream_session[i]->p_queue == NULL ) { + log_error ("get_file_stream(): tdata->streamer->stream_session[%d].p_queue alloc failed.\n", i); + return NULL; + } + pthread_mutex_init(&tdata->streamer->stream_session[i]->p_queue->mutex, NULL); + pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_avail, NULL); + pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_used, NULL); + tdata->streamer->stream_nr++; + break; + } else { + // 2.1.2 tdata->streamer->stream_session[i] $B$,(B NULL $B$G$O$J$$(B + if ( ! tdata->streamer->stream_session[i]->is_valid ) { + // 2.1.2.1 tdata->streamer->stream_session[i] $B$,L$;HMQ>uBV$G$"$k>l9g(B + file->detail.stream.id = i; + //tdata->streamer->stream_nr++; + tdata->streamer->stream_session[i]->is_valid = true; + tdata->streamer->stream_session[i]->p_queue = create_queue(STREAM_QUEUE); + if ( tdata->streamer->stream_session[i]->p_queue != NULL ) { + pthread_mutex_init(&tdata->streamer->stream_session[i]->p_queue->mutex, NULL); + pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_avail, NULL); + pthread_cond_init(&tdata->streamer->stream_session[i]->p_queue->cond_used, NULL); + } else { + log_error ("get_file_stream(): tdata->streamer->stream_session[%d].p_queue alloc failed.\n", i); + return NULL; + } + break; + } + } + } + pthread_mutex_unlock(&tdata->streamer->mutex); + if ( i == STREAM_MAX ) { + log_verbose ("get_file_stream(): cannot get new file_stream.\n"); + } + + file->detail.stream.p_queue = tdata->streamer->stream_session[i]->p_queue; + file->fullpath = strdup (fullpath); + file->pos = 0; + file->type = FILE_STREAM; + file->detail.stream.len = 100*1024*1024; //$B%U%!%$%k%5%$%:(B($B;DO?2h;~4V(Bx$B%S%C%H%l!<%H(B) + file->detail.stream.len = file->detail.stream.len - (file->detail.stream.len % LENGTH_PACKET); + file->detail.stream.qbuf = stream_dequeue(file->detail.stream.p_queue); + if ( file->detail.stream.qbuf == NULL ) { + log_error ("get_file_stream(): stream_dequeue error.\n"); + return NULL; + } + log_verbose ("get_file_stream(): finish.\n"); + + return ((UpnpWebFileHandle) file); +} + + static UpnpWebFileHandle +http_open (const char *filename, enum UpnpOpenFileMode mode) +{ + extern struct ushare_t *ut; + struct upnp_entry_t *entry = NULL; + struct web_file_t *file; + int fd, upnp_id = 0; + extern thread_data *gp_tdata; + thread_data *tdata = gp_tdata; + + if (!filename) + return NULL; + + if (mode != UPNP_READ) + return NULL; + + if (!strcmp (filename, CDS_LOCATION)) + return get_file_memory (CDS_LOCATION, CDS_DESCRIPTION, CDS_DESCRIPTION_LEN); + + if (!strcmp (filename, CMS_LOCATION)) + return get_file_memory (CMS_LOCATION, CMS_DESCRIPTION, CMS_DESCRIPTION_LEN); + + if (!strcmp (filename, MSR_LOCATION)) + return get_file_memory (MSR_LOCATION, MSR_DESCRIPTION, MSR_DESCRIPTION_LEN); + + if (ut->use_presentation && ( !strcmp (filename, USHARE_PRESENTATION_PAGE) + || !strncmp (filename, USHARE_CGI, strlen (USHARE_CGI)))) + return get_file_memory (USHARE_PRESENTATION_PAGE, ut->presentation->buf, + ut->presentation->len); + + upnp_id = atoi (strrchr (filename, '/') + 1); + entry = upnp_get_entry (ut, upnp_id); + if (!entry) + return NULL; + + if (!entry->fullpath) + return NULL; + + /* + * 1. http_open() $B$G$O(B entry $B$,%9%H%j!<%`:F@8MQ$N$b$N$G$"$k>l9g$K!"(B + * get_file_stream()$B$r8F$S=P$7%O%s%I%i$rJV5Q$9$k(B + */ + log_verbose ("Fullpath : %s\n", entry->fullpath); + if (!strcmp (entry->fullpath, STREAM_LOCATION)) + return get_file_stream (STREAM_LOCATION, tdata); + + fd = open (entry->fullpath, O_RDONLY | O_NONBLOCK | O_SYNC | O_NDELAY); + if (fd < 0) + return NULL; + + file = malloc (sizeof (struct web_file_t)); + file->fullpath = strdup (entry->fullpath); + file->pos = 0; + file->type = FILE_LOCAL; + file->detail.local.entry = entry; + file->detail.local.fd = fd; + + return ((UpnpWebFileHandle) file); +} + + static int +http_read (UpnpWebFileHandle fh, char *buf, size_t buflen) +{ + struct web_file_t *file = (struct web_file_t *) fh; + ssize_t len = -1; + extern thread_data *gp_tdata; + thread_data *tdata = gp_tdata; + + //log_verbose ("http_read file:[%s]\n", file->fullpath); + + if (!file) + return -1; + + switch (file->type) + { + case FILE_LOCAL: + log_verbose ("Read local file.\n"); + len = read (file->detail.local.fd, buf, buflen); + break; + case FILE_MEMORY: + log_verbose ("Read file from memory.\n"); + len = (size_t) MIN (buflen, file->detail.memory.len - file->pos); + memcpy (buf, file->detail.memory.contents + file->pos, (size_t) len); + break; + case FILE_STREAM: + //log_verbose ("Read file from stream.\n"); + if ( file->detail.stream.qbuf->size <= file->pos ) { + free(file->detail.stream.qbuf->data); + file->detail.stream.qbuf->data = NULL; + free(file->detail.stream.qbuf); + file->detail.stream.qbuf = NULL; + file->detail.stream.qbuf = stream_dequeue(file->detail.stream.p_queue); + file->pos = 0; + } + if ( file->detail.stream.qbuf == NULL ) { + log_verbose ("http_read stream_dequeue error NULL\n"); + return -1; + } + len = (size_t) MIN (buflen, file->detail.stream.qbuf->size - file->pos); + memcpy (buf, file->detail.stream.qbuf->data + file->pos, (size_t) len); + break; + default: + log_verbose ("Unknown file type.\n"); + break; + } + + if (len >= 0) + file->pos += len; + + //log_verbose ("Read %zd bytes.\n", len); + + return len; +} + + static int +http_write (UpnpWebFileHandle fh __attribute__((unused)), + char *buf __attribute__((unused)), + size_t buflen __attribute__((unused))) +{ + log_verbose ("http write\n"); + + return 0; +} + + static int +http_seek (UpnpWebFileHandle fh, off_t offset, int origin) +{ + struct web_file_t *file = (struct web_file_t *) fh; + off_t newpos = -1; + + log_verbose ("http_seek\n"); + + if (!file) + return -1; + + switch (origin) + { + case SEEK_SET: + log_verbose ("Attempting to seek to %lld (was at %lld) in %s\n", + offset, file->pos, file->fullpath); + newpos = offset; + break; + case SEEK_CUR: + log_verbose ("Attempting to seek by %lld from %lld in %s\n", + offset, file->pos, file->fullpath); + newpos = file->pos + offset; + break; + case SEEK_END: + log_verbose ("Attempting to seek by %lld from end (was at %lld) in %s\n", + offset, file->pos, file->fullpath); + + if (file->type == FILE_LOCAL) + { + struct stat sb; + if (stat (file->fullpath, &sb) < 0) + { + log_verbose ("%s: cannot stat: %s\n", + file->fullpath, strerror (errno)); + return -1; + } + newpos = sb.st_size + offset; + } + else if (file->type == FILE_MEMORY) + newpos = file->detail.memory.len + offset; + break; + } + + switch (file->type) + { + case FILE_LOCAL: + /* Just make sure we cannot seek before start of file. */ + if (newpos < 0) + { + log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (EINVAL)); + return -1; + } + + /* Don't seek with origin as specified above, as file may have + changed in size since our last stat. */ + if (lseek (file->detail.local.fd, newpos, SEEK_SET) == -1) + { + log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (errno)); + return -1; + } + break; + case FILE_MEMORY: + if (newpos < 0 || newpos > file->detail.memory.len) + { + log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (EINVAL)); + return -1; + } + break; + case FILE_STREAM: + log_verbose ("%s: cannot seek: %s\n", file->fullpath, "STREAM"); + newpos = file->pos; + break; + } + + file->pos = newpos; + + return 0; +} + + static int +http_close (UpnpWebFileHandle fh) +{ + struct web_file_t *file = (struct web_file_t *) fh; + extern thread_data *gp_tdata; + thread_data *tdata = gp_tdata; + STREAM_QUEUE_T *p_queue; + int i; + int id = 0; + int j; + + if (!file) + return -1; + + switch (file->type) + { + case FILE_LOCAL: + close (file->detail.local.fd); + break; + case FILE_MEMORY: + /* no close operation */ + if (file->detail.memory.contents) + free (file->detail.memory.contents); + break; + case FILE_STREAM: + p_queue = file->detail.stream.p_queue; + if ( p_queue != NULL) { + id = file->detail.stream.id; + pthread_mutex_lock(&tdata->streamer->mutex); + tdata->streamer->stream_session[id]->is_valid = false; + pthread_mutex_unlock(&tdata->streamer->mutex); + pthread_mutex_lock(&p_queue->mutex); + while ( 0 < p_queue->num_used ) { + free(p_queue->buffer[p_queue->out]->data); + p_queue->buffer[p_queue->out]->data = NULL; + free(p_queue->buffer[p_queue->out]); + p_queue->buffer[p_queue->out] = NULL; + + p_queue->out++; + p_queue->out %= p_queue->size; + p_queue->num_avail++; + p_queue->num_used--; + } + pthread_mutex_unlock(&p_queue->mutex); + destroy_stream_queue(p_queue); + tdata->streamer->stream_session[id]->p_queue = NULL; + } + break; + default: + log_verbose ("Unknown file type.\n"); + break; + } + + if (file->fullpath) + free (file->fullpath); + free (file); + + return 0; +} + +struct UpnpVirtualDirCallbacks virtual_dir_callbacks = +{ + http_get_info, + http_open, + http_read, + http_write, + http_seek, + http_close +};