/* Copyright (C) 2003 Commonwealth Scientific and Industrial Research Organisation (CSIRO) Australia Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of CSIRO Australia nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * plugin_cmml.c * * Shane Stephens * * oggplay_* functions in this file dodgily copied from oggplay_tcp_reader.c * because poor widdle windows can't handle even mildy complicated cases of * #include dependencies. Honestly, I wonder if that child will ever grow * up normal. */ #include #include #include #include #ifdef WIN32 #include #include #include #define snprintf _snprintf #else #include #include #include #include #include #include #include #include #include #include #endif #include #include "plugin_cmml.h" #define PRINT_BUFFER(s,m) \ printf("%s: in_mem: %d size: %d pos: %d stored: %d\n", \ s, m->amount_in_memory, m->buffer_size, \ m->current_position, m->stored_offset); #ifndef WIN32 typedef int SOCKET; #define INVALID_SOCKET -1 #endif #define START_TIMEOUT(ref) \ (ref) = oggplay_sys_time_in_ms() #ifdef WIN32 #define CHECK_ERROR(error) \ (WSAGetLastError() == WSA##error) #else #define CHECK_ERROR(error) \ (errno == error) #endif #define RETURN_ON_TIMEOUT_OR_CONTINUE(ref) \ if (oggplay_sys_time_in_ms() - (ref) > 500) { \ return E_OGGPLAY_TIMEOUT; \ } else { \ oggplay_millisleep(10); \ continue; \ } static SOCKET oggplay_create_socket() { SOCKET sock; #ifdef WIN32 WORD wVersionRequested; WSADATA wsaData; #ifdef HAVE_WINSOCK2 wVersionRequested = MAKEWORD(2,2); #else wVersionRequested = MAKEWORD(1,1); #endif if (WSAStartup(wVersionRequested, &wsaData) == -1) { printf("Socket open error\n"); return INVALID_SOCKET; } if (wsaData.wVersion != wVersionRequested) { printf("Incorrect winsock version [%d]\n", wVersionRequested); WSACleanup(); return INVALID_SOCKET; } #endif sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { printf("Could not create socket\n"); #ifdef WIN32 WSACleanup(); #endif return INVALID_SOCKET; } return sock; } /* * this function guarantees it will return malloced versions of host and * path */ static void oggplay_hostname_and_path(char *location, char *proxy, int proxy_port, char **host, int *port, char **path) { char * colon; char * slash; char * end_of_host; /* if we have a proxy installed this is all dead simple */ if (proxy != NULL) { *host = strdup(proxy); *port = proxy_port; *path = strdup(location); return; } /* find start_pos */ if (strncmp(location, "http://", 7) == 0) { location += 7; } colon = strchr(location, ':'); slash = strchr(location, '/'); /* * if both are null, then just set the simple defaults and return */ if (colon == NULL && slash == NULL) { *host = strdup(location); *port = 80; *path = strdup("/"); return; } /* * if there's a slash and it's before colon, there's no port. Hence, after * this code, the only time that there's a port is when colon is non-NULL */ if (slash != NULL && colon > slash) { colon = NULL; } /* * we might as well extract the port now. We can also work out where * the end of the hostname is, as it's either the colon (if there's a port) * or the slash (if there's no port) */ if (colon != NULL) { *port = (int)strtol(colon+1, NULL, 10); end_of_host = colon; } else { *port = 80; end_of_host = slash; } *host = strdup(location); (*host)[end_of_host - location] = '\0'; if (slash == NULL) { *path = strdup("/"); return; } *path = strdup(slash); } static OggPlayErrorCode oggplay_connect_to_host(SOCKET socket, struct sockaddr *addr) { ogg_int64_t time_ref; START_TIMEOUT(time_ref); while (connect(socket, addr, sizeof(struct sockaddr_in)) < 0) { if ( CHECK_ERROR(EINPROGRESS) || CHECK_ERROR(EALREADY) #ifdef WIN32 /* see http://msdn2.microsoft.com/en-us/library/ms737625.aspx */ || CHECK_ERROR(EWOULDBLOCK) || CHECK_ERROR(EINVAL) #endif ) { RETURN_ON_TIMEOUT_OR_CONTINUE(time_ref); } else if CHECK_ERROR(EISCONN) { break; } printf("Could not connect to host; error code is %d\n", errno); return CHECK_ERROR(ETIMEDOUT) ? E_OGGPLAY_TIMEOUT : E_OGGPLAY_SOCKET_ERROR; } return E_OGGPLAY_OK; } static char * get_all_cmml(char *location, char *proxy, int proxy_port) { char * host; char * path; int port; char http_request_header[1024]; SOCKET socket = oggplay_create_socket(); struct sockaddr_in addr; struct hostent * he; int r; char * buffer = NULL; int n_chunks = 0; int bytes_read = 0; if (socket == INVALID_SOCKET) return NULL; oggplay_hostname_and_path(location, proxy, proxy_port, &host, &port, &path); snprintf(http_request_header, 1024, "GET %s HTTP/1.0\n" "Host: %s\n" "User-Agent: AnnodexFirefoxPlugin/0.1\n" "Accept: text/x-cmml\n" "Connection: Keep-Alive\n\n", path, host); he = gethostbyname(host); free(host); free(path); if (he == NULL) { return NULL; } memcpy(&addr.sin_addr.s_addr, he->h_addr, he->h_length); addr.sin_family = AF_INET; addr.sin_port = htons(port); r = oggplay_connect_to_host(socket, (struct sockaddr *)&addr); if (r != E_OGGPLAY_OK) return NULL; #ifdef WIN32 r = send(socket, http_request_header, strlen(http_request_header), 0); #else r = write(socket, http_request_header, strlen(http_request_header)); #endif if (r < 0) return NULL; #define CHUNK_SIZE 16394 while (1) { n_chunks ++; buffer = realloc(buffer, CHUNK_SIZE * n_chunks); #ifdef WIN32 r = recv(socket, buffer + bytes_read, CHUNK_SIZE, 0); #else r = read(socket, buffer + bytes_read, CHUNK_SIZE); #endif if (r < 0) { free(buffer); #ifdef WIN32 closesocket(socket); #else close(socket); #endif return NULL; } if (r == 0) { buffer[bytes_read] = '\0'; #ifdef WIN32 closesocket(socket); #else close(socket); #endif return buffer; } bytes_read += r; } } typedef struct { char * file; char * proxy; int proxyPort; nsPluginInstance * plugin; } CmmlThreadInfo; static void * cmml_thread(void *handle) { CmmlThreadInfo * info = (CmmlThreadInfo *)handle; char * cmml; cmml = get_all_cmml(info->file, info->proxy, info->proxyPort); if (cmml == NULL) { return NULL; } onCMMLData(info->plugin, &cmml, 1, 1); return NULL; } void start_cmml_thread(nsPluginInstance *i, const char *movie, char *proxy, int proxyPort) { CmmlThreadInfo * info; //char * mdot = strrchr(movie, '.'); char * fname;// = malloc(mdot - movie + 6); #ifdef WIN32 HANDLE thread; int tid; #else pthread_t thread; #endif fname = strdup(movie); info = malloc(sizeof(CmmlThreadInfo)); info->file = fname; info->proxy = proxy; info->proxyPort = proxyPort; info->plugin = i; #ifdef WIN32 thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)cmml_thread, (LPVOID)info, 0, &tid); #else pthread_create(&thread, NULL, cmml_thread, (void *)info); #endif }